第 14 章:Embedding 和向量数据库

4 阅读2分钟

第 14 章:Embedding 和向量数据库

本章目标

这一章讲清楚 Embedding 和向量库的作用,并完成一个教学版向量检索。

本章效果

右侧“引用来源”展示了向量检索命中的片段和相似度分数。

Embedding 与向量检索转存失败,建议直接上传图片文件

Embedding 是什么

Embedding 会把文本转换成一组数字向量。语义相近的文本,向量距离通常也更近。

比如:

“报销需要发票吗”
“费用申请要提供票据吗”

这两句话字面不一样,但语义接近,向量检索有机会把它们匹配到同一份制度。

向量库做什么

向量库存储:

  • chunk 文本
  • chunk metadata
  • embedding 向量

提问时:

用户问题 -> embedding -> 和库里的向量比较 -> 返回最相似的 chunks

向量记录结构

export interface VectorRecord {
  id: string;
  content: string;
  embedding: number[];
  metadata: {
    documentId: string;
    title: string;
    source: string;
    chunkIndex: number;
  };
}

余弦相似度

教学版可以自己写相似度:

export function cosineSimilarity(a: number[], b: number[]) {
  let dot = 0;
  let normA = 0;
  let normB = 0;

  for (let i = 0; i < a.length; i++) {
    dot += a[i] * b[i];
    normA += a[i] * a[i];
    normB += b[i] * b[i];
  }

  return dot / (Math.sqrt(normA) * Math.sqrt(normB));
}

内存向量库

const records: VectorRecord[] = [];

export function addRecords(nextRecords: VectorRecord[]) {
  records.push(...nextRecords);
}

export function searchRecords(queryEmbedding: number[], topK = 5) {
  return records
    .map((record) => ({
      record,
      score: cosineSimilarity(queryEmbedding, record.embedding)
    }))
    .sort((a, b) => b.score - a.score)
    .slice(0, topK);
}

注意:内存向量库只适合教学,不适合生产。服务重启数据就没了。

Embedding 模型

可以封装:

import { OpenAIEmbeddings } from "@langchain/openai";

export function createEmbeddings() {
  return new OpenAIEmbeddings({
    model: process.env.EMBEDDING_MODEL ?? "text-embedding-3-small"
  });
}

生成向量:

const embeddings = createEmbeddings();
const vector = await embeddings.embedQuery("报销需要发票吗?");

批量处理 chunk:

const vectors = await embeddings.embedDocuments(chunks.map((chunk) => chunk.content));

实战任务

完成:

  • Embedding 模型封装
  • VectorRecord 类型
  • 内存向量库
  • 文档 chunk 入库
  • 问题向量检索

常见坑

Embedding 模型和聊天模型不是一回事。不要用聊天模型生成向量。

不同 Embedding 模型生成的向量维度可能不同,不能混用。

生产环境不要用内存向量库。后续可以替换成 pgvector、Qdrant、Milvus、Pinecone。

本章小结

Embedding 和向量库让文档具备语义检索能力。下一章会把检索结果交给模型,完成真正的知识库问答。