feat(knowledge): Executor 行动记忆 + 归一化质量等级解决 ISS-002 重复检索

- RetrievedDocTracker 升级为域级+文档级双层记录(Map<sessionId, Map<domain, Set<filePath>>>)
- LookupKnowledgeTool 新增 Min-Max 归一化层(BGE-M3 L2 距离→[0,1] similarity)
- 三等级 relevanceLevel:PRECISE / HIGHLY_RELEVANT / REFERENCE + completenessHint 兜底信号
- LookupResult 新增 relevanceLevel、completenessHint、retrievedDomainsThisSession
- Executor prompt 重写:4 条检索约束 + 合法出口不查全不追责,重复检索才惩罚
- 入库可观测性:V010 迁移 + retrieval_details JSON 扩展
- 归档 executor-action-memory-relevance change
This commit is contained in:
zhuyongxin
2026-07-01 18:24:41 +08:00
parent e4f37cb9e6
commit e438df4355
21 changed files with 1076 additions and 88 deletions
@@ -0,0 +1,89 @@
# Proposal: executor-action-memory-relevance
## 问题
ISS-002:Executor 在单次会话中调用 `lookup_knowledge` 20+ 次,大部分是同域换变体的冗余调用。
根因:
1. **行动记忆缺失**:Executor 不知道自己已经检索过哪些域,反复用不同关键词查同一个域
2. **质量信号缺失**:检索结果没有归一化质量等级,LLM 无法判断"结果够不够"
3. **Prompt 约束缺失**:现有 executor prompt 要求"所有需要外部信息的地方都必须调用工具",没有"放弃检索"的合法出口
## 建议方案
### 1. 行动记忆(通过工具返回值传递)
`RetrievedDocTracker` 数据结构升级:`Map<sessionId, Map<domain, Set<filePath>>>`。
每次 `lookup_knowledge` 返回值附带 `retrievedDomainsThisSession`,让 Executor 知道自己本次会话已检索过哪些域。
**不给 Executor knowledge map**——保持 Agent 边界清晰:Planner 知道全域(规划查哪个域),Executor 只知道自己做了什么(执行检索 + 基于结果推理)。
### 2. 归一化质量等级(封装 L0/L1 分数差异)
在 `LookupKnowledgeTool` 内部新增归一化层,将 L0 匹配数和 L1 score 统一为三个等级:
| 等级 | 含义 | LLM 应做什么 |
|------|------|-------------|
| `PRECISE` | 精准命中 | 直接使用,不再检索 |
| `HIGHLY_RELEVANT` | 高度相关 | 综合推理,大概率不需要继续查 |
| `REFERENCE` | 相关参考 | 可参考,如需更精准请明确缺什么维度 |
归一化逻辑:
- L0 唯一匹配 → PRECISE
- L0 命中 + L1 高分 → HIGHLY_RELEVANT
- L0 多匹配 + L1 中分 → HIGHLY_RELEVANT
- L0 多匹配 + 无 L1 → REFERENCE
- 仅 L1 命中 → 按 score 分 HIGHLY_RELEVANT / REFERENCE
**L0/L1 原始分数不返回给 LLM**,只在归一化层内部使用。原始分数入库(`tool_invocation.retrieval_details`)保留可观测性。
### 3. 兜底信号(completenessHint)
每次返回附带 `completenessHint`,给 LLM "天花板"信号:
| relevanceLevel | completenessHint |
|----------------|-----------------|
| PRECISE | "知识库中不存在比上述结果更精准的文档" |
| HIGHLY_RELEVANT | "当前结果已高度相关,继续检索不太可能找到更精准的文档" |
| REFERENCE | "当前结果为相关参考,如需更精准信息请明确缺少的具体维度" |
### 4. Executor prompt 重写检索约束
- 基于 `retrievedDomainsThisSession` 判断重复(不是"不要重复",而是"重复了该怎么办")
- 给 LLM 合法出口:"不查全不会被追责,重复检索才会被惩罚"
- 利用 `relevanceLevel` + `completenessHint` 判断质量
### 5. 入库可观测性
`tool_invocation` 表新增 `relevance_level` 和 `dedup_reason` 列。
`retrieval_details` JSON 扩展:加入归一化等级、兜底信号、已检索域、去重原因、L1 top score。
## 范围
- `LookupKnowledgeTool`:归一化层 + 行动记忆注入 + 域级拦截
- `RetrievedDocTracker`:数据结构升级(域级记录)
- `LookupResult`:新增 `relevanceLevel`、`completenessHint`、`retrievedDomainsThisSession`
- `chat-executor-prompt.md`:检索约束重写
- `ToolInvocation` 实体 + V010 迁移:新增列
- `LookupKnowledgeTool.saveToolInvocation()`:扩展入库字段
## 非目标
- 不给 Executor 注入 knowledge map(保持 Agent 边界)
- 不修改 Planner prompt 或 Planner 逻辑
- 不修改 `PrimaryResult`/`SupplementResult` 的字段(不暴露原始分数给 LLM)
- Phase 2 域级硬限制暂不实施,先观察 prompt 约束效果
## 风险
1. L1 score 阈值(0.3/0.7)需要根据实际 embedding 分布调优,当前为初始值
2. 归一化等级可能让 LLM 过早停止检索——需实测观察 REFERENCE 场景下的行为
3. Prompt 约束仍依赖 LLM 遵守——如果效果不足,需启用 Phase 2 域级硬限制
## 来自 devflow 的上下文约束
- 前序 change `session-dedup-knowledge-map`:已实现文档级去重(RetrievedDocTracker + filePath)和 Planner knowledge map 注入
- ISS-001:文档级重复召回已修复
- glossary:ReactAgent 是自主决策工具调用的 Agent,不受外部流程控制
- JPA ddl-auto 使用 validate 模式,表结构修改必须通过 Flyway 迁移