多轮会话与状态管理
真实产品里用户会连续说很多句,智能体必须记住前面说过什么、现在进行到哪一步。多轮会话的核心是状态管理:如何组织历史消息、如何裁剪、如何表达"当前处于什么阶段"。本章为实战章节的状态设计打基础。
消息历史组装
对话状态的最基本载体是消息列表。每轮把用户输入追加进历史,连同系统提示一起发给模型:
messages = [{"role": "system", "content": "你是订票助手"}]
messages.append({"role": "user", "content": "帮我订明天去上海的高铁"})
resp = client.chat.completions.create(model="deepseek-chat", messages=messages)
messages.append(resp.choices[0].message) # 助手回答也入历史
# 输出:下一轮继续携带完整 messages 即可"记得"上文
上下文窗口裁剪策略
历史无限增长会超出窗口、抬高成本,常见裁剪手段:
| 策略 | 做法 | 适用 |
|---|---|---|
| 截断最旧 | 只保留最近 N 轮 | 简单闲聊 |
| 摘要压缩 | 旧消息压缩成摘要置顶 | 长会话 |
| 关键信息抽取 | 只存用户意图、槽位值 | 任务型对话 |
| 分页/外置 | 历史存库,按需取回 | 超长会话 |
# 只保留最近 10 轮
messages = messages[:1] + messages[-20:] # 系统提示 + 最近 20 条
状态机:该不该用
简单问答不需要状态机,消息历史就是全部状态。但任务型流程(订票:收集出发地 → 目的地 → 日期 → 确认支付)建议引入显式状态机,让代码清晰可控:
# 状态流转示例:IDLE -> ASK_CITY -> ASK_DATE -> CONFIRM -> DONE
# 非法状态(如在 CONFIRM 直接问出发地)应被拦截或引导回退
会话 ID 与服务端状态
多轮状态必须存服务端而不是前端内存:客户端每次请求带 session_id,服务端据此取出该会话的历史与状态,支持多端续聊、断线恢复:
{"session_id": "sess_8f2a1c", "state": "ASK_DATE",
"history": ["...最近 10 轮..."], "slot": {"from": "北京", "to": "上海"}}
长任务与恢复 Checkpoint
耗时的多步任务(批量处理、长文档生成)若中途失败,全部重来代价高昂。可引入 checkpoint:每完成一步就把进度与中间结果落库,恢复时从断点继续:
{"task_id": "t_99", "done": ["step1", "step2"], "todo": ["step3"],
"partial": {"report": "已汇总 80% 数据"}}
恢复时读取 checkpoint,把"已完成摘要 + 剩余步骤"重新组装进上下文即可继续执行。
小结
多轮会话 = 状态管理:消息历史组装保证连续性,裁剪策略控成本,状态机管任务流程,session_id 支撑服务端状态,checkpoint 支撑长任务断点续跑。这些正是后面实战章节要落地的核心设计。