摘要:本文面向正在做企业知识库、客服问答、文档问答和内部资料助手的开发者,复盘 RAG 项目从 Demo 到上线时最容易出错的环节:文档清洗、切片策略、向量召回、关键词兜底、重排、引用展示、离线评测和线上日志。
开篇:RAG 的难点不是接入向量库,而是让答案可信
很多 RAG Demo 看起来很顺:上传 PDF,切片,写入向量库,用户提问后召回几段文本,再让大模型组织答案。这个流程跑通并不难,难的是上线后持续答得准、答得稳、答得可解释。
我见过不少知识库项目,演示时效果很好,一接真实文档就开始翻车:召回内容看似相关但答非所问;答案说得很顺却找不到出处;同一个问题换个问法结果差很多;文档更新后旧内容还在被引用。
所以这篇文章不讨论“哪家模型更强”,而是从工程角度整理一套 RAG 知识库避坑清单。目标很明确:让知识库不只是能搜到资料,而是能稳定生成有依据的答案。
一、先处理文档质量,再谈向量检索
RAG 的第一步不是 embedding,而是文档清洗。很多知识库效果差,不是模型不行,而是喂进去的材料本身很脏。
常见问题包括:PDF 页眉页脚重复进入正文、表格被拆碎、目录和正文混在一起、扫描件 OCR 错字、同一份制度多个版本同时存在、附件里的编号和正文引用对不上。
我的建议是给文档入库前加一个最小清洗流程:提取正文、去掉页眉页脚、保留标题层级、记录来源文件、记录页码或章节、给每个片段生成稳定 ID。没有这些元数据,后面即使召回成功,也很难给用户可靠引用。
二、切片不要只按字数,要保留语义边界
最粗暴的切片方式是每 500 字切一段,重叠 50 字。这个方法能跑,但经常破坏语义。比如一个制度条款被切成两半,或者标题和正文分离,召回时只拿到正文却不知道它属于哪个章节。
更合理的策略是按文档结构切片:标题、段落、列表、表格优先保留完整语义。如果文档格式比较规范,可以先按标题层级切,再对过长段落做二次切分。
切片的核心不是长度,而是“召回后能不能单独解释问题”。一个片段最好包含足够上下文,同时不要混入太多无关内容。
三、只靠向量召回不够,关键词召回要保留
向量检索擅长语义相似,但不擅长精确匹配。很多企业知识库里有型号、合同编号、客户简称、内部系统名、产品编码,这些信息向量化以后未必好找。
所以我更倾向于混合召回:向量召回负责语义,关键词召回负责精确命中。两路结果合并后再去重、排序。
例如用户问“DR-2026 试用额度怎么开”,向量检索可能理解成“试用政策”,关键词检索能直接命中 DR-2026。两者结合,召回质量通常比单一路线稳得多。
四、重排不是锦上添花,是真实项目的分水岭
很多 RAG 项目会一次召回 top 10,然后直接塞给大模型。问题是 top 10 里经常有半相关内容,模型会被噪音带偏。
重排的作用,是在初召回后重新判断“哪个片段最能回答这个问题”。哪怕不用复杂模型,先做一些规则也有帮助:问题关键词覆盖率、标题匹配、时间版本、文档优先级、片段长度、是否包含明确答案。
真正上线时,我通常会把召回拆成两层:第一层尽量召全,第二层尽量排准。宁可多花一点重排成本,也不要把明显无关的内容丢给生成模型。
五、答案必须带出处,否则用户无法信任
企业知识库和普通聊天最大的区别,是用户不只要一个流畅答案,还要知道依据来自哪里。
建议每条答案都带引用:来源文件、章节标题、页码或片段 ID。如果答案里涉及制度条款、价格、合同、交付范围,引用更重要。
这里还有一个细节:不要让模型自己编引用。引用应该由系统根据召回片段生成,模型只负责组织语言。否则很容易出现“看起来像引用,实际上查不到”的问题。
六、给模型明确边界:找不到就说找不到
RAG 最怕的不是回答少,而是凭空补全。
我会在系统提示词里明确要求:只基于给定资料回答;资料不足时说明缺少信息;不要猜测制度、价格、合同条款;必要时列出需要补充的文件。
这类提示词看似保守,但对企业场景更安全。用户宁愿看到“当前知识库没有找到依据”,也不希望系统自信地给出错误答案。
七、没有评测集,就不知道优化有没有用
RAG 优化不能只靠人工感觉。建议从真实问题里整理一组评测集:常见问题、边界问题、容易混淆的问题、需要精确引用的问题。
每次改切片、召回、重排或 Prompt 后,用同一组问题跑一遍,看命中率、引用准确率、拒答是否合理、答案是否过长。
评测集不用一开始很大,先做 30 到 50 条高频问题就很有价值。它能避免团队陷入“我感觉这版更好”的争论。
一个最小可落地的检索流程
下面这段伪代码展示的是流程,不绑定具体向量库。重点是把清洗、混合召回、重排、引用和日志拆开,而不是把所有逻辑塞进一个函数。
def answer_question(question: str):
cleaned_query = normalize_query(question)
vector_hits = vector_search(cleaned_query, top_k=30)
keyword_hits = keyword_search(cleaned_query, top_k=20)
candidates = merge_and_deduplicate(vector_hits, keyword_hits)
ranked = rerank(question, candidates, top_k=6)
if not ranked or ranked[0].score < 0.62:
return {
answer: 当前知识库没有找到足够依据,建议补充相关文档。,
references: [],
}
answer = generate_answer(
question=question,
contexts=[item.content for item in ranked],
instruction=只基于给定资料回答,并保留出处。
)
return {
answer: answer,
references: [item.source for item in ranked],
}
我的实践记录
我在整理 AI 调用和知识库工程化问题时,会把一些实践记录放在 www.dreamrouter.top。这里不把它写成推荐,只作为一个观察入口:做知识库系统时,可以顺手关注调用日志、模型消耗、失败率和召回链路是否可追踪。
无论你用自研方案还是第三方工具,真正要关注的是:答案是否有依据,问题是否能复盘,优化是否有数据支持。
结尾:欢迎补充你的 RAG 踩坑经历
RAG 项目上线以后,最有价值的不是“接入了多少模型”,而是能不能把企业资料变成可信、可追踪、可维护的问答能力。
如果你也做过知识库,评论区可以聊聊:你遇到最多的是切片问题、召回问题、引用问题,还是文档质量问题?觉得这份检查表有帮助,可以点赞、收藏,后面我继续整理 RAG 评测和线上日志设计。


被折叠的 条评论
为什么被折叠?



