# Decisions: executor-action-memory-relevance ## 过程日志 ### Clarify 阶段 **入口摘要**:ISS-002 Executor 无约束重复调用 lookup_knowledge(单会话 20+ 次),需要行动记忆 + 归一化质量等级 + prompt 约束来解决。 **slug**: `executor-action-memory-relevance` **规模分档**: `standard`(涉及 7 个文件,跨 DTO/工具层/持久化/Prompt,有设计决策需澄清) ### Context 阶段 **devflow/index.md 使用状态**: 已命中。前序 change `session-dedup-knowledge-map`(archived)提供了 RetrievedDocTracker、KnowledgeDomainService、ISS-002 文档。 **相关 ADR**: 无直接 ADR,但 `session-dedup-knowledge-map` 的 decisions.md 和 evidence.md 记录了文档级去重和 knowledge map 注入的决策。 **不能违反的历史决策**: 1. RetrievedDocTracker 的文档级去重必须保留 2. knowledge map 只注入 Planner,不注入 Executor(本次讨论确认) 3. L0/L1 原始分数不暴露给 LLM,只在归一化层内部使用(本次讨论确认) **需进入 OpenSpec 的上下文点**: 1. L1 score 是 L2 距离(值域 [0,+∞)),不是归一化分数——阈值设计需基于实际分布 2. L0 的 category 可从 KnowledgeEntry.getCategory() 直接获取;L1 需解析 metadata JSON 3. ReactAgent 是自主决策工具调用的 Agent,Prompt 约束是软约束 ### Grill 阶段 — Question Pool **维度:术语** 1. [evidence-driven] `relevanceLevel` 三个等级(PRECISE/HIGHLY_RELEVANT/REFERENCE)的边界是否清晰,是否存在 LLM 误解的可能? → **已查证**:三个等级语义明确,PRECISE=唯一匹配、HIGHLY_RELEVANT=高分命中、REFERENCE=低置信度参考。LLM 理解风险低。 **维度:边界** 2. [evidence-driven] L1 score 是 L2 距离(值域 [0,+∞)),当前代码无阈值判断。归一化阈值如何设计? → **已查证**:L2 距离典型范围取决于 BGE-M3 1024 维 embedding 的尺度,需从 `tool_invocation.retrieval_details` 中查询实际 `l1_scores` 分布才能定阈值。当前先以常量定义,标记为"需实测校准"。 3. [evidence-driven] L1 结果的 category 提取需要解析 metadata JSON 字符串,当前 `SearchResult.metadata` 是 `toString()` 的结果。归一化层是否需要 L1 的 domain? → **已查证**:L1 的 domain 主要用于 RetrievedDocTracker 的域级记录。如果 L0 已命中且包含 category,可直接用 L0 的 category;如果仅 L1 命中,需解析 metadata 提取 category。当前知识库中 L0 大概率先命中,L1 domain 提取作为兜底路径。 4. [user-interview] 归一化阈值(L1 score 分界线)在实测数据不足时,是否接受先用保守初始值 + 后续调优的策略? → **用户待确认** **维度:验收** 5. [evidence-driven] 现有 `tool_invocation` 表 `retrieval_details` JSON 中 `l1_scores` 存的是 L2 距离原始值,新增的 `relevance_level` 和 `completeness_hint` 入库后是否需要回填历史数据? → **已查证**:不需要回填历史数据,新列 nullable 即可,历史记录 relevance_level=null。 ### Grill 结论 **evidence-driven 汇报**: - E1: relevanceLevel 三等级语义清晰,LLM 误解风险低 - E2: L1 score 是 L2 距离,值域不固定,阈值需实测校准 - E3: L0 category 直接可用,L1 category 需解析 metadata(兜底路径) - E4: 历史数据不回填,新列 nullable **user-interview 已确认**: - Q4: 归一化阈值先用保守初始值 + 后续调优 → **用户已确认**,并建议用 Min-Max 归一化到 [0,1] ### Specify 阶段补充 **BGE-M3 L2 归一化实测验证**: - FullPipelineSmokeTest.embeddingBgeM3Works() 新增 L2 范数断言 - 结果:范数=1.00000002,误差 < 0.01,测试通过 - 结论:BGE-M3 输出为 L2 归一化单位向量,L2 距离数学硬上界 = 2.0 - Min-Max 归一化公式:`similarity = 1 - min(l2Score, 2.0) / 2.0` **Cross-artifact 对齐检查**: | 对齐项 | 状态 | |--------|------| | brief 目标/范围/非目标 → proposal 覆盖 | 已对齐 | | proposal 范围/约束 → design 覆盖 | 已对齐 | | design 归一化/行动记忆/接口影响 → specs 覆盖 | 已对齐 | | specs 可观察行为 → tasks 覆盖 | 已对齐 | **接口影响分级**: - RetrievedDocTracker 数据结构升级 → L2(内部接口,消费者只有 LookupKnowledgeTool) - LookupResult 新增 3 字段 → L2(工具返回值,无跨模块调用方) - tool_invocation 新增 2 列 → L2(Flyway nullable,不影响现有查询) - chat-executor-prompt.md 更新 → L1(Prompt 文本变更) ### Audit 阶段 **架构风险评估**(5 句以内): 1. 归一化层嵌入 LookupKnowledgeTool 内部(静态方法),无跨模块耦合风险。 2. RetrievedDocTracker 升级为双层结构,数据量级不变(文档数 × session 数),内存无风险。 3. L1 metadata 解析 category 是兜底路径,如果 JSON 格式不一致可能解析失败——已有 try-catch 兜底。 4. 归一化阈值 yml 配置化,运行时调优不需要改代码和重启——运维友好。 5. Prompt 约束仍依赖 LLM 遵守——如果 Phase 1 效果不足,Phase 2 域级硬限制的 isDomainRetrieved 已就绪,无需额外改造。