内部控制面边界

把 AI coding agent 理解成“会写代码的模型”,很容易低估真正的工程难点。模型只是运行时里的一个决策器;让它长时间稳定地改代码、跑命令、处理错误、收敛到可验证结果,靠的是更外层的控制面。

这层控制面通常由五件东西组成:

  • Agent loop:一次任务怎样从观察、计划、行动、再观察,走到验证和停止。
  • Hook:在关键生命周期点插入确定性规则、审计、阻断、上下文补充和通知。
  • Plugin / Skill / MCP server:把工具、上下文、工作流、文档、浏览器、团队协作能力装进运行时。
  • Sandbox / Permission:决定哪些动作可以自动执行,哪些必须询问,哪些直接拦截。
  • Trace:把循环、hook 决策、工具调用、错误、重试和验证证据留下来,供复盘和调试。

协议层和运行时层要先分清。MCP、A2A、AG-UI 这类协议主要回答“对外怎么互通”:客户端、服务器、工具、资源、提示词、传输、能力协商和界面事件如何标准化。Hook、loop、plugin、sandbox 和 trace 回答的是“内部怎么行动”:一个 agent 何时调用工具,调用前后怎样审查,失败后怎样重试,什么动作要审批,凭什么停下来。

协议能让工具接进来,但不能替 agent 决定是否该用这个工具,也不能自动保证权限、重试和验证正确。真正的 agent 产品差异,往往藏在这层运行时控制面里。

内容 放在 A 文 放在 B 文
MCP / A2A / AG-UI 的分层地图 讲连接方向、协议边界和生态位置 只讲它们落到 runtime 后如何变成 tool、server、permission 和 trace
Agent framework / runtime 生态 讲框架如何承接协议、状态、评估和平台能力 只抽取和 coding agent 控制面相关的 loop、hook、插件、沙箱设计
Observability / Evaluation 讲跨框架观测、评估工具和语义约定 讲本机执行 trace 如何复盘 hook、权限、工具调用和失败路径
Codex、Claude Code、OpenCode、OMC/OMX 作为 runtime 层例子 作为正文主体,分别拆解运行时机制

两层边界:协议互通与运行时行动

层次 主要问题 典型机制 不直接解决的问题
协议层 外部系统怎样被 agent 发现和调用 MCP 的 host / client / server、JSON-RPC、tools、resources、prompts、roots、sampling、elicitation agent 是否应该调用、调用是否越权、失败后是否重试、验证是否充分
运行时层 agent 内部怎样稳定完成任务 loop、hooks、plugins、skills、sandbox、permission、trace、subagents 跨产品的标准传输、第三方工具生态的统一接口
编排层 多个 agent 或多阶段任务怎样协同 team mode、subagent tracking、checkpoint、mode routing、handoff、nudge 单个外部工具的协议语义

MCP 官方规格把协议范围讲得很清楚:它提供 LLM 应用与外部数据源、工具之间的标准连接方式,服务器提供 resources、prompts、tools,客户端还可以提供 sampling、roots、elicitation 等能力。安全原则也写进了规格:用户需要知情和控制,工具代表任意代码执行路径,host 需要构建授权和同意流程。

这正好说明 MCP 的边界。协议规定“工具怎样暴露”,但 host 仍然要自己实现“工具怎样被批准”。因此,一个成熟的 coding agent 不能只接 MCP server,还要有自己的 loop、permission profile、hook event、插件信任模型、日志和回放能力。

一个可复盘的 agent loop

AI coding agent 的核心循环可以压缩成:

1
observe -> plan / think -> act / tool -> observe -> verify -> stop / retry

每个环节都不是抽象口号,而是运行时里的具体接口。

阶段 输入 运行时责任 常见失败
observe 用户请求、仓库文件、配置、历史、工具结果 组织上下文,加载 AGENTS / CLAUDE / skill 说明,识别当前状态 上下文缺失、旧证据污染、误读项目规则
plan / think 任务目标、约束、可用工具、权限边界 选择下一步动作,拆分验证路径,判断是否需要子 agent 过度规划、越界假设、忽略最小验证
act / tool 工具名、参数、目标文件、命令 经 hook 和 permission 过滤后执行动作 参数危险、路径越界、命令副作用过大
observe stdout、stderr、diff、MCP result、浏览器状态 把外部世界反馈重新纳入上下文 只看成功码不看语义、丢掉错误细节
verify 测试、构建、lint、类型检查、手工检查点 证明当前 claim 成立,或把失败转成新任务状态 未验证即停止、验证命令不覆盖改动面
stop / retry 验证结果、预算、用户约束、阻塞条件 停止、重试、降级、请求授权或报告 blocker 无限重试、无证据完工、阻塞原因不明确

这里的 think 不等于把模型内部推理暴露出来。工程上更重要的是:运行时必须维护一个可检查的 task state。状态里至少应包含目标、已读证据、已改文件、失败原因、验证命令、剩余风险和停止条件。

Hook 是 loop 的插槽

Hook 的意义不是“多执行几段脚本”。它是把确定性控制点插进不稳定的 LLM 循环。

flowchart TD
  U["User prompt"] --> CI["Context injection hooks"]
  CI --> O["Observe: repo, config, prior state"]
  O --> P["Plan / think: choose next action"]
  P --> R{"Tool request"}
  R --> PRE["Pre-tool-use hooks"]
  PRE --> PERM{"Sandbox / permission"}
  PERM -->|allow| ACT["Tool / plugin / MCP / skill / subagent"]
  PERM -->|ask| UI["Approval UI"]
  PERM -->|deny| BO["Blocked observation"]
  UI -->|approve| ACT
  UI -->|reject| BO
  ACT --> POST["Post-tool-use hooks"]
  POST --> NO["New observation"]
  BO --> NO
  NO --> VER["Verification hooks / checks"]
  VER --> DONE{"Stop condition met?"}
  DONE -->|retry| P
  DONE -->|stop| TR["Trace: events, decisions, evidence"]

常见 hook 可以按用途分成六类。

Hook 类型 典型触发点 作用 例子
context injection hook SessionStart、UserPromptSubmit、PreCompact、PostCompact 把项目规则、记忆、当前任务状态、恢复摘要注入上下文 加载 AGENTS.md,压缩前补充“当前修改文件”和“下一步”
pre-tool-use hook Bash、Edit、Read、MCP tool 调用前 检查工具参数、重写安全参数、阻断危险输入 拦截 rm -rf,禁止读取 .env,给 shell 注入隔离环境变量
permission hook 工具执行前、沙箱外动作前 给出 allow / ask / deny,或转交用户审批 git status 自动放行,对 git push 询问,对密钥文件读取拒绝
post-tool-use hook 工具执行后 记录结果、更新状态、触发二次检查 记录命令输出、更新 token / turn 指标、检查 diff
verification hook Stop、SubagentStop、TaskCompleted、PostToolBatch 停止前确认 claim 是否有证据 未跑测试时阻止“完成”,失败后要求 retry
notification hook Notification、SessionIdle、SessionError 通知用户、唤醒会话、把后台任务带回前台 桌面通知、终端提示、team lane 空闲提醒

Hook 的关键属性有三个:

  • 生命周期位置:它发生在模型决策前、工具执行前、工具执行后,还是停止前。
  • 决策权:它只能记录,还是能改参数、阻断、要求审批、注入上下文。
  • 信任边界:它来自用户、项目、企业策略、插件,还是当前会话临时配置。

越靠近工具执行的 hook,越要强调可审计和可禁用。一个会改命令参数的 hook,本质上拥有和 agent 一样危险的执行能力。

Codex:权限与插件都进入正式控制面

Codex 的公开文档已经把控制面拆成了几个明确页面:Sandboxing、Rules、Hooks、Plugins、Skills、MCP、AGENTS.md、Subagents 等。这个拆法本身就很有信息量:Codex 不把 agent 当成一个“聊天窗口 + shell”,而是把本地编码代理拆成权限、规则、扩展和项目指令几层。

Codex hooks 文档把 hook 定义为注入 agentic loop 的扩展框架。事件包括 PreToolUsePermissionRequestPostToolUsePreCompactPostCompactUserPromptSubmitSubagentStartSubagentStopStopSessionStart。这覆盖了工具调用前后、权限请求、上下文压缩、用户输入、子 agent 和停止阶段。

权限侧分成两个互补机制:

  • Permission profile / sandbox:定义文件系统和网络边界,比如只读、workspace-write、网络 allow / deny、local network guard。
  • Rules:定义命令能否在 sandbox 外执行,决策值是 allowpromptforbidden,并且最严格规则获胜。

插件侧则更接近发行单元。Codex plugins 可以打包 skills、MCP servers 和 app integrations。Skills 是渐进加载的说明、资源、脚本和子 agent 定义;MCP servers 负责把外部工具和上下文接进来;plugin 把它们变成可安装、可共享、可管理的能力包。

Codex 的 trace 能力也不只靠终端输出。公开文档里有 verbose logging、MCP server event、OpenTelemetry trace exporter 等入口。一个理想的 Codex trace 不应只记录“跑了什么命令”,还应记录 hook 是否触发、permission 为何要求审批、sandbox 是否拦截、子 agent 何时开始和停止。

Claude Code:事件面最细,策略面靠 settings 分层

Claude Code 的 hooks 文档给出的事件面非常细。除了 PreToolUsePermissionRequestPostToolUseNotificationStopSubagentStartSubagentStop,还包括 PermissionDeniedPostToolUseFailurePostToolBatchMessageDisplayTaskCreatedTaskCompletedInstructionsLoadedConfigChangeCwdChangedFileChangedWorktreeCreateWorktreeRemovePreCompactPostCompactElicitationElicitationResultSessionEnd 等。

这意味着 Claude Code 更像一个事件总线:工具调用、文件变化、配置变化、工作区变化、消息显示、任务生命周期都可以被外部逻辑观察或干预。

Hook handler 也不只一种。Claude Code 支持 command、HTTP、MCP tool、prompt、agent 等 handler 类型。也就是说,hook 不一定是本地 shell 脚本,也可以是 HTTP endpoint、MCP 工具、LLM prompt 或另一个 agent。

配置侧,Claude Code 使用分层 settings.json:用户、项目、本地、企业 managed policy、插件、skill / agent frontmatter 都可以参与。权限规则按 deny、ask、allow 的方向组织;安全文档强调默认读权限较严格,额外动作需要明确授权,bash 可以进入文件系统和网络隔离的 sandbox。

Claude Code 的风险也来自这个强大的事件面。事件越多,越容易出现“hook 本身成为供应链”的问题。因此它的 managed settings、plugin-only customization、sandbox 设置、permission rules 和 hook location 分层,构成了同一套信任模型。

OpenCode:JS/TS 插件把 hook、工具和 compaction 接在一起

OpenCode 的控制面更偏开源框架式。它支持 TUI、桌面和 IDE;使用 opencode.json / opencode.jsonc 管配置;.opencode/ 目录下可以放 agents、commands、modes、plugins、skills、tools、themes。

权限模型非常直接:permission 配置把动作解析成 allowaskdeny。它支持全局规则、按工具规则、按输入 pattern 的细粒度规则。规则以更靠后的匹配为准。默认值偏宽松,但 .env 读取默认拒绝,external_directorydoom_loop 默认询问。doom_loop 把同一工具调用以相同输入重复 3 次的情况纳入权限守卫,相当于把 loop 风险直接做成 permission 维度。

OpenCode 的插件是 JavaScript / TypeScript 模块。插件函数返回 hooks object,可以订阅:

  • tool.execute.before / tool.execute.after
  • permission.asked / permission.replied
  • session.created / session.updated / session.error / session.idle / session.compacted
  • file.edited / file.watcher.updated
  • tui.toast.show / tui.prompt.append
  • shell.env

插件还可以定义 custom tools。官方示例里,插件能在 tool.execute.before 阻止读取 .env,能用 shell.env 注入环境变量,也能用 tool() helper 暴露新工具。另一个重要例子是 experimental.session.compacting:压缩上下文前,插件可以向 continuation summary 注入额外上下文,甚至替换 compaction prompt。

Skills 在 OpenCode 里通过原生 skill tool 按需加载。OpenCode 会从 .opencode/skills/~/.config/opencode/skills/.claude/skills/.agents/skills/ 等位置发现 SKILL.md。这说明它把 skill 当成跨生态的可复用指令包,而不是某个产品私有格式。

MCP 侧,OpenCode 同时支持 local 和 remote server,并把 MCP tools 与内置工具一起交给配置系统管理。工具太多会增加上下文,因此 OpenCode 文档建议谨慎启用 MCP server,并支持全局禁用、按 agent 启用。

OMC/OMX:在宿主 agent 上叠一层编排运行时

OMC/OMX 更适合被看成 meta-runtime,而不是另一个纯 coding agent。它不替代 Codex、Claude Code 或 OpenCode 的底层工具执行面,而是在其上叠加 workflow、skills、prompts、team lanes、state、checkpoint 和日志。

当前仓库的运行时状态暴露出几个设计特征:

  • .omx/state/session.json 记录当前 session id、native session id、cwd、pid、platform。
  • .omx/state/subagent-tracking.json 记录 leader thread、subagent thread、turn_count、last_turn_id。
  • .omx/logs/turns-*.jsonl 记录 agent-turn-complete、thread_id、turn_id、input_preview、output_preview。
  • .omx/logs/omx-*.jsonl 记录 session_start
  • .omx/logs/tmux-hook-*.jsonl 记录 auto nudge 是否触发、是否因为 unmanaged session 跳过。
  • .omx/state/*notify-hook-state.json 记录最近完成 turn,用于通知去重。
  • .omc/state/checkpoints/*.json 记录 checkpoint 时间、trigger、active_modes、todo_summary 和 background_jobs。
  • .omc/project-memory.json 记录项目技术栈、构建命令、脚本、目录结构。

这些文件暴露出 OMC/OMX 的核心关注点:不是单次工具调用,而是长任务的组织、恢复、分派和复盘。

OMX 的控制面更像这样:

  1. 通过项目规则和 keyword routing 决定是否进入某个 workflow。
  2. 通过 skill 和 prompt 选择角色表面,例如 explore、researcher、executor、verifier。
  3. 通过 subagent tracking 维护 leader / worker 关系。
  4. 通过 checkpoint 和 state 文件保存当前模式、任务摘要、后台 job。
  5. 通过 turn logs 和 hook logs 形成可追踪执行轨迹。
  6. 权限和 sandbox 仍主要依赖宿主 agent 或外层 Codex App / CLI 的执行限制。

这层架构适合处理“多步、长程、可恢复”的任务。它的优势不是更多工具,而是把任务生命周期显式化:谁是 leader,哪个子任务在哪个 thread,什么时候需要验证,什么时候取消,什么时候恢复。

四套控制面的结构对比

下面的表不是品牌排名,而是运行时结构对照。

维度 Codex Claude Code OpenCode OMC/OMX
基本定位 本地/IDE/App coding agent,强调 sandbox、rules、plugins、skills、MCP CLI/IDE coding agent,事件 hook 面很细,settings 分层 开源 TUI/Desktop/IDE agent,JS/TS 插件可编程 宿主 agent 之上的编排层和 workflow runtime
Agent loop 暴露方式 通过 tool event、permission request、subagent event、stop event 暴露关键插槽 通过大量 lifecycle hooks 暴露工具、消息、文件、任务、配置、工作区事件 通过 plugin events 和 Plan / Build mode 暴露任务流 通过 mode、skill、team、subagent tracking、checkpoint 显式化长任务
Pre-tool-use PreToolUse hooks,可匹配工具 PreToolUse 可阻断工具调用 tool.execute.before 可改参数或抛错 通常由宿主 agent hook 或 workflow guardrail 承担
Permission PermissionRequest hooks,permission profiles,rules 的 allow / prompt / forbidden PermissionRequest、settings permission rules、sandboxed bash permission 的 allow / ask / deny,支持 external_directory 和 doom_loop 编排层决定任务是否继续;底层执行权限交给宿主
Post-tool-use PostToolUse、Stop、SubagentStop、Pre/PostCompact PostToolUsePostToolUseFailurePostToolBatch、StopFailure tool.execute.after、session/file/message events turn logs、notify hook state、checkpoint、metrics
插件形态 plugin 可打包 skills、MCP servers、app integrations plugins、skills、agents、MCP、managed settings 可共同扩展 JS/TS plugin,可加 hooks、custom tools、compaction logic prompts、skills、agents、workflow mode、team lane
Skill 形态 渐进加载的指令、资源、脚本、agents;可由插件分发 与配置、插件和 agents 一起成为定制入口 SKILL.mdskill tool 按需加载,兼容 .claude / .agents 路径 skill 更像 workflow 命令和角色路由表面
MCP 角色 Codex 是 MCP client,也可作为 MCP server 暴露 codex 工具 Claude Code 可连接 MCP server,hook handler 也可用 MCP tool local / remote MCP server 作为工具加入,可全局或按 agent 管理 可把 MCP 作为宿主能力的一部分,不是核心差异点
Sandbox 文件系统与网络 profile,local/private network guard sandboxed bash、文件/网络隔离、读写规则 主要通过 permission 和 external directory 守卫;沙箱粒度相对依赖运行环境 依赖宿主 sandbox;由上层负责流程边界和状态
Trace 日志、MCP events、OTEL trace exporter、hook 输出 hook JSON 输入/输出、工具事件、外部脚本日志 session events、structured app log、permission events、plugin logs .omx/.omc JSONL、state、checkpoint、subagent tracking
设计气质 安全边界和插件发行并重 生命周期事件细,企业策略和 hook 信任模型重 开源可编程性强,插件可直接改 hook 行为 长程任务、团队协作、恢复和复盘优先

Sandbox / Permission 的三层判断

权限设计不应只问“用户是否信任 agent”。更好的切法是把动作分成三层。

动作类型 默认建议 原因
工作区内只读搜索、列目录、读取非敏感源码 自动放行 这是 observe 的基础能力,频繁审批会破坏 loop
目标文件内的明确编辑、格式化、测试、构建 在 workspace sandbox 内自动或半自动放行 可逆、可 diff、可验证
安装依赖、访问网络、调用 remote MCP、生成大规模文件 询问或按 allowlist 放行 可能影响供应链、成本、时间或隐私
外部目录读写、读取 .env、SSH key、浏览器 cookie、凭据文件 默认拒绝或强审批 跨越项目边界,风险不随任务目标自然成立
rm -rf、重写历史、删除部署目录、强推、生产发布 默认拒绝,除非用户明确授权 破坏性强,恢复成本高
同一工具同一参数反复失败 触发 retry guard 或 doom_loop permission 防止 agent 卡在无意义循环
停止前声称完成但没有验证证据 verification hook 拦截或降级为风险声明 防止“看起来完成”的伪收敛

自动放行不等于无风险。它应该满足三个条件:作用域明确、可逆或可重跑、能被 trace 捕获。审批也不应只是 yes / no,最好能生成 session 级 allow rule,比如“本会话允许 git status*,但不允许 git push*”。

Trace 要记录控制面,而不只是轨迹

很多 agent 复盘材料只记录 trajectory:模型说了什么、调用了什么工具、工具返回了什么。这还不够。真正有工程价值的 trace 应该记录控制面决策。

一个可调试 trace 至少包含:

  • 用户目标和可见约束。
  • 被注入的项目规则、skill、memory、compaction summary。
  • 每次 plan 选择下一步的公开状态摘要。
  • 工具请求:工具名、参数、目标路径、预期效果。
  • pre-tool-use hook 的结果:通过、改写、阻断、异常。
  • permission 决策:allow / ask / deny、命中的规则、用户审批结果。
  • sandbox 结果:是否越界、是否因网络或文件系统限制失败。
  • 工具执行结果:退出码、stdout / stderr 摘要、结构化返回、diff。
  • post-tool-use hook 的结果。
  • retry 原因:错误类型、修正策略、是否重复调用。
  • verification 证据:测试、构建、类型检查、浏览器截图、人工检查点。
  • stop 原因:完成、用户取消、权限阻塞、缺少信息、预算耗尽。

Trajectory 描述“走过哪条路”。Trace 描述“为什么走这条路,哪些门开了,哪些门没开,凭什么停下”。对于长程 coding agent,后者才是调试和治理的最小单位。

运行时设计的几个判断

Agent loop 需要显式停止条件。没有停止条件,模型会把“继续尝试”误当成勤奋;没有 retry guard,工具失败会变成噪声循环。

Hook 需要信任模型。项目 hook、用户 hook、企业 managed hook、插件 hook 权限不能混在一起。能改参数或阻断工具的 hook,应当有审计、hash trust、禁用和来源标记。

Plugin 不能只看“能接多少工具”。工具越多,模型上下文越膨胀,prompt injection 面越大,选择成本越高。高质量插件应同时提供工具、权限建议、说明文档、验证 hook 和 trace 字段。

Skill 更像延迟加载的运行时指令。它不应该把所有知识一次性塞进上下文,而应先暴露 name / description,命中场景后再加载完整说明、脚本和参考资料。

MCP server 是协议边界,不是安全边界。host 仍要决定工具是否可信、输入是否越权、输出是否污染上下文、调用结果是否需要验证。

OMC/OMX 这类 meta-runtime 的价值在于把“长程任务管理”外化。普通 agent 更关注单次工具调用;编排层更关注模式、角色、子任务、恢复、checkpoint 和复盘。

一个实践化的控制面模板

设计一个能长期工作的 coding agent runtime,可以从下面这张表倒推。

控制点 最小实现 更稳的实现
observe 读取文件、搜索、git 状态 项目规则、skill、历史摘要、外部上下文分层注入
plan 模型自行决定下一步 任务状态机、可见 TODO、验证计划、风险标记
pre-tool 简单命令黑名单 参数级策略、路径边界、secret 扫描、hook trust
permission 每次弹窗 allowlist、denylist、session rule、企业 managed policy
sandbox 工作区写权限 文件系统、网络、本地网络、外部目录分层
act 内置 shell / edit plugin tools、MCP tools、browser、docs、automation、subagent
observe result stdout/stderr 结构化 tool result、diff summary、错误分类
verify 用户手工看 测试、构建、lint、typecheck、截图、Stop hook
trace 终端日志 JSONL events、hook decisions、permission reason、OTEL / metrics
recover 重新开一轮对话 compaction hook、checkpoint、session state、handoff prompt

成熟的 agent 产品会逐步向右侧移动。区别不在于模型会不会写代码,而在于运行时能不能稳定地约束模型、恢复任务、解释动作、证明完成。

工程结论

AI coding agent 的竞争,不只是模型能力竞争,也是 runtime control plane 的竞争。

协议层负责互通:MCP 让工具、资源、提示词用统一方式被 host 发现和调用。运行时层负责行动:loop 决定节奏,hook 决定插槽,plugin / skill 决定能力装配,sandbox / permission 决定边界,trace 决定能否复盘。

Codex 的结构更像安全边界和插件发行并进;Claude Code 的事件面更细,适合做强生命周期控制;OpenCode 把 JS/TS 插件、权限配置、skills 和 MCP 做成开源可编程表面;OMC/OMX 则把长程任务、团队协作、checkpoint 和状态管理叠在宿主 agent 之上。

真正值得关注的问题不是“哪个 agent 更聪明”,而是它的运行时能不能回答这几个工程问题:

  • 它看到了什么上下文?
  • 它为什么决定调用这个工具?
  • 这个调用由谁批准?
  • 哪个 hook 改写或阻断过它?
  • 失败后为什么重试?
  • 停止前有什么验证证据?
  • 事后能不能从 trace 复盘整条路径?

这些问题回答清楚,coding agent 才从“会执行命令的聊天模型”变成可治理、可恢复、可协作的工程运行时。

参考资料

版本快照:以上链接按 2026-06-13 官方文档检索。Codex、Claude Code、OpenCode 和 MCP 的 hook、permission、plugin、sandbox 文档都在快速演进,正式引用前应重新确认对应版本。