为什么需要向量嵌入?
计算机的"语言困境"
作为人类,我们天然理解文字的含义:
- "猫" → 毛茸茸的、会喵喵叫、养来抓老鼠...
- "狗" → 忠诚的、会看家、是人类的好朋友...
但对计算机来说,"猫"和"狗"只是两个字符串,它不知道它们有什么关系。
Embedding 的核心思想
Embedding(向量化) 的核心思想是:把文字转换成一串数字(向量)。
scss
"猫" → [0.12, -0.34, 0.56, 0.78, ...] (1024维向量)
"狗" → [0.15, -0.31, 0.53, 0.80, ...] (1024维向量)
"汽车"→ [0.89, 0.12, -0.34, 0.21, ...] (1024维向量)
关键规律:如果两个向量的距离很近,说明对应的文本在语义上也相似。
向量距离的计算
衡量两个向量"远近"的常用方法:
| 方法 | 公式 | 说明 |
|:----------|:-------------------|:--------------|---|---|-----|---------------|
| 余弦相似度 | `cos(θ) = (A·B)/( | A | | B | )` | 关注方向,值越接近1越相似 |
| 欧氏距离 | d = √Σ(ai - bi)² | 关注绝对距离,值越小越相似 |
| 点积 | A·B = Σai×bi | 结合方向和长度 |
Embedding 的应用场景
| 应用场景 | 说明 | 前端示例 |
|---|---|---|
| 语义搜索 | 搜"手机"也能找到"智能手机" | 站内搜索、文档检索 |
| 相似推荐 | 找到和当前内容最相似的内容 | 相关文章推荐 |
| 分类聚类 | 把相似的内容自动归类 | 新闻自动分类 |
| 去重判断 | 判断两段文字是否重复 | 内容查重 |
向量嵌入原理深入
从文本到向量的转换流程
传统数据库 vs 向量数据库
传统数据库存储的是精确值:
sql
SELECT * FROM products WHERE name = '手机'; -- 只能精确匹配
但如果你搜"手机",就搜不到"智能手机"。
向量数据库的优势:即使搜索词不完全一样,也能找到语义上相关的内容!
| 对比维度 | 传统数据库 | 向量数据库 |
|---|---|---|
| 查询方式 | 精确匹配 | 相似度匹配 |
| 返回结果 | 满足条件的记录 | 语义最相似的 Top-K |
| 对模糊输入的容忍度 | 低 | 高 |
| 典型场景 | 用户登录、订单查询 | 语义搜索、推荐系统 |
本地轻量向量数据库部署
Chroma 简介
Chroma 是一款以 AI 为原生、开源的向量数据库,专注于开发者生产力和幸福感。它采用 Apache 2.0 许可,非常适合前端开发者快速上手。
Chroma 的核心优势:
| 特性 | 说明 |
|---|---|
| 零配置启动 | 无需复杂部署,开箱即用 |
| 轻量级 | 内存存储,适合开发测试 |
| LangChain 原生支持 | 官方提供完整集成 |
| 元数据过滤 | 支持结构化条件查询 |
Docker 部署(推荐)
bash
# 拉取 Chroma 镜像
docker pull chromadb/chroma
# 运行容器
docker run -d -p 8000:8000 chromadb/chroma
启动成功后,服务运行在 http://localhost:8000。
npm 安装与配置
bash
# 安装 LangChain Chroma 集成包
npm install @langchain/community chromadb @langchain/openai
# 如果使用阿里云百炼 Embedding,还需安装
npm install @langchain/openai
本地连接配置
typescript
// 本地 Chroma 连接配置
import { Chroma } from "@langchain/community/vectorstores/chroma";
import { OpenAIEmbeddings } from "@langchain/openai";
const embeddings = new OpenAIEmbeddings({
model: "text-embedding-v3",
apiKey: process.env.BAILIAN_API_KEY,
configuration: {
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
},
});
const vectorStore = new Chroma(embeddings, {
collectionName: "my_knowledge_base",
url: "http://localhost:8000", // Docker 服务地址
});
文本向量化存储代码实战
完整 TS 实现
typescript
// src/vector-store-demo.ts
import { OpenAIEmbeddings } from "@langchain/openai";
import { Chroma } from "@langchain/community/vectorstores/chroma";
import { Document } from "@langchain/core/documents";
import dotenv from "dotenv";
dotenv.config();
// 1. 初始化 Embedding 模型(阿里云百炼)
const embeddings = new OpenAIEmbeddings({
apiKey: process.env.BAILIAN_API_KEY,
configuration: {
baseURL: process.env.BAILIAN_BASE_URL,
},
model: "text-embedding-v3", // 阿里云百炼模型
});
// 2. 准备文档数据
const documents: Document[] = [
{
pageContent: "RAG(检索增强生成)是一种结合检索和生成的技术方案,可以有效解决大模型幻觉问题。",
metadata: { source: "rag_intro.md", topic: "RAG基础" },
},
{
pageContent: "向量数据库专门用于存储和检索高维向量数据,支持相似度搜索而非传统的关系型精确匹配。",
metadata: { source: "vector_db.md", topic: "向量数据库" },
},
{
pageContent: "Embedding 模型将文本转换为数值向量,使得语义相似的文本在向量空间中距离更近。",
metadata: { source: "embedding.md", topic: "嵌入模型" },
},
];
// 3. 创建向量存储并添加文档
async function createVectorStore() {
console.log("🔄 正在创建向量数据库...");
const vectorStore = await Chroma.fromDocuments(
documents,
embeddings,
{
collectionName: "rag_knowledge_base",
url: "http://localhost:8000", // Chroma 服务地址
}
);
console.log("✅ 向量数据库创建成功!");
console.log(`📊 已存储 ${documents.length} 个文档块\n`);
return vectorStore;
}
// 4. 执行相似度检索
async function searchSimilar(vectorStore: Chroma, query: string) {
console.log(`🔍 查询: "${query}"\n`);
const results = await vectorStore.similaritySearch(query, 2);
console.log("📋 检索结果(按相似度排序):");
results.forEach((doc, idx) => {
console.log(`\n ${idx + 1}. [相似度排名${idx + 1}]`);
console.log(` 内容: ${doc.pageContent.slice(0, 100)}...`);
console.log(` 来源: ${doc.metadata.source}`);
});
return results;
}
// 5. 带分数返回的检索
async function searchWithScore(vectorStore: Chroma, query: string) {
const resultsWithScore = await vectorStore.similaritySearchWithScore(query, 2);
console.log("\n📊 检索结果(含相似度分数):");
resultsWithScore.forEach(([doc, score], idx) => {
console.log(`\n ${idx + 1}. 相似度: ${score.toFixed(4)}`);
console.log(` 内容: ${doc.pageContent.slice(0, 80)}...`);
});
return resultsWithScore;
}
// 主函数
async function main() {
try {
// 创建向量存储
const vectorStore = await createVectorStore();
// 测试检索
await searchSimilar(vectorStore, "什么是RAG技术?");
await searchWithScore(vectorStore, "如何解决大模型幻觉");
} catch (error) {
console.error("❌ 错误:", error);
}
}
main();
使用过滤器的高级检索
typescript
// 带元数据过滤的检索
async function searchWithFilter(vectorStore: Chroma) {
const filter = { topic: "向量数据库" };
const results = await vectorStore.similaritySearch(
"向量存储",
2,
filter // 只检索 topic 为"向量数据库"的文档
);
console.log("🔍 带过滤条件的检索结果:");
results.forEach((doc, idx) => {
console.log(` ${idx + 1}. ${doc.pageContent.slice(0, 80)}...`);
});
}
批量添加文档
typescript
// 批量添加文档(支持自定义 ID)
async function batchAddDocuments(vectorStore: Chroma) {
const newDocs: Document[] = [
{ pageContent: "Chroma 是一款开源的向量数据库...", metadata: { type: "intro" } },
{ pageContent: "LangChain 提供了丰富的向量存储集成...", metadata: { type: "tutorial" } },
];
const ids = await vectorStore.addDocuments(newDocs, { ids: ["doc_001", "doc_002"] });
console.log(`✅ 已添加 ${ids.length} 个文档,ID: ${ids.join(", ")}`);
}
删除文档
typescript
// 按 ID 删除文档
async function deleteDocument(vectorStore: Chroma) {
await vectorStore.delete({ ids: ["doc_001"] });
console.log("✅ 已删除指定文档");
}
向量数据库选型建议
选型决策框架
根据数据规模和场景需求选择合适的向量数据库:
text
开始
│
├─ 数据规模 < 10万条?
│ ├─ 是 → Chroma(轻量级嵌入式)
│ └─ 否 ↓
│
├─ 数据规模 10万-500万条?
│ ├─ 是 → Qdrant(高性能独立部署)
│ └─ 否 ↓
│
├─ 数据规模 > 500万条?
│ ├─ 是 → Milvus(分布式集群)
│ └─ 否 ↓
│
└─ 已有 PostgreSQL 基础设施?
└─ 是 → pgvector(数据库插件)
主流方案对比
| 数据库 | 架构模式 | 性能 | 混合搜索 | 分布式 | 适用场景 |
|---|---|---|---|---|---|
| Chroma | 轻量级嵌入式 | ★★☆ | ✗ | ✗ | 本地开发测试、POC验证 |
| Qdrant | 高性能独立 | ★★★★ | ✓ | ✓ | 实时推荐、复杂查询 |
| Milvus | 分布式集群 | ★★★★★ | ✓ | ✓ | 千万级生产环境 |
| pgvector | PostgreSQL扩展 | ★★★ | 有限 | ✗ | 已有PG基础设施 |
场景选型建议
| 使用场景 | 推荐方案 | 理由 |
|---|---|---|
| 本地开发/学习 | Chroma | 零配置、开箱即用 |
| 前端 Demo 项目 | Chroma | 轻量、集成简单 |
| 中小企业生产 | Qdrant | 性能好、运维成本适中 |
| 大规模生产环境 | Milvus | 分布式、高可用 |
| 已有 PostgreSQL | pgvector | 复用现有基础设施 |
常见问题与解决方案
问题 1:Embedding 模型调用失败
现象 :401 Unauthorized 或 InvalidApiKey
解决方案:
- 检查 API Key 是否正确
- 确认 Base URL 是
https://dashscope.aliyuncs.com/compatible-mode/v1 - 确认模型名称正确(
text-embedding-v3)
问题 2:Chroma 连接失败
现象 :ECONNREFUSED 连接错误
解决方案:
bash
# 检查 Chroma 服务是否运行
curl http://localhost:8000/api/v1/heartbeat
# 如果未运行,启动 Docker 容器
docker run -d -p 8000:8000 chromadb/chroma
问题 3:向量检索结果不相关
现象:检索到的内容与查询语义不匹配
解决方案:
- 检查 Embedding 模型是否与检索时一致
- 增加文档块的重叠度
- 调整检索的
k值(返回数量) - 使用
similaritySearchWithScore查看相似度分数,评估检索质量
问题 4:添加文档时向量维度不匹配
现象 :dimension mismatch 错误
解决方案:
- 确保所有文档使用相同的 Embedding 模型
- 检查是否在创建 Collection 时指定了正确的维度
typescript
// 创建时明确指定维度
const vectorStore = new Chroma(embeddings, {
collectionName: "my_collection",
numDimensions: 1024, // 与 text-embedding-v3 匹配
});
结语
通过这篇教程,我们深入学习了向量嵌入的原理和向量数据库的实践用法。
对于文章中错误的地方或有任何疑问,欢迎在评论区留言讨论!