RAG 技术入门:让 AI 基于你的数据回答问题
大模型很聪明,但它不知道你公司的内部文档。RAG 就是让 AI 「先查资料,再回答」的技术。
我们团队之前尝试用 ChatGPT 来回答公司内部的技术问题,结果发现它对我们自己的 API 文档、部署规范一无所知,还经常一本正经地胡说八道。后来用 RAG 搭建了一个内部知识库问答系统,把几百篇文档导入向量数据库,效果提升了一个数量级——它终于能准确引用我们的文档来回答问题了。
1. RAG 是什么?为什么需要它?
RAG(Retrieval-Augmented Generation,检索增强生成)的核心思想很简单:在让大模型回答问题之前,先从你的知识库中检索相关内容,把检索结果作为上下文一起传给模型。
RAG 的工作流程
RAG 的优势
- 不需要微调模型,成本低
- 数据可以实时更新
- 回答可追溯(引用源文档)
- 减少模型幻觉
RAG 的局限
- 检索质量决定回答质量
- 不擅长跨文档的推理
- 需要维护向量数据库
- 对中文的分块策略要求高
2. 核心概念:Embedding 和向量搜索
什么是 Embedding?
Embedding 是把文本转换成数字向量的过程。关键在于:语义相近的文本,向量也相近。
# 示意(实际向量是 1536 维的)
embed("如何部署 Node.js 应用") → [0.12, -0.34, 0.56, ...]
embed("Node.js 项目怎么上线") → [0.13, -0.33, 0.55, ...] # 非常接近!
embed("今天天气怎么样") → [0.89, 0.21, -0.67, ...] # 差距很大
# 通过计算向量之间的余弦相似度,
# 就能找到语义最相关的文本块主流 Embedding 模型
| 模型 | 维度 | 特点 |
|---|---|---|
| OpenAI text-embedding-3-small | 1536 | 性价比高,中文效果好 |
| Cohere embed-v4 | 1024 | 多语言优秀,支持本地部署 |
| BGE-M3 | 1024 | 开源,中文表现突出 |
| Jina embeddings-v3 | 1024 | 开源,支持代码搜索 |
3. 动手搭建:用 TypeScript 实现简易 RAG
Step 1:安装依赖
# 使用 LangChain.js 简化开发
pnpm add langchain @langchain/openai @langchain/community
pnpm add chromadb # 轻量级向量数据库Step 2:文档分块与向量化
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter'
import { OpenAIEmbeddings } from '@langchain/openai'
import { Chroma } from '@langchain/community/vectorstores/chroma'
// 1. 文本分块
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 500, // 每块最大 500 字符
chunkOverlap: 50, // 块之间重叠 50 字符,保持上下文连贯
separators: ['\n\n', '\n', '。', '!', '?', ' '], // 中文友好的分隔符
})
const chunks = await splitter.splitDocuments(docs)
// 2. 向量化并存入 ChromaDB
const embeddings = new OpenAIEmbeddings({
modelName: 'text-embedding-3-small',
})
const vectorStore = await Chroma.fromDocuments(chunks, embeddings, {
collectionName: 'my-docs',
})Step 3:检索与问答
import { ChatOpenAI } from '@langchain/openai'
import { RetrievalQAChain } from 'langchain/chains'
const llm = new ChatOpenAI({ modelName: 'gpt-4o' })
const retriever = vectorStore.asRetriever({
k: 4, // 检索最相关的 4 个文本块
})
const chain = RetrievalQAChain.fromLLM(llm, retriever, {
returnSourceDocuments: true, // 返回引用来源
})
// 使用
const result = await chain.call({
query: '我们的 API 鉴权方式是什么?',
})
console.log(result.text)
// "根据文档,你们的 API 使用 JWT Bearer Token 鉴权..."
console.log(result.sourceDocuments)
// [{ pageContent: '...', metadata: { source: 'auth-guide.md' } }, ...]4. RAG 优化:从 60 分到 90 分
优化 1:混合检索(Hybrid Search)
纯向量搜索有时候会「理解过度」——用户搜索一个精确的错误码,向量搜索可能返回语义相似但不相关的内容。解决方案是结合关键词搜索(BM25)和向量搜索,两者的结果取并集并重新排序。
优化 2:智能分块策略
按固定字数切分是最简单但效果最差的方案。更好的方式:
按语义切分:识别段落、章节边界,保持语义完整性。
父子分块:小块用于检索(精确匹配),检索到后返回它所在的大块(保持上下文)。
按文档类型定制:代码文件按函数切分,Markdown 按标题切分,对话记录按轮次切分。
优化 3:Re-ranking
初始检索返回的 top-k 结果中,可能有些只是表面相似。用一个专门的 Re-ranker 模型(如 Cohere Rerank)对结果重新排序,能显著提高相关性。这一步的计算成本很低,但效果提升明显。
5. 向量数据库选型
| 数据库 | 适用场景 | 特点 |
|---|---|---|
| ChromaDB | 原型开发、小项目 | 零配置,嵌入式运行 |
| Pinecone | 生产环境、大规模 | 全托管,高可用 |
| Qdrant | 自部署、高性能 | Rust 实现,丰富的过滤 |
| Weaviate | 多模态搜索 | 支持图片/文本混合检索 |
| pgvector | 已有 PostgreSQL | 无需新增基础设施 |
选型建议:如果你的项目已经在用 PostgreSQL,直接用 pgvector 扩展是最省事的方案。数据量不大的话(10 万条以下),ChromaDB 开箱即用,开发体验最好。需要生产级可靠性就上 Pinecone 或 Qdrant。