第 5 章 · Streaming 与实时交互

01 · 为什么 agent 需要实时反馈

没有中间输出的 agent 像黑箱——用户只能等它结束。加上事件流,agent 从脚本升级成交互工具。

黑箱问题

到第 4 章结束时,agent 已经具备了不少能力:能搜索代码、读写文件、执行命令,还有权限检查。但从用户的视角看,使用体验是这样的:

  1. 输入任务
  2. 等待……
  3. 继续等待……
  4. 最终答案出现

如果任务简单(一次工具调用就搞定),几秒钟就能出结果。但如果任务复杂——模型要搜索、读文件、创建计划、执行多个步骤——用户可能要等上一两分钟,期间终端什么都看不到

这不是小问题。黑箱等待会让用户焦虑:

  • 不知道在不在工作——agent 是在思考还是卡死了?
  • 不知道进度——它做了几步?还有几步?
  • 不知道方向对不对——如果模型跑偏了,要等它全部跑完才能看到

对比一下 Claude Code、Codex CLI 这些产品的体验:它们都会在执行过程中实时显示"正在搜索"、"正在读文件"、"正在执行命令"。用户能随时看到 agent 在做什么。

一个观察:agent 内部已经知道自己在做什么

回头看 agent 循环的代码,每一步其实都有明确的动作:

for (let i = 0; i < maxIterations; i++) {
  /** 模型思考 */
  let response = await model.chat(allMessages, allTools);

  /** 执行工具 */
  for (const toolCall of response.toolCalls) {
    const result = await tool.execute(toolCall.arguments, state);
    /** ... */
  }
}

模型在"思考"、工具在"执行"、计划在"更新"——这些信息都存在于 agent 的执行过程中,只是没有暴露出来。

问题不是"没有信息",而是"信息没有传递出去"。

解决方案:事件流(Event Stream)

思路很简单:在 agent 的关键节点,把正在发生的事情告诉外部。

就像浏览器的事件模型一样——用户点击按钮,按钮发出一个 click 事件,谁关心谁监听。agent 也可以做一样的事:

  • 开始处理任务时 → 发出 agent_start 事件
  • 调用模型时 → 发出 model_call 事件
  • 执行工具时 → 发出 tool_call_start 事件
  • 工具返回结果时 → 发出 tool_call_result 事件
  • 计划更新时 → 发出 todo_update 事件
  • 任务完成时 → 发出 agent_done 事件

这些事件构成了一条时间线,完整地描述了 agent 的执行过程。外部只需要监听这条时间线,就能实时展示 agent 的状态。

角色分离

事件流的一个重要好处是角色分离——agent 负责做事,渲染器负责展示,两者互不干扰。

agent(做事)→ 发出事件 → emitter(传递)→ renderer(展示)
  • agent 不关心事件怎么展示。它只在关键节点发出事件,不管谁在监听。
  • emitter 是事件总线。有人注册监听器,有人发出事件,它负责传递。
  • renderer 不关心 agent 的内部逻辑。它只关心"收到什么事件,怎么渲染"。

这种分离带来两个好处:

  1. 可替换。 今天用简单的 console.log 渲染,明天可以换成 ink(React 终端框架)做漂亮的 TUI,agent 代码不需要改。
  2. 可测试。 测试时注册一个监听器,检查发出的事件是否正确,不需要真的在终端里看输出。

和第 4 章权限系统的关系

事件流和权限系统是正交的——它们解决不同的问题,但可以协同工作。

权限系统解决的是安全问题:"这个操作能不能执行?" 事件流解决的是可见性问题:"agent 正在做什么?"

当权限系统弹出确认请求时,这也是一个事件(permission_ask)。渲染器可以用特殊的方式显示它——比如高亮、暂停、等待用户输入。这样用户能同时看到"agent 想做什么"和"系统要求确认"两个信息。

本章目标

这一章要实现三件事:

1. 事件类型系统。 定义 agent 在执行过程中会发出哪些事件,每个事件携带什么数据。

2. 事件发射器。 一个简单的 AgentEmitter 类,支持注册监听器和发出事件。

3. 终端渲染器。 一个 TerminalRenderer 类,监听事件并把状态实时输出到终端。

完成后,agent 从"黑箱脚本"升级为"可观察的交互工具"——用户能实时看到每一步在做什么。

登录以继续阅读

解锁完整文档、代码示例及更多高级功能。

立即登录

On this page