Run 生命周期
Run 是 wescode 中一次完整的 Agent 执行过程。本文介绍 Run 的创建、执行、事件流和终止路径。
Run 概念
一次 Run 是从用户发送消息到 AI 完成响应的完整过程:
用户发送消息
↓
cell.Runtime().Run(ctx, AppRunRequest{...})
↓
Agent Loop(工具调用循环)
↓
CognitiveSettlement(认知结算)
↓
Run 结束
每次 Run 返回一个事件流(<-chan engine.Event),消费方通过流式接收事件来呈现 AI 响应。
AppRunRequest 必填字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
Actor | string | ✅ | 请求主体(SD-15,空 → ErrActorRequired) |
Model | string | ✅ | 模型名(INV-MODEL-01,空 → ErrModelRequired) |
SessionID | string | ✅ | 会话 ID |
AgentID | string | 推荐 | Agent ID |
Messages | []Message | ✅ | 消息列表 |
示例
events, err := cell.Runtime().Run(ctx, wesgine.AppRunRequest{
Actor: "local", // ★ 必填
Model: "claude-sonnet-4-20250514", // ★ 必填
SessionID: "s-abc123",
AgentID: "code-assistant",
Messages: []wesgine.Message{
{Role: "user", Content: "帮我重构这个函数"},
},
})
Run 事件流
Run 通过事件流返回进展信息:
核心事件
| 事件类型 | 说明 |
|---|---|
EventStreamDelta | LLM 流式输出片段 |
EventToolCall | 工具调用开始 |
EventToolResult | 工具调用结果 |
EventDone | Run 正常完成 |
EventError | Run 错误 |
EventInterrupted | Run 被中断 |
Plan 事件
| 事件类型 | 说明 |
|---|---|
EventPlanCreated | Plan 创建 |
EventPlanUpdated | Plan 步骤更新 |
EventPlanCompleted | Plan 完成 |
EventPlanInterrupted | Plan 中断 |
EventPlanNudge | Plan Nudge 提醒 |
其他事件
| 事件类型 | 说明 |
|---|---|
EventBudgetStatus | 预算状态更新 |
EventCycleWarning | CycleDetector 警告 |
EventHITLRequest | HITL 审批请求 |
Run 终止路径
Run 有 11 种终止原因:
| 终止原因 | 类型 | 说明 |
|---|---|---|
end_turn | 正常 | AI 主动结束 |
interrupted | 用户 | 用户中断(/stop) |
context_cancelled | 用户 | context 取消(窗口关闭) |
cycle_detected | 引擎 | 检测到死循环 |
budget_exhausted | 外部 | 预算耗尽 |
error_streak | 引擎 | 连续错误(伪进展) |
quality_revision_limit | 引擎 | 质量修订上限 |
cognitive_error | 引擎 | 认知内部错误 |
execute_error | 引擎 | 执行错误 |
panic_stop | 引擎 | 崩溃停止 |
missing_terminal_event | 保底 | 缺少终端事件(异常) |
Run 与 SSE 解耦(INV-RUN-DETACH)
Run 生命周期始终与传输层(SSE / HTTP 连接)解耦:
SSE 连接建立 → Run 开始
SSE 连接断开 → Run 继续执行(不取消!)
Run 完成 → 事件存入 RunEventBuffer
SSE 重连 → 从 RunEventBuffer 续播
取消 Run 的唯一途径
| 途径 | 说明 |
|---|---|
POST /sessions/{sid}/interrupt | 用户主动中断 |
| CycleDetector HITL 超时 | 真循环检测后用户无响应 |
| ErrorStreakDetector HITL 超时 | 伪进展检测后用户无响应 |
| BudgetCheckFn Exhausted | 外部预算检查返回耗尽 |
| Cell Stop | Cell 生命周期结束 |
引擎不因 counter 阈值主动终止(INV-TERM-01)。
CognitiveSettlement
Run 终态必须包含 CognitiveSettlement 产物(INV-CTX-40 / INV-COORD-13):
Run 终止(任何路径)
↓
CognitiveSettlement 执行
├── 产出 SessionState
├── 产出 Memory entries
├── 产出 Reflections
└── 产出 Learnings
↓
Run 完成
覆盖所有终止路径
| 终止路径 | Settlement |
|---|---|
| end_turn | ✅ 执行 |
| interrupted | ✅ 执行 |
| cycle_detected | ✅ 执行 |
| budget_exhausted | ✅ 执行 |
| error_streak | ✅ 执行 |
| context_cancelled | ✅ 执行 |
| panic_stop | ❌ 唯一例外 |
Run 终端事件保证(INV-TERM-04)
runLoop 的每个 return 路径必须在返回前发出终端事件:
EventDone/EventError/EventInterruptedcallLLM/execute返回phaseReturn时,若ctx.Err() != nil,必须发出EventDone("context_cancelled")- 不得依赖 for 循环顶部的
ctx.Err()检查(因为phaseReturn直接return,永远到不了那里) - 缺少终端事件 → terminal guard 合成虚假
EventError(missing_terminal_event)
wescode 中的 Run
chatInFlight 锁(INV-WS-11)
wescode 中,每个 workspace 同时最多一个 Chat Run:
用户发送消息
↓
chatInFlight 加锁(per workspace key)
↓
Run 执行
↓
收到终端事件(done/error/interrupted)
↓
chatInFlight 解锁
锁的生命周期 = 用户可见生成轮次,不是 chat/send RPC。RPC 返回后的收尾(Settlement、SaveExplorationPaths、CKG 写库)是后台整理,不占用发送权。
Run 后台整理
Run 终端事件发出后,还有后台工作:
EventDone 发出(chatInFlight 解锁,用户可发新消息)
↓ ← 后台 goroutine
SaveExplorationPaths(CKG 更新)
CognitiveSettlement(认知结算)
CKG WriterDB 写入
这些后台任务不阻塞用户交互。
事件消费示例
events, err := cell.Runtime().Run(ctx, req)
if err != nil {
return err
}
for evt := range events {
switch evt.Type {
case engine.EventStreamDelta:
// 流式输出
fmt.Print(evt.Delta)
case engine.EventToolCall:
// 工具调用
log.Info("tool call", "name", evt.ToolName)
case engine.EventDone:
// 完成
log.Info("done", "reason", evt.DoneReason)
case engine.EventError:
// 错误
log.Error("error", "kind", evt.ErrorKind)
}
}
常见问题
Q:为什么关闭浏览器后 AI 还在执行?
Run 与 SSE 连接解耦(INV-RUN-DETACH)。关闭连接不取消 Run。使用 /stop 或 POST /sessions/{sid}/interrupt 显式中断。
Q:Run 终端事件缺失怎么办?
terminal guard 会合成 EventError(missing_terminal_event)。检查 Go 后端日志中是否有 phaseReturn 路径未发出终端事件的问题。