docs(mvp): update RAG docs for py-rag extraction
- rewrite RAG architecture doc for py-rag service contract mapping, ingest and rebuild ops - refresh observability/trace doc for post-L0 single-attempt semantics - add py-rag chain exploration note; mark superseded Milvus/L0 notes with status banners - close ISS-017 (superseded by L0 sinking); mark knowledge_domain table orphaned - refresh architecture/mvp/engineering indexes
This commit is contained in:
@@ -20,14 +20,19 @@
|
||||
|
||||
## RAG
|
||||
|
||||
> 2026-09-29 RAG 模块已抽离为独立 py-rag 知识服务(架构见
|
||||
> [../architecture/RAG知识检索架构.md](../architecture/RAG知识检索架构.md))。
|
||||
> 下列纪要保留决策过程价值,涉及进程内 Milvus / L0 的实现细节以各文顶部说明为准。
|
||||
|
||||
| 文档 | 内容 |
|
||||
|---|---|
|
||||
| [rag/RAG排序-多路召回与RRF.md](rag/RAG排序-多路召回与RRF.md) | K、多路融合、RRF、L0 边界 |
|
||||
| [rag/RAG-Hybrid质量分与后处理.md](rag/RAG-Hybrid质量分与后处理.md) | qualityScore 统一、L2 伪装废止、后处理 |
|
||||
| [rag/RAG-Agent如何读relevance_level.md](rag/RAG-Agent如何读relevance_level.md) | Agent 侧相关度标签含义与误读 |
|
||||
| [rag/RAG离线评测-基线设计.md](rag/RAG离线评测-基线设计.md) | Golden/Fixture、hybrid 评测与闸门 |
|
||||
| [rag/Milvus-Hybrid接入清单.md](rag/Milvus-Hybrid接入清单.md) | Hybrid 交付拆分与接入清单 |
|
||||
| [rag/RAG审计补丁-stepid-query-E2E验收.md](rag/RAG审计补丁-stepid-query-E2E验收.md) | step_id / query 审计 live 验收 |
|
||||
| [rag/RAG证据链探索笔记-从py-rag响应到引用验真.md](rag/RAG证据链探索笔记-从py-rag响应到引用验真.md) | **抽离后链路首发导读**:数据逐站形态、关键字段、设计哲学与化石清单 |
|
||||
| [rag/RAG排序-多路召回与RRF.md](rag/RAG排序-多路召回与RRF.md) | K、多路融合、RRF、L0 边界(实现已下沉 py-rag,判断框架仍有效) |
|
||||
| [rag/RAG-Hybrid质量分与后处理.md](rag/RAG-Hybrid质量分与后处理.md) | qualityScore 统一、后处理(分数语义现为 RERANK 直传) |
|
||||
| [rag/RAG-Agent如何读relevance_level.md](rag/RAG-Agent如何读relevance_level.md) | Agent 侧相关度标签含义与误读(现行) |
|
||||
| [rag/RAG离线评测-基线设计.md](rag/RAG离线评测-基线设计.md) | Golden/Fixture、hybrid 评测与闸门(需按新语义重新校准) |
|
||||
| [rag/Milvus-Hybrid接入清单.md](rag/Milvus-Hybrid接入清单.md) | Hybrid 交付拆分与接入清单(已过时,仅历史追溯) |
|
||||
| [rag/RAG审计补丁-stepid-query-E2E验收.md](rag/RAG审计补丁-stepid-query-E2E验收.md) | step_id / query 审计 live 验收(现行) |
|
||||
|
||||
架构对照:
|
||||
|
||||
@@ -36,7 +41,7 @@
|
||||
|
||||
相关 Issue:
|
||||
|
||||
- [ISS-017 L0 过滤收窄与 Fallback 加固](../issues/active/ISS-017-rag-l0-filter-fallback-hardening.md)(暂缓,保持现网)
|
||||
- [ISS-017 L0 过滤收窄与 Fallback 加固](../issues/active/ISS-017-rag-l0-filter-fallback-hardening.md)(已失效:L0 下沉 py-rag,问题前提不复存在)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
**状态**:SUCCESS 完整诊断主文档;工具阶段按**现行**审计能力(`step_id` / `query`)说明
|
||||
**文档路径**:`mvp/engineering/diagnosis/一次诊断全流程-E2E导读.md`
|
||||
|
||||
> **现状说明(2026-09-29)**:本文基于 2026-07 的 live Run 编写,图中 `VectorSearchService` /
|
||||
> `MilvusHybridKnowledgeStore`(进程内 Milvus)已替换为 py-rag 服务调用;审计/Trace 结构不变。
|
||||
> 当前检索链路见 [../architecture/RAG知识检索架构.md](../architecture/RAG知识检索架构.md)。
|
||||
|
||||
### 主样本(正文数值与 timeline 来源)
|
||||
|
||||
| 项 | 值 |
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# Harness RAG 检索体系学习笔记:从 query 到可验证证据
|
||||
|
||||
> **现状说明(2026-09-29)**:RAG 模块抽离后,本文「检索前 L0 导航」与
|
||||
> `MilvusHybridKnowledgeStore` / `KnowledgeQueryTransformer` 相关章节描述的类已删除
|
||||
> (L0 与向量检索下沉 py-rag 服务端);检索后处理/打包/投影/审计部分仍然现行。
|
||||
> 当前架构见 [../../architecture/RAG知识检索架构.md](../../architecture/RAG知识检索架构.md)。
|
||||
|
||||
**更新日期**:2026-08-03
|
||||
**主题**:lookup_knowledge 完整后端链路——检索前/检索/检索后/打包/组装/降级/契约/验证
|
||||
**设计文档**:`mvp/engineering/rag/`(RAG 排序、Hybrid 质量分、relevance_level 等)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# Harness 整体架构学习笔记:从装配到入口到记忆到知识库写入
|
||||
|
||||
> **现状说明(2026-09-29)**:RAG 模块抽离后,本文「知识库写入链路」章节描述的
|
||||
> `KnowledgeIndexService` / Milvus 写入路径已删除(入库下沉 py-rag `documents:ingest`);
|
||||
> 其余装配/入口/记忆章节仍现行。当前架构见
|
||||
> [../../architecture/RAG知识检索架构.md](../../architecture/RAG知识检索架构.md)。
|
||||
|
||||
**更新日期**:2026-08-04
|
||||
**主题**:整体架构五块补充(了解层面)——配置装配中心 / HTTP 入口层 / 会话系统与记忆体系 / 知识库写入链路
|
||||
**配套**:九域主线笔记(core/retry/progress/tool/guard/release/agent/audit/contract 全 ✅)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# Milvus Hybrid Search 接入对照清单
|
||||
|
||||
> **已过时(2026-09-29)**:RAG 模块已抽离为独立 py-rag 知识服务,进程内 Milvus
|
||||
> (`MilvusHybridKnowledgeStore` / `VectorSearchService`)与本文描述的接入路径已整体删除。
|
||||
> 当前检索架构与契约映射见 [../../architecture/RAG知识检索架构.md](../../architecture/RAG知识检索架构.md)。
|
||||
> 本文仅作历史决策追溯。
|
||||
|
||||
**日期**:2026-07-27
|
||||
**前提**:旧 Milvus SDK 直连检索路径后续废弃,不作为长期实现基础
|
||||
**目标**:在现有 `lookup_knowledge` pipeline 上接入 dense + sparse/BM25 混合检索,融合优先走服务端 RRF
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# 混合检索上线之后:为什么还要统一 qualityScore,以及上一代后处理错在哪
|
||||
|
||||
> **现状说明(2026-09-29)**:本文讨论的后处理排序/去重/判级仍在 Java 侧
|
||||
> (`KnowledgeEvidencePostProcessor` / `RetrievalScoreNormalizer`);
|
||||
> 但分数语义已变:py-rag 服务端返回 rerank 绝对分(scoreLabel=`RERANK`,[0,1] 越大越好),
|
||||
> quality 直传,不再走本文所述 dense L2 / hybrid rank 归一化分支(分支保留作兼容)。
|
||||
> 判级阈值 0.75/0.5 与 py-rag 契约一致。当前架构见
|
||||
> [../../architecture/RAG知识检索架构.md](../../architecture/RAG知识检索架构.md)。
|
||||
|
||||
**日期**:2026-07-28
|
||||
**范围**:hybrid 检索后的分数语义、后处理排序、相关度闸门、scoreLabel 约定
|
||||
**读者**:已经(或准备)上 dense+BM25+RRF,却发现「召回变了、质量判断还拧着」的工程同学
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# 诊断 Agent 场景下的 RAG 排序:从 K=3 规则加分,到多路召回与 RRF
|
||||
|
||||
> **现状说明(2026-09-29)**:本文的排序判断框架(多路召回、RRF、Rerank 选型)仍是理解
|
||||
> py-rag 服务端检索设计的背景材料;但 RRF 融合、BM25、rerank 的**实现**已下沉 py-rag 服务端,
|
||||
> Java 侧不再有 `RrfFusion` / `MilvusHybridKnowledgeStore`。
|
||||
> 当前架构见 [../../architecture/RAG知识检索架构.md](../../architecture/RAG知识检索架构.md)。
|
||||
|
||||
**日期**:2026-07-27
|
||||
**范围**:知识检索排序、多路召回、分数融合、Rerank 选型
|
||||
**读者**:需要在 Agent 系统里落地 RAG,而不是只做 Demo 问答的工程同学
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# RAG 离线评测:讨论、设计与落地
|
||||
|
||||
> **需重新校准(2026-09-29)**:RAG 模块抽离后,L0 hint / categoryFilter 已下沉 py-rag、
|
||||
> 质量分改为 RERANK 直传、attempt 只剩 `UNFILTERED_VECTOR`;本文设计的 fixture/baseline
|
||||
> 基于旧 L0/scoreLabel 语义构建,回归评测需按新语义重新生成 fixture 并校准闸门。
|
||||
> 当前架构见 [../../architecture/RAG知识检索架构.md](../../architecture/RAG知识检索架构.md)。
|
||||
|
||||
**日期**:2026-07-28
|
||||
**范围**:`eval/rag-retrieval` 离线 baseline、fixture 生成、与 hybrid/quality 主路径对齐
|
||||
**读者**:要维护或扩展知识库回归评测的工程同学
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
# RAG 证据链探索笔记:从 py-rag 响应到引用验真
|
||||
|
||||
**日期**:2026-09-29
|
||||
**范围**:RAG 模块抽离后的完整数据链路——每个站点"数据长什么样、字段怎么来、为什么这样设计"
|
||||
**读者**:要理解或维护 `lookup_knowledge` 证据链的工程同学
|
||||
**关联文档**:
|
||||
|
||||
- 架构规范:[../../architecture/RAG知识检索架构.md](../../architecture/RAG知识检索架构.md)(本次抽离后的权威口径)
|
||||
- Trace / 审计:[../../architecture/RAG检索可观测性与审计.md](../../architecture/RAG检索可观测性与审计.md)
|
||||
- 契约原文:py-rag 仓库 `docs/Java接入文档.md`(API v1 冻结面)
|
||||
- 前置知识:`RAG-Hybrid质量分与后处理.md`(分数语义演进史)
|
||||
|
||||
> 本文按一次真实代码探索的顺序组织:从 py-rag 返回的 JSON 出发,沿着数据走过的每一站,
|
||||
> 讲清关键字段、加工规则与设计取舍,最后到 Agent 引用验真收口。
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景路线图
|
||||
|
||||
```text
|
||||
py-rag JSON ──► KnowledgeSearchHit 站点1-2:数据形态与防腐层映射
|
||||
│
|
||||
① 后处理 ──► EvidencePostprocessResult 站点3:去重/限流/判级
|
||||
│
|
||||
② 打包 ──► ContextPack 站点4:文本储备(非 Agent 口粮)
|
||||
│
|
||||
③ 组装 ──► LookupResult 站点5:内部真相全集
|
||||
│
|
||||
④ 投影 ──► RagToolResult ──► Agent 站点6-7:冻结契约与 Agent 视图
|
||||
│
|
||||
⑤ Agent 写报告引用 toolCallId
|
||||
│
|
||||
⑥ EvidenceGuard 对账验真 ──► 语义审查 ──► 发布 站点8:证据安全链
|
||||
(旁路:每站关键字段 ──► tool_invocation 审计表,供人排障)
|
||||
```
|
||||
|
||||
一句话定位:**py-rag 负责"把对的片段找出来",Java 侧负责"把找到的证据管起来"**;
|
||||
检索质量可以整体外换,证据治理一寸不动——这次抽离(71 文件 / 约 -8000 行)本身就是证明。
|
||||
|
||||
---
|
||||
|
||||
## 1. 站点一:py-rag 返回的数据结构
|
||||
|
||||
响应 JSON 按职责分四块,四块数据两条去路(①②③喂后处理,④喂审计):
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "网关超时怎么排查", // ① 入参回显
|
||||
"mode": "hybrid", // ① 查询模式(hybrid | semantic)
|
||||
"hits": [ // ② 命中数组(检索的基本单位是 chunk)
|
||||
{
|
||||
"evidence_key": "e2e-gateway-b9c1fa12-md-34223174#chunk-1", // chunk 级身份
|
||||
"document_id": "e2e-gateway-b9c1fa12-md-34223174", // 所属文档
|
||||
"source": "e2e-gateway-b9c1fa12-md", // 来源路径
|
||||
"title": "网关超时排查",
|
||||
"breadcrumb": "网关超时排查 > 处理步骤",
|
||||
"excerpt": "网关超时先检查 upstream 配置…", // 正文
|
||||
"quality_score": 0.9147, // rerank 绝对分
|
||||
"relevance_level": "PRECISE" // py-rag 自己的判级(Java 不用)
|
||||
}
|
||||
],
|
||||
"relevance_level": "PRECISE", // ③ 顶层判级(top1)
|
||||
"evidence_status": "supported", // ③ 业务状态
|
||||
"retrieval_trace": { // ④ 过程记录(不参与后处理,进审计)
|
||||
"mode": "hybrid", "filters": {"category": "gateway"},
|
||||
"recall_count": 20, "rerank_model": "BAAI/bge-reranker-v2-m3",
|
||||
"no_evidence_basis": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
关键语义:
|
||||
|
||||
- **`evidence_status` 只有两类**:`supported`(正常)/ `no_evidence`(查到了但都是垃圾或无候选,
|
||||
此时 `hits` 恒为空)。它是 **200 正常业务响应**,Java 侧直接走"无知识可用"分支,不重试不报错。
|
||||
- **`relevance_level` 是分数的档位化**:`quality_score ≥0.75 → PRECISE`,`≥0.5 → REFERENCE`,
|
||||
`<0.5` 不输出。阈值当前未校准(契约已知边界)。**它是给模型的,分数是给系统的**——
|
||||
同一信息两种表达,服务两种消费者。
|
||||
- **命中没有独立 metadata map**(旧 Milvus 方案遗留概念),文档归属信息就是那几个平铺字段。
|
||||
|
||||
---
|
||||
|
||||
## 2. 站点二:防腐层映射——`KnowledgeSearchHit`
|
||||
|
||||
`PyRagKnowledgeSearchAdapter` 把每个 hit 映射成可移植结构。从此全 Java 侧只认这个类型,
|
||||
py-rag 字段再怎么变只改 adapter 一处。后处理实际只消费其中 **7 个字段**:
|
||||
|
||||
```text
|
||||
evidenceKey → 去重键(docId#chunk-N,原样采纳)
|
||||
docId / chunkIndex → 文档分桶(单文档上限);chunkIndex 从 "#chunk-N" 解析
|
||||
content ← excerpt,最终证据正文
|
||||
score + scoreLabel ← quality_score + 常量 "rerank"(quality 直传)
|
||||
originalRank ← 数组下标 +1,排序的权威
|
||||
source / title / breadcrumb → 透传展示
|
||||
```
|
||||
|
||||
`retrieval_trace` 不进这条链——它走审计旁路。**进入后处理时,每个 hit 被浓缩成
|
||||
"身份 + 分数 + 名次 + 正文"四组信息,四步加工全部围绕它们转。**
|
||||
|
||||
---
|
||||
|
||||
## 3. 站点三:后处理四步——`EvidencePostprocessResult`
|
||||
|
||||
```text
|
||||
① 打分 score + scoreLabel → qualityScore(RERANK 分支直传,clamp [0,1])
|
||||
② 排序 只按 originalRank —— ★ 分数不参与排序,只用于判级
|
||||
③ 去重截断 evidenceKey 去重 → 单文档 chunk ≤2 → 总数 ≤5(return-n)
|
||||
④ 判级 顶分 ≥0.75 PRECISE / ≥0.5 REFERENCE / 否则无档
|
||||
```
|
||||
|
||||
输出结构逐字段(7 条命中进、5 块存出的例子):
|
||||
|
||||
| 字段 | 规则 | 例值 |
|
||||
|---|---|---|
|
||||
| `candidateCount` | 输入候选数 | 7 |
|
||||
| `evidenceBlockCount` | 存活块数(差值 = 被治理掉的) | 5 |
|
||||
| `topSimilarity` | 排序后第一名的 qualityScore | 0.91 |
|
||||
| `relevanceLevel` | Java 按本地阈值独立判定(**不用 py-rag 返回的档位**,那个字段映射时已丢弃) | PRECISE |
|
||||
| `completenessHint` | 档位绑定的天花板提示文案 | "知识库中不存在比上述结果更精准的文档" |
|
||||
| `rerankTrace[]` | 每个**存活**块的最终序账本 | finalRank 1~5 |
|
||||
|
||||
### 3.1 去重辨析:三条规则别混淆
|
||||
|
||||
| 层 | 键 | 规则 | 目的 |
|
||||
|---|---|---|---|
|
||||
| 去重 | `evidenceKey = docId#chunk-N` | 同一块再现 → **合并**(hitReasons 取并集) | 同一片段只出现一次 |
|
||||
| 单文档上限 | `docId`(分桶) | 同文档**不同** chunk 可共存,最多 2 | 防单文档刷屏 |
|
||||
| 总条数 | — | 最多 5 | 上下文预算 |
|
||||
|
||||
**合并键是 evidenceKey 而不是 docId**——同文档的第 0 段和第 1 段是两份不同证据,按 docId
|
||||
合并会把多片段证据文档级折叠掉。单次检索正常不会返回同一个 chunk 两次(每个 chunk 是唯一
|
||||
索引条目),合并分支是给"索引脏数据 / 未来多路归并"准备的**防御性兜底**,成本一次 map 查询。
|
||||
|
||||
### 3.2 为什么会同文档多块命中——根源在分块
|
||||
|
||||
入库时文档切成多个 chunk,每块独立 embedding、独立索引;检索按 chunk 算相似度。
|
||||
这是刻意的颗粒度选择:整篇文档一个向量会稀释语义,且上下文也塞不下全文。
|
||||
**切块是为了检索得准、取得少;同文档多块命中是切块的自然结果;
|
||||
"chunk 级身份 + 单文档上限"就是为管理这个现象而生的。**
|
||||
|
||||
### 3.3 `rerankTrace`:最终序账本
|
||||
|
||||
```java
|
||||
Item { finalRank; source; baseScore; finalScore; boostReasons }
|
||||
```
|
||||
|
||||
- 条数 = 存活块数(finalRank 连续编号);被合并/截断的块不产生条目;
|
||||
- `baseScore == finalScore` **恒相等**——双字段是规则加分时代的化石(当年关键词加分,
|
||||
现已废除防操纵);`boostReasons` 同理,装的已是纯解释标签;
|
||||
- 只记到**文档级**(无 evidenceKey),chunk 身份要看 `evidenceBlocks[]`;
|
||||
- Agent 看不到它,审计默认不落库——活在内存 LookupResult 与评测快照里。
|
||||
|
||||
### 3.4 附带闸门
|
||||
|
||||
`isLowQuality()`:无可用块或顶分 <0.5 即低质。原触发 filtered→unfiltered 重试,
|
||||
L0 下沉后分支休眠、闸门保留——将来 Java 侧重引过滤策略可直接接上。
|
||||
|
||||
---
|
||||
|
||||
## 4. 站点四:打包——`ContextPack`(不是 Agent 口粮)
|
||||
|
||||
`KnowledgeContextPacker` 把证据块压成**一段有字符预算的文本**(默认 4000 字符):
|
||||
|
||||
```text
|
||||
策略 ranked_evidence_char_budget:名次即优先级,先到先得
|
||||
[Evidence 1]
|
||||
source: gateway-timeout.md
|
||||
breadcrumb: 网关超时排查 > 处理步骤
|
||||
reasons: semantic_rank:1, attempt:UNFILTERED_VECTOR ← 召回溯源标签
|
||||
content:
|
||||
网关超时先检查 upstream 配置…
|
||||
预算见底:装得下 header → 正文截断加 "...";连 header 都放不下 → 整条进 omittedSources
|
||||
```
|
||||
|
||||
```java
|
||||
ContextPack { packedText; strategy; charBudget; usedChars; includedSources; omittedSources }
|
||||
```
|
||||
|
||||
**必须澄清的定位**:主诊断链路里 **Agent 不消费 packedText**(主代码零调用)——Agent 拿的是
|
||||
站点六投影后的结构化列表。它的价值:人工回放可读、评测快照存证、以及将来任何
|
||||
"证据进 prompt"路径的现成格式化出口(字符预算是与后处理"块数预算"互补的物理闸门)。
|
||||
|
||||
`reasons:` 行是证据的"简历":`semantic_rank:N`(本次检索第几名)+ `attempt:X`(哪次尝试产出,
|
||||
当前只剩 `UNFILTERED_VECTOR`;历史值 `FILTERED_VECTOR` / `UNFILTERED_VECTOR_RETRY` 已随
|
||||
L0 下沉绝迹)。
|
||||
|
||||
> 小化石:`ContextPack` 的 javadoc 仍写 "Agent-facing",与 packer 侧注释矛盾——
|
||||
> 它诞生时确实面向 Agent,投影路线成为主路径后退居内部,注释没跟上身份变化。
|
||||
|
||||
---
|
||||
|
||||
## 5. 站点五:组装——`LookupResult` 真相全集
|
||||
|
||||
`LookupResultAssembler` 把三样东西合体成内部契约出口:
|
||||
|
||||
```text
|
||||
EvidencePostprocessResult(证据集+档位+trace)┐
|
||||
ContextPack(打包文本) ├─► LookupResult
|
||||
RetrievalTrace(检索路径) ┘
|
||||
```
|
||||
|
||||
只有两个字段是"算"出来的:`found = hasUsableEvidence()`(false 时附固定兜底文案
|
||||
"知识库未检索到可用证据,请结合日志、指标、告警继续排查");两个 count 把"进多少/出多少"
|
||||
带给审计。其余字段一一搬运。
|
||||
|
||||
---
|
||||
|
||||
## 6. 站点六:投影——`RagResultProjector`(加工链最后一站)
|
||||
|
||||
**输入是 LookupResult 的 JSON 字符串而非对象**——内部结构随便演化,冻结契约纹丝不动,
|
||||
解耦的关键就是这层"字符串边界"(字段名还带防御性别名对:`evidenceBlocks`/`evidence_blocks`)。
|
||||
|
||||
```text
|
||||
① query 截断(≤500 字) 动了 → truncated
|
||||
② 逐块过三道闸:
|
||||
条数 ≤8(超出 truncated+停止)
|
||||
身份去重(evidenceKey → document_id → docId#chunk-N → 序号兜底 的回退链)
|
||||
摘录 ≤1200 字(截断 → truncated)
|
||||
③ evidence_status 客观判定:数组空不空(EVIDENCE_FOUND / NO_EVIDENCE)
|
||||
④ relevance_level:有证据才读,解析不出 → null
|
||||
⑤ fitBudget 总字节兜底:整个 JSON 超 16KB → 从尾部逐条裁
|
||||
裁到空 → 诚实降级 NO_EVIDENCE;还超 → 抛异常(fail closed)
|
||||
```
|
||||
|
||||
**三道递进预算闸**(条数管语义 / 片段管局部 / 字节管整体)集中在 `ToolProjectionLimits`
|
||||
一个 record(8 条 / 1200 字 / 16KB,与日志、MySQL 工具共用)。**`truncated` 只要任何一处
|
||||
动过就置位——Agent 永远知道"看到的可能不全"**,不会把截断结果当全量。
|
||||
|
||||
---
|
||||
|
||||
## 7. 站点七:Agent 视图——模型实际看到的 JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"evidence_status": "EVIDENCE_FOUND",
|
||||
"tool_call_id": "call_x1",
|
||||
"query": "网关超时怎么排查",
|
||||
"evidence": [
|
||||
{ "document_id": "e2e-gateway-…-34223174#chunk-1",
|
||||
"source": "e2e-gateway-b9c1fa12-md",
|
||||
"title": "网关超时排查",
|
||||
"breadcrumb": "网关超时排查 > 处理步骤",
|
||||
"excerpt": "网关超时先检查 upstream 配置…" }
|
||||
],
|
||||
"returned_count": 5,
|
||||
"relevance_level": "PRECISE",
|
||||
"truncated": false
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 模型的正确用法 |
|
||||
|---|---|
|
||||
| `evidence_status` | `NO_EVIDENCE` → 老实换工具(日志/指标/MySQL),不许编 |
|
||||
| `evidence[].excerpt` | 结论唯一的内容依据 |
|
||||
| `evidence[].document_id` | **引用坐标**——报告里逐字引用它,EvidenceGuard 只认这个 |
|
||||
| `relevance_level` | PRECISE 可放心下结论;REFERENCE 结合上下文判断,必要时说清还缺什么维度 |
|
||||
| `truncated` | true → 看到的可能不全,可收窄 query 重搜 |
|
||||
|
||||
**三个"看不到"**:分数(防未校准数字诱导过度自信)、过程(attempt/trace 留给人)、
|
||||
其他站的内部字段。一句话:**留坐标、留内容、留诚实,剥掉一切会误导或撑爆上下文的东西。**
|
||||
|
||||
---
|
||||
|
||||
## 8. 站点八:验真——`EvidenceGuard`(证据安全链第一道闸)
|
||||
|
||||
Agent 写完诊断产出结构化草稿 `DiagnosisDraft`(analysis 带引用的 toolCallIds,
|
||||
conclusion/action_plan 带 basedOnAnalysisIds,limitations 必填)。EvidenceGuard 纯规则验真:
|
||||
|
||||
```text
|
||||
A 结构校验 id 唯一、正文非空、报告引用必须指向已登记分析、limitations 必填
|
||||
B 引用验真 runId+toolCallId → Redis 账本可查 → READY 状态 → 同 Run(防跨 Run 挪用)
|
||||
→ kind 语义匹配:NORMAL↔EVIDENCE_FOUND / NEGATIVE_OBSERVATION↔NO_EVIDENCE
|
||||
C 重读重建 从账本严格反序列化投影(多一个字段都违规)→ 用账本内容重建证据快照
|
||||
```
|
||||
|
||||
四个设计点:
|
||||
|
||||
1. **证据内容从账本重读,不信模型复述**——模型转述的"检索结果"进不了快照;
|
||||
2. **kind 匹配堵两头撒谎**——"查到了装没查到"与"没查到装查到了"都过不去;
|
||||
3. **runId 绑定 + READY**——别的 Run 的证据、失败/过期的调用不可引用;
|
||||
4. **纯规则、20 个违规码全枚举**——便宜、确定、可审计;"结论是否夸大"留给下一道
|
||||
SemanticGuard,这里只回答"引用是否真实、结构是否合法"。
|
||||
|
||||
---
|
||||
|
||||
## 9. 附:模型怎么知道要输出那份 JSON
|
||||
|
||||
四层合力,没有一刻依赖模型"自觉":
|
||||
|
||||
| 层 | 机制 |
|
||||
|---|---|
|
||||
| 格式 | `DiagnosisDraftOutputSchema`(BeanOutputConverter)从 Java 类自动生成 JSON Schema 注入 prompt;解析失败抛异常 |
|
||||
| 语义 | `diagnosis-agent-prompt.md`:证据充分 → 全填并建立完整引用链;证据不足 → `conclusion=null` 是合法成功(schema 层把 conclusion 类型改成 `object\|null`);limitations 无条件必填 |
|
||||
| 时机 | 模型自主停止(无有效查询范围即停)+ Harness 强制(连续 NO_GAIN / 预算耗尽 → STOP_REQUIRED 后必须直接交卷) |
|
||||
| 兜底 | 格式错/引用违规 → evidence-repair-prompt 修复重试 → 仍败 → 固定降级 FALLBACK |
|
||||
|
||||
---
|
||||
|
||||
## 10. 设计思路总结(五条哲学)
|
||||
|
||||
1. **防腐层:换引擎不换证据链**——`KnowledgeSearchPort` 是接缝,抽离时消费面零改动、
|
||||
250 个测试原样通过,这是接口设计价值的最硬证明。
|
||||
2. **分数给系统,档位给模型**——未校准的连续分数会诱导过度自信;`quality_score` 在
|
||||
Java 侧做闸门和审计,Agent 只见 PRECISE/REFERENCE。
|
||||
3. **chunk 级身份贯穿始终**——入库分块、检索按块、去重按 `docId#chunk-N`、引用坐标到块、
|
||||
验真对块。颗粒度统一,才有多片段证据共存与伪引用无处遁形。
|
||||
4. **诚实标记**——`truncated`、`no_evidence`、`unchanged`、`no_evidence_basis`:
|
||||
系统从不假装"看到的即全部",每个不完整/为空都有显式信号与原因。
|
||||
5. **所见即所证**——投影结果是"模型看到的"与"Redis 存证的"同一份;EvidenceGuard 对账
|
||||
没有翻译损耗,伪引用无处遁形。
|
||||
|
||||
### 化石清单(读代码时的辨认指南)
|
||||
|
||||
| 化石 | 现状 |
|
||||
|---|---|
|
||||
| `RerankTrace.baseScore/finalScore` 双字段 | 恒相等(规则加分已废),保留兼容旧审计格式 |
|
||||
| `boostReasons` 字段名 | 装的是纯解释标签,不再加分 |
|
||||
| `ContextPack` javadoc "Agent-facing" | 身份已变(内部/储备),注释未跟上 |
|
||||
| `LookupResultAssembler.deduped()` | 会话级去重回包的历史占位,主链路不再调用 |
|
||||
| `FILTERED_VECTOR` / `UNFILTERED_VECTOR_RETRY` attempt | L0 下沉后不可达,仅存于旧 Run 回放 |
|
||||
| `KnowledgeQuery` 的 L0 hint 字段 | 恒空结构,供后处理与 trace 兼容保留 |
|
||||
|
||||
---
|
||||
|
||||
## 11. 关键字段速查
|
||||
|
||||
| 字段 | 哪一站 | 一句话 |
|
||||
|---|---|---|
|
||||
| `evidence_key` | py-rag → 全程 | chunk 级身份 `docId#chunk-N`,去重与验真的锚 |
|
||||
| `quality_score` | py-rag → 后处理 | rerank 绝对分 [0,1],quality 直传 |
|
||||
| `evidence_status` | py-rag / 投影 | 两类:supported / no_evidence(正常业务响应) |
|
||||
| `relevance_level` | 后处理 → Agent | PRECISE/REFERENCE 档位,Java 按本地阈值独立判定 |
|
||||
| `originalRank` | adapter → 后处理 | 排序唯一权威,分数不动序 |
|
||||
| `candidateCount / evidenceBlockCount` | 后处理 | 进多少 / 出多少,差值即治理幅度 |
|
||||
| `truncated` | 投影 | 任何截断都置位,诚实标记 |
|
||||
| `tool_call_ids` | Draft → EvidenceGuard | 引用验真的入口,runId 绑定 + READY 校验 |
|
||||
Reference in New Issue
Block a user