
书名:《Agent Runtime 工程化:从工具调用循环到可恢复执行系统》
作者:陈堂会
平台:极客日志(https://zeeklog.com)
X:@Megick_com
联系邮箱:[email protected]
章节:第二章 TypeScript / Node Runtime 基础
第二章 TypeScript / Node Runtime 基础
写 Agent Runtime 之前,先补一门不太体面的课:异步任务、流和进程控制。
Agent Runtime 表面上是在调用模型,实际上是在管理一串可能很慢、会失败、有副作用、需要取消的异步任务。模型请求会流式返回,shell 命令会持续输出,文件系统可能被修改,用户可能中途按下取消,工具可能超时,子进程可能留下孤儿进程。没有扎实的 Node runtime 基础,Agent 很容易变成'看起来在运行,实际上已经失控'的程序。
这一章不是为了讲 Node 入门语法,而是回答一个工程问题:当模型决定'我要执行一个工具'之后,Runtime 如何让这件事可取消、可观测、可限流、可清理、可恢复?
2.1 Promise 不是全部
很多人学会 async/await 后,会误以为异步编程已经结束。对普通 Web API 来说,这通常够用;对 Agent Runtime 来说,不够。
Runtime 面对的不是一次请求一次响应,而是长时间过程:
- 模型流式吐出 token 和 tool call delta;
- shell 命令持续输出 stdout/stderr;
- 用户可能在工具运行中取消;
- 多个只读工具可以并行;
- 写入工具必须串行或受事务保护;
- 某个 step 超时后需要清理资源;
- 任务恢复后不能重复执行已完成的副作用。
所以你需要掌握的不只是 await model.call(),而是异步迭代、取消传播、背压、进程生命周期和资源清理。

图 2-1:异步任务生命周期。取消、超时、流式输出和资源清理不是 UI 细节,它们要贯穿模型请求、工具执行、子进程和 trace。
可以把一次工具执行看成一条小流水线:
- Runtime 创建
AbortController,绑定用户取消和 timeout; - 执行器启动模型请求或子进程;
- 输出被拆成事件,而不是堆成一个巨大字符串;
- 事件一边发给 UI,一边写 trace,一边按策略摘要给下一轮模型;
- 任一环节失败时,先清理资源,再决定是否重试。
很多'Agent 卡住了'的问题,其实不是模型卡住,而是这条流水线某处没有关闭。比如子进程 stdout 没有人消费,缓冲区满了;比如 UI 已经取消,执行器没收到 signal;比如 timeout 只 reject 了 Promise,没有杀掉进程。第二章要练的正是这些脏活。

