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
This commit is contained in:
zhuyongxin
2026-07-01 18:27:04 +08:00
parent e438df4355
commit a1c896ebda
14 changed files with 834 additions and 3 deletions
@@ -0,0 +1,33 @@
# Brief: session-dedup-knowledge-map
## 背景
ISS-001:Executor 在单次对话中重复调用 `lookup_knowledge` 多达 20 次,同一文档被召回 13 次。原因是工具层无状态、Planner 无知识边界感知。
## 目标
1. 彻底消除 session 内重复文档召回(Part A)
2. 给 Planner 注入知识图谱,让其在规划阶段就能判断需要检索哪个域、只检索一次(Part B)
## 范围
- `LookupKnowledgeTool`:session 级去重
- `Frontmatter` / `KnowledgeEntry`:新增 covers + whenToRetrieve
- `DocumentManagementService`:上传时 LLM 生成文档级字段
- `KnowledgeDomainService`(新):域级聚合与 DB 存储
- `knowledge_domain` 表(新)
- `ChatService` + `chat-planner-prompt.md`:注入 knowledge map
## 非目标(Phase 2)
- Executor 文档级 when_to_retrieve 细粒度筛选
- RRF 混合重排
- 文档 frontmatter 自动生成(手动覆盖 LLM 优先已支持)
## 分档
standard
## 关联 OpenSpec
openspec/changes/session-dedup-knowledge-map/
@@ -0,0 +1,60 @@
# 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<sessionId, Set<filePath>>`,`SessionContextHolder.clear()` 时同步清理