Skip to content

05. Streaming、取消与 Events

本章说明 agent run 如何从“一次性返回结果”变成“运行中持续输出”。流式输出、取消和事件模型是后续真实 agent 体验的基础。

读完本章后,应该理解:

  • streaming route 与普通 JSON route 的区别
  • AbortSignal 为什么要传过 runtime 边界
  • agent event 与前端事件为什么要分开
  • projection boundary 如何把内部事件转换成 UI 可消费事件

背景

同步 agent endpoint 很难使用。真实 agent 工作会持续一段时间,并有可观察阶段:

  • prompt built
  • model started
  • assistant text streaming
  • tool requested
  • tool started
  • tool finished
  • run succeeded or failed

Agent run 需要在执行过程中持续暴露进度。

Streaming Route

项目加入:

text
app/api/agent/stream/route.ts

这个 route 返回 Server-Sent Events。它仍然是 route boundary,不是 agent runtime。

后端发出内部 AgentEvent,route 把它投影成 frontend SSE events。

取消边界

取消被设计成真实 abort chain,而不是发给模型的一句 prompt instruction:

text
React AbortController
  -> fetch signal
  -> NextRequest.signal
  -> AgentRunContext.signal
  -> OpenAI SDK request option
  -> stream chunk guard
  -> tool runtime checks

关键文件:

text
lib/agent-run-context.ts

Runtime 会在关键 checkpoint 调用 assertAgentRunNotAborted(...)

Agent Events

lib/agent-events.ts 引入内部 event model。

例子:

text
run_started
model_started
assistant_delta
tool_requested
tool_started
tool_finished
step_created
run_succeeded
run_failed

后续阶段又加入:

text
model_requested
model_completed
tool_permission_decided
approval_requested

Projection Boundary

lib/agent-stream-projection.ts 把内部 events 映射成 frontend events。

这让浏览器不会变成 runtime。前端观察;服务端拥有模型调用和工具执行。

数据流

Git 证据

相关提交:

text
d5f8ad8 Stream agent progress and answer
edf8405 Add cancellable agent runtime boundaries
72bed76 Add agent harness event state
6fb0b86 Project agent events to stream responses

取舍

Streaming 带来更多 event names,但保留了清晰方向:

text
runtime event -> projection -> frontend event

这个决定让后来的 Debug Console 成为可能。

常见误解

误解一:流式就是把最终答案拆成小块

Agent 流式不只是答案分块。它还包括模型过程文本、工具开始、工具结束、错误、取消和最终提交等事件。

误解二:取消只需要前端停止显示

取消必须进入 runtime。否则前端虽然不显示了,后端仍可能继续调用模型或执行工具。

误解三:内部事件可以直接给 UI

内部事件通常包含 runtime 细节。投影层可以稳定前端协议,也为未来 telemetry 保留更完整的内部语义。

本章小结

这一章把 agent run 变成 live process:前端可以持续接收事件,runtime 可以响应取消,内部事件通过 projection boundary 转换成前端协议。

本章验证点

验证两件事:校验失败不会打开 SSE 流;run 的终态事件有确定性测试兜底。

  1. 空 body 打 /api/agent/stream(无需 key)。实测返回的是普通 JSON 400(content-type: application/json),不是 SSE error event——validation 在流打开之前就完成了:
bash
curl -s -i -X POST http://localhost:3000/api/agent/stream \
  -H 'Content-Type: application/json' -d '{}'
text
HTTP/1.1 400 Bad Request
content-type: application/json

{"ok":false,"error":"Request body validation failed.","validationErrors":{"formErrors":[],"fieldErrors":{"task":["Field `task` is required."]}}}
  1. 终态事件测试(无需 key,fake gateway):
bash
npx tsx --test tests/agent-run-terminal-events.test.ts
text
✔ an aborted run emits run_cancelled as its terminal event
✔ a failed run emits run_failed as its terminal event
ℹ pass 2