Skip to content

Harness:外部执行引擎 ​

最近整理:2026-10-10。本文合并原“Harness 资源与外部执行引擎”“Harness 执行流程”“Harness 执行状态与租约”三篇;Lead 通过工具管理 Harness 的用法见下文 Lead 管理 Harness 的工具,工具参数见Harness 模块。

决策 ​

Harness 是正式的可复用配置资源;Codex 是执行引擎;Agent 是独立身份与角色。一个 Agent 选择内置 Model 或外部 harness,二者互斥。Harness 资源不是群成员,外部框架的内部子 Agent 不会自动成为 AGT 联系人。

模块与边界 ​

  • agt-harness:Harness 资源配置和安全文件存储、Codex app-server 协议适配、进程生命周期。
  • agt-agent:model/harness 互斥引用、Prompt/Skill/内置工具挂载。
  • agt-service:Harness CRUD、删除引用检查、统一资源工具契约。
  • agt-runtime/harness.rs:整项任务执行、Run、外部会话映射、历史增量、取消围栏。
  • agt-runtime/scheduler.rs:执行调度、群聊提及规则和唯一结果发布。
  • CLI/Web:资源与 Agent 配置入口、无推理连接检查和流式预览。
  • agt-im:保持与引擎无关。

外部执行不实现 ModelClient:完整工具循环只归 Codex 所有,不套用 AGT 的模型重试、参与判断或摘要模型调用。调度器仅在私聊或明确提及时运行外部 Agent,manual 仍有效。

首个接入:本机 Codex ​

使用 codex app-server --listen stdio://。initialize / initialized 后通过 thread/start 或 thread/resume 建立线程,turn/start 启动任务。item/agentMessage/delta 更新有界预览,item/completed 的非 commentary 消息作为最终回复,turn/completed 确认最终状态。错误与空最终回复不会自动重跑。

协议依据:https://developers.openai.com/codex/app-server/;本机 codex-cli 0.154.0 的 app-server generate-json-schema --experimental 用于核实字段与枚举。动态工具属于实验接口,连接成功不等于所有版本兼容,也不等于模型认证可用。

Harness 配置:engine=codex、服务器绝对 executable、working_directory、可选 model、sandbox=read_only/workspace_write、timeout_seconds(默认 1800)。不保存 API Key,沿用运行账户的 Codex 认证与配置。工作区文件可能共享,协作开发应明确分配目录/worktree。审批使用 Codex 原生 on-request + user:引擎发出的人工审批请求进入 AGT 审批队列,决定回传原 JSON-RPC 请求,详见权限审批。用户输入类请求仍不支持,遇到时停止并说明原因。Harness 写入需要 Manager 或挂载 resource_management 的 Agent。AGT 工具权限不构成外部终端的文件隔离;实际文件边界由 Codex sandbox 与操作系统控制。本轮在 Linux 验证进程组清理及 SIGINT/SIGTERM 取消;Windows 的完整进程树终止尚未验收。

Prompt 和 Skill 正文作为 developerInstructions 注入;allowed-tools 约束未实现时拒绝运行。框架工具通过 Codex dynamicTools 桥接,逐次使用 AGT 固定身份校验并检查取消围栏。message_send 可联系其他会话;向当前会话发送时拒绝,由调度器唯一发布最终答复,避免重复消息。AGT 挂载 MCP 和显式 --delegate allowlist 首版不支持,配置/执行时明确拒绝。Codex 自身配置中的工具仍由 Codex 管理,其权限和认证不由 AGT 冒充控制。

资源与执行会话 ​

资源使用 harnesses/<category>/<name>/harness.yaml 文件及通用 CRUD,CLI 入口为 agt harness resource …,工具资源类型为 harness,Agent 通过 harness 字段引用。旧 environment/env 命令、environment 资源类型、Agent 字段和 environments 目录不再支持,不提供别名或自动迁移。资源包括项目目录、说明、默认模型和默认 reasoning_effort。引擎适配器封装 app-server;AGT 会话记录与 Codex session_id 分离,thread ID 只取自引擎响应。

harness_sessions 按 Agent 配置、principal、conversation、reset cutoff 标识 AGT 会话记录。首次准备记录时保存 Harness 配置快照;首次执行时固定引擎返回的默认模型/档位。更新资源默认值不影响已有记录;/new 后新记录采用新默认值。修改 Agent 的角色/工具配置会另建记录,避免旧线程保留失效权限。执行会话使用新表,不加载或迁移旧格式会话;引擎自行保存的线程不由资源重命名操作删除。同一目录中的文件与运行账户的 Codex 全局配置仍共享;快照不是文件系统或账户隔离。

后端先提供结构化接口:

  • agt harness capabilities:声明已接入能力与明确未支持的功能。
  • agt harness resource create/update/list/info/delete …:Harness 资源 CRUD。
  • agt harness models/skills/mcp <资源引用>:查询该连接的真实能力,模型列表遍历分页。
  • agt harness session current --principal <Agent> --conversation <聊天>:准备/读取当前快照,不启动线程。
  • agt harness session list/info/models/skills/mcp …:管理 AGT 保存的会话、查询实例能力。
  • agt harness session configure <记录ID> --model <模型> [--effort <档位>]:实时校验目录支持,下一轮应用,保留 thread ID。
  • agt harness session rename <记录ID> <标题>:通过 thread/name/set 同步 Codex;未执行的会话在首次执行后应用。
  • agt harness session compact <记录ID>:恢复线程并等待压缩 turn/completed;失败保留 dirty 围栏。
  • agt harness session cancel <记录ID>:请求终止活跃执行/压缩;之后检查并重置上下文。

修改模型、命名、执行和压缩共享数据库互斥围栏。执行中拒绝修改;取消不等于回滚文件操作。CLI 是本机管理员入口,Web 另行验证当前操作者及目标 Agent 的会话成员身份。 /review、/diff、任意线程导入/恢复和 fork 本阶段明确不支持;不提供任意 RPC 透传。

Web /api/harness 使用固定操作者,验证操作者及目标 Agent 当前成员身份,按当前会话记录 ID 拒绝过期或其他会话的操作;不提供任意 RPC 透传。停止讨论、重置和解散也会取消相关压缩。

本轮无推理的真实 Codex model/list 验证返回 6 个模型,包含 max/ultra 等后端声明的档位;目录查询不是模型推理端到端验收。协议桩覆盖会话配置隔离、命名、压缩完成及取消围栏。

会话与恢复 ​

runtime/runtime.db 的 harness_sessions 表保存外部 thread、配置快照、历史游标、已发送历史摘要哈希及 dirty 标记。作用域包括 Agent 配置、principal、聊天 conversation 和上下文 reset cutoff;资源默认值不纳入会话标识。无聊天身份的显式 agent run 每次新建独立线程。恢复时校验已发送历史仍可见且未被修改;不匹配则新建线程。正常恢复只发送增量历史。

输入格式(v2) ​

调度 envelope 仍保存在 AGT Run 中用于审计,但不直接传给 Codex。显式 agent run 保留输入原文(包括合法 JSON);普通单条私聊同样仅发送对方原文。多条消息按 IM seq 排序,显示真实发送者名称、ID 和身份类型。历史背景与新消息分区,每条消息只出现一次。身份、参与者和通信工具规则置于 developerInstructions。恢复线程时,通过发布消息的 client_message_id、原 Run、发送者、会话、正文和外部 thread_id 一起验证,剔除 Codex 已保存的自身答复;不能只按发送者或相同正文过滤。原生运行器保留结构化 JSON,但当前批次不重复出现在 history,trigger 只引用消息 ID。

输入格式版本纳入会话指纹。升级自 v1 时创建新的干净 Codex 线程,并以可见 Web 历史作为背景;旧 Codex 线程与 Web 聊天记录保留。旧线程的私有工具上下文不会复制到新线程。

Web 命令控制通道 ​

共享命令解析器覆盖 Web 与 HTTP 写入边界。单行斜杠命令不写入 IM,也不调用模型:

  • /、/help:可用命令与能力说明。
  • /new、/clear:现有上下文重置确认;后者不删除 Web 聊天记录。
  • /stop:现有结束讨论确认。
  • /model:按 Agent 查询 Codex 模型目录与档位,校验后修改当前会话快照,下一轮应用。
  • /status、/permissions:查看当前 Harness 会话与权限快照;内置 Agent 仍使用运行信息页。
  • /rename:同步 Codex 会话名称;/compact:启动并等待引擎压缩,可取消。
  • /skills、/mcp:查询 Codex 自身项目能力,不等于挂载 AGT 的 Skills/MCP。
  • /review、/diff、/resume、/fork 尚未映射。未知命令和带参数命令明确说明不支持,不静默转成用户消息。
  • //model 通过显式 literal_message 标记发送 /model 文本,用于讨论命令本身。多行引用、普通句子中的斜杠和 /tmp/project 一类路径仍属于普通消息。

命令来源仅限 Web 操作者;Agent 发送的斜杠文本不成为管理员控制命令。群聊模型/状态按 Agent 分别展示;不支持 @ 定向执行这些命令。命令语义依据:https://developers.openai.com/codex/cli/slash-commands/。 /new 通过现有调度围栏终止旧任务,新的 reset cutoff 使下次执行使用新线程。它不会擦除 Codex 的长期记忆、项目文件或 AGT 聊天历史。

执行前持久化 dirty 防止并发或崩溃后重复执行。失败/中断不自动恢复:检查 Run 和文件,用 /new 后继续。资源配置改变只影响新会话;已有线程采用快照。进程关闭不会删除 Codex 保存的线程。外部输入有 1 MiB 传输保护,超过时明确失败;工作上下文压缩交给外部引擎。

执行流程 ​

Harness 接管整个任务,外部 Codex 引擎拥有自己的模型与工具循环。AGT 负责身份、权限、任务责任、会话和运行记录,不通过 Native 的 ModelClient 或模型重试执行它。普通后续消息仍排队为独立 Run;本次整理不增加运行中的 steering 注入。

阶段与状态归属 ​

text
Runtime: claim → execute_claimed → finish
  Harness: prepare → acquire session lock → prepare input → connect
    Codex: start/resume thread → submit turn → receive protocol events
      event → check fence → persist progress/session facts
      framework tool → prepare → before → execute → after
      native approval → persist request → poll human decision → reply to original RPC
    failure → notify stopping → recover submitted turn → interrupt/confirm
  Harness: collect diagnostics → close connection → settle result/checkpoint
模块职责
agt-runtime/src/harness.rs宿主编排;连接与关闭外部引擎,串联各阶段
harness/prepare.rsPreparedHarness 持有配置、提示词、工具服务、历史和会话库;准备输入与校验续跑指纹
harness/events.rsHarnessRun 的取消/租约检查,按事件顺序持久化线程与执行状态
harness/tools.rsToolBridge 的 Framework 工具校验、审计、执行围栏与结果处理
harness/approval.rs绑定原执行实例的人工审批桥,不运行 Native 审核策略
harness/finish.rs完成与取消竞态、调用审计结算、会话进度推进和等待 checkpoint
harness_input.rsIM 历史到外部输入的转换与去重
harness_sessions.rs / harness_sessions/lifecycle.rs会话配置与所有权租约、执行状态、恢复责任
agt-harness/src/codex.rs稳定的 Codex 接口和事件类型
codex/transport.rs子进程、限长协议帧、RPC、超时、stderr 诊断和进程组回收
codex/turn.rs线程准备、正常回合事件循环和动态工具 RPC 响应
codex/approval.rs原生审批选项、请求关联及响应编码
codex/cancellation.rs已提交回合的停止恢复、中断请求及终态确认
codex/control.rs元数据操作与显式压缩

PreparedHarness 拥有准备资源;HarnessRun 借用它并持有会话执行锁。配置快照必须在取得锁之后读取。事件回调修改 Run 记录,工具桥独立持有等待标志和失败计数,结束时再交给收尾阶段。没有插件注册表,也不共享 Native transcript。

工具阶段 ​

  • prepare_tool_call:检查取消、等待状态、服务可用性、工具前缀和当前聊天发送限制。
  • before_tool_call:创建脱敏审计并保存 Started。
  • execute_tool_call:调用固定身份的 ToolService;保留普通工具的 scheduler 围栏,以及 task/harness 控制工具已有的专用保护路径。
  • after_tool_call:记录等待状态、成功/失败审计与失败计数,将结果交回协议适配器。

Codex 协议层先检查动态工具是否在暴露列表中,再进入宿主工具桥。工具业务失败以失败工具结果回复引擎,不重新提交回合。这些阶段只管理 AGT Framework 工具;Codex 内部的命令、文件、MCP 等工具由 Codex 自己驱动,AGT 接收其进度事件。

不可改变的时序契约 ​

  1. 线程 ID 在收到 Thread 事件后持久化;默认模型与推理配置在对应事件后保存。
  2. Submitted、Turn、Settled 分别推进执行状态。失败/中断的已确认输入进度必须在释放执行围栏前保存,不能统一挪到成功收尾中。
  3. 中断 RPC 的响应不等于中断完成。只接受匹配线程与回合的终态通知。
  4. 取消发生在读取协议帧之后时保留 pending 帧;尚无 turn ID 时进行有界恢复。停止恢复只读协议事实,不执行工具、不重放已提交任务。
  5. Stopping 是诊断通知,其失败不能跳过中断清理;继续保留原始停止错误。InterruptRequested 与 Settled 的控制回调仍保留原有检查。
  6. 已确认完成可以赢得与取消的竞态;连接丢失不视为已确认失败,也不自动重放。
  7. 等待续跑保存原有 session/thread/config 指纹 checkpoint;恢复仍检查三者一致。
  8. 外部进程在收尾前关闭;连接释放时回收进程组,包括外部工具创建的子进程。

验证 ​

协议 fixture 是本地 Python app-server,不连接真实模型。 agt-harness 测试覆盖成功与工具失败、工具白名单、取消前后 start ack、错回合终态、完成/取消竞态,以及执行和压缩的停止通知失败后仍中断确认。Runtime 原有测试覆盖权限、会话锁、终态分类、输入历史、工具控制和等待恢复。

原生人工审批、取消与回传状态见权限审批。

执行状态与租约 ​

本模块将执行结果和并发执行权分开。harness_sessions 保存配置、引擎线程 ID 和已接收输入进度; harness_operations 保存每个会话最近一次操作的状态和租约。Run 继续承担模型执行审计,任务队列继续管理依赖和汇报。操作的私有输出不通过会话查询、SSE 或 Web 状态接口返回。

状态 ​

状态含义能否开始下一操作
preparing连接、准备和提交过程;submitted 标记是否进入可能已发送的边界否
running引擎已接受否
canceling请求取消,等待确认否
reconciling执行者租约过期,检查持久状态否
succeeded结果已保存是,原持有者已释放时
failed提交前失败、明确拒绝或引擎确认失败是,原持有者已释放时
canceled提交前取消,或请求停止后引擎确认中断是,原持有者已释放时
interrupted引擎主动确认中断,未收到本地停止请求是,原持有者已释放时
needs_review已提交但无法确认结果,或未能可靠保存结果否;先检查原线程及副作用

失败和取消不回滚项目文件,不自动重做旧任务。界面 busy 是状态与持有者的派生值,不是另一套持久状态。配置和重命名也使用同一执行权,避免与模型执行并发修改会话快照。

获取、续租和隔离 ​

原子获取执行权,分配 UUID 执行归属和递增 generation。租期 30 秒,独立线程每 5 秒续租,因此长工具调用不会仅因为没有流式消息而失去执行权。续租不得复活过期租约。提交请求之前先保存可能已提交的标记,避免发送成功但标记尚未写入的窗口。会话写入和释放均核对 owner、generation 和 expires。过期持有者的写入、释放均被拒绝。运行进程仍须尊重 scheduler 的授权;租约不提供新的资源或工具权限。

正常完成、失败与取消 ​

正常完成先保存会话输入进度和结果,再释放执行权。失败或中断得到确认后,先记录引擎已收到的输入,再结束操作,避免新请求再次注入同一批历史。明确拒绝不推进输入进度。

取消不是终态:发送 turn/interrupt 后,最多等待 2 秒匹配原 thread/turn 的终态通知。RPC 应答本身不算中断确认;无关 turn 的通知不能解除执行保护。确认中断后释放执行权,允许继续会话。若取消前已经完成且成功取得结果,保留完成结果;讨论停止后的公开发布仍受 scheduler 授权限制。压缩同样区分请求接受、终态和取消;只有得到可识别的 turn 才能发送定向中断。

进程强杀与恢复边界 ​

SIGKILL 不运行析构函数。过期租约由 scheduler 的修复扫描、会话查询或下次获取执行权时回收:

  • 未进入提交边界:结束为 failed,可以继续。
  • 已持久保存明确失败/取消:保留终态,移除失效持有者。
  • 可能已提交且无可靠终态:经过 reconciling 转为 needs_review,不自动发送新请求。

当前 Codex 适配器没有实现跨进程重新附着活跃 turn,因此不能把失联解释为远端已停止,也不能从不存在的连接中自动取回完成结果。needs_review 时保留原线程 ID,用户应检查 Codex 线程和项目产出,确认旧执行停止后通过现有 /new 开始新上下文。旧执行仍可能留下外部副作用。这释放的是无主的执行租约,不是绕过未确认结果的业务保护。

旧 dirty=1 数据升级为 needs_review,旧配置、线程 ID 和进度保留,不自动清理或重放。状态变化进入已有权限隔离事件源;续租不会产生进度事件或被解释成模型工作进展。

验证 ​

crates/agt-runtime/src/scheduler/tests/harness_session_lock_tests.rs 集中覆盖启动失败、明确拒绝、失败后继续、取消确认、压缩取消、真实子进程强杀、租约到期、旧归属隔离和旧数据迁移。协议桩位于 crates/agt-harness/tests/fixtures/codex.py,不调用真实模型。

任务终止诊断 ​

讨论期限只限制新的 admission(包括子任务、恢复片段和通知模型调用)。在途执行继续;自然到期不能撤销其运行权限、调用计数或结果投递。停止、上下文重置、权限变化仍撤销执行权限。对账单独检查执行归属,允许仍持有有效租约的执行者完成停止确认。

Run 与 Work 使用 succeeded/failed/canceled/interrupted/outcome_unknown 对应的明确终态(Run 成功名为 completed)。超时且确认中断为 failed;主动取消且确认停止为 canceled;引擎自行中断为 interrupted;提交后断线、失去归属或缺失可保存结果为 outcome_unknown。取消请求期间 execution_status=canceling;该值由取消意图与运行事实派生,不新增独立任务状态机。

termination 保存 code/source/observed_at/interrupt_requested_at/confirmed_at/confirmation/side_effects/actor。首个原因保留,不用清理错误覆盖;副作用可能存在与停止是否确认分开表达。task_get 提供安全诊断和 run/thread/turn/session 标识,不携带进程 stderr。主机 run info 的 execution.diagnostics 可查看子进程退出码、信号和最多 8 KiB 的 stderr 尾部;常见凭据行被过滤,仍按主机私有日志处理。已有已完成结果不会因迟到取消丢失,但向失效上下文的发布仍受权限检查。

验证位于 crates/agt-runtime/src/scheduler/tests/work_tests/expiry.rs、work_tests/termination.rs 和 harness_session_lock_tests.rs。

Lead 管理 Harness 的工具 ​

带有 resource_management 内置能力的 Lead 可以管理 Harness 资源并选择某个 Agent 在某个聊天中的模型。原生 Agent 与 Codex 托管 Agent 共用工具契约,不依赖 Web 弹窗。CLI tool list/call 和 stdio MCP 使用同一个服务入口;本地管理入口仍需固定身份与管理权限。

建议调用顺序 ​

  1. resource_write(kind="harness", create=true, ...) 保存 Codex 连接配置。这不会安装 Codex,也不会创建容器;可执行文件和项目目录须已可用。
  2. harness_models(reference) 读取该连接实际提供的模型和思考档位。
  3. 创建引用 Harness 的 Agent,创建身份并 agent_bind 启用;先查现有绑定,避免重复。
  4. harness_session_get(conversation, target) 获取当前执行会话。
  5. harness_session_models(conversation, target, session) 获取当前会话配置快照对应的模型目录。
  6. harness_session_configure(conversation, target, session, model, effort?) 修改该 Agent/聊天的设置。

session 是第 4 步返回的 AGT 记录 id;引擎的 session_id 仍由 Codex 生成。修改 Harness 默认值仅影响新会话;修改会话不会更改共享资源或其他聊天。省略 effort 时使用所选模型报告的默认档位,不推测可用模型或档位。

调用者必须拥有资源管理能力,调用者与目标必须是该活跃聊天的当前成员。会话记录变化、正在执行、恢复中或需要人工核对时拒绝配置,不自动停止或重置。被撤销的调度任务不能写入配置。模型发现不占用调度写锁,最终写入同时受任务校验与会话租约保护。

模块边界 ​

  • agt-service/harness_tools 定义工具契约与权限入口;通过 HarnessControl 注入实现。
  • agt-runtime/harness_control 绑定调用身份、聊天范围和调度任务。
  • HarnessSessions 负责会话配置、真实目录验证和执行锁;agt-harness 负责 Codex 协议。
  • CLI/MCP、托管 Agent 和 Web 的模型目录/配置操作复用以上实现。

进度与隐私 ​

工具调用的开始、完成和失败来自持久化调用审计;tool_progress 将其投影为最近 40 条活动。原生 Agent 与 Codex 的框架工具都展示可读名称、具体工具名、状态和耗时。摘要采用框架白名单,例如“找到 2 个模型”“会话模型已更新”;不展示参数正文、密钥、路径、模型内部推理或工具返回正文。Codex 命令与文件活动保留类型提示,MCP 工具名以协议实际提供为准。

聊天横条默认折叠,展开显示近期操作及仍在执行的操作。任务详情和回复说明可查看活动记录;回复说明展示首批调用中的最近 40 个工具,完整分页记录仍在调试模式中查看。执行结束但缺少工具结束记录时显示“状态待确认”,不会误称仍在运行或已经成功。Lead 后台查询和配置不打开 Web 弹窗,用户主动使用会话控制面板时才出现相应交互。

后续接入契约 ​

Claude Code、其他 Harness 以独立适配器实现相同的任务/事件/工具桥接边界,必须核实结构化输出、恢复、取消与工具权限能力;不按终端文字猜测状态。目前只注册 Codex,不提供假可用选项。后续扩展远程连接、临时执行工具前,分别补充权限和生命周期测试。远程任务取消不能等同于断开本地连接。

测试范围 ​

无真实模型调用的协议桩覆盖启动/恢复、动态工具、流式与最终消息分离、空输出、错误、取消、未知交互与进程退出;调度测试覆盖提及、私聊、增量历史、/new 及旧任务禁止发布。资源测试覆盖旧 Agent 配置、互斥引用、Harness 依赖删除、Manager 写权限。Web 验证创建/编辑Harness 与 Agent、缺少模型时选择外部 Harness、桌面和移动端布局。真实账户端到端执行需另行验收,不能以连接检查代替。

基于 MIT 许可发布