# Proposal: session-dedup-knowledge-map ## 问题 1. **ISS-001 重复召回**:`LookupKnowledgeTool` 每次调用完全无状态,同一 session 中同一文档可被重复召回 13+ 次,浪费 token、压缩上下文窗口、导致 `tool_call_count` 虚高。 2. **Planner 缺少全局视野**:Planner 不知道知识库里有哪些域,只能靠 Executor 反复试探,导致低效的"盲目检索"模式。 ## 建议方案 ### Part A:工具层去重(彻底修复 ISS-001) 在 `LookupKnowledgeTool` 的 session 维度维护已召回文档 ID 集合。 每次检索时,过滤掉已召回的文档;相同 query 命中相同文档则直接跳过(返回"已在上下文中"提示)。 状态存储:`ConcurrentHashMap>`,生命周期随 session(`SessionContextHolder.clear()` 时清理)。 ### Part B:知识图谱注入 Planner 启动时(`KnowledgeIndexService.loadIndex()` 完成后),将 L0 索引中的所有 `KnowledgeEntry` 聚合为域级摘要(knowledge map)。 每次构建 Planner prompt 时(`buildChatPlannerAgent()`),将 knowledge map 注入 system prompt,让 Planner 有"知识边界"。 聚合策略:按 `category` 字段分组,生成结构: ``` available_knowledge_domains: - domain_id: "payment" description: "..." covers: [...] document_count: N when_to_retrieve: "..." ``` 知识图谱的 `description` / `when_to_retrieve` 字段来源于: - 选项 1:直接聚合 KnowledgeEntry 的 title/summary - 选项 2:文档 frontmatter 中新增 `domain_description` / `when_to_retrieve` 字段 - 选项 3:上传时 LLM 自动生成这两个字段 ## 范围 **In scope**: - `LookupKnowledgeTool`:添加 session 级去重状态管理 - `KnowledgeIndexService`:添加 `buildKnowledgeMap()` 方法 - `ChatService.buildChatPlannerAgent()`:注入 knowledge map 到 prompt - `chat-planner-prompt.md`:添加如何使用 knowledge map 的指令 **Out of scope**(本次不做): - `EvaluationService.tool_call_count` 的统计口径调整(去重后虚高问题自然消失,但评分规则不改) - RRF 混合重排 - 文档 frontmatter 自动生成(上传时 LLM 生成,留 Phase 2) ## 风险 - Part A 引入 JVM 内存 Map,高并发时多 session 并发需线程安全 - Part B knowledge map 注入 Planner prompt 会增加每次请求的 token 消耗(固定开销) - 文档 `category` 字段缺失或不规范时,聚合结果可能混乱 ## 上下文约束 - `SessionContextHolder` 是 ThreadLocal,异步路径不安全(已知限制,Part A 需确认同步路径) - `EvaluationService` 依赖 `tool_call_count`,去重会降低此值(是修复,不是回归) - `KnowledgeEntry` 已有 `category` 字段,但当前数据库中的文档是否都有 `category` 需确认