Grok Build:精读 SpaceXAI 的 agent harness
这篇帖子选一个当前的开源 agent harness 并精读它。我先按一个只记得一半的名字「PiAgent」去找,没找到:GitHub、npm 或 arXiv 上都不存在什么值得注意的东西恰好叫这个名字,只有一个小的 Unity 包、一个 npm bot 和一个 VS Code 扩展。于是我读了 2026 年真实存在的最突出的 harness:Grok Build,按它的 README 描述,这是 SpaceXAI 的终端 AI 编码 agent(SpaceXAI 的,按 README 的说法;仓库在 xai-org GitHub 组织下),2026-07-14 以 Apache 2.0 许可开源。我查 GitHub API 时这个 Rust 仓库已经过了 23.6k star 和 4.5k fork,所以够得上值得注意的门槛。
先定框架:这是一次案头分析。我读了仓库 README、crate 布局和随仓库发布的用户指南;文档在 docs.x.ai/build/overview。我没有运行这个工具,也不会假装我运行了。我带着三个对每个 harness 都会问的问题去读它:它如何沙箱化 agent?如何门禁工具接口?如何处理状态?
领域地图
2026 年的 agent harness 分成三个家族。
编码 harness 在你的机器上对着真实仓库运行 agent。Aider 和更早的终端编码 agent 开了这条线,Claude Code 把它做成主流形态,现在 Grok Build、openai/codex 和 sst/opencode 充实着这一族。这些家族正在肉眼可见地趋同:Grok Build 的第三方声明里列出了从 codex 和 opencode 移植进树的工具实现,它还读取 Claude 的设置格式来做权限规则。
Eval harness 给 agent 打分而不是发布它们。Inspect 来自英国 AI 安全研究所(UK AI Security Institute),是这里的参照:工具使用、沙箱化执行和带类型的事件日志全是头等公民。
研究平台夹在中间。OpenHands 是文档最全的一个:一个让 agent 写代码、跑命令、浏览网页的平台,内置沙箱化代码执行和多 agent 协调,以 MIT 许可发布。
还有一层:Agent Client Protocol 标准化了 agent 与编辑器之间基于 JSON-RPC 的通信,能复用 MCP 的 JSON 形状时就复用;Grok Build 以 ACP 服务器形态嵌入编辑器。这是三个家族底下的那个朴素而重要的押注:harness 即服务;GUI 只是客户端。
Grok Build 是这些里面文档最彻底的:它的沙箱化、权限门禁和状态模型全都是第一方、有文档、且开放的。
沙箱化:内核级、覆盖整个进程
我查的第一件事:沙箱是包裹每一条命令,还是包裹整个 agent?Grok Build 在启动时用 OS 原语把沙箱应用到整个进程:Linux 上是 Landlock(内核 5.13+),macOS 上是 Seatbelt。每一个工具操作都被覆盖,包括 bash 派生的子进程。它不是逐命令包裹。沙箱化章节 记录了四个内置 profile,外加默认的 off:
workspace:可读任意位置,写入当前目录、~/.grok/和临时目录,允许网络read-only:可读任意位置,只写入~/.grok/和临时目录,Linux 上子进程网络被阻断strict:只读当前目录和系统路径,写入受限,Linux 上子进程网络被阻断devbox:用于一次性开发 VM,除/data外可写任意位置
自定义 profile 扩展一个基础 profile,并添加 restrict_network、read_only 和 read_write 路径,以及一个 deny 列表。deny 列表值得精读。它对读和写/重命名做内核级强制,使用 gitignore 风格的 glob:**/.env 和 **/*.pem 是文档给出的例子。在 macOS 上,每个 glob 变成运行时应用的 Seatbelt 正则,所以启动后创建的文件也会被拒绝。在 Linux 上,glob 在启动时展开并绑定覆盖,文档承认这是尽力而为:任何必须万无一失的东西都要在那里给出精确路径。自定义 profile 的失败模式是 fail-closed:格式错误的 glob、缺失的 bubblewrap 或无法应用的自定义 profile,都会让 Grok 拒绝启动,而不是在弱执行状态下运行。
内置 profile 以另一种方式失败,而这是更尖锐的批评。如果一个内置 profile 应用失败,Grok 会警告并继续在无强制状态下运行;它仍拒绝启动主 agent,所以工具不会被委托到别处,但你在要求沙箱之后,agent 还是在无沙箱状态下运行。启动滚动输出里的一行警告,是拒绝的弱替代品。
有两个决定我会照搬。第一,沙箱 profile 在会话存续期内固定:用不同的 profile 恢复会被拒绝,因为放宽一个受限会话是一个 footgun。第二,沙箱一旦应用就不可逆;agent 无法在运行时放宽它。
这些细节显示出真正的攻击模型思维。状态目录对会话文件保持可写,但内核拒绝写入用作全局 hook 来源的路径,符号链接的 GROK_HOME 在启动时被拒绝,这样 deny 集合就无法被重定向。shell 环境策略控制子进程继承什么:all、core 或 none;一旦配置了策略,对匹配 KEY、SECRET 或 TOKEN 的变量名有内置丢弃(开箱状态下环境保持原样)。沙箱事件(包括违规)追加到 ~/.grok/sandbox-events.jsonl。最后这一条很小但非常正确:你观察不到的执行,就是你无法调试的执行。
我不会照搬的:沙箱默认关闭;在 macOS 上子进程网络阻断是一个 no-op,这让 strict profile 在多数读者使用的平台上明显更弱。另外,沙箱保护宿主免受 agent 之害;它并没有给 agent 一个安全运行不受信内容的地方。这是另一个轴;我下面会回到这一点。
工具接口:分层门禁
工具集很紧凑:read_file、search_replace、list_dir、bash、grep、web_search、web_fetch、ask_user_question、todo_write,外加 MCP 服务器和 ACP。crate 边界是 xai-grok-tools;接口才是设计工作的所在,它是一个 pipeline,不是一个开关。权限与安全章节 写明了顺序:
- PreToolUse hooks 先运行,可以拒绝任何调用。
- 来自配置和 CLI 的权限规则生效,deny 压过 ask、ask 压过 allow,贯穿每一层作用域。
- 早前交互式批准中记住的授权生效,按项目限定作用域。
- 内置自动批准覆盖只读工具和一份固定的只读 shell 命令列表。
- 权限模式设定提示策略:default ask、acceptEdits、auto、dontAsk 或 bypassPermissions,即总是批准模式。
Deny 总是赢;文档说得明明白白。交互式授权按项目存储,永远不会写进仓库;而 .grok/config.toml 中的声明式规则则意在提交并接受审查。个人授权与可审查策略之间的这种切分,是正确形态。
接下来是边缘情况——这正是 harness 赢得或失去信任的地方。
Hooks 失败即放行(fail open)。如果 hook 脚本崩溃、超时或缺失,调用照常进行,如同被允许一样。文档明确警告:用作安全边界的 hook 必须自己处理自己的错误。我会翻转这个默认值。
只读命令列表是一个启发式规则,文档也这么说:tee 被排除,因为它可以写任意位置;cargo check 被排除,因为 build.rs 会运行代码。指引的字面意思就是把这份列表当作便利设施;它不是安全边界。
链式命令按段逐段做 deny 和 ask 检查,但 allow 规则匹配整个字符串。文档自己的例子:Bash(git *) 自动批准 git status && rm -rf /。一条窄的 allow 规则是一扇宽门,除非配上 deny 规则。
这与 agent–计算机接口的研究一致:SWE-agent 论证过,agent 是一类新的终端用户,他们需要为自身模态而建、而非为人而建的接口。一个小的、结构化的工具词汇表,带显式结果、由分层策略门禁,是那个想法的生产版本。来自 codex 和 opencode 的移植表明,业界正在向同一套词汇表收敛。
状态处理:会话即事件日志
每段对话都会自动保存为 ~/.grok/sessions/ 下的一个会话,每个会话一个目录,按编码后的工作目录分组。会话管理章节 把布局当作架构:
updates.jsonl:权威日志——每行一个 ACP session-update 事件,只追加,驱动恢复与还原chat_history.jsonl:发给模型的原始消息plan.json:TODO/任务状态rewind_points.jsonl:每次用户提示时拍下的文件快照signals.json:token 用量与工具/轮次计数器summary.json:索引条目
只追加的 JSONL 是正确选择:增量写入、流式读取,每一行都是你可以调试的有效 JSON。/rewind 还原真实的文件快照,而不是让 agent 重建更早的状态。Compaction 检查点让对话在压缩之下依然保留。通过 x.ai/git/worktree 扩展,分叉会话可以为每个会话创建一个隔离的 git worktree,这样同一仓库的并行分支不会互相冲突。ACP 客户端拿到 session/new 和 session/load,沙箱 profile 随会话一起走。
缺失的东西,正是大多数 harness 日志里也缺失的东西:事件没有为评测而类型化。没有 score 事件,没有结构化的批准决定,没有分支标记。signals.json 存的是计数器,不是语义。
对比 Inspect 的事件日志:那里的事件是类型化的类——ModelEvent;带 arguments、result、error 和 cancelled 的 ToolEvent;针对 exec 和文件 I/O 的 SandboxEvent;记录决定与批准人的 ApprovalEvent;承载中间与最终分数的 ScoreEvent;标记分支轨迹在何处分叉的 BranchEvent;还有 spans 与 timeline 工具,因此轨迹可以被回放、过滤和渲染。当日志是产品而不是管道时,「事件日志即轨迹」就是这副样子。研究侧的 OpenHands 押了同样的注:这个平台的价值在于,agent、沙箱和 benchmark 都在同一条动作与观察流上协调。
我会采纳什么,我会拒绝什么
采纳:内核优先的整进程沙箱化;fail-closed 的自定义 profile、glob 与 bubblewrap 错误;会话固定且不可变的沙箱 profile;内核强制的 deny glob;环境变量策略;带文件快照回退的只追加会话 JSONL;deny 获胜语义的声明式、可提交权限规则;以及作为产品模式的 plan mode。
拒绝或修正:沙箱默认关闭;内置 profile 应用失败时警告并继续无强制运行——这比 fail-closed 更糟,因为用户以为沙箱开着;macOS 网络强制是 no-op;hooks 失败即放行;匹配整个命令字符串的 allow 规则;以及 plan mode 的编辑门禁停留在工具级。
关于最后这一条,plan mode 文档 对缺口的坦诚令人佩服。Plan mode 让 plan.md 成为唯一可编辑的文件,在每一种权限模式下都是,包括 always-approve。接着:bash 命令不会被检查文件写入,所以 shell 重定向能绕过门禁。每个 subagent 都从全新的 plan-mode 追踪器开始,所以一个可写 subagent 可以在父 agent 还在规划时编辑文件,同时继承父 agent 的权限模式。门禁检查的是调用了哪个工具,而不是世界发生了什么变化。任何基于工具名的门禁都会被 shell 绕过;任何想在 subagent 面前活下来的门禁,都必须看效果。
我会交付的替代方案:在事件日志上门禁。批准提案,而不是调用;在真实副作用的流上检查不变量;profile 无法强制时 fail-closed;让日志保持足够的类型化,以便打分与回放。
这对我的 harness 工作意味着什么
我每天打交道的三个模式,在这里以生产形态出现。
提案先行审查。PiPlan.ai 的 pipeline 在任何东西执行之前审查提案。Grok Build 的 plan mode 是同样的直觉:先探索,把计划写进唯一可写的文件,带行内评论呈交批准,在那之前让世界保持只读。我要拿走的是强制力;我要修的是绕过路径:审查必须门禁效果,subagent 必须在门禁之内。这正是我的 SafeRoutes 构建笔记 与之缠斗的问题:无论哪个 agent 或工具在场都成立的不变量门禁。
仿真沙箱。Grok Build 的沙箱保护宿主免受 agent 之害。另一个方向是保护世界免受 agent 行为之害,研究锚点是 ToolEmu——它模拟工具执行,让你在真实运行之前就暴露失败;人工审查确认,它识别出的失败中有 68.8% 会成为真实世界中的有效失败。PiPlan.ai 的仿真沙箱是同样的形态:在提案的效果触碰任何真实之物之前预演它们。harness 需要这两层,而多数只有第一层。
事件日志即轨迹。会话 JSONL 是个好的开始,但轨迹在类型化且可打分时才配得上它的价值:model、tool、approval、sandbox、score。这就是能调试的日志与能评测的日志之间的差别。轨迹不是对话;它是带语义的事件流。
一个月前,我不会知道要去看 Grok Build。「哪个 harness」这个问题的正确答案自带时间戳。截至 2026 年中期,它就是值得读的那一个。
本文链接的资料来源均经抓取并核实。