为什么需要向量嵌入?
做一个RAG应用,首当其冲要搞明白的就是向量嵌入。很多人一上来就掉进技术细节里,反而忘了最基础的问题:为什么非得用它?
计算机的"语言困境"
作为人类,我们天然理解文字的含义:

- "猫" → 毛茸茸的、会喵喵叫、养来抓老鼠...
- "狗" → 忠诚的、会看家、是人类的好朋友...
但对计算机来说,"猫"和"狗"只是两个字符串,它不知道它们有什么关系。
Embedding 的核心思想
Embedding(向量化) 的核心思想简单到出乎意料:把文字转换成一串数字(向量)。
"猫"→[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 的应用场景
| 应用场景 | 说明 | 前端示例 |
|---|---|---|
| 语义搜索 | 搜"手机"也能找到"智能手机" | 站内搜索、文档检索 |
| 相似推荐 | 找到和当前内容最相似的内容 | 相关文章推荐 |
| 分类聚类 | 把相似的内容自动归类 | 新闻自动分类 |
| 去重判断 | 判断两段文字是否重复 | 内容查重 |
向量嵌入原理深入
从文本到向量的转换流程
flowchart LRsubgraph 文本向量化流程direction LRA[原始文本
我爱编程]B[分词
我,爱,编程]C[Token ID
101,234,567,...]D[Embedding模型
Transformer编码器]E[向量数组
0.12, -0.34, 0.56,...]A --> BB --> CC --> DD --> Eend
传统数据库 vs 向量数据库
传统数据库存储的是精确值:
SELECT * FROM products WHERE name = '手机';-- 只能精确匹配
但如果你搜"手机",就搜不到"智能手机"。
向量数据库的优势:即使搜索词不完全一样,也能找到语义上相关的内容!
| 对比维度 | 传统数据库 | 向量数据库 |
|---|---|---|
| 查询方式 | 精确匹配 | 相似度匹配 |
| 返回结果 | 满足条件的记录 | 语义最相似的 Top-K |
| 对模糊输入的容忍度 | 低 | 高 |
| 典型场景 | 用户登录、订单查询 | 语义搜索、推荐系统 |
本地轻量向量数据库部署
Chroma 简介
Chroma 是一款以 AI 为原生、开源的向量数据库,专注于开发者生产力和幸福感。它采用 Apache 2.0 许可,非常适合前端开发者快速上手。
Chroma 的核心优势:
| 特性 | 说明 |
|---|---|
| 零配置启动 | 无需复杂部署,开箱即用 |
| 轻量级 | 内存存储,适合开发测试 |
| LangChain 原生支持 | 官方提供完整集成 |
| 元数据过滤 | 支持结构化条件查询 |
Docker 部署(推荐)
# 拉取 Chroma 镜像docker pull chromadb/chroma# 运行容器docker run -d -p 8000:8000 chromadb/chroma
启动成功后,服务运行在 https://localhost:8000。
npm 安装与配置
# 安装 LangChain Chroma 集成包npm install @langchain/community chromadb @langchain/openai# 如果使用阿里云百炼 Embedding,还需安装npm install @langchain/openai
本地连接配置
// 本地 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: "https://localhost:8000",// Docker 服务地址});
文本向量化存储代码实战
完整 TS 实现
// src/vector-store-demo.tsimport { 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: "https://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();
使用过滤器的高级检索
// 带元数据过滤的检索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)}...`);});}
批量添加文档
// 批量添加文档(支持自定义 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(", ")}`);}
删除文档
// 按 ID 删除文档async function deleteDocument(vectorStore: Chroma) {await vectorStore.delete({ ids: ["doc_001"] });console.log("✅ 已删除指定文档");}
向量数据库选型建议
选型决策框架
根据数据规模和场景需求选择合适的向量数据库:
开始│├─ 数据规模 < 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 连接错误
解决方案:
# 检查 Chroma 服务是否运行curl https://localhost:8000/api/v1/heartbeat# 如果未运行,启动 Docker 容器docker run -d -p 8000:8000 chromadb/chroma
问题 3:向量检索结果不相关
现象:检索到的内容与查询语义不匹配
解决方案:
- 检查 Embedding 模型是否与检索时一致
- 增加文档块的重叠度
- 调整检索的
k值(返回数量) - 使用
similaritySearchWithScore查看相似度分数,评估检索质量
问题 4:添加文档时向量维度不匹配
现象:dimension mismatch 错误
解决方案:
- 确保所有文档使用相同的 Embedding 模型
- 检查是否在创建 Collection 时指定了正确的维度
// 创建时明确指定维度const vectorStore = new Chroma(embeddings, {collectionName: "my_collection",numDimensions: 1024,// 与 text-embedding-v3 匹配});
结语
通过这篇教程,我们深入学习了向量嵌入的原理和向量数据库的实践用法。从计算机如何理解语义,到用Chrom搭建本地向量库,再到完整的TypeScript代码实战——每一步都踩在了实际开发的关键点上。选型建议和常见问题的梳理,希望能让你在后续项目中少走弯路。
对于文章中错误的地方或有任何疑问,欢迎在评论区留言讨论!
