Damn AgentBeta

RAG 与知识系统

Agent 使用外部知识时的采集、切分、索引、召回、重排、引用和更新机制。

RAG(Retrieval-Augmented Generation,检索增强生成)不是“给模型接一个向量库”这么简单。它是一套让 Agent 在回答前检索外部知识、把证据带入上下文、生成可追溯回答,并持续处理知识更新的工程系统。

好的 RAG 系统要同时回答三个问题:

  1. 该从哪里取可信知识?
  2. 该把哪些片段交给模型?
  3. 该如何证明回答来自这些片段,而不是模型临场编造?

定义与边界

RAG 的核心边界是:把可更新、可审计、可引用的外部知识放在模型上下文之外,在需要时检索并注入上下文。它补充模型参数中的通用知识,但不等同于训练、微调或完整知识图谱。

概念解决的问题不适合承担的职责
RAG让模型基于外部资料回答,并保留来源替代权限系统、替代人工审批、保证所有事实永远正确
微调改变模型行为风格、输出格式或稳定技能高频更新的事实库、长尾内部文档检索
工具调用查询实时系统、执行动作、读写业务状态承载大量非结构化背景材料
知识图谱表达实体、关系、规则和推理链路直接替代自然语言文档证据
长上下文临时塞入大量材料,减少检索复杂度解决权限、freshness、证据定位和成本控制

在 Agent 系统里,RAG 更像“证据供应链”:它从知识源开始,到用户看到带引用的回答结束。只要其中任何一环没有记录来源、版本和处理方式,系统就很难排查错误。

分层架构

刚接触 RAG 时,很多人会把它理解成“把文档放进向量库,用户一问就搜一下”。这个理解只说中了很小一部分。真正可用的 RAG 更像一个图书馆和客服系统的组合:先把资料收进来,整理成目录,给每段资料贴标签;用户提问时,系统先找可能相关的资料,再挑出最能回答问题的证据,最后让模型基于这些证据组织答案。

可以用一个简单问题来理解整条链路:用户问“企业版订单超过 7 天还能退款吗?”系统不能直接让模型凭感觉回答,而要先找到最新计费文档里的退款规则,确认这条规则适用于企业版,再把对应段落交给模型,并在答案里标出来源。为了做到这件事,RAG 通常会拆成数据层、切分层、索引层、召回层、生成层和评测层。

从工程实现看,一个标准的 RAG 系统不是单个“检索函数”,而是一条分层管线。每一层都有自己的输入、输出和检查方式。把层次拆开以后,系统答错时才知道问题出在哪里:是资料没收进来,还是切分太碎,还是索引没命中,还是重排选错了证据,还是模型没有按证据回答。

层级核心职责产物
数据层采集外部知识,清洗成可追溯、可权限控制、可增量更新的知识单元原始文档、结构化字段、来源元数据、权限和版本信息
切分层按文档结构、语义边界和引用粒度切分内容,并去除重复或低价值片段chunk、章节路径、页码 / 行号、去重指纹
索引层为 chunk 建立向量索引、关键词索引和结构化索引embedding、BM25 倒排索引、元数据表、实体关系记录
召回层根据问题组合语义、关键词和结构化条件,先宽召回再排序候选片段、召回分数、过滤原因
生成层管理上下文、拼接证据、约束模型基于证据输出并保留引用prompt 上下文、答案、引用列表
评测层用指标和样例集持续验证检索与生成质量召回率、精确率、引用正确率、一致性和幻觉率

这张表可以先当成一张地图看,不需要一开始就理解每个术语。下面按“资料从进入系统到生成答案”的顺序展开。

数据层:先把知识变成可治理资产

数据层回答的是“知识从哪里来,能不能信,谁能看”。它的工作不是简单上传文件,而是把外部资料变成可管理的知识资产。常见来源包括产品文档、帮助中心、客服工单、API 文档、代码仓库、数据库导出、会议纪要、PDF 和人工维护的 FAQ。

每份资料进入系统时,至少要记录来源、负责人、更新时间、版本、权限范围和原文定位方式。例如一段退款规则应该知道它来自哪个文档、哪个章节、什么时候更新、适用于哪个产品版本、普通用户还是内部员工可以看到。没有这些信息,模型即使答对一句话,也很难证明答案可靠。

解析资料时也不能只抽纯文本。标题层级、表格字段、代码路径、页码、行号、警告块和业务生效范围都很重要。比如表格里“企业版”“团队版”“个人版”是列名,如果清洗时把列名丢掉,后面模型可能把企业版规则误用到个人版。

对经常更新的知识源,数据层还应支持增量同步和变更检测。例如文档页面更新后,只重新解析受影响的章节;工单状态变化后,只刷新对应记录;某个来源被下线后,可以批量撤销它生成的 chunk 和索引。

切分层:让片段能被单独召回和引用

切分层回答的是“资料应该拆成多大的片段”。RAG 不会每次都把整本手册塞给模型,而是先把文档拆成很多小片段,检索时只取最相关的几个。这个小片段通常叫 chunk。

好的 chunk 不是平均切 token,而是能在脱离原文后仍然看懂的最小证据单元。比如退款规则不能只切出“7 天内申请”,还要带上“企业版客户”“订单创建后”“已使用的增值服务费用不退还”等上下文。否则模型看到半句话,很容易误解适用条件。

不同资料要用不同切法:产品文档适合按标题层级和段落切分,API 文档适合按 endpoint、参数表或返回结构切分,代码适合按文件、函数、类和相邻注释切分,表格适合按行或业务实体切分,并把字段名一起放进去。

切分后还要做去噪和去重:去掉导航、版权、重复页眉页脚,合并高度重复的片段,保留必要的父级标题和摘要。重叠 chunk 可以提升召回,但最终引用必须能定位到原文的 URL、页码、行号、章节或记录 ID。

索引层:向量、关键词和结构化索引并用

索引层回答的是“系统以后怎么快速找到这些片段”。可以把索引想成图书馆的目录卡:同一段内容可以同时按语义、关键词、标签和实体关系被查到。

只建向量索引通常不够。向量检索擅长处理“意思相近但说法不同”的问题,比如用户问“能不能退钱”,文档写的是“退款政策”。关键词索引适合错误码、版本号、函数名、产品名和精确术语,因为这些内容一旦写错一个字符,语义相似也没用。结构化索引适合按权限、时间、语言、产品线、版本、租户等条件过滤。

在更复杂的系统里,还可以加入知识图谱(Knowledge Graph)。知识图谱更适合表达“实体和实体之间的关系”,例如“企业版订单”属于“计费产品线”,“退款规则”适用于“订单创建后 7 天内”。当用户问题涉及实体关系或规则匹配时,知识图谱可以补充纯文本检索。

常见做法是为同一个 chunk 同时保存三类入口:可语义匹配的 embedding、可精确匹配的关键词文本、可过滤和排序的元数据。查询时先按权限和业务范围过滤,再组合不同索引的候选结果。

召回与重排:先广泛找候选,再筛出证据

召回层回答的是“用户一问,先找哪些资料”。召回不是一次就找出最终答案,而是先找一批可能相关的候选片段。这个阶段宁愿多找一点,也不要把真正有用的证据漏掉。

实际系统常用混合检索:向量召回负责找语义相近的片段,BM25 或关键词召回负责找精确词,结构化过滤负责排除权限不匹配、版本不匹配或产品线不匹配的内容,知识图谱匹配可以补充实体关系。对于表达模糊的问题,还可以用 query rewrite 把用户问题改写成更适合检索的查询,或用 multi-query 生成多个检索角度。

宽召回拿到 topN 候选后,再进入重排和过滤。重排关注的不是文本像不像,而是片段能不能回答当前问题;过滤要剔除低分、过期、越权、冲突或来源质量低的片段。最终进入上下文的 topK 片段应该少而准,并且每个片段都有可解释的入选原因。

还是用退款问题举例:召回阶段可能找出“退款政策”“企业版计费说明”“订单状态说明”“客服特批 SOP”等几十个片段;重排阶段要判断哪些片段真正能回答“超过 7 天还能不能退”,最后可能只保留两三个最关键证据。

生成层:管理上下文和输出引用

生成层回答的是“模型拿到证据后怎么组织答案”。这一层会把用户问题、证据片段、来源信息和回答规则拼成上下文,再交给模型生成。这里的重点不是让模型自由发挥,而是约束它只基于证据回答事实问题。

生成层通常会明确告诉模型:事实性结论必须由证据支持;证据不足时要说明缺失或拒答;多个来源冲突时要指出冲突,不要擅自二选一;答案里要带上可定位引用。这样做的目的,是把模型从“聊天者”变成“基于材料回答的助手”。

引用最好从检索结果的元数据一路传递到最终答案,而不是生成后再靠字符串匹配补引用。对于多片段答案,生成层还要处理证据合并、冲突提示、时间适用范围和输出格式,避免模型把不同版本、不同产品线或不同权限范围的材料混在一起。

评测层:分别评估检索、生成和引用

评测层回答的是“怎么知道这个 RAG 系统真的可靠”。RAG 的评测不能只看最终答案“像不像对”,因为一个看起来合理的答案可能没有证据支撑,也可能引用了错误来源。要把检索、重排、生成和引用分别评估。

常见指标可以这样理解:召回率看“正确证据有没有被找出来”;精确率看“放进上下文的片段是不是大多有用”;引用正确率看“答案里的引用能不能定位并支撑结论”;一致性看“答案是否和证据保持一致”;幻觉率看“模型有没有编造证据里不存在的内容”。

实践中可以维护一组包含高频问题、长尾问题、反例问题、权限问题和知识更新问题的测试集。每次调整切分策略、索引参数、重排模型或 prompt 后,都用同一套样例回归,避免某个指标变好时悄悄牺牲另一个指标。

工程流程

一个可维护的 RAG pipeline 通常分成离线知识处理、在线检索生成、质量评估和更新治理四层。

正在渲染图表…

1. 采集与权限过滤

先明确知识源的权威等级,而不是把所有文档直接入库。常见来源包括产品文档、工单、代码仓库、数据库导出、PDF、会议纪要、网页和人工维护的 FAQ。

采集阶段至少记录:

  • source_id:稳定来源标识。
  • source_type:网页、PDF、代码、表格、数据库记录等。
  • owner:资料负责人或系统。
  • acl:可见范围,避免检索时越权泄露。
  • version / updated_at:用于判断 freshness。
  • canonical_url:用户能打开或系统能追溯的来源地址。

2. 清洗与结构化

清洗不是把文本“弄干净”就完事。它要保留标题层级、表格、代码块、页码、行号、章节路径和时间戳。很多 RAG 误答来自清洗阶段丢掉了上下文,例如表格列名、警告块、代码注释或“本规则仅适用于旧版本”。

推荐做法:

  • HTML 保留 h1h6 的章节路径。
  • PDF 保留页码、段落顺序和表格边界。
  • 代码保留文件路径、符号名、起止行号。
  • 表格按行记录字段名,不要只拼成无列名纯文本。
  • 对重复页眉、页脚、导航和版权声明做去噪。

3. 切分 chunk

Chunk 的目标不是平均切 token,而是让每个片段在被单独召回时仍然可理解、可引用、可合并。切分策略应该随材料类型变化。

材料类型推荐切分方式关键元数据
产品文档按标题层级和段落切分,必要时带父标题摘要章节路径、URL、更新时间
API 文档一个 endpoint、参数表或返回结构为单元方法、路径、版本、语言
代码按文件、类、函数、相邻注释切分repo、commit、path、line_start、line_end
工单 / 对话按问题、结论、操作记录切分时间、参与人、状态、系统
表格按行或业务实体切分,字段名随内容一起进入文本sheet、row_id、字段名

一个常用原则是:chunk 可以重叠,但引用不能含糊。重叠内容用于提升召回,最终展示引用时仍应指向原文的明确位置。

4. 索引与召回

RAG 检索通常不只用一种方式:

  • 向量检索:适合语义相近但措辞不同的问题。
  • BM25 / 关键词检索:适合版本号、错误码、函数名、产品名、精确术语。
  • 混合检索:把语义召回和关键词召回合并,提升稳定性。
  • 元数据过滤:按权限、时间、产品线、语言、版本过滤。
  • Query rewrite:把用户问题改写成更适合检索的查询。
  • Multi-query:为同一问题生成多个检索角度,覆盖别名和近义表达。

工程上通常先做宽召回,再做重排。召回阶段追求不要漏,重排阶段再判断哪些片段真正能支撑答案。

5. 重排、过滤与上下文组装

重排模型或规则应该关注“是否能回答这个问题”,而不只是文本相似度。进入模型上下文前,还要过滤掉低分、过期、权限不匹配、互相冲突或来源质量较低的片段。

上下文组装时建议显式区分:

  • 用户问题。
  • 检索到的证据片段。
  • 每个片段的来源、时间和可信等级。
  • 回答规则,例如“没有证据时说明缺失,不要猜测”。
  • 引用格式,例如 [source:doc-17#L20-L35]

6. 生成、引用与日志

生成阶段要让模型知道:答案必须由证据支持,无法从证据推出的内容要标注不确定或拒答。引用不应该在生成后靠字符串匹配补上,而应该从上下文中的 source metadata 传递到最终答案。

日志至少记录:

  • 原始问题和改写后的 query。
  • 召回结果、分数和过滤原因。
  • 最终注入上下文的片段。
  • 模型输出和引用列表。
  • 用户反馈、人工修正和失败标签。

这些日志是后续调参、回归测试和问题复盘的基础。

关键例子

文档切分结构

下面是一个面向产品文档的 chunk 结构。重点是同时保存“可检索文本”和“可追溯引用”。

knowledge-chunk.json
{
  "chunk_id": "billing-docs:v3:refund-policy:002",
  "source_id": "billing-docs",
  "title": "退款规则",
  "section_path": ["计费", "退款", "退款规则"],
  "text": "企业版客户可在订单创建后 7 天内申请退款。已使用的增值服务费用不退还。",
  "canonical_url": "https://example.com/docs/billing/refund",
  "updated_at": "2026-05-18T10:20:00+08:00",
  "line_start": 42,
  "line_end": 45,
  "acl": ["support", "sales", "admin"],
  "metadata": {
    "product": "billing",
    "version": "v3",
    "language": "zh-CN"
  }
}

检索配置示例

retrieval-policy.ts
export const retrievalPolicy = {
  queryRewrite: true,
  recall: {
    vectorTopK: 40,
    keywordTopK: 20,
    metadataFilters: ["acl", "product", "version", "language"],
  },
  rerank: {
    topK: 8,
    minScore: 0.62,
  },
  context: {
    maxChunks: 6,
    maxTokens: 3500,
    requireCitation: true,
    preferFreshWithinDays: 90,
  },
};

回答引用示例

用户问:“企业版订单超过一周还能退款吗?”

好回答:

根据当前计费文档,企业版客户只能在订单创建后 7 天内申请退款;超过一周通常不符合退款窗口。已使用的增值服务费用也不退还。
来源:计费 / 退款 / 退款规则,2026-05-18,billing-docs:v3:refund-policy:002

坏回答:

一般来说企业版可以联系客服特殊处理。

坏回答的问题是:它可能符合客服经验,但没有来自当前证据的支持。如果确实存在人工特批流程,应检索到相应 SOP 后再回答。

常见坑

把向量库当成完整 RAG

向量库只解决“相似片段查找”的一部分问题。没有清洗、权限、重排、引用、日志和更新机制,系统上线后很难解释为什么答错。

Chunk 太碎或太大

太碎会丢掉标题、条件和例外;太大会让召回分数变钝,并浪费上下文窗口。切分后要抽样检查:单个 chunk 是否能独立回答一个小问题,多个 chunk 是否能组合回答复杂问题。

只评估最终回答,不评估召回

如果没有单独评估召回命中率和引用正确率,生成模型可能用“看起来合理”的语言掩盖检索失败。RAG 评测至少要拆成检索、重排、生成和引用四段。

忽略权限和多租户

检索前后都要做权限过滤。只在最终展示层过滤是不够的,因为模型可能已经在上下文里看到了不该看的片段。

Freshness 没有产品语义

不是所有资料都按时间越新越好。法规、合同、版本化 API 和历史工单都可能需要按适用范围选择知识。更新策略要同时考虑发布时间、版本、状态和业务生效范围。

引用不可打开或不可定位

“来源:内部文档”不是合格引用。用户或审核系统应能定位到 URL、页码、行号、章节或记录 ID。否则引用无法支撑信任。

让模型自己判断冲突事实

当多个来源冲突时,系统应该提供来源优先级、时间规则或人工仲裁机制。不要只把冲突片段全部塞给模型,让它自由选择。

检查清单

上线前可以用下面的清单做一次最小审查。

知识源

  • 是否列出了所有知识源、负责人和更新方式?
  • 是否区分权威来源、辅助来源和过期来源?
  • 是否记录了 source_id、版本、更新时间和 canonical URL?
  • 是否能从最终答案反查到原始材料?

切分与索引

  • Chunk 是否保留标题、表格字段、代码路径、页码或行号?
  • 是否针对不同材料类型使用不同切分策略?
  • 是否同时支持语义检索和关键词检索?
  • 是否有元数据过滤,例如权限、版本、语言、产品线?

检索与生成

  • 是否记录 query rewrite、召回分数、重排分数和过滤原因?
  • 是否有“召回为空”或“证据不足”的回答策略?
  • 是否限制模型只能基于证据回答事实问题?
  • 是否在答案中展示可定位引用?

评测与回归

  • 是否有覆盖高频问题、长尾问题、反例问题和权限问题的测试集?
  • 是否分别评估召回命中率、引用准确率、答案正确率和拒答质量?
  • 是否保存失败样例,并能在改索引、改 prompt、换模型后回归?
  • 是否有人审流程处理冲突事实和高风险回答?

运维与治理

  • 是否有增量更新、全量重建和回滚方案?
  • 是否监控索引延迟、检索耗时、空召回率和引用缺失率?
  • 是否能删除、下线或更正错误知识?
  • 是否有敏感信息脱敏和访问审计?

延伸阅读与来源

On this page