Skip to content
Go back

从 Codex 源码学 Agent 工程|07 长命令管理:等待、输出、超时与收尾

Edit page

观察窗口先返回与进程继续跑的两条时间线、write_stdin 续读、有界输出缓存,以及两类超时语义为何不能混用。

权限决定这一步能怎么跑;长命令管理承接后面的等待、观察与收尾。测试跑了几十秒还没结束——工具调用该一直卡住吗?先返回的话,又怎么知道同一个测试跑到哪了?第七章讲长命令管理。

先记住两个区别

  • 一次等待结束 ≠ 整个进程结束;
  • 可持续交互的调用 ≠ 只等待完成的调用——超时语义不同。

两条时间线:工具调用 vs 命令进程

第一次调用解读命令并返回一段结果。若等待窗口到了而进程还活着,宿主可先把已有输出和一个可继续操作的会话标识返回——模型不必无限挂住。下面的进程线没结束;后续读输出访问的是同一个已启动进程,不是重跑测试。只有确认进程退出,才把退出码与剩余输出作为结束依据。因此需要保存进程句柄。

工具返回了文字 ≠ 命令完成;一段时间没输出 ≠ 命令失败。要一起看:调用标识、退出状态与新输出。

启动时登记什么

仍先过权限与执行策略。通过后创建进程,并把后期管理需要的对象登记起来——只记一个字符串编号不够,还要能访问进程句柄、读输出、判断是否退出、需要时清理。图中的会话标识是工具协议找到管理对象的 key,不能简单当成 OS 的 PID;可能映射本地进程,也可能对应执行服务的句柄。对模型而言,重点是保存返回的标识,在后续请求里关联到同一次执行。即使这次工具调用已返回,输出读取任务仍可继续;但内存管理不是永久托管——进程与会话会退出、清理或失效,拿到标识 ≠ 永远可恢复。

观察窗口与返回内容

等待窗口决定的是这次调用何时交回结果,不是给进程设统一寿命上限。窗口结束后检查进程状态:已退出就返回结果信息;仍在运行则保留后续观察入口。实践里等待参数有范围,不是传任意大数字就一直等;还可能受交互暂停等状态影响。返回结果至少应包含:这次拿到了什么,以及进程现在是否仍可继续观察——不要只返回一段日志让模型猜完成状态。

继续观察:write_stdin 类接口

名字容易让人以为必须写入。输入为空时可等待并读后续输出;非空则尝试写入再收集结果。两种用法共享同一标识,交互含义不同。普通字符串输入往往需要启动时设置 TTY;非 TTY 路径不能继续写普通字符,可能只对 Ctrl-C 等特殊处理用于中断。非空输入还可能要审批,且进程须处于可交互状态——已结束的测试不能靠几个字符重新开始。

同一终端的读写需串行协调(交互锁),避免两请求同时消费同一输出缓冲。不同会话可并发观察,但同一标识不要安排多个未协调的读取者。

有界缓存与界面/模型分流

长命令日志若无限保存在内存并原样发给模型,速度与上下文都吃不消。Codex 把持续输出与有界缓存放在进程对象附近,并提供输出通知。一次工具结果是本次收集并格式化后的内容 ≠ 无限完整日志;超限时常留头尾并带截断标记。界面增量日志与模型收到的工具结果要分开:屏幕滚过不保证下次请求带上全部文字。排查时看实际交给模型的结果与是否出现截断提示。

收尾顺序

进程退出与输出读完可能不是同一瞬间。看到退出码就立刻丢掉全部管理对象,最后的报错或测试汇总可能丢失。应协调退出状态与输出读取,再刷新管理记录。最终结果应能表达退出码与剩余输出;宿主还要清理句柄、网络审批上下文等登记。退出码为 0 只说明命令按自己约定成功退出,不能代表当前任务完整验收——空字符串场景是否覆盖、旧接口是否保持,还要看用例与代码。

两类超时语义不要混

  1. 交互式等待:窗口到了调用先返回,进程可继续跑;
  2. 只等待完成:把超时当终止条件,超时后尝试确认终止,而不是留给下次普通轮询。

取消则走取消处理,协调终止已启动进程;不能把某一个等待者被取消推导成所有外部工作都结束。所有终止方式都不代表撤销已产生的副作用(已写文件、已发请求)。问「超时以后还跑不跑」要先问清:是哪条路径、超时的是等待窗口还是完成期限。

标识会失效

进程退出并移除后再访问可能得到未知标识。资源管理有容量与回收逻辑,会考虑活动情况,避免在某些正在交互状态下贸然移除。后台进程也需要资源预算:不能为了显得忙就无限启动新命令,也不能用重复启动代替跟踪已有命令。标识失效时核对现状,而不是假设进程仍活着或一定没产生结果。

接回修 bug 场景

启动测试若返回会话标识就记住它;需要进展时对同一标识读输出;收到新错误就分析;仍在跑且无新消息时合理安排下次观察,不要把连续空轮询误当成持续推进。等待期间能否做别的事看依赖:读不冲突的文件可能可并行,但不要在测试运行中改它正在验证的内容。命令结束后检查退出码、汇总与实际修改,再决定修正、补测或交付。

四个自检位置

  1. 启动时有没有把权限与进程状态一起保存;
  2. 先返回时有没有明确告诉调用方是否仍在运行;
  3. 继续观察时能不能关联到同一对象;
  4. 结束或取消时能不能把输出、退出状态与资源清理做好。

下一期讲工具按需发现:工具越来越多时,如何登记、检索定义,再发起真实调用。


Edit page
Share this post on:

Previous Post
从 Codex 源码学 Agent 工程|08 工具按需发现:BM25、定义与后续调用
Next Post
从 Codex 源码学 Agent 工程|06 权限与沙箱:审批、文件范围与网络