从零实现一个 RAG 系统:切分、检索、重排与防幻觉
2026 年 07 月 20 日 1 分钟阅读 302 次浏览
RAG 解决什么问题
大模型的知识截止于训练时间,也不了解你的私有数据。RAG(检索增强生成)的思路很直接:先从知识库里检索相关片段,再把它们塞进 Prompt 让模型基于事实回答。它比微调更轻、更新更快、可溯源。
最小可用架构
文档 → 切分(chunk) → 向量化(embedding) → 存入向量库
↓
用户提问 → 向量化 → 相似度检索 → Top-K 片段 → 拼进 Prompt → LLM 生成
一、文档切分
切分粒度直接影响召回质量。经验值:
- 每块 300~800 tokens,块间重叠 10~15%(保留上下文连续性)。
- 优先按语义边界切(标题、段落),而不是硬按字数切断句子。
function chunk(text: string, size = 500, overlap = 60): string[] {
const chunks: string[] = [];
for (let i = 0; i < text.length; i += size - overlap) {
chunks.push(text.slice(i, i + size));
}
return chunks;
}
二、向量化与存储
用 embedding 模型把文本转成向量,存进向量数据库(pgvector、Qdrant、Milvus 等):
const res = await embeddings.create({
model: "text-embedding-3-small",
input: chunks,
});
// 将 res.data[i].embedding 与原文一起写入向量库
三、检索与重排
朴素相似度检索会有噪声。生产级 RAG 常加一步 rerank:
- 向量检索召回 Top-20(粗排,快)。
- 用 rerank 模型对 20 条精排,取 Top-5(准)。
四、拼 Prompt 的纪律
const prompt = `请仅依据下面的资料回答问题,若资料不足请回答"不确定"。
资料:
${topChunks.map((c, i) => `[${i + 1}] ${c.text}`).join("\n\n")}
问题:${question}`;
关键约束:“仅依据资料” + “不足就说不知道”,能显著降低幻觉。
常见坑
- 切太碎:单块信息不完整,模型拼不出答案。
- 不 rerank:召回里混入不相关片段,干扰生成。
- 不给引用:无法溯源,用户无法验证。让模型输出
[编号]引用。 - 忽略元数据过滤:先按时间/权限过滤,再做向量检索,效果和安全性都更好。
RAG 不是"接个向量库"这么简单,切分、重排、Prompt 约束每一步都在和"幻觉"作斗争。
评论区未配置
在 .env 中设置
PUBLIC_WALINE_SERVER_URL
即可启用 Waline 评论。