docs: 完成 MVP 架构设计文档
- 数据库设计:3张核心表 (diagnosis_record/case_library/api_document) - Agent架构:4 Agent协作 (Supervisor/Planner/Executor/Verifier) - 意图识别:L0正则+L1小模型Agent分层 - RAG两层加载:L1预加载通用知识 + L2按需加载具体文档 - Skill体系:/diagnose-by-orderid 标准化诊断流程 - Harness控制:5 Gates + 中断机制 - 会话管理:Redis临时存储 + 扩展方案 - 闭环机制:用户反馈 → BadCase → 优化 - 实施规划:3阶段13天
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
# 会话管理设计
|
||||
|
||||
## 会话存储策略
|
||||
|
||||
### Redis(主)
|
||||
|
||||
**数据结构**:
|
||||
```
|
||||
key: session:{session_id}
|
||||
value: {
|
||||
"sessionId": "sess-abc",
|
||||
"userId": "user-123",
|
||||
"currentDiagnosisId": "diag-001",
|
||||
"messages": [
|
||||
{"role": "user", "content": "诊断订单 A"},
|
||||
{"role": "assistant", "content": "完整报告..."}
|
||||
],
|
||||
"context": {
|
||||
"province": "广东",
|
||||
"apiName": "社保查询",
|
||||
"errorCode": "40003"
|
||||
},
|
||||
"createdAt": "2024-06-15T14:30:00Z",
|
||||
"lastActiveAt": "2024-06-15T14:35:00Z"
|
||||
}
|
||||
ttl: 1800秒(30分钟)
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- ✅ 快速读写
|
||||
- ✅ 自动过期
|
||||
- ✅ 支持追问(保存上下文)
|
||||
|
||||
---
|
||||
|
||||
### MySQL(辅助,可选)
|
||||
|
||||
**同步策略**:
|
||||
1. 重要会话同步
|
||||
- 有用户反馈的会话
|
||||
- 诊断失败的会话(BadCase)
|
||||
- 多轮对话 > 3 轮的会话
|
||||
|
||||
2. 同步时机
|
||||
- 会话结束时(30分钟过期)
|
||||
- 用户反馈时(实时)
|
||||
- 定时任务(每小时,可选)
|
||||
|
||||
3. 同步目标
|
||||
- conversation_history 表
|
||||
- 用于长期分析和审计
|
||||
|
||||
---
|
||||
|
||||
## 数据流设计
|
||||
|
||||
### 场景1:单次诊断(主流 80%)
|
||||
|
||||
```
|
||||
1. 用户发起诊断
|
||||
POST /api/diagnosis/start
|
||||
{
|
||||
"orderId": "202406150001"
|
||||
}
|
||||
|
||||
2. 创建会话(Redis)
|
||||
key: session:sess-abc
|
||||
ttl: 1800秒
|
||||
|
||||
3. 创建诊断记录(MySQL)
|
||||
INSERT INTO diagnosis_record
|
||||
- diagnosis_id: diag-001
|
||||
- session_id: sess-abc
|
||||
- status: RUNNING
|
||||
|
||||
4. Agent 执行诊断
|
||||
- 调用工具(queryOrder, queryLogs, searchDoc...)
|
||||
- 生成报告
|
||||
|
||||
5. 更新诊断记录(MySQL)
|
||||
UPDATE diagnosis_record
|
||||
- status: SUCCESS
|
||||
- root_cause: "idCard字段缺失"
|
||||
- report_markdown: "完整报告..."
|
||||
|
||||
6. 返回报告
|
||||
→ 大部分用户到此结束
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 场景2:追问(少数 20%)
|
||||
|
||||
```
|
||||
1. 用户追问
|
||||
POST /api/chat
|
||||
{
|
||||
"sessionId": "sess-abc",
|
||||
"message": "为什么会缺失字段?"
|
||||
}
|
||||
|
||||
2. 从 Redis 获取上下文
|
||||
GET session:sess-abc
|
||||
- 有之前的诊断结果
|
||||
- 有对话历史
|
||||
|
||||
3. Agent 基于上下文回答
|
||||
- 不创建新的 diagnosis_record
|
||||
- 只是普通对话
|
||||
|
||||
4. 更新 Redis 会话
|
||||
- 追加对话历史
|
||||
- 刷新 TTL(重新计时30分钟)
|
||||
|
||||
5. 可选:保存到 conversation_history(MySQL)
|
||||
- 如果需要长期分析
|
||||
- 异步存储
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 场景3:同一会话多次诊断
|
||||
|
||||
```
|
||||
1. 用户第一次诊断
|
||||
"诊断订单 A"
|
||||
→ diagnosis_record(diag-001, session_id=sess-abc)
|
||||
|
||||
2. 用户第二次诊断
|
||||
"再诊断订单 B"
|
||||
→ diagnosis_record(diag-002, session_id=sess-abc)
|
||||
|
||||
3. 会话关联
|
||||
- 同一个 session_id
|
||||
- 两条 diagnosis_record
|
||||
- Redis 中保存完整对话历史
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 会话生命周期
|
||||
|
||||
```
|
||||
创建
|
||||
↓
|
||||
活跃(每次交互刷新TTL)
|
||||
↓
|
||||
30分钟无活动
|
||||
↓
|
||||
自动过期
|
||||
↓
|
||||
可选:同步到 MySQL(重要会话)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 实现示例
|
||||
|
||||
### Java 代码
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class SessionService {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
private static final String SESSION_PREFIX = "session:";
|
||||
private static final Duration SESSION_TTL = Duration.ofMinutes(30);
|
||||
|
||||
// 创建会话
|
||||
public String createSession(String userId) {
|
||||
String sessionId = UUID.randomUUID().toString();
|
||||
|
||||
SessionData session = SessionData.builder()
|
||||
.sessionId(sessionId)
|
||||
.userId(userId)
|
||||
.messages(new ArrayList<>())
|
||||
.context(new HashMap<>())
|
||||
.createdAt(LocalDateTime.now())
|
||||
.lastActiveAt(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
String key = SESSION_PREFIX + sessionId;
|
||||
redisTemplate.opsForValue().set(key, toJson(session), SESSION_TTL);
|
||||
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
// 获取会话
|
||||
public SessionData getSession(String sessionId) {
|
||||
String key = SESSION_PREFIX + sessionId;
|
||||
String json = redisTemplate.opsForValue().get(key);
|
||||
return json != null ? fromJson(json) : null;
|
||||
}
|
||||
|
||||
// 更新会话(刷新TTL)
|
||||
public void updateSession(SessionData session) {
|
||||
session.setLastActiveAt(LocalDateTime.now());
|
||||
String key = SESSION_PREFIX + session.getSessionId();
|
||||
redisTemplate.opsForValue().set(key, toJson(session), SESSION_TTL);
|
||||
}
|
||||
|
||||
// 删除会话
|
||||
public void deleteSession(String sessionId) {
|
||||
String key = SESSION_PREFIX + sessionId;
|
||||
redisTemplate.delete(key);
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user