Files
SuperBizAgent-java/mvp/architecture/RAG知识检索架构.md
zhuyongxin 7ae9707a3b feat(harness,rag): dual LLM audit fields, run conclusion, and hybrid quality
Persist provider reasoning and assistant text separately on agent_reasoning_audit
(DeepSeekAssistantMessage path), extract diagnosis_run.conclusion, enrich RAG
tool audit (step_id/query/qualityScore), gate empty mysql tools, drop devtools,
and align MVP docs after live E2E verification.
2026-07-28 19:43:13 +08:00

12 KiB
Raw Permalink Blame History

RAG 知识检索架构

更新日期:2026-07-28
状态:当前可运行架构
关联实现:lookup_knowledge、MilvusHybridKnowledgeStore、KnowledgeSearchPort
关联运维:scripts/rebuild_hybrid_knowledge.py、POST /api/knowledge/rebuild-hybrid

1. 定位

知识检索是 Diagnosis Agent 的显式证据工具,不是隐式 Advisor。

Diagnosis Agent
  -> lookup_knowledge(query)
  -> Harness ToolBoundary / ACI projection
  -> EvidenceGuard 只认当前 Run 的 READY canonical 证据

目标:

  • 保留 Agent 可见的工具调用与证据边界
  • 用单一向量后端完成 dense + BM25 hybrid 检索
  • 用 chunk 级证据身份保证同文档多片段可同时进入上下文
  • 检索行为可配置、可重建、可审计

2. 稳定边界

flowchart LR
    subgraph AgentBoundary["Agent boundary"]
        Agent["Diagnosis Agent"]
        Tool["lookup_knowledge"]
    end

    subgraph HarnessBoundary["Harness boundary"]
        Adapter["RagToolAdapter"]
        Projector["RagResultProjector"]
        Canonical["Redis canonical invocation"]
    end

    subgraph RetrievalBoundary["Retrieval boundary"]
        Backend["LookupKnowledgeTool"]
        Port["KnowledgeSearchPort"]
        Store["MilvusHybridKnowledgeStore"]
    end

    Agent --> Tool
    Tool --> Adapter
    Adapter --> Backend
    Backend --> Port
    Port --> Store
    Adapter --> Projector
    Adapter --> Canonical
边界 职责 不负责
Agent 决定何时检索、如何用证据写报告 不直接访问 Milvus / MySQL 元数据表
Harness Tool 校验、投影裁剪、canonical 存证 不改写检索排序算法
Retrieval L0 hint、dense/BM25 召回、后处理、打包 不绕过 ACI 直接给 Agent 原始库响应

3. 当前主链路

flowchart TD
    A["lookup_knowledge(query)"] --> B["KnowledgeQueryTransformer"]
    B --> C["L0 hint: domain / keywords / categoryFilter"]
    C --> D["KnowledgeDocumentRetriever"]
    D --> E["KnowledgeSearchPort"]
    E --> F["VectorSearchService"]
    F --> G{"retrieval.search.mode"}
    G -->|dense| H["MilvusHybridKnowledgeStore.searchDense"]
    G -->|hybrid| I["MilvusHybridKnowledgeStore.searchHybrid"]
    H --> J["candidates + chunk identity"]
    I --> J
    J --> K["KnowledgeEvidencePostProcessor"]
    K --> L["evidenceKey dedup / maxChunksPerDocument / return-n"]
    L --> M{"filtered low quality?"}
    M -->|yes and had categoryFilter| N["unfiltered retry"]
    N --> K
    M -->|no| O["KnowledgeContextPacker"]
    O --> P["LookupResultAssembler"]
    P --> Q["RagResultProjector"]
    Q --> R["Agent-facing RagToolResult"]

对应代码:

阶段 类 职责
Tool 编排 LookupKnowledgeTool 串联 transform / retrieve / post / pack
Query 理解 KnowledgeQueryTransformer + KnowledgeIndexService L0 只产 hint 与可选 category filter
检索端口 KnowledgeSearchPort / VectorKnowledgeSearchAdapter 屏蔽底层存储细节
检索门面 VectorSearchService dense 或 hybrid 路由
向量后端 MilvusHybridKnowledgeStore 唯一知识库读写后端(MilvusClientV2)
后处理 KnowledgeEvidencePostProcessor 归一化、规则 boost、chunk 去重、相关度等级
打包 KnowledgeContextPacker 有界 context pack
投影 RagResultProjector 只暴露 Agent 可见 evidence 字段

4. 唯一向量后端:MilvusClientV2

4.1 已废弃路径

以下路径不再用于 lookup_knowledge:

  • legacy MilvusServiceClient search / insert
  • retrieval.vector-store.mode=sdk|spring|auto
  • Spring AI VectorStore 作为知识检索主路径

4.2 当前后端

写入:
  VectorIndexService
    -> MilvusHybridKnowledgeStore.upsertChunk

读取:
  VectorSearchService
    -> MilvusHybridKnowledgeStore.searchDense
    -> MilvusHybridKnowledgeStore.searchHybrid

默认 collection:

milvus:
  collection: biz

重建时会 drop + recreate 该 collection,并按 dense + BM25 schema 重建。

5. Collection Schema

biz(可配置)逻辑字段:

字段 类型 用途
id VarChar PK chunk 级主键
content VarChar 返回给 Agent 的原文片段
search_text VarChar + analyzer BM25 输入文本
sparse_vector SparseFloatVector BM25 Function 输出
vector FloatVector dense embedding
metadata JSON docId / chunkIndex / category / kb_scope / title / breadcrumb 等

Function:

BM25(search_text -> sparse_vector)

索引:

vector        -> IVF_FLAT + L2
sparse_vector -> SPARSE_INVERTED_INDEX + BM25

写入时:

  • content 保存原始 chunk 正文
  • search_text / dense embedding 使用 Title + Path + Content 拼装文本
  • metadata 必须带 docId、chunkIndex,供 chunk 级证据身份使用

6. 检索模式

配置:

retrieval:
  search:
    mode: hybrid   # dense | hybrid(见 6.0 用途约定)
  hybrid:
    rrf-k: 60
  kb-scope: ""
rag:
  retrieve-k: 20
  return-n: 5
  max-chunks-per-document: 2

6.0 模式用途约定(保留双 mode 的原因)

知识库 只维护一套 dense + BM25 schema 数据(默认 collection biz)。
retrieval.search.mode 切换的是同库上的查询算法,不是两套互斥索引、也不是两套写入路径。

模式 定位 说明
hybrid 线上主路径 / 默认 dense ANN + 服务端 BM25 + RRF;lookup_knowledge 正式召回只认此模式
dense 对照 / 评测 / 排障 仅 dense ANN,用于和 hybrid 对比召回效果(命中文档/chunk、排名差异等)

约定:

  1. 生产配置保持 mode: hybrid;不要把 dense 当成第二套长期并行的线上策略。
  2. 需要看「去掉 BM25+RRF 后召回差在哪」时,临时切 mode: dense,其它参数(retrieve-k、return-n、category filter、query 集)尽量固定,再切回 hybrid。
  3. hybrid 入库的数据 完全适用于 dense-only 查询:每条 chunk 都写了 vector;dense 模式只是不使用 sparse_vector / BM25 子路。
  4. 代码里 @Value 在配置缺失时的兜底仍可能是 dense(历史兼容);以 application.yml 的 hybrid 为准。若做回归,确认运行配置而不是只看注解默认值。

不建议的用法:

  • 按请求/按租户在 dense 与 hybrid 之间当产品功能随意切换(当前也无稳定的 per-call mode 覆盖)。
  • 把 dense 模式的相关度表现直接当成 hybrid 的最终质量结论(hybrid 排序信 RRF,后处理分数仍多 L2 兼容,见下节)。

6.1 dense(对照基线)

query
  -> embedding
  -> dense ANN on vector
  -> topK

仅走 vector 字段的 L2 ANN。用于基线对比,不作为正式主路径。

6.2 hybrid(当前默认 / 主路径)

query
  -> path A: dense ANN(query embedding)
  -> path B: BM25 sparse ANN(raw query text)
  -> Milvus hybridSearch + RRFRanker(k)
  -> topK fused hits

说明:hybrid 内部的 dense 子路是融合的一部分,与配置项 mode=dense(整次检索只跑单路 ANN)不是同一概念。

分数与后处理(quality 统一,2026-07-28):

  • 一级 scoreLabel 仅 dense | hybrid(旧别名 canonicalize)。
  • dense:score = L2;qualityScore = 1 - clamp(L2)/maxL2Distance。
  • hybrid:返回序 = RRF 序;qualityScore 由 本轮 rank 线性映射(不把 RRF 原分当 L2;不做 dense L2 回填覆盖主分;无 m25_only_* 一级 label)。
  • 后处理:统一消费 qualityScore;排序主序 = originalRank;不做 L0 关键词/domain contains 加分改序(重叠仅可写 hitReasons 解释)。

elevance_level / category 低质 unfiltered retry:只看 top qualityScore 与阈值。

  • 实现:RetrievalScoreNormalizer、KnowledgeEvidencePostProcessor;详见 OpenSpec ag-quality-score-unify。

6.3 category filter 与降级

if L0 给出唯一 domain:
  先 filtered 检索
  if 无证据或 topSimilarity < referenceThreshold:
    再 unfiltered retry
else:
  直接 unfiltered

这里的 filter 是 metadata category / kb_scope 约束,不是第二套向量库。

7. 证据身份与去重

Delivery 1 已落地:

evidenceKey =
  docId#chunk-{chunkIndex}
  fallback: vector:{id}
  fallback: rank:{n}

规则:

  • 去重按 evidenceKey,不是按 source 文档路径
  • 同文档不同 chunk 可同时保留
  • rag.max-chunks-per-document 限制单文档最多进入结果的 chunk 数
  • rag.return-n 限制后处理后最多返回条数
  • Agent 投影中的 document_id 使用 chunk 级 evidenceKey

这保证 hybrid 召回的多片段不会在后处理/投影阶段被文档级折叠吞掉。

8. L0 / L1 职责

层 做什么 不做什么
L0 domain/keyword hint、可选 category filter、trace 解释、轻规则 boost 不直接当事实 evidence
L1 dense/BM25 事实证据召回 不依赖 frontmatter 关键词命中才返回正文

L0 命中文档正文不会在 L1 失败时兜底成 evidence。

9. Agent 可见契约

Agent 只看到有界 RagToolResult:

  • evidence_status
  • tool_call_id
  • query
  • evidence[]:document_id / source / title / breadcrumb / excerpt
  • relevance_level
  • truncated / returned_count

不暴露:

  • raw score / fused score
  • retrievalTrace / rerankTrace
  • contextPack 全文
  • Milvus 内部字段与凭据

完整内部结果仍在 LookupResult 中,供审计与调试使用。

9.1 Trace 与审计(现行入口)

请求内 retrievalTrace / 落库 tool_invocation / Trace API 读法见:

RAG检索可观测性与审计.md

要点:

  • Agent 看不到完整 retrievalTrace;人通过 GET /api/diagnosis/{sessionId}/trace 的 toolInvocations[].retrievalDetails 回放。
  • relevance_level 列存 RAG 等级(PRECISE/REFERENCE);evidence_status 在 details JSON。
  • 默认审计不落原始 query 全文与 excerpt 正文。

10. 写入与重建

10.1 日常写入

文档上传 / 知识库初始化:

markdown
  -> frontmatter + body
  -> DocumentChunkService
  -> dense embedding + search_text
  -> MilvusHybridKnowledgeStore.upsertChunk
  -> MySQL api_document + L0 memory index

10.2 全量重建

危险操作,需显式确认:

python scripts/rebuild_hybrid_knowledge.py --confirm REBUILD

等价 API:

POST /api/knowledge/rebuild-hybrid?confirm=REBUILD

服务端顺序:

  1. drop + recreate milvus.collection(默认 biz)
  2. 清空 MySQL api_document
  3. 清空内存 L0
  4. 扫描 knowledge_base/**/*.md(跳过 README.md)force 导入

不会修改磁盘上的 knowledge_base/ 源文件。

11. 与旧文档的差异

旧描述(已归档) 当前实现
Spring AI VectorStore 主路径 + SDK fallback 单一 MilvusClientV2 后端
retrieval.vector-store.mode=auto/sdk/spring 已移除;改为 retrieval.search.mode=dense/hybrid
source 级 evidence 去重 chunk 级 evidenceKey 去重
应用层 sparse-lite lexical 伪 hybrid 库内 dense ANN + BM25 + RRFRanker
新建 biz_hybrid 过渡 collection 默认使用并重建 biz

历史材料见:

  • mvp/architecture/archive/2026-07-22-legacy/rag-architecture.md
  • mvp/architecture/archive/2026-07-22-legacy/modular-rag-pipeline.md

12. 当前已知边界

  • hybrid 依赖云端/实例支持 BM25 Function 与 sparse index
  • 全量重建受 embedding API 与 Milvus 写入延迟影响,可能较慢
  • L0 关键词匹配仍较粗,只作 hint,不作主召回
  • 尚未做邻块上下文自动扩展、cross-encoder rerank、真 query rewrite
  • totalVectors 统计接口仍可能返回 0,不代表 collection 为空;以 rebuild/init 结果与检索命中为准
  • retrieval.search.mode=dense 仅作召回对照,不是第二套主路径
  • 同一 hybrid schema 数据可被 dense / hybrid 两种查询复用;从纯旧 dense-only collection 升级必须 rebuild
  • hybrid 质量闸门优先用 denseDistance 绝对 L2;无 dense 时 rank 回退;排序仍跟 RRF
  • 后处理不再用 L0 关键词 boost 改序;词面信号以库内 BM25+RRF 为准
  • Trace/审计细节与限制见 RAG检索可观测性与审计.md