vm0取消与超时恢复:stuck tool如何被SIGKILL收割而不泄漏资源
【免费下载链接】okouOkou connects to the tools your team already uses and does the work — across marketing, sales, engineering, and operations, under your control.项目地址: https://gitcode.com/GitHub_Trending/vm/okou
Okou(vm0)是一个开源 AI 工作流自动化平台,把任务放进独立的 Firecracker 微虚拟机里执行,并由 guest-agent 全程看管进程生命周期。本文带你读懂它的取消与超时恢复机制:当一个网络工具(如 WebFetch)卡死时,stuck tool 看门狗如何一步步升级到SIGKILL收割整个进程组,确保沙箱不泄漏任何资源。
为什么 AI Agent 会"卡住"?
Agent 执行任务时会调用 WebSearch、WebFetch 等网络工具。一旦某个请求挂起(对方服务器不响应、DNS 卡死、网络抖动),CLI 进程会一直等待,表现为:
- 任务永远跑不完,沙箱被白白占用
- 子进程、管道句柄、网络连接无人回收,形成"泄漏"
- 用户取消(cancellation)时,如果 CLI 忽略了温和的终止信号,取消就像没发生
vm0 的解法是:看门狗定时巡检 + 信号逐级升级 + 进程组整体收割。
🕵️ 看门狗:每 5 秒巡检一次未返回的工具
追踪逻辑在 StuckToolTracker:
- CLI 每发出一个 WebSearch / WebFetch 调用,看门狗就记下调用 ID 和起始时间;
- 一旦收到对应的
tool_result,立即从表中移除; - 容量上限 256 条(MAX_TRACKED_STUCK_TOOLS),单条 ID 最长 1KB,防止异常子进程无限刷事件撑爆内存;
- 表满了不驱逐旧条目——驱逐可能掩盖真正卡死的请求,宁可让新调用暂时无追踪(track_use)。
巡检节奏由两个常量决定(constants.rs):
| 常量 | 默认值 | 含义 |
|---|---|---|
STUCK_TOOL_TIMEOUT_SECS | 300 秒 | 单次网络工具调用最长等待 |
STUCK_TOOL_CHECK_INTERVAL_SECS | 5 秒 | 看门狗巡检间隔 |
主事件循环(select 分支)每秒级醒来时调用 oldest_expired,找出最早超时的那条调用。命中即判定:Tool timeout: WebFetch stuck for …,并触发终止流程。
📡 SIGTERM → SIGKILL:一次"先礼后兵"的状态机
真正决定怎么杀人的是 termination.rs 里的终止状态机,注释里画得很清楚(状态迁移表):
| 从 | 触发 | 到 | 动作 |
|---|---|---|---|
Idle | 卡死被检出 | SigtermPending | 对整个进程组发SIGTERM,同时启动 SIGKILL 宽限期 |
SigtermPending | 宽限期到 | SigkillPending | 仍没退出?直接SIGKILL收割 |
SigkillPending | 宽限期到 | Done | SIGKILL 生效,流程收尾 |
| 任意 pending | 子进程主动退出 | Done | 不再发信号 |
两个设计细节值得新手注意:
- Done 是"粘性"的:迟到第二个
type=result事件无法重新武装定时器,所有信号只发一次(should_arm_post_result),杜绝重复杀进程的竞态。 - 按进程组(pgid)发信号:不是只杀 CLI 本体,而是杀掉它 fork 出来的所有子孙进程。日志形如
Tool timeout: WebFetch stuck for {elapsed}s, SIGTERM pgid={pgid}(ControlTerminationLog),这就是"不泄漏资源"的关键——孤儿进程无处藏身。
终止原因被完整记录为诊断信息,StuckTool对应的标签就是"stuck-tool watchdog"(TerminationReason::label),方便事后审计到底是执行超时、用户取消还是看门狗动手的。
✅ 用端到端测试证明"收割真的发生了"
这套机制不是纸上谈兵,仓库里有一组专门的集成测试来"逼"它走完升级链路:
- stuck_tool_reap_sigkill.rs:mock CLI 故意忽略 SIGTERM,看门狗在 1 秒超时后先发 SIGTERM,宽限期内进程装死,随后被 SIGKILL 收割。测试断言终止原因正是
StuckToolWatchdog、发出的信号是Sigkill、且标记了escalated; - stuck_tool_stdout_eof_reap_sigkill.rs:stdout 意外断开时的收割路径;
- 配套的取消恢复场景见 user_cancellation_recovery.rs 与 execution_timeout_recovery.rs。
更上游的"取消意图如何从 API 一路传到 Runner"的完整设计,可以读 run-cancellation-reconciliation.md——它描述了 cooperative(协作式)与 hard(硬取消)两种模式如何持久化、如何投递到沙箱。
📌 关键路径速查
| 模块 | 相对路径 |
|---|---|
| 工具调用追踪(看门狗核心) | crates/guest-agent/src/cli/claude.rs |
| 终止状态机与信号策略 | crates/guest-agent/src/cli/termination.rs |
| 主事件循环巡检分支 | crates/guest-agent/src/cli/mod.rs |
| 超时/巡检间隔常量 | crates/guest-agent/src/constants.rs |
| 进程组信号发送 | crates/guest-agent/src/cli/process_group.rs |
| 取消对账官方文档 | docs/run-cancellation-reconciliation.md |
一句话总结
vm0 用"有界追踪 + 定期巡检 + 信号逐级升级 + 进程组整体收割 + 端到端测试背书"五件套,把"stuck tool 卡死沙箱"这类最脏的边界情况变成了可审计、可复现、零泄漏的受控流程——这也是它能放心让 Agent 7×24 小时无人值守干活的安全底座。
【免费下载链接】okouOkou connects to the tools your team already uses and does the work — across marketing, sales, engineering, and operations, under your control.项目地址: https://gitcode.com/GitHub_Trending/vm/okou
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考