Skip to content

Embedding & VectorStore

向量化与语义检索是 RAG 系统的核心基础设施,理解 Embedding 和 VectorStore 是构建知识库应用的关键。

什么是 Embedding?

Embedding(向量嵌入)是将文本转换为高维数值向量的过程,语义相似的文本在向量空间中距离更近:

"Java 虚拟线程"  → [0.23, -0.45, 0.67, ..., 0.12]  (1536 维)
"JVM 轻量级线程" → [0.25, -0.43, 0.65, ..., 0.11]  (语义相近,向量相近)
"Python 协程"   → [0.18, -0.38, 0.71, ..., 0.09]  (相关但不同)
"今天天气真好"   → [-0.12, 0.34, -0.23, ..., 0.56] (语义无关,向量差异大)

EmbeddingModel 接口

java
public interface EmbeddingModel extends Model<EmbeddingRequest, EmbeddingResponse> {

    // 单文本向量化
    float[] embed(String text);

    // 批量向量化(推荐,减少 API 调用次数)
    EmbeddingResponse call(EmbeddingRequest request);

    // 获取向量维度
    default int dimensions() {
        return embed("test").length;
    }
}

使用 DashScope Embedding

java
@Autowired
private EmbeddingModel embeddingModel;

// 单文本
float[] vector = embeddingModel.embed("Spring AI Alibaba 是什么?");
System.out.println("向量维度:" + vector.length); // 1536

// 批量(推荐)
EmbeddingRequest request = new EmbeddingRequest(
    List.of("文本1", "文本2", "文本3"),
    EmbeddingOptions.EMPTY
);
EmbeddingResponse response = embeddingModel.call(request);
List<Embedding> embeddings = response.getResults();

配置 Embedding 模型

yaml
spring:
  ai:
    dashscope:
      embedding:
        options:
          model: text-embedding-v3  # 推荐最新版本
模型维度特点
text-embedding-v31024/2048最新,效果最好
text-embedding-v21536稳定版本
text-embedding-async-v21536异步批量处理

VectorStore 接口

VectorStore 是向量数据库的统一抽象,屏蔽了不同数据库的实现差异:

java
public interface VectorStore {

    // 存储文档(自动向量化)
    void add(List<Document> documents);

    // 删除文档
    Optional<Boolean> delete(List<String> idList);

    // 相似度搜索
    List<Document> similaritySearch(String query);

    // 带过滤条件的搜索
    List<Document> similaritySearch(SearchRequest request);
}

Document 数据模型

java
// Document 是 VectorStore 的基本单元
Document doc = new Document(
    "Spring AI Alibaba 是阿里云推出的 Java AI 框架...",  // 内容
    Map.of(                                              // 元数据
        "source", "官方文档",
        "chapter", "概述",
        "version", "1.1.0",
        "url", "https://java2ai.com/docs/overview"
    )
);

// 访问文档属性
String id = doc.getId();           // 自动生成的 UUID
String content = doc.getContent(); // 文本内容
Map<String, Object> metadata = doc.getMetadata(); // 元数据
float[] embedding = doc.getEmbedding(); // 向量(存储后填充)

SearchRequest 高级搜索

java
// 基础搜索
List<Document> results = vectorStore.similaritySearch("什么是 RAG?");

// 高级搜索
SearchRequest request = SearchRequest.builder()
    .query("Spring AI 如何配置?")
    .topK(5)                          // 返回最相似的 5 条
    .similarityThreshold(0.75)        // 相似度阈值(0-1)
    .filterExpression("version == '1.1.0' && source == '官方文档'")
    .build();

List<Document> filtered = vectorStore.similaritySearch(request);

支持的 VectorStore 实现

1. SimpleVectorStore(开发/测试)

java
@Bean
public VectorStore vectorStore(EmbeddingModel embeddingModel) {
    return new SimpleVectorStore(embeddingModel);
    // 内存存储,重启丢失,仅用于开发测试
}

2. Redis VectorStore(生产推荐)

xml
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-redis-store-spring-boot-autoconfigure</artifactId>
</dependency>
yaml
spring:
  ai:
    vectorstore:
      redis:
        uri: redis://localhost:6379
        index: spring-ai-index
        prefix: "doc:"
        initialize-schema: true
java
// 自动装配,无需手动创建
@Autowired
private VectorStore vectorStore; // RedisVectorStore

3. Elasticsearch VectorStore

yaml
spring:
  elasticsearch:
    uris: http://localhost:9200
  ai:
    vectorstore:
      elasticsearch:
        index-name: spring-ai-docs
        dimensions: 1536
        similarity: cosine

4. PGVector(PostgreSQL)

yaml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/vectordb
  ai:
    vectorstore:
      pgvector:
        index-type: HNSW
        distance-type: COSINE_DISTANCE
        dimensions: 1536

5. 阿里云向量检索服务

yaml
spring:
  ai:
    vectorstore:
      dashvector:
        api-key: ${DASHVECTOR_API_KEY}
        endpoint: ${DASHVECTOR_ENDPOINT}
        collection-name: my-collection

文档处理流水线

在存入 VectorStore 之前,通常需要对文档进行预处理:

java
@Service
public class DocumentIngestionService {

    @Autowired
    private VectorStore vectorStore;

    @Autowired
    private TikaDocumentReader tikaReader;

    public void ingestDocument(Resource resource) {

        // 1. 读取文档(支持 PDF、Word、HTML 等)
        List<Document> rawDocs = new TikaDocumentReader(resource).get();

        // 2. 文本分块(Chunking)
        TextSplitter splitter = new TokenTextSplitter(
            512,   // chunk size(tokens)
            128,   // overlap(重叠 token 数,保持上下文连贯)
            5,     // min chunk size
            10000, // max chunk size
            true   // keep separator
        );
        List<Document> chunks = splitter.apply(rawDocs);

        // 3. 添加元数据
        chunks.forEach(doc -> {
            doc.getMetadata().put("ingested_at", LocalDateTime.now().toString());
            doc.getMetadata().put("source", resource.getFilename());
        });

        // 4. 向量化并存储(自动调用 EmbeddingModel)
        vectorStore.add(chunks);

        log.info("成功导入 {} 个文档块", chunks.size());
    }
}

相似度算法

VectorStore 支持多种相似度计算方式:

余弦相似度(Cosine Similarity):最常用
  cos(θ) = (A·B) / (|A| × |B|)
  范围:[-1, 1],1 表示完全相同

欧氏距离(Euclidean Distance):
  d = √(Σ(ai - bi)²)
  距离越小,越相似

点积(Dot Product):
  A·B = Σ(ai × bi)
  适用于归一化向量

向量索引优化

java
// HNSW 索引(Hierarchical Navigable Small World)
// 适合大规模数据,查询速度快,但内存占用较高
@Bean
public VectorStore pgVectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel em) {
    return PgVectorStore.builder(jdbcTemplate, em)
        .dimensions(1536)
        .distanceType(PgVectorStore.PgDistanceType.COSINE_DISTANCE)
        .indexType(PgVectorStore.PgIndexType.HNSW)
        .initializeSchema(true)
        .build();
}

实战:构建文档知识库

java
@RestController
@RequestMapping("/knowledge")
public class KnowledgeController {

    @Autowired
    private VectorStore vectorStore;

    @Autowired
    private ChatClient chatClient;

    // 上传文档
    @PostMapping("/upload")
    public ResponseEntity<String> upload(@RequestParam MultipartFile file) {
        Resource resource = file.getResource();
        List<Document> docs = new TikaDocumentReader(resource).get();

        TextSplitter splitter = new TokenTextSplitter(512, 128);
        List<Document> chunks = splitter.apply(docs);

        vectorStore.add(chunks);
        return ResponseEntity.ok("成功导入 " + chunks.size() + " 个文档块");
    }

    // 语义搜索
    @GetMapping("/search")
    public List<Document> search(@RequestParam String query) {
        return vectorStore.similaritySearch(
            SearchRequest.builder()
                .query(query)
                .topK(5)
                .similarityThreshold(0.7)
                .build()
        );
    }

    // 知识库问答(RAG)
    @GetMapping("/ask")
    public String ask(@RequestParam String question) {
        return chatClient.prompt()
            .user(question)
            .advisors(new QuestionAnswerAdvisor(vectorStore))
            .call()
            .content();
    }
}

性能调优建议

生产环境建议

  1. 批量导入:使用 vectorStore.add(List<Document>) 批量操作,减少 API 调用
  2. 合理分块:chunk size 512-1024 tokens,overlap 10-20%
  3. 索引选择:大数据量用 HNSW,小数据量用 IVFFlat
  4. 缓存 Embedding:相同文本的向量结果可以缓存,避免重复计算
  5. 异步处理:文档导入使用异步任务,避免阻塞主线程

相关组件

本站内容由 褚成志 整理编写,仅供学习参考