4.4 KiB
4.4 KiB
会话管理设计
会话存储策略
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(辅助,可选)
同步策略:
-
重要会话同步
- 有用户反馈的会话
- 诊断失败的会话(BadCase)
- 多轮对话 > 3 轮的会话
-
同步时机
- 会话结束时(30分钟过期)
- 用户反馈时(实时)
- 定时任务(每小时,可选)
-
同步目标
- 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 代码
@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);
}
}