docs: 重构文档结构,分离学习笔记和 MVP 架构设计
**变更概述:** - 将 MVP 架构设计文档独立到项目根目录 `mvp/` - 整理 `docs/` 为纯学习和分析文档目录 - 按类型分类:learning(学习)、analysis(分析)、reports(报告)、guides(指南) **目录结构:** ``` mvp/ # MVP 架构设计(独立) ├── README.md # 数据库设计总览 ├── architecture/ # 架构文档 │ ├── agent-architecture-mvp.md │ ├── implementation-plan.md │ └── ... └── tables/ # 数据表设计 docs/ # 学习和分析文档 ├── learning/ # 学习笔记(00-08 编号) ├── analysis/ # 分析笔记 + 重构计划 ├── reports/ # 临时报告 └── guides/ # 指南文档 ``` **详细变更:** - docs/README.md → mvp/README.md(数据库设计入口) - docs/architecture/ → mvp/architecture/(架构设计) - docs/tables/ → mvp/tables/(数据表设计) - docs/学习笔记-*.md → docs/learning/07-*.md, 08-*.md - docs/项目学习路径.md → docs/learning/00-*.md - docs/功能分析报告.md → docs/analysis/ - docs/修复报告-*.md → docs/reports/ - docs/日志配置*.md → docs/guides/ 或 docs/reports/ - docs/design/ → docs/analysis/(问题分析和重构计划)
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