跳转到内容

工具执行域

工具执行域负责任务中工具块的实际执行。它是按目标运行时架构从 Task 中拆出的第一个执行器:reducer 负责决策,执行器负责执行,两者只通过命令和事件交互。

源码位于 src/core/task/executors/tool/。

早先的尝试是把工具相关的方法逐个移出 src/core/task/index.ts,结果停滞了:像助手消息展示器这样的成员,在同一个函数里同时服务流式输出和工具预览;最终轮次还要处理服务商计费、上下文压缩记录和后续任务交接。按成员拆分,要么带走无关职责,要么把工具工作留在原处。

按职责拆分消除了这个冲突:混合职责的成员留在原处负责编排,工具工作以命令的形式移出。

每个被接纳的工具块对应一条命令:

interface ToolRunCommand {
commandId: string
turnId?: string
dlineTid: string
mode?: "serial" | "parallel"
}

命令只携带身份和执行模式,不携带工具输入;生产代码按 dlineTid 查找已经准备好的准入记录再执行。mode 由 TurnDriver 在创建轮次时根据任务设置决定,reducer 把它复制到执行 effect 中。执行域不根据 mode 做调度决定:它一次收到一个块或一批块,就执行收到的内容。turnId 随命令传递,使 reducer 已经离开的轮次的结果能够被识别,而不是被悄悄应用。

handle(command: ToolRunCommand): void
halt(order: HaltOrder): Promise<void>

handle 立即返回。 调用方是串行的运行时分发路径,如果在这里等待工具完成,所有后续事件都会被最慢的工具阻塞。

每个结果都成为事件。 成功时发出 tool.block_result,工具抛出异常时发出 tool.block_failed,停止之后到达的命令发出 tool.rejected。执行域不会让拒绝逃逸为未处理的 Promise,也不会直接写状态。

halt 是排空屏障。 只有在进行中的工作全部结束、自有资源释放之后,才会发出回执 tool.halted。停止之前产生的结果会被丢弃,不会被应用,因此一个在拆除期间恰好完成的工具不能让已取消的轮次复活。停止之后调用 handle 会得到 tool.rejected;运行时开始新一代工作时通过 rearm 重新接纳命令。

同一个 haltId 的重复停止不会产生效果,这保证了每一代停止只释放一次资源。生产代码使用的 haltToolDomain() 为每次停止设置 5 秒预算:超时或出错时会报告失败并执行回退释放,此时可能没有正常的排空回执。

执行域通过 OwnedToolResources 描述它使用的工作区资源:

interface OwnedToolResources {
revertDiff(): Promise<void>
cancelCommand(): Promise<void>
}

以前同一职责分散在中止、中断、终止和服务商重试等多条路径上,每条路径都要记得还原 diff:漏掉一条会留下打开的编辑器,重复一条会还原两次。把所有停止都经过同一个释放点,使“每次停止只重置一次”成为结构上的保证。

createToolDomainRunner() 提供的释放适配器会尝试执行两个重置,即使第一个失败也会继续第二个,并报告失败而不重新抛出。直接注入的 runner 如果在 release 中抛出异常,halt 仍会在 finally 中发出回执,但自身会以该异常拒绝。

当前生产环境的 Task 注入的 cancelCommand 是空操作,前台命令的取消仍由 Task 中已有的调用点负责。

与界面相关的能力在装配执行域时授予,而不是在每个处理器内部检查:

type ToolDomainSurface = UserFacingSurface | DeniedSurface

使用 DeniedSurface 装配的执行域中,ask、say、openInteraction 和可见的 diff 根本不存在。另一种做法是枚举:覆盖一份已知的回调列表只能保护被记得覆盖的成员,每新增一个回调都要重新检查。收回整个界面则直接移除了能力本身。

拒绝是显式的:确实需要用户参与的工作会抛出 SurfaceDeniedError,说明缺少的能力和原因,而不是看似成功、输出却落到别处。收回界面不会禁止工作本身,文件仍然可以写入,只是不渲染。

目前生产环境唯一的执行域装配使用 UserFacingSurface;子智能体通过自己的 runner 执行已接纳的处理器。DeniedSurface 装配目前在单元测试中验证。

这个执行域是目标架构中一个执行器的提前实现,不是它的替代品。这里交付的是工具执行域和 tool.* 命令与事件词汇,以及仅限本执行域的资源归属和停止回执。统一内核、替代当前运行时分发的事件循环,以及服务商、对话、组合、后台和协作任务等执行器,仍属于更大范围的运行时收敛工作。

行为由 src/core/task/executors/tool/__tests__/ 中的单元测试覆盖:

  • handle 在工具块结束之前返回,一批块并发执行;
  • 抛出异常的工具变成失败事件,而不是逃逸的拒绝;
  • 停止回执在排空之后发出,停止之前的结果被丢弃;
  • 重复取消只释放一次资源,新一代停止会再次释放;
  • 收回界面后所有界面能力都被拒绝,但工具仍然执行。