Files
SuperBizAgent-java/mvp/architecture/archive/2026-07-05-legacy/action-memory-relevance.md
T

8.5 KiB
Raw Blame History

行动记忆与检索质量归一化

Executor 行动记忆 + 归一化质量等级设计,解决 ISS-002 Executor 无约束重复检索问题。


一、问题背景

ISS-001 修复文档级去重后,Executor 在单次会话中仍调用 lookup_knowledge 20+ 次。根因:

  1. 行动记忆缺失:Executor 不知道自己已检索过哪些域
  2. 质量信号缺失:检索结果没有给 LLM 判断"结果够不够"的信号
  3. Prompt 缺少合法出口:原 prompt 要求"所有外部信息都必须调用工具",LLM 不敢停止检索

二、整体架构

lookup_knowledge(query)
  │
  ├─ Step 1: L0 精确匹配(keywords 索引)
  ├─ Step 2: L1 语义检索(Milvus 向量)
  ├─ Step 3: computeRelevance()
  │     ├─ 归一化:L2 → similarity [0,1]
  │     └─ 判定:PRECISE / HIGHLY_RELEVANT / REFERENCE
  ├─ Step 4: RetrievedDocTracker 检查
  │     ├─ 文档级去重 → isDocRetrieved(sessionId, docKey)
  │     ├─ 域级检查 → isDomainRetrieved(sessionId, domain)
  │     └─ 记录 → markRetrieved(sessionId, domain, docKey)
  └─ Step 5: 返回 LookupResult
        ├─ primary / supplement(原始内容,不含分数)
        ├─ relevanceLevel(PRECISE / HIGHLY_RELEVANT / REFERENCE)
        ├─ completenessHint(兜底信号)
        └─ retrievedDomainsThisSession(行动记忆)

设计原则

原则 说明
Agent 边界清晰 不给 Executor 注入 knowledge map,Executor 只知道做了什么,不用知道有什么
分数封装 L0/L1 原始分数不在 LookupResult 中返回 LLM,只在归一化层内部使用
原始分数只入库 原始 L2 距离写进 tool_invocation.retrieval_details JSON 用于可观测
软约束 + 硬拦截 Prompt 约束(软)+ 工具层域级去重(硬)两层防御

三、归一化质量等级

L2 距离归一化

BGE-M3 输出为 L2 归一化单位向量(实测范数=1.00000002),L2 距离数学硬上界 = 2.0。

similarity = 1 - min(l2Score, maxL2Distance) / maxL2Distance
L2 距离 similarity 等级
0.0 1.0 PRECISE
0.383 0.8085 HIGHLY_RELEVANT
0.5 0.75 HIGHLY_RELEVANT
0.6031 0.6984 REFERENCE
1.0 0.5 REFERENCE 边界
2.0+ 0.0 不视为有效结果

三等级判定

等级 条件 completenessHint LLM 行为
PRECISE L0 matchCount == 1 "知识库中不存在比上述结果更精准的文档" 直接使用,禁止再检索
HIGHLY_RELEVANT L0 命中 + similarity ≥ 0.75,或仅 L1 similarity ≥ 0.75 "当前结果已高度相关,继续检索不太可能找到更精准的文档" 可综合推理,大概率不需要继续查
REFERENCE 其余命中(similarity ≥ 0.5) "当前结果为相关参考,如需更精准信息请明确缺少的具体维度" 可参考,如需更精准请指出缺少的维度后定向补充

阈值配置

retrieval:
  normalization:
    max-l2-distance: 2.0              # L2 距离上界
    highly-relevant-threshold: 0.75   # similarity ≥ 0.75 → HIGHLY_RELEVANT
    reference-threshold: 0.5          # similarity ≥ 0.5 → REFERENCE

四、行动记忆

RetrievedDocTracker 数据结构

// 从单层升级为双层:session → domain → filePath 集合
ConcurrentHashMap<String, Map<String, Set<String>>> sessionRetrievals;

API

方法 作用
markRetrieved(sessionId, domain, filePath) 记录一次检索
isDocRetrieved(sessionId, filePath) 文档级去重
isDomainRetrieved(sessionId, domain) 域级检查
getRetrievedDomains(sessionId) 获取已检索域列表
clearSession(sessionId) 清理会话记录

LookupResult 返回

LookupResult.builder()
    .found(true)
    .primary(primaryResult)
    .supplement(supplementResult)
    .relevanceLevel("HIGHLY_RELEVANT")           // PRECISE / HIGHLY_RELEVANT / REFERENCE
    .completenessHint("当前结果已高度相关...")    // 兜底信号
    .retrievedDomainsThisSession(["infrastructure", "api"])  // 行动记忆
    .message("...")
    .build();

五、Executor Prompt 约束

4 条检索约束

  1. 判断重复:基于 retrievedDomainsThisSession 判断语义重叠
  2. 重复了怎么办:禁止换关键词重查;先指缺少的维度,再定向补充
  3. 合法出口:"不查全不会被追责,重复检索才会被惩罚"
  4. 利用质量信号:PRECISE → 停止;HIGHLY_RELEVANT + 域已检索 → 禁止;REFERENCE → 指出缺少维度

关键变化

原有 prompt:"所有需要外部信息的地方,都必须调用对应的工具" → 改为:"需要外部信息时调用工具,但须遵守下方的检索约束"


六、数据库变更

V010

ALTER TABLE tool_invocation
    ADD COLUMN relevance_level VARCHAR(20) COMMENT 'PRECISE/HIGHLY_RELEVANT/REFERENCE/DEDUPED',
    ADD COLUMN dedup_reason VARCHAR(32) COMMENT 'doc_retrieved/domain_retrieved/null';

retrieval_details JSON 扩展

{
  "l0_titles": ["MySQL 数据库连接池配置", "Redis 缓存配置指南"],
  "l1_scores": [0.383, 0.4502, 0.7011],
  "l1_top_score": 0.383,
  "l1_top_similarity": 0.8085,
  "relevance_level": "HIGHLY_RELEVANT",
  "completeness_hint": "当前结果已高度相关,继续检索不太可能找到更精准的文档",
  "retrieved_domains": ["infrastructure"]
}

扩展字段使用方式:

字段 用途
l1_top_score 原始 L2 距离最小值(可观测性)
l1_top_similarity 归一化后的相似度 [0,1]
relevance_level 归一化质量等级
completeness_hint 兜底信号
retrieved_domains 已检索域列表
dedup_reason 去重原因(如有)

七、使用场景

场景 1:正常检索

用户:数据库连接池怎么配置?

Executor 内部:
1. lookup_knowledge("数据库连接池配置")
   → relevanceLevel=HIGHLY_RELEVANT (similarity=0.8085)
   → completenessHint="当前结果已高度相关..."
   → retrievedDomainsThisSession=["infrastructure"]
2. 基于已有信息直接回答,不再检索

场景 2:行动记忆阻止重复

Executor 步骤列表:
- 查数据库连接池配置
- 查 HikariCP 参数
- 查连接池耗尽排查

实际行为:
1. lookup("数据库连接池") → relevance=HIGHLY_RELEVANT, domains=["infrastructure"]
2. lookup("HikariCP 参数") → retrievedDomainsThisSession=["infrastructure"]
   LLM 判断:infrastructure 域已检索过,禁止换关键词重查
   → 基于已有信息回答,指出缺少的具体维度
3. lookup("连接池耗尽") → 同域,被 prompt 约束拦截或工具层去重拦截

场景 3:PRECISE 精确匹配

用户:ERR_TIMEOUT 是什么?

Executor 内部:
1. lookup_knowledge("ERR_TIMEOUT")
   → L0 matchCount=1(唯一精确匹配)
   → relevanceLevel=PRECISE
   → completenessHint="知识库中不存在比上述结果更精准的文档"
2. 直接使用,不再检索

场景 4:REFERENCE + 定向补充

用户:如何排查生产故障?

Executor 内部:
1. lookup_knowledge("故障排查")
   → relevanceLevel=REFERENCE (similarity=0.6)
   → retrievedDomainsThisSession=["troubleshooting"]
2. LLM 判断:信息不足,缺少"日志分析"维度的具体步骤
3. lookup_knowledge("日志分析步骤")
   → 定向补充,不盲目换关键词

八、可观测性

查询质量分布

SELECT relevance_level, COUNT(*) AS cnt
FROM tool_invocation
WHERE tool_name = 'lookup_knowledge'
GROUP BY relevance_level;

去重原因分布

SELECT dedup_reason, COUNT(*) AS cnt
FROM tool_invocation
WHERE tool_name = 'lookup_knowledge'
GROUP BY dedup_reason;

归一化分数分布

SELECT
    JSON_EXTRACT(retrieval_details, '$.l1_top_similarity') AS similarity,
    COUNT(*) AS cnt
FROM tool_invocation
WHERE tool_name = 'lookup_knowledge'
  AND retrieval_details IS NOT NULL
GROUP BY similarity
ORDER BY similarity;

九、扩展方向(Phase 2)

  • 域级硬限流:isDomainRetrieved 已就绪,在 LookupKnowledgeTool 入口直接拦截同域调用,不依赖 LLM 遵守 prompt
  • DEDUPED 等级:去重时单独标记为 DEDUPED 等级,与 REFERENCE 区分
  • 分数反馈调优:基于 feedback 数据优化归一化阈值