跳转至

当前 RAG 全链路

本页描述仓库里“当前已经落地”的 RAG 实现,不讨论远期方案或抽象蓝图。它回答的是:

  • 文档如何从上传一路走到可检索状态
  • 会话请求到来时,RAG 具体按什么顺序检索
  • 为什么 Semantic Splitter 配置正确,查询仍然可能查不到
  • 出问题时应该看哪里、改哪里、怎么排查

如果你只关心某一个局部:

一图看懂

flowchart TB
    SourceConfig[知识源与 Agent 配置]
    Upload[上传文档]
    Inspect[可选 Inspect 与 Preview]
    Ingest[触发 Ingestion]
    Parse[Parser 解析]
    Chunk[Chunking 策略分块]
    Embed[Embedding 写入向量]
    Stores[PostgreSQL 与 Qdrant]
    Mount[知识源挂载到 Agent]
    UserTurn[用户会话请求]
    Orchestrator[聊天编排器]
    SourceResolve[解析活跃 source 与 priority]
    VectorRecall[向量召回]
    CorrectionRecall[托管纠错定向召回]
    KeywordFallback[关键词回退补位]
    Rerank[可选重排]
    ContextBuild[回表并格式化上下文]
    LLM[模型生成回复]
    Debug[Turn RAG Debug 与 provenance]
    CorrectionPublish[管理员纠错发布]

    SourceConfig --> Upload --> Inspect --> Ingest --> Parse --> Chunk --> Embed --> Stores
    Stores --> Mount --> UserTurn --> Orchestrator --> SourceResolve --> VectorRecall
    VectorRecall --> CorrectionRecall --> KeywordFallback --> Rerank --> ContextBuild --> LLM
    LLM --> Debug
    CorrectionPublish --> Ingest

关键入口

用户与运维入口

入口 作用
Admin Knowledge Sources 创建知识源、上传文档、触发 ingestion、查看 inspect/run 状态
Admin Agent Detail 配置 Agent 默认行为,挂载知识源,决定聊天时可见 source 范围
Admin Conversations -> RAG 查看某个 Turn 的持久化 RAG provenance,并重新跑一次 retrieval debug
POST /stream / WS /ws/{session_id} 正常对话入口,聊天态 RAG 在这里被编排器调用
POST /sessions/{session_id}/turns/{turn_id}/rag-debug 运维排障入口,解释“为什么没命中这段知识”
POST /sessions/{session_id}/turns/{turn_id}/correction/publish 把管理员纠错重新写回托管纠错知识源

代码入口

路径 角色
ai_service/services/ingestion.py 摄取主编排,负责下载、解析、分块、向量化、写库
ai_service/services/chunking.py 分块策略与 capability 校验
ai_service/services/embedding.py 文本向量生成
ai_service/services/rag_retrieval.py 运行时检索主入口
ai_service/services/rag_debug.py 检索调试报告构建
ai_service/orchestrator/graph.py 默认聊天运行时中的 RAG 集成
ai_service/orchestrator/react_agent.py 实验 ReAct 运行时中的 RAG 集成
ai_service/utils/settings.py rag.top_krag.score_thresholdrerankmulti_query 等配置入口

第一段链路:知识如何变成可检索数据

1. 创建知识源并确定默认分块策略

知识源并不是“文件夹”概念,而是一个带默认 chunking 配置、挂载关系和后续文档集合的检索来源。

系统当前会在知识源层保存:

  • 默认 chunking strategy
  • 默认 chunking params
  • 文档与 ingestion job
  • 后续是否作为 Agent 的普通知识源或托管纠错来源参与检索

这里决定的是“文档如何被切开”,不是“查询如何被匹配”。

2. 上传文档

上传后,原始文件先落到对象存储,文档记录与后续状态落在 PostgreSQL。此时文档还不能被检索,只有等 ingestion 完成后,chunk 和向量才会进入检索链路。

3. 可选 Inspect

在真正 ingestion 前,可以先做 source 级或 document 级 inspect:

  • vector_health 用于检查当前知识源在 Qdrant 中的 live 向量健康状态
  • chunking_preview 用于预演候选分块策略与参数,不写 live chunk 和向量

这一步的价值是先回答“是不是切块策略本身就有问题”,避免盲目重建。

4. IngestionService 串起解析、分块、向量化和写库

当前摄取主链路是:

  1. 从 MinIO 下载原文件
  2. Parser 解析出纯文本与结构信息
  3. 选择生效的 chunking strategy
  4. 生成 document_chunks
  5. 调用 embedding service 批量生成向量
  6. 文本块写 PostgreSQL,向量写 Qdrant
  7. 更新文档与 ingestion job 状态

如果这一段失败,最常见的结果是:

  • 文档存在,但没有 chunk
  • chunk 存在,但没有向量
  • PostgreSQL 与 Qdrant 的状态不一致

这些问题通常不该从聊天检索侧开始看,而应先回到 ingestion 和 inspect。

第二段链路:会话请求到来时如何检索

1. 编排器决定是否进入 RAG

聊天请求带着 agent_id 进入编排器后,运行时才会触发 RAG。没有 agent_id、或者当前路径不需要聊天生成时,RAG 阶段会被跳过。

默认聊天运行时和实验 ReAct 运行时都复用同一套 RAG 服务,只是 orchestration 方式不同。

2. 先解析 Agent 当前可见的知识范围

检索不会扫全库,而是先读取:

  • Agent 当前挂载的 source
  • 每个 source 的 priority
  • 每个 source 的 source_kind

所以“查不到”最先要排除的,往往不是向量模型,而是:

  • source 没挂上
  • source 被停用
  • 命中的内容其实在别的 Agent 上

3. 主路径仍然是向量召回

当前实现的主路径仍是:

  1. 对 query 生成 embedding
  2. 在 Qdrant 中按 active source_id 过滤召回
  3. 合并多路向量结果
  4. 回表取回 chunk 文本

如果启用了 multi_query,系统会先生成多个 query 变体,再把多个召回结果按 chunk 合并。

4. 托管纠错来源会被额外照顾

如果某个 Agent 同时挂了普通知识源和 managed_correction 知识源,系统会额外对托管纠错来源做一次定向召回,再把结果并回主召回集合。

目的不是让纠错来源“天然得分更高”,而是避免它们在全局 top-k recall 阶段就被普通 source 淹掉。

最终排序时,系统还会优先考虑:

  1. 是否是 managed_correction
  2. agent_knowledge_links.priority
  3. 向量分数或 rerank 分数

5. 关键词回退只是一层补位安全网

当前 RAG 不是纯 hybrid retrieval。它仍然是“向量优先”,关键词回退只在特定条件下补位:

  • query 呈现明显关键词特征
  • 向量结果没有补满 top_k
  • 命中的 lexical chunk 仍然属于当前 Agent 可见的 source

关键词回退的目标是解决这类场景:

  • hazardous
  • ddp
  • cargo pulse
  • Is De Well certified to transport hazardous materials?

也就是“词很短”或“问句更长,但核心仍是几个强关键词锚点”的查询。

6. rerank 发生在向量候选之后

如果启用了 rerank,当前实现会扩大向量候选召回,再对向量候选做重排。关键词回退本身不是 rerank 的输入主路径,而是最终补足剩余 slots 的机制。

这意味着:

  • rerank 主要改善语义候选排序
  • 它不能替代“短关键词根本召不回来”的问题
  • 关键词回退也不会主动打乱已有 vector-first 结果

7. 最后才把 chunk 格式化成注入给模型的上下文

RAGRetrievalService 最终会把命中的 chunk 转成格式化上下文文本,再交给模型生成阶段使用。

chat Agent 来说,还有一个额外开关:

  • hide_rag_source_filename

它只影响给模型看的上下文头部,不影响后台 provenance、审计和 debug 里的真实文件名。

第三段链路:为什么 Semantic Splitter 配好了,查询仍然可能失败

这是当前最容易误解的一点。

Semantic Splitter 只决定 chunk 如何切,不决定 query 如何命中

像下面这类参数:

  • Buffer Size
  • Breakpoint Percentile Threshold

影响的是:

  • chunk 的边界
  • chunk 的平均长度
  • 一段文本被切成几块

它们不直接决定:

  • 查询是否一定能在向量阶段过阈值
  • 单个关键词是否一定能召回
  • 长问句里的专有词是否一定被 embedding 模型识别为高相似

为什么会出现“文档里明明有词,但还是查不到”

当前实现里,真正决定运行时命中的因素至少有四层:

  1. 文档是否已成功 ingestion 并写入向量库
  2. 该 source 是否已挂到当前 Agent
  3. 向量召回是否通过 rag.score_threshold
  4. 在向量结果不足时,关键词回退是否被判定为 eligible

因此,Semantic Splitter 参数偏保守时,常见后果反而是 chunk 更大、语义更平均。对 hazardous 这类短 query 来说,embedding 相似度可能被更长 chunk 稀释,最后在阈值处被直接过滤掉。

当前系统如何缓解这类问题

当前真实缓解手段有三类:

  • 调整 chunking 策略与参数,改变 chunk 颗粒度
  • 调整运行时 rag.top_krag.score_threshold
  • 依赖关键词回退为明显关键词 query 补位

所以这类问题不应只盯着 Semantic Splitter 本身看,而应把“摄取结果 + source 挂载 + 检索阈值 + fallback 条件”一起看。

第四段链路:纠错如何重新进入 RAG

管理员在 Conversations 页面做回答纠错后,可以把纠错内容发布到知识库。

这条链路会:

  1. 生成或复用 Agent 对应的 managed_correction 知识源
  2. 生成候选纠错文档
  3. 触发 ingestion
  4. 等 ingestion 成功后进入检索链路

从那之后,它会在后续检索中被优先考虑,并通过 source kind / priority 与普通知识源共同参与最终排序。

这也是为什么“模型答错了但后来被修正”,不一定要改系统提示词或重新训练模型,很多时候只要看 correction 是否成功发布并完成 ingestion。

排障入口

1. 先看哪一层出问题

现象 优先检查
文档刚上传后完全查不到 ingestion job、document 状态、inspect
某个 Agent 查不到,另一个 Agent 查得到 挂载关系与 source_id 可见范围
文档里明明有词,短 query 仍 miss rag.score_threshold、关键词回退是否触发
管理员纠错发布后仍没生效 correction publish 状态、managed correction ingestion 是否成功
模型引用内容不对,但检索其实命中了 看 final retrieval 和注入上下文,而不是只看 raw candidates

2. rag-debug 用来回答什么问题

POST /sessions/{session_id}/turns/{turn_id}/rag-debug 主要回答:

  • 当前 Agent 到底挂了哪些 source
  • raw vector candidates 有没有命中
  • final retrieval 里实际保留了哪些 chunk
  • 这次是否启用了 keyword fallback
  • expected text 对应的 chunk 是否进入 raw candidate 或 final retrieval

当前 Admin Conversations -> RAG 页签也已经把这些信息暴露出来,可以基于选中 Turn 直接重跑调试。

3. 当前最常改的配置入口

运行时默认值现在统一从 [rag] 读取:

[rag]
top_k = 5
score_threshold = 0.3

对应环境变量:

  • RAG_TOP_K
  • RAG_SCORE_THRESHOLD

另外常见联动配置还有:

  • [multi_query]
  • [rerank]
  • 知识源默认 chunking strategy / params
  • Agent 的 hide_rag_source_filename

建议阅读顺序

如果你是第一次接手这套 RAG,建议按这个顺序阅读:

  1. 本页,先建立全链路心智模型
  2. RAG 知识库管理,理解 Admin 操作与 ingestion
  3. RAGRetrievalService 概览,理解运行时检索细节
  4. 编排器,理解 RAG 在整轮聊天中的位置
  5. 端点概览,定位 debug / correction / knowledge source API

相关页面