- 新增 mvp/architecture/session-dedup-knowledge-map.md - 更新 mvp/README.md 文档导航 - ISS-001 issue 关联架构文档
189 lines
6.6 KiB
Markdown
189 lines
6.6 KiB
Markdown
# 会话级去重与知识域地图
|
||
|
||
文档级去重 + 知识域地图注入 Planner,解决 ISS-001 Executor 重复召回同一文档问题。
|
||
|
||
---
|
||
|
||
## 一、整体架构
|
||
|
||
本 change 包含两个独立但互补的部分:
|
||
|
||
```
|
||
Part A: 工具层去重
|
||
LookupKnowledgeTool
|
||
├── 维护 ConcurrentHashMap<sessionId, Set<filePath>>(JVM 内)
|
||
├── 每次检索前过滤已召回文档
|
||
└── SessionContextHolder.clear() 时同步清理
|
||
|
||
Part B: 知识域地图
|
||
┌─────────────────────────────────────────────┐
|
||
│ 文档上传 (DocumentManagementService) │
|
||
│ → LLM 生成 doc.covers + doc.when_to_retrieve │
|
||
│ → 存入 api_document.metadata │
|
||
│ → 触发域级重算 (KnowledgeDomainService) │
|
||
└─────────────────────────────────────────────┘
|
||
↓
|
||
┌─────────────────────────────────────────────┐
|
||
│ 域级聚合 (KnowledgeDomainService) │
|
||
│ → 读取同域所有文档的 when_to_retrieve │
|
||
│ → LLM 生成 domain.when_to_retrieve │
|
||
│ → 存入 knowledge_domain 表 │
|
||
└─────────────────────────────────────────────┘
|
||
↓
|
||
┌─────────────────────────────────────────────┐
|
||
│ 启动 (KnowledgeIndexService.loadIndex) │
|
||
│ → 加载 knowledge_domain 表 │
|
||
│ → 某域无记录则触发域级生成 │
|
||
└─────────────────────────────────────────────┘
|
||
↓
|
||
┌─────────────────────────────────────────────┐
|
||
│ Planner prompt (ChatService) │
|
||
│ → 注入 knowledge map(域级) │
|
||
│ → Planner 做粗粒度检索决策 │
|
||
└─────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 二、Part A:工具层去重
|
||
|
||
### RetrievedDocTracker
|
||
|
||
session 级已召回文档追踪组件,将去重责任从 LLM 移交到工具层。
|
||
|
||
```java
|
||
ConcurrentHashMap<String, Set<String>> retrieved
|
||
key: sessionId
|
||
value: Set<filePath>
|
||
```
|
||
|
||
| 方法 | 作用 |
|
||
|------|------|
|
||
| `isAlreadyRetrieved(sessionId, filePath)` | 检查文档是否已召回 |
|
||
| `markRetrieved(sessionId, filePath)` | 记录已召回文档 |
|
||
| `clearSession(sessionId)` | 清理会话记录(SessionContextHolder.clear 触发) |
|
||
|
||
### 去重流程
|
||
|
||
```
|
||
lookup_knowledge(query)
|
||
→ L0 检索 → 命中一批文档
|
||
→ 遍历结果,过滤 isAlreadyRetrieved=true 的文档
|
||
→ 剩余文档作为 primary/supplement 返回
|
||
→ 实际返回的文档调用 markRetrieved
|
||
```
|
||
|
||
---
|
||
|
||
## 三、Part B:知识域地图
|
||
|
||
### Frontmatter 新增字段
|
||
|
||
文档上传时 LLM 自动生成以下两个字段:
|
||
|
||
```yaml
|
||
covers: ["支付失败排查", "扣款无回调"] # 业务场景标签
|
||
when_to_retrieve: "用户描述支付失败、超时时" # 文档级检索时机
|
||
```
|
||
|
||
### knowledge_domain 表
|
||
|
||
```sql
|
||
CREATE TABLE knowledge_domain (
|
||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||
domain_id VARCHAR(64) NOT NULL UNIQUE,
|
||
description VARCHAR(256),
|
||
when_to_retrieve TEXT,
|
||
document_count INT DEFAULT 0,
|
||
updated_at DATETIME,
|
||
created_at DATETIME
|
||
);
|
||
```
|
||
|
||
### Knowledge Map(注入 Planner 的 YAML)
|
||
|
||
```yaml
|
||
available_knowledge_domains:
|
||
- domain_id: "payment"
|
||
description: "支付链路问题排查"
|
||
when_to_retrieve: "用户问题涉及支付、退款、对账时检索;优先检索一次,勿重复"
|
||
documents:
|
||
- title: "支付失败排查手册"
|
||
covers: ["支付超时", "扣款无回调"]
|
||
- title: "退款处理指南"
|
||
covers: ["退款未到账", "退款状态异常"]
|
||
- domain_id: "infrastructure"
|
||
...
|
||
```
|
||
|
||
### 注入链路
|
||
|
||
```
|
||
文档上传/删除
|
||
→ KnowledgeDomainService.onDocumentChange(category)
|
||
→ 读取同域所有文档的 when_to_retrieve
|
||
→ LLM 聚合为 domain.when_to_retrieve
|
||
→ 写入 knowledge_domain 表
|
||
|
||
应用启动
|
||
→ KnowledgeIndexService.loadIndex()
|
||
→ 加载 knowledge_domain → 无记录则触发聚合
|
||
→ ChatService.buildChatPlannerAgent() 注入 prompt
|
||
|
||
Planner prompt 中包含知识域地图
|
||
→ Planner 做粗粒度检索决策("查 payment 域")
|
||
→ Executor 收到步骤后执行具体检索
|
||
```
|
||
|
||
---
|
||
|
||
## 四、关键设计决策
|
||
|
||
| 决策 | 方案 | 原因 |
|
||
|------|------|------|
|
||
| 域级 when_to_retrieve 存 DB | 持久化 | 避免每次重启调 LLM,文档变更时只重算受影响域 |
|
||
| 文档级 when_to_retrieve 存 metadata JSON | 沿用现有路径 | 无需新增数据库字段 |
|
||
| RetrievedDocTracker 独立于 SessionContextHolder | 职责分离 | SessionContextHolder 只持有 sessionId,Tracker 是业务状态 |
|
||
| Planner 只看域级 | 分层决策 | 文档级 when_to_retrieve 留 Executor 筛选(Phase 2) |
|
||
| LLM 调用同步执行 | 上传时即时生成 | 接受约 1-2s 延迟,保证数据库和 L0 索引立即一致 |
|
||
|
||
---
|
||
|
||
## 五、Agent 边界
|
||
|
||
```
|
||
Planner 角色:知道"有什么域"
|
||
└─ 知识域地图:选定要检索的域(一次规划)
|
||
|
||
Executor 角色:知道"做了什么"
|
||
└─ 行动记忆:域级 + 文档级去重(ISS-002 升级为双层记忆)
|
||
```
|
||
|
||
Part B(知识域地图)只注入 Planner prompt,**不注入 Executor prompt**。Executor 只通过 RetrievedDocTracker 知道自己已检索了哪些文档,不需要知道全局域有哪些。
|
||
|
||
---
|
||
|
||
## 六、数据库变更
|
||
|
||
### V009
|
||
|
||
```sql
|
||
CREATE TABLE knowledge_domain (
|
||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||
domain_id VARCHAR(64) NOT NULL UNIQUE,
|
||
description VARCHAR(256),
|
||
when_to_retrieve TEXT,
|
||
document_count INT DEFAULT 0,
|
||
updated_at DATETIME,
|
||
created_at DATETIME
|
||
);
|
||
```
|
||
|
||
---
|
||
|
||
## 七、参考资料
|
||
|
||
- **架构文档**:`mvp/architecture/knowledge-retrieval-architecture.md`
|
||
- **使用指南**:`mvp/architecture/knowledge-retrieval-usage.md`
|
||
- **OpenSpec**:`openspec/changes/archive/2026-06-30-session-dedup-knowledge-map/`
|