对话运行周期
本文详解 wesclaw 中一次 AI 对话(Run)从发起到结束的完整生命周期,以及 SSE 流式事件、中断与恢复、认知结算等核心机制。
Run 的完整生命周期
什么是 Run
Run(运行)是 wesclaw 中一次 AI 对话交互的完整执行过程。当你发送一条消息后,引擎会创建一个 Run 来处理这条消息。
Run 生命周期阶段
消息发送 → Run 创建 → 上下文组装 → LLM 调用 → 工具执行(循环) → 最终回复 → 认知结算 → Run 结束
详细阶段说明:
| 阶段 | 描述 | 用户可见 |
|---|---|---|
| 消息持久化 | 你的消息被保存到 wes_messages 表 | 消息气泡立即出现 |
| Run 创建 | 引擎创建 Run 实例并分配 runID | — |
| 上下文组装 | 组装系统提示词、记忆、技能、历史消息 | — |
| LLM 调用 | 向大模型发送请求 | 出现 loading 指示器 |
| 流式输出 | 模型逐 token 返回文字 | 逐字显示 AI 回复 |
| 工具调用 | 模型请求调用工具(可能多轮) | 显示工具执行卡片 |
| 最终回复 | 模型生成最终文字回复 | 显示完整回复 |
| 认知结算 | 后台压缩会话状态 | 用户不可见 |
| Run 结束 | 写入系统摘要消息,清理临时资源 | 回复完成,可以继续输入 |
Run 与 SSE 的关系
Run 的生命周期与 SSE 连接解耦。这意味着:
- SSE 连接断开不会取消正在执行的 Run
- 断开后重新连接可以继续接收事件
- Run 的取消只能通过以下方式:
- 用户主动中断(点击停止按钮)
- CycleDetector 检测到死循环且 HITL 超时
- ErrorStreak 检测到伪进展且 HITL 超时
- Cell 停止(生命周期结束)
SSE 流式事件
事件类型
wesclaw 使用 SSE(Server-Sent Events)将 Run 的实时状态推送到前端:
| 事件类型 | 说明 | 示例场景 |
|---|---|---|
delta | 模型输出增量文本 | AI 逐字生成回复 |
tool_start | 工具开始执行 | 开始搜索网页 |
tool_progress | 工具执行进度 | exec 命令持续输出 |
tool_end | 工具执行完成 | 搜索结果返回 |
thinking | 模型推理过程 | 深度思考内容 |
plan_created | 计划创建 | AI 创建执行计划 |
plan_updated | 计划步骤更新 | 某个步骤完成 |
done | Run 正常结束 | 回复完成 |
error | 发生错误 | Provider 报错 |
interrupted | Run 被中断 | 用户点击停止 |
SSE 数据格式
event: delta
data: {"type":"text","content":"你好"}
event: delta
data: {"type":"text","content":",我是"}
event: tool_start
data: {"tool_name":"web_search","tool_call_id":"tc_123","input":{"query":"天气预报"}}
event: tool_end
data: {"tool_call_id":"tc_123","output":"北京:晴,25°C"}
event: done
data: {"reason":"end_turn","run_id":"run_abc123"}
SSE 断线重连
如果 SSE 连接意外断开,可以使用断线续播功能:
GET /cells/{cellID}/runs/{runID}/events?from_seq=42
from_seq参数指定从第几个事件开始续播- Run 内存中保留了事件缓存(RunEventBuffer),支持重连后续播
中断与恢复
中断 Run
有多种方式可以中断正在执行的 Run:
| 方式 | 操作 | 效果 |
|---|---|---|
| 停止按钮 | 点击对话中的停止按钮 | 优雅中断,保存已完成的工作 |
| 斜杠命令 | 在输入框输入新消息 | 当前 Run 自动中断 |
| API 调用 | POST /sessions/{sid}/interrupt | 通过 API 中断 |
中断后的行为
- AI 停止生成回复
- 已完成的工具调用结果保留
- 对话历史保存到断点
- 认知结算仍然会执行(除非是 panic_stop)
- Run 状态变为
interrupted
恢复对话
中断后你可以:
- 继续提问 — 发送新消息,引擎创建新的 Run
- 重试 — 使用重试按钮,重新发送上一条消息
- 开始新对话 — 使用
/new或快捷键创建新对话
已中断的 Run 不会"恢复"执行。新消息会创建一个全新的 Run,但会话上下文(包括之前的工具调用结果)会被保留。
认知结算(CognitiveSettlement)
什么是认知结算
认知结算是 Run 结束时的一个强制阶段。引擎使用同一个对话模型对整轮对话进行总结和压缩,产出下一轮对话所需的历史状态。
为什么需要认知结算
| 问题 | 认知结算如何解决 |
|---|---|
| 对话越来越长,token 消耗剧增 | 将长对话压缩为紧凑的会话状态 |
| 重要信息可能被遗忘 | 结算时提取关键事实和决策 |
| 工具结果占用大量上下文 | 压缩工具输出,只保留关键证据 |
认知结算产出
| 产出物 | 说明 |
|---|---|
| SessionState | 压缩后的会话状态,作为下一轮对话的历史上下文注入 |
| Memory Entries | 值得长期记忆的事实和偏好 |
| Plan State | 如有计划,计划的当前进度状态 |
关键特性
- 对用户不可见 — 认知结算在后台执行,不会产生 SSE 事件或对话气泡
- 使用主对话模型 — 不使用辅助模型,保证压缩质量
- 强制执行 — 所有正常终止的 Run 都会执行(唯一例外:panic_stop)
- 降级保护 — 结算失败时有机械压缩作为 fallback
认知结算与上下文的关系
第 1 轮对话:
系统提示词 + 用户消息 + AI 回复 → 认知结算 → SessionState₁
第 2 轮对话:
系统提示词 + SessionState₁ + 新用户消息 + AI 回复 → 认知结算 → SessionState₂
第 N 轮对话:
系统提示词 + SessionState_{N-1} + 新用户消息 + AI 回复 → ...
SessionState 是下一轮上下文中唯一的历史来源,不从旧消息推断历史。
Run 终止原因
Run 结束时会携带终止原因(done reason):
| 终止原因 | 说明 | 重试? |
|---|---|---|
end_turn | 模型正常结束 | 通常不需要 |
context_cancelled | 用户主动中断 | 按需 |
interrupted | 被外部中断 | 按需 |
cycle_detected | 循环检测触发 | 建议换种方式 |
budget_exhausted | 预算耗尽 | 不建议 |
cognitive_error | 认知内部错误 | 可以重试 |
panic_stop | 引擎内部崩溃 | 可以重试 |
错误的可重试性
wesclaw 会自动判断错误是否可重试:
- 可重试 — Provider 瞬时错误(503、429)、认知内部错误 → 显示重试按钮
- 不可重试 — 安全策略阻止、预算用尽、治理拦截 → 显示提示信息,不显示重试按钮
- 静默处理 — 用户主动中断(
context_cancelled)→ 不显示任何错误
多轮工具调用
工具调用循环
一个 Run 中可能包含多轮 LLM 调用和工具执行:
LLM 调用 → 工具调用 1 → 工具调用 2 → LLM 调用 → 工具调用 3 → LLM 调用 → 最终回复
并行工具调用
引擎支持在一轮中并行调用多个工具:
LLM 调用 → [工具 A, 工具 B, 工具 C] (并行执行) → LLM 调用 → ...
迭代预算
每个 Run 有迭代预算限制(默认 90 轮),防止无限循环。到达预算上限后,引擎会给 AI 一次"终结机会"让它总结当前进度。
会话摘要消息
每个 Run 结束时,引擎会在 wes_messages 中写入一条 role=system 的摘要消息,记录:
- Run 的终止原因
- 对话的简要总结
- 工具调用统计
这条摘要消息对用户不可见,但会影响后续对话的上下文组装。