Files
zhuyongxin a1c896ebda chore(docs): 归档 ISS-001 session-dedup-knowledge-map + ISS-002 mvp 文档
- 移动 session-dedup-knowledge-map OpenSpec 到 archive 目录
- 提交 ISS-001 遗留的 devflow 档案文件
- 更新 ISS-002 状态为已修复
- 新增 mvp/architecture/action-memory-relevance.md 设计文档
- 更新 mvp/README.md 文档导航
- 更新 devflow/index.md OpenSpec 链接指向 archive
2026-07-01 18:27:04 +08:00

4.1 KiB
Raw Permalink Blame History

Functional Spec: session-dedup-knowledge-map

REQ-01:工具层去重(Part A)

触发:LookupKnowledgeTool.lookupKnowledge(query) 被调用

行为:

  1. 从 SessionContextHolder.getSessionId() 获取当前 sessionId;若为 null(非会话上下文)跳过去重逻辑,正常检索
  2. L0+L1 检索完成后,将结果中已在 RetrievedDocTracker 中标记的 filePath 过滤掉
  3. 若过滤后 L0 结果为空、L1 结果也为空(全部已召回),返回 LookupResult.found=false,并在结果中附带提示文本:"以下文档已在本会话中检索过:[列表],无需重复召回"
  4. 未被过滤的文档正常返回后,将其 filePath 写入 RetrievedDocTracker
  5. SessionContextHolder.clear() 调用时,RetrievedDocTracker.clearSession(sessionId) 同步清理

验收:

  • 同一 session 内同一文档第二次命中时,返回去重提示而非完整文档内容
  • 不同 session 之间互不影响
  • sessionId 为 null 时不影响正常检索流程

REQ-02:文档级 LLM 字段生成(Part B - 文档级)

触发:DocumentManagementService.uploadDocument() 完成 frontmatter 解析后

行为:

  1. 若文档 frontmatter 中已包含 covers 和 whenToRetrieve,跳过 LLM 生成(作者手动填写优先)
  2. 否则,调用 LLM,输入为文档 title + summary + 正文前 1000 字符
  3. Prompt 要求 LLM 返回 JSON:{"covers": [...], "whenToRetrieve": "..."}
  4. 解析结果,回填到 Frontmatter 对象
  5. 序列化存入 api_document.metadata;同步更新 KnowledgeEntry 写入 L0 索引
  6. LLM 调用失败时,covers 置为空列表,whenToRetrieve 置为 summary(降级),不阻断上传流程

验收:

  • 上传后 api_document.metadata 中包含 covers 和 whenToRetrieve 字段
  • frontmatter 已有这两个字段时不覆盖
  • LLM 调用异常时文档仍上传成功,字段降级填充

REQ-03:域级聚合与存储(Part B - 域级)

触发:文档上传成功后;文档删除后;loadIndex() 时发现某域在 knowledge_domain 表无记录

行为:

  1. KnowledgeDomainService.onDocumentChange(category) 读取该 category 下所有 KnowledgeEntry 的 title + covers + whenToRetrieve
  2. 调用 LLM,生成域级 when_to_retrieve(要求 LLM 识别域内文档边界,输出含区分语义的路由描述)
  3. 写入 knowledge_domain 表(upsert by domain_id),同时更新 document_count
  4. LLM 调用失败时,domain.when_to_retrieve 保留上次 DB 记录;若无历史记录则置为空字符串

验收:

  • 上传文档后,对应 category 的 knowledge_domain 记录被更新
  • 删除文档后,对应 category 的 document_count 减少,when_to_retrieve 重新生成
  • loadIndex() 时无 DB 记录的域自动触发生成

REQ-04:knowledge map 注入 Planner(Part B - 注入)

触发:ChatService.buildChatPlannerAgent() 调用时

行为:

  1. 调用 KnowledgeDomainService.buildKnowledgeMap() 生成 YAML 文本
  2. YAML 结构:域列表,每个域包含 domain_id、description、when_to_retrieve、documents(title + covers)
  3. 若 knowledge_domain 表为空(无任何域记录),跳过注入,不修改 prompt
  4. 注入位置:Planner system prompt 末尾,独立区块

Planner prompt 附加规则:

  • 制定步骤时,先查看 available_knowledge_domains,按 when_to_retrieve 判断是否需要检索该域
  • 每个域最多指示 Executor 检索一次;已检索过的域不再安排检索步骤

验收:

  • Planner prompt 包含 available_knowledge_domains 区块
  • 无域记录时 prompt 不包含该区块(不注入空结构)
  • knowledge map 文本长度 < 1000 字符(6 个文档场景下)

REQ-05:Frontmatter 字段扩展

行为:

  • Frontmatter.java 新增 List<String> covers 和 String whenToRetrieve
  • KnowledgeEntry.java 新增同名字段
  • FrontmatterParser.java 解析 covers(YAML 数组)和 when_to_retrieve(YAML 字符串)

验收:

  • 现有文档(无新字段)上传/解析不报错,字段为 null 或空列表
  • 含新字段的文档正确解析