# decisions.md — session-dedup-knowledge-map ## Question Pool | # | 问题 | 类型 | 状态 | |---|---|---|---| | Q1 | domain.when_to_retrieve 来源(手动/自动聚合/LLM上传时生成) | user-interview | 已确认 | | Q2 | LLM 生成时机(同步上传 vs 异步补全) | user-interview | 已确认 | | Q3 | knowledge map 结构(域级平铺 vs 两层) | user-interview | 已确认 | | Q4 | domain.when_to_retrieve 存储(内存 vs DB) | user-interview | 已确认 | | Q5 | Executor 文档级细粒度筛选是否进 MVP | user-interview | 已确认 | | E1 | ThreadLocal 在多 Agent 路径是否安全 | evidence-driven | 已汇报 | | E2 | 6 个文档是否全部有 category 字段 | evidence-driven | 已汇报 | | E3 | 去重 key 设计 | evidence-driven | 已汇报 | | E4 | Planner prompt token 增量是否可接受 | evidence-driven | 已汇报 | | E5 | EvaluationService.tool_call_count 影响 | evidence-driven | 已汇报 | ## Evidence-Driven 结论 - **E1**:`AsyncConfig` 只启用 `@EnableAsync`,无 TaskDecorator。`SupervisorAgent.invoke()` 是同步阻塞调用,工具调用与主线程同线程,ThreadLocal 当前路径安全。异步扩展时需补 TaskDecorator。 - **E2**:全部 6 个文档均有 `category` 字段:api(1)、domain(1)、infrastructure(3)、troubleshooting(1)。 - **E3**:`KnowledgeEntry.filePath` 在 L0 内唯一,L1 `_source` 字段也是 filePath,统一用 filePath 作去重 key。 - **E4**:当前 planner prompt 21 行,注入 knowledge map 约增加 200-400 字符,可接受。 - **E5**:去重后 `agent_step.has_tool_call` 减少,`tool_call_count` 降低,这是修复效果,`EvaluationService` 评分规则无需改动。 ## User-Interview 确认记录 **Q1** — doc.when_to_retrieve 来源 用户原话:选 C(上传时 LLM 自动生成) 确认状态:已确认 **Q2** — LLM 生成时机 用户原话:选 X(同步,上传时当场生成) 确认状态:已确认 **Q3** — knowledge map 结构 用户原话:认可两层结构(domain → documents[]) 确认状态:已确认 补充:Planner 只注入域级 when_to_retrieve,文档级 when_to_retrieve 留 Executor 筛选(Phase 2) **Q4** — domain.when_to_retrieve 存储 用户原话:存 DB,这样每次启动都不用让 LLM 再总结一次 确认状态:已确认 → 新建 knowledge_domain 表,Flyway 迁移脚本 **Q5** — Executor 文档级细粒度筛选 用户原话:留 Phase 2 确认状态:已确认,MVP 不做 ## Pre-apply 补充决策 - **P1:KnowledgeIndexService.parseDocumentToEntry 替换为 Jackson**:`extractJsonValue` / `extractJsonArray` 手写解析器遇到含逗号、引号的自然语言字段(whenToRetrieve)会截断。全量替换为 `objectMapper.readValue(metadata, Frontmatter.class)`,影响范围仅 `KnowledgeIndexService`,行为更健壮。(用户确认) - **P2:LookupResult 新增 message 字段**:去重命中时 `found=false` + `message="文档已在本会话中检索过:xxx"`,不复用 `primary.content`。语义清晰,LLM 能理解原因不会重试。(用户确认) ## 关键设计决策 1. **两级 when_to_retrieve**:文档级(upload 时 LLM 生成,存 metadata)+ 域级(文档变更时 LLM 聚合,存 knowledge_domain 表) 2. **域级重算触发**:文档上传后、文档删除后,只重算受影响的域(不是全量);`loadIndex()` 时如果某域在 DB 没有记录,则触发生成 3. **注入 Planner 只给域级**:knowledge map 只包含域级 when_to_retrieve + documents[](title + covers),不暴露文档级 when_to_retrieve 4. **去重 key**:filePath(L0+L1 统一) 5. **去重状态存储**:JVM 内 `ConcurrentHashMap>`,`SessionContextHolder.clear()` 时同步清理