LangChain 学习笔记 03:RAG 的效果,从文档进入系统时就开始决定

做 RAG 时,最容易被关注的是最后那次模型回答。但真正动手以后,我发现很多“模型答不好”的问题,往前追几步就会变成:文档没读全、切块切断了、元数据丢了,或者正确证据根本没有被召回。

第 4 章把加载器、文档转换、Embedding、向量存储和 Retriever 放在“数据增强模块”中。与其把它们记成五个类,我更愿意把它们看成一条知识加工流水线。

先把 RAG 写成一条数据流

原始文件
  -> 加载为 Document
  -> 清洗与切分
  -> 生成 Embedding
  -> 写入向量库
  -> 根据问题检索
  -> 把证据交给模型回答

这条流程里,生成答案只是最后一步。前面任何一个环节出错,模型都可能在缺少证据的情况下给出一段很流畅的回答。

Loader 不能只留下纯文本

假设知识库里有一份 80 页的产品手册。如果加载完成后只剩下一长串文字,后面即使检索正确,也很难告诉用户答案来自第几页;文档更新时,也不知道应该替换哪些向量。

因此 Document 除了正文,还应尽量保留:

  • 文件名、路径或业务主键;
  • 页码、章节标题和段落层级;
  • 文档版本、更新时间和权限标签;
  • 可回到原文的 URL 或定位信息。

这些元数据不会直接让模型变聪明,却决定了引用、过滤、更新和审计能不能做好。

切块不是“随便定个 500”

块太大,一段里混进多个主题,Embedding 很难表达重点;块太小,定义和上下文被拆开,检索回来只剩半句话。

例如下面这段产品说明:

退款条件:订单支付后 24 小时内可自助退款。
例外情况:已使用优惠券的订单需要人工审核。

如果刚好从两句中间切开,用户询问“用了优惠券能否退款”时,系统可能只召回第一句,进而给出过度确定的答案。

所以切分要尽量尊重标题、段落、列表、代码块和表格结构。重叠能缓解边界断裂,但也会增加存储量和重复召回。块大小、重叠量和分隔规则,最好用一组真实问题做实验,而不是凭感觉拍板。

Embedding 找的是“相近”,不是“正确”

Embedding 把文本映射成向量,适合发现同义表达。例如用户问“账号进不去”,系统仍可能召回标题为“登录失败排查”的文档。

但向量相似度不会自动判断哪份资料更新、哪份更权威,也不擅长所有精确匹配。订单号、错误码、版本号和专有名词,往往仍需要关键词检索。

实际系统常采用组合方式:

元数据过滤 -> 关键词与向量召回 -> 合并去重 -> 重排序 -> 返回前 k 条证据

更换 Embedding 模型时,也不能把新查询向量直接拿去搜索旧索引。模型和维度变化后,通常需要重新生成向量,并把模型版本记录下来。

Vector Store 和 Retriever 分工不同

向量库负责保存向量并执行相似度搜索;Retriever 面向应用,决定查询怎样改写、按什么条件过滤、取多少条,以及是否融合多个数据源。

把两层分开有一个实际好处:底层从本地向量库换成云服务时,上层问答流程不必全部改写。反过来,要加入时间过滤、混合检索或重排序,也不一定要替换存储。

Retriever 最终返回的内容,至少应包含正文、来源和必要元数据。相似度分数可以辅助分析,但不要把不同模型、不同索引产生的分数直接当成统一置信度。

先评估检索,再评估答案

如果只看最终回答,很容易被模型的语言能力误导。它可能没有检索到资料,却依靠参数知识碰巧答对。

我会先准备一组“问题 + 标准证据”,单独检查:

  1. 正确证据是否出现在前 k 条;
  2. 召回结果里有多少无关内容;
  3. 权限、时间和文档类型过滤是否生效;
  4. 引用能否准确回到原文;
  5. 没有资料或资料冲突时,系统是否会承认不确定。

等检索链路稳定以后,再评估生成答案是否忠于证据、是否遗漏关键信息。

这章给我的启发

RAG 不是“给模型接一个向量数据库”,而是在建设一条可更新、可追踪、可评估的知识通道。

加载决定资料有没有丢,切分决定知识以什么颗粒度存在,Embedding 和检索策略决定证据能不能被找到,最后的 Prompt 才决定模型怎样使用这些证据。把问题沿这条链路拆开,调试就不再只是反复修改一句提示词。

文章摘自:https://www.cnblogs.com/hazy-star/p/22067463