前言:为什么「无头」与自动化能力,是 AI 编程 Agent 落地的关键

Grok Build 是 xAI 出品的终端 AI 编程智能体,日常形态是一条 grok 命令:你在终端里敲提示词,它像一位坐在隔壁的工程师那样读代码、跑测试、改文件,与你来回对话。这种交互体验固然很好,但它天然假设了「屏幕前坐着一个人」。而真实工程世界里,大量的代码工作并不总有人盯着:凌晨两点 CI 挂掉要自动定位、合并请求合入前要自动做一轮代码评审、几十个文件要按同一套规则批量迁移——这些场景需要的是无人值守的 Agent:把提示词交给程序,程序把结果拿回来,中间没有人类按回车。

这正是官方文档里 Headless Mode(无头模式)以及周边机制存在的意义。所谓无头模式,就是让 Grok 以非交互方式从命令行运行:接受一条提示词,以完整工具访问权限执行它,然后把结果返回给你。这句话听起来平淡,分量却很重——它意味着 AI 编程 Agent 第一次变成了一个「可编程的管道单元」:可以被 shell 脚本调用、被 CI 流水线驱动、被定时任务唤醒、被程序化地解析输出。再配上 Agent Mode(长驻服务 + ACP 协议)、Subagents(子代理并行)、Sessions(会话断点续传)、Background Tasks(后台长任务),一个完整的「Agent 自动化拼图」就成型了:既能一次性单发,也能长连接常驻;既能并行分工,也能事后追忆上下文。

本文带读 xai-org/grok-build 仓库中 crates/codegen/xai-grok-pager/docs/user-guide/ 下的六章官方文档:14-headless-mode、15-agent-mode、16-subagents、17-sessions、19-plan-mode、20-background-tasks。文中所有命令、参数、字段都以上述文档为准;个别细节官方可能随版本演进调整,凡拿不准处我都会注明「以当前版本官方文档为准」。文档示例中的模型名(如 grok-4.6)只是示例,实际使用请以你账号内可用模型为准。另外说明:本文不覆盖 grok 的安装与账号鉴权流程(官方另有 authentication 章节与 README),实战示例默认你已经完成安装并持有有效凭据。

阅读路线建议:先读无头模式打底,理解单发调用的输入输出;再看 Agent Mode 与 Plan Mode,理解两种「更重」的工作形态;随后用子代理、会话、后台任务三章把「规模与韧性」补齐;最后用一个 GitHub Actions 实战把全部机制串起来。

你现在应该掌握了什么:你应当理解了「无头 = 非交互单发执行」这个核心定义,知道本文要带读哪六章官方文档,并清楚本文约定——所有命令以官方文档为准、模型名与版本以你当前环境实际为准、安装鉴权不在本文范围内。

无头模式 Headless Mode:把 Agent 变成一条命令行管道

基本用法:一条提示词换一份结果

无头模式是文档开篇就定义清楚的概念:以非交互方式从命令行运行 Grok,接受单条提示词,以完整工具访问权限执行,返回结果。触发方式也很直观——传入提示词即进入无头模式,最常用的是 -p 参数(它是 --single 的简写);--prompt-json 与 --prompt-file 同样会触发无头模式,分别表示「以 JSON 内容块形式给提示词」和「从文件读取提示词」。执行流程是:Grok 处理提示词、按需调用工具、把结果打印到标准输出,响应一完成进程即退出。

# 最基础的无头调用
grok -p "Your prompt here"

# 从文件读提示词,适合超长上下文
grok --prompt-file ./prompt.txt

一个值得强调的细节:无头模式不会把管道喂进来的 stdin 当作提示词。想携带外部内容,官方推荐两条路——命令替换(command substitution)或 --prompt-file。最常见的用法是把 git diff 塞进上下文让它写提交信息:

# 用命令替换把暂存区改动作为上下文(官方文档示范写法)
grok -p "Write a concise commit message for these changes:

$(git diff --staged)"

# 输出重定向到文件,或管道给 jq 提取正文
grok -p "Generate a README" > README.md
grok -p "List files" --output-format json | jq -r '.text'

命令行选项:一份够用的速查表

官方文档给了一份很长的参数表,这里提炼自动化场景最常用的一批(完整列表见官方文档 14-headless-mode.md):

参数 作用
-p, --single <PROMPT> 传入单条提示词(或用 --prompt-json / --prompt-file)
-m, --model <MODEL> 指定模型,如 grok-4.6(示例型号,以实际可用为准)
-s, --session-id <ID> 用你指定的 UUID 新建会话;ID 非法或已被占用会报错,不用于续传
-r, --resume <ID_或标题> 按 ID 续传指定会话;非 ID 值按当前目录下会话标题匹配(忽略大小写)
-c, --continue 续传当前目录最近的会话
--cwd <PATH> 指定工作目录
--output-format <FMT> 输出格式:plain / json / streaming-json / streaming-messages-json
--yolo 自动批准全部工具执行(等价 --always-approve)
--rules <TEXT> 追加自定义规则进系统提示词
--tools <TOOLS> 内置工具白名单(逗号分隔),仅无头模式有效
--disallowed-tools <TOOLS> 内置工具黑名单(逗号分隔),仅无头模式有效
--max-turns <N> 本轮最多智能体轮数上限,仅无头模式有效
--reasoning-effort / --effort <LEVEL> 推理强度:none/minimal/low/medium/high/xhigh/max,TUI 与无头均可用
--allow <RULE> / --deny <RULE> 权限放行/拒绝规则,支持 glob,可重复传入

注意几个边界:--tools、--disallowed-tools、--max-turns 是无头专用参数,若在交互式 TUI 里使用只会打印警告然后被忽略;而 --reasoning-effort、--permission-mode、--allow、--deny 两种模式都生效。-s 的语义尤其容易踩坑——旧版本里它曾具备「不存在就新建、存在就续传」的隐藏行为,新版本已经移除:-s 只负责新建,续传请用 -r/-c,这与 Claude Code 的防覆盖模型一致。

给 Agent 装上护栏:工具过滤与权限规则

自动化场景里,让 Agent 拿到全部工具往往是危险的——你只想让它读代码,它却可能去改文件、上网、执行命令。官方文档提供了两层控制,值得分清:

  • 工具过滤(从工具集里移除):--tools 只保留显式列出的工具(白名单),--disallowed-tools 从默认集里删掉指定工具(黑名单)。注意工具名要用内部 ID:shell 工具叫 run_terminal_cmd 而不是 bash。--disallowed-tools 还支持特殊的 Agent 条目来控制子代理:Agent 表示禁止一切子代理派发,Agent(explore) 只禁 explore 型,Agent(explore, plan) 禁多个指定类型。两个参数同时出现时,黑名单优先。
  • 权限规则(工具保留但执行时被拦):--allow/--deny 用 ToolPrefix(glob) 语法控制「哪些调用自动放行、哪些拒绝、哪些仍需确认」,前缀包括 Bash(...)、Edit(...)、Write(...)、Read(...)、Grep(...)、WebFetch(...)、MCPTool(...)。路径类规则里 * 匹配单层、** 递归匹配;Bash 规则里 * 可匹配任意字符包括空格;不带括号的裸前缀(如 Bash)匹配该类型全部调用。规则可重复传入,且 deny 优先于 allow。
# 只允许只读工具:适合"讲解代码库"这类审查任务
grok -p "Explain this codebase" --tools "read_file,grep,list_dir"

# 摘掉联网与改文件能力,只做本地评审
grok -p "Review this code" --disallowed-tools "web_search,web_fetch,search_replace"

# 禁止派发任何子代理
grok -p "Fix this bug" --disallowed-tools "Agent"

# 权限规则:放行 npm、拦截 sudo、拒绝 rm 开头的一切 shell 命令
grok -p "Clean up this project" --deny "Bash(rm*)"
grok -p "Set up the project" --allow "Bash(npm*)" --deny "Bash(sudo*)"
grok -p "Build the project" --allow "Bash"

输出格式:机器可读才是自动化的灵魂

无头模式有四种输出格式,用 --output-format 选择,自动化场景的核心诉求就是「能解析」:

  • plain(默认):人类可读文本,适合直接展示或重定向进文件。
  • json:响应全部结束后输出单个 JSON 对象,包含响应文本、停止原因、会话 ID、请求 ID(有推理时还有 thought);当提示词确实到达模型时,同一对象还携带费用字段(usage、num_turns、modelUsage 与成本)。其中 sessionId 字段对后续 --resume 至关重要——把会话 ID 捞出来,多步自动化就能串场续聊。失败时 Grok 输出形如 {"type":"error","message":"Couldn't start session: ..."} 的错误对象,进程以非零码退出。
  • streaming-json:换行分隔 JSON(NDJSON),每行一个带 type 标签的事件对象,源自 Agent 的 ACP 会话更新。事件类型包括 text(响应文本块)、thought(内部推理)、tool_call / tool_call_update(工具调用与进度)、usage(每次模型响应边界的用量)、plan、available_commands、end(恒为最后一个事件,携带元数据与费用)与 error。消费端按 type 分发即可,官方还提示事件列表并不穷尽,未来可能追加 max_turns_reached、auto_compact_* 之类新事件,代码要能容忍未知类型。
  • streaming-messages-json:Messages API 的 stream-json 线上格式,以 system/init 行开场,assistant 消息的内容块承载文本、思考与 tool_use,user 消息承载 tool_result,result 行收尾。若你的下游本来就是按 Messages 协议重建消息的消费者,这种格式几乎零改造接入。

JSON 格式里 usage 的字段口径也值得说透:usage.input_tokens 与 modelUsage.*.inputTokens 只统计未命中缓存的部分,缓存命中单独记在 cache_read_input_tokens 里,total_tokens 才是三桶(未缓存输入、缓存读取、缓存写入)加输出的总和。费用方面,total_cost_usd 只有在服务端报告了完整成本时才出现——字段缺席不代表免费,只代表未报告或不完整;部分调用缺成本时 cost_is_partial 为真且所有浮点成本字段会被整体省略,防止你把残缺的行加出一张假账单。另有 total_cost_usd_ticks 以整数刻度表达同一金额(1 美元 = 10 的 10 次方刻度),官方建议对账走整数刻度。若子代理用量没能结算、或成功路径的排空超时(turn 任务最多等 120 秒),usage_is_incomplete 会置真并同样省略成本浮点。一句话:别拿残缺的 usage 当账单。想看一次真实长什么样,下面是一条官方文档的 streaming-json 示例,读者可以直观感受事件流结构(已精简):

{"type":"thought","data":"Analyzing the directory structure..."}
{"type":"tool_call","toolCallId":"call_1","title":"Read","kind":"read","status":"in_progress","toolName":"read_file","rawInput":{"path":"src/main.rs"},"content":[],"locations":[]}
{"type":"tool_call_update","toolCallId":"call_1","status":"completed","content":[],"rawOutput":{"lines":42},"locations":[]}
{"type":"text","data":"Here's a summary"}
{"type":"end","stopReason":"end_turn","sessionId":"abc123","requestId":"xyz789","usage":{},"num_turns":7}

退出码与 CI 判定

官方明确了一张退出码表,脚本里据此分支非常可靠:

退出码 含义
0 成功:提示词正常完成
1 错误:鉴权失败、网络错误或运行时错误
130 被 SIGINT 中断(Ctrl+C,即 128+2)
143 被 SIGTERM 终止(即 128+15)

中断还有两个重要补充:Grok 会把会话状态保存到最后一个完整执行完的工具调用为止;但工具对文件做的修改不会回滚。CI 里可用 130/143 与普通错误 1 区分「被打断」与「真失败」,事后可用 grok -p "continue" --resume "<id>" 恢复现场继续干。

环境变量与鉴权:让 CI 跑起来的前提

无头模式受几个环境变量影响,CI 里最常用到:XAI_API_KEY(无浏览器登录时的鉴权凭据)、GROK_HOME(覆盖配置目录,默认 ~/.grok)、RUST_LOG(日志级别,无头模式日志走 stderr,因此 stdout 能保持干净给 JSON 输出)。没有图形界面的 CI 机器上鉴权有三条路:设 XAI_API_KEY(最省事,密钥来自 console.x.ai)、grok login --device-auth(设备码登录,无需本机浏览器)、或 grok login(浏览器 OAuth,仅限有 GUI 的机器)。已登录过则自动使用缓存凭据。建议 CI 里同时设置 GROK_DISABLE_AUTOUPDATER=1 并用 --no-auto-update 关掉更新检查——更新提示走 stderr 虽不污染 stdout,但每次任务都做更新检查会拖慢流水线。

export XAI_API_KEY="xai-..."
export GROK_DISABLE_AUTOUPDATER=1
grok -p "Run the test suite" --yolo --no-auto-update

适合场景小结

无头模式适合一切「输入明确、输出可解析、无需人工在场」的任务:CI 流水线里的自动代码评审、pre-commit 钩子、跨文件批量迁移、PR 描述生成、定时健康检查。它的定位是单发事务——需要长连接、双向往来或细粒度事件订阅时,请转向下一节的 Agent Mode。

你现在应该掌握了什么:你应该能说出无头模式的触发方式(-p 及 --prompt-json/--prompt-file),会查常用参数表并区分 -s(只新建)与 -r/-c(续传)的语义;知道 --tools/--disallowed-tools 是仅无头生效的工具过滤(内部 ID 命名、Agent(...) 条目拦子代理),--allow/--deny 是两种模式通用的权限规则且 deny 优先;理解四种输出格式的差异、json 的 sessionId 字段用途与 usage/费用字段的缺失语义;记住退出码 0/1/130/143 与「中断不回滚文件改动」;并能在无浏览器环境用 XAI_API_KEY 或 grok login --device-auth 完成鉴权。

Agent Mode 与 Plan Mode:长驻服务与动手前的规划

如果说无头模式解决的是「一次性把事情办完」,那么 Agent Mode 与 Plan Mode 解决的分别是「把 Agent 变成一台可以持续对话的服务」和「动手之前先想清楚怎么做」。两者都面向更重的工作形态,本节分别拆解,最后给一张对比表帮你选型。

Agent Mode:通过 ACP 协议把 Grok 变成常驻服务

Agent Mode 的本质,是把 Grok 跑成一个长驻服务器,客户端通过 ACP(Agent Client Protocol,基于 JSON-RPC 的智能体客户端协议)与它对话。它的目标用户是 IDE 插件、SDK、评测框架(eval harness)与自研应用;而如果你只是要一条「打印结果就退出」的命令,官方明确建议直接用 grok -p 无头模式,不要杀鸡用牛刀。

启动方式分两种传输:

# stdio 传输:本地进程,SDK/插件最常用
grok agent --always-approve stdio

# WebSocket 服务器:网络客户端连 127.0.0.1:2419,用 secret 令牌鉴权
grok agent --always-approve serve --bind 127.0.0.1:2419 --secret <token>

参数排布规则值得注意:Agent 的通用选项放在 agent 之后、模式名之前(如 grok agent --always-approve --model grok-4.6 stdio),模式专属参数放在模式名之后(如 serve --bind)。通用选项里,--always-approve(别名 --yolo)让工具执行不再弹权限确认,是脚本、CI、评测场景的起步配置,deny 规则与 hooks 依然生效;--reauth 在服务启动前完成鉴权;--leader/--no-leader 控制是否接入共享 leader 进程。serve 模式若省略 --secret,启动时 Agent 会打印一个自动生成的令牌,也可用环境变量 GROK_AGENT_SECRET 指定;服务进程会在客户端断线重连之间保持状态。官方特别提醒:这是你自己跑的服务器,Grok 托管的云端沙箱里并不运行 grok agent serve。

一段典型会话的生命周期按 ACP 走五步:客户端发 initialize 声明能力;发 session/new 携带工作目录建会话;发 session/prompt 发送用户消息;Agent 以 session/update 通知流式回传内容;涉及工具执行时按权限模式决定是否需要客户端批准。更新通知都带 sessionUpdate 字段标识类型:agent_message_chunk 是回复文本块,agent_thought_chunk 是推理块,tool_call 与 tool_call_update 是工具调用及其进度,plan 是执行计划。客户端据此可以分面板渲染推理、工具调用与正文。在 session/new 的 _meta 里还能按会话粒度开 yoloMode: true(本会话全自动批准)或 autoMode。ACP 之上,Grok 还定义了一批 x.ai/ 前缀的扩展方法,覆盖文件系统、Git、worktree、模糊搜索、终端、会话管理、鉴权与遥测等类别,文档明确说这组扩展会随版本增长,应把 initialize 响应当作发现方法的权威来源。

什么时候选 Agent Mode?当你的消费者是程序而不是人、且需要持续双向通信时:IDE 扩展(官方列的兼容客户端有 Zed、Neovim、Emacs、marimo notebook,JetBrains 标注 Coming soon)、用 ACP SDK(TypeScript、Rust、Python、Go、Kotlin 均有官方或社区 SDK)自建的聊天面板与评测工具。反过来说,一条命令搞定的事,别上服务。

Plan Mode:先规划、后动工,把返工成本压到最低

Plan Mode 是「结构化规划阶段」:Agent 先探索代码库、设计实现方案,在写任何代码之前把方案呈给你批准。官方给它的适用判据非常干脆——用于「对正确做法存在真实歧义」的任务,值得在编码前听取你的输入以避免大返工。

激活后的行为链是固定的:Agent 阅读与检索代码库,理解既有模式与架构;把实现思路写进计划文件;必要时用 ask_user_question 向你澄清;最后调用 exit_plan_mode 呈上计划请求批准。关键约束是只读性:除了会话目录里的计划文件 plan.md(写入自动批准),对任何其他文件的编辑都会被直接拒绝——工具调用会带着一句点名「plan.md 是唯一可编辑路径」的短消息失败。这条禁令在所有权限模式下都成立,包括 always-approve:也就是说 yolo 的自动批准只是「垫在 plan mode 底下」,非编辑类工具照常自动跑,但文件编辑被拦到批准退出规划为止。顺带两个细则:plan mode 拦截的是编辑工具,并不检查 bash 命令里的 shell 重定向;且父会话的 plan-mode 编辑闸门不覆盖子代理——子代理各自带一个全新的 plan-mode 追踪器(默认 Inactive),general-purpose 这类可写子代理在父会话还在规划时就能改文件,同时继承父会话的权限模式。

进入方式有三条:Agent 主动——当它判断任务有真实歧义时调用 enter_plan_mode 工具,但需要你批准后才激活,你拒绝它就留在普通模式;你手动——在 TUI 里敲 /plan(支持 /plan <描述> 一步进入并携带该描述开跑),或者按 Shift+Tab 循环会话模式(普通 → Plan → Always-approve → 普通,从普通态按一下即落在 Plan)。计划一旦生成,可用 /view-plan(别名 /show-plan、/plan-view)随时重新打开预览。计划文件本身位于会话目录 ~/.grok/sessions/<cwd编码>/<会话ID>/plan.md,内容含四部分:为什么改(Context)、推荐方案(而非罗列所有备选)、要改动的关键文件路径、可复用的既有函数与工具及其路径,外加一份端到端验证方式。

批准交互是一套键盘流:方向键或 j/k 滚动预览;a 批准并开工(有待批注时变成 approve w/ comments);s 请求修改,焦点落到输入框让你写修订意见;c 对选中行加行内评论;y 整份复制;q 放弃计划并退出规划。反馈(行内评论 + 自由批注)发给 Agent 后它会修订方案,plan mode 保持激活,你可反复迭代直到满意。

官方给的计划模式状态机如下,四态之间按固定路径迁移,且状态会持久化到磁盘、进程重启后依然记得(Pending、ExitPending 这类瞬态重启时折叠为 Inactive):

Inactive  --(agent 调 enter_plan_mode 且你批准)--> Active
Inactive  --(你敲 /plan 或 Shift+Tab)-------------> Pending
Pending   --(你发出首个 prompt)-------------------> Active
Active    --(exit_plan_mode 获批,或空闲时你关掉)----> Inactive
Active    --(turn 进行中你关掉 plan mode)----------> ExitPending
ExitPending --(当前 turn 跑完)--------------------> Inactive

什么时候用 Plan Mode,官方给了正反两面清单,值得抄进笔记。该用:存在多个合理方案、架构歧义显著的任务(例:给应用加认证——会话方案还是 JWT、令牌存哪、中间件结构);需求不清晰、需要先探索再动手的任务;改错方向代价高昂的高影响重构。不该用:实现路径清晰的任务(如给用户资料页加删除按钮);看懂 bug 就能直接修的问题;遵循既有惯例新增功能;改名、格式化、补测试这类直接改动;以及研究与探索型任务——文档点名这类「用子代理而不是 plan mode」。

两节小结:Agent Mode 与 Plan Mode 如何取舍

维度 无头模式 grok -p Agent Mode (ACP) Plan Mode
进程形态 单发,跑完即退 长驻服务,跨请求保持状态 会话内的一种模式状态
通信方式 命令行参数 + stdout JSON-RPC(stdio / WebSocket) TUI 键盘流 / 工具调用
典型消费者 shell、CI、定时任务 IDE 扩展、SDK、评测框架 坐在终端前的人
核心能力 一次执行 + 可解析输出 会话生命周期、流式事件、权限协商 先探索后规划、批准门槛
何时不用 需要长连接双向往来 一条命令能搞定的事 路径清晰的小任务、纯研究

一句话选型心法:一次性自动化用 -p,程序化双向服务用 agent,模糊大改造在 TUI 里先进 /plan。

你现在应该掌握了什么:你应该能解释 Agent Mode 是 ACP JSON-RPC 之上的常驻服务(grok agent stdio / serve 两种启动与参数排布规则、session/new 的 yoloMode 开关、五步会话生命周期、sessionUpdate 事件类型、x.ai/ 扩展方法的存在),并判断它适合 IDE/SDK/评测这类程序化消费者;同时理解 Plan Mode 的只读铁律(仅 plan.md 可写、yolo 垫底但不解锁编辑、子代理不受父会话规划闸门约束)、三种进入方式(Agent 发起需批准、/plan、Shift+Tab)、计划文件内容四件套、审批键盘流,以及正反面适用清单与四态状态机。

Subagents 子代理:让一个 Agent 变成一支并行小队

概念:独立上下文的孩子会话

子代理(Subagents)是官方文档里一句话就能讲透的概念:独立的子会话,并行处理任务。每个子代理拥有自己独立的上下文窗口,因此主代理可以把研究、实现、测试、代码评审这类工作委派出去,而不消耗自己的上下文——子代理干完活,把一份总结汇报回父代理。它们默认是开启的。这解决的是 AI 编程 Agent 最现实的一个瓶颈:上下文窗口再大也有边,而工程任务天然可以切分——查资料、写测试、审代码互相之间没有强依赖,串行做既慢又费主上下文,并行派给子代理则两全。

工作机制上,当主代理识别出可委派的工作时,会调用 spawn_subagent 工具拉起一个子会话。子代理以独立上下文窗口运行,工具集由它的 agent 类型与可选能力模式决定,可选的人格(persona)指令在解析阶段叠加。父代理在孩子结束时收到它的输出——通常是一份总结。

内置的 agent 类型官方定义了三种,也是日常最高频的选择:

类型 说明
general-purpose 默认类型。全能力 agent,什么任务都能干
explore 研究型。搜索、读文件、grep、跑 shell,但不编辑文件,适合代码库摸底
plan 规划型。探索代码库并产出结构化实现计划,同样不编辑文件

项目级或用户自定义的 agent 可以新增类型,也可以按名字覆盖这些内置类型。spawn_subagent 的核心参数包括:prompt(完整任务提示词)、description(3-5 个词的任务短标签)、subagent_type(默认 general-purpose)、background(后台运行并立即返回子代理 ID,默认 false)、isolation(none 共享工作区 / worktree 隔离 git worktree,默认 none)、resume_from(接着一个已完成子代理的对话继续,传它的子代理 ID)、cwd(子代理工作目录,与 worktree 互斥)。后台子代理的结果稍后用 get_command_or_subagent_output 取回。对活跃子代理还可以用 send_subagent_message 发消息——注意该工具目前只对根会话开放、只能发给本会话拥有的活跃子代理,其 queue 参数控制投递策略:缺省走 Steer(空闲则成为一条受保护的排队回合,运行中则在当前回合下一个安全点插入),true 则走 Queue 排队语义。

能力模式与嵌套边界

子代理的工具集由 agent 类型决定,能力模式不是 spawn 参数。文档给出四档能力模式对照:read-only 能读能搜能检查(含联网搜索与 LSP),不能编辑文件也不能跑 shell;read-write 能读且能增删改移文件,但不能跑 shell;execute 能读且能跑 shell 与后台任务,不能编辑文件;all 全放开,是 general-purpose 的默认。内置的 explore、plan 落在「能读能搜能跑命令但改不了文件」这一档。

嵌套边界是一条硬规则:只有顶层会话能派生子代理,子代理不能再派生子代理,最大嵌套深度为 1。子代理若调用 spawn_subagent 会收到深度限制错误直接失败。这条规则让 agent 树保持扁平,防止失控的递归派发。想接力上下文,正确姿势是用 resume_from:先派一个研究子代理摸问题,再派第二个子代理把 resume_from 指向前者的 ID,让它带着完整研究上下文接着干——新子代理继承来源的完整转录、工具状态与模型,其系统提示词与工具集按当前 agent 定义重新渲染;前提是来源必须已完成(非运行中)、属于当前会话、且 agent 类型一致。多阶段流水线由此而来。

Personas 与角色:给子代理叠加行为

Personas(人格)是命名的行为叠加层:它的指令以 <system-reminder> 形式注入子代理对话,塑造语气、输出格式与任务焦点,不改变子代理的 agent 类型、模型或工具。官方强调一个容易误解的点:persona 是经由子代理解析与角色机制应用的,不是 spawn_subagent 的参数——主代理 spawn 时不传 persona 名字。定义方式二选一:写在 config.toml 的 [subagents.personas.xxx] 里,或写进 .toml 文件(只发现 .toml)。文件发现优先级从高到低:项目 .grok/personas/*.toml、用户 ~/.grok/personas/*.toml、内置 persona 目录;文件名(去扩展名)即 persona 名;行内 config.toml 定义优先于文件。persona 字段有 instructions(指令文本)、instructions_file(指令文件路径,spawn 时加载并合并其后)、description、inputs/outputs(声明输入输出契约,缺省 io_type 为 file,含 required 与 description,可借此把 persona 串成链——上一个的输出文件成为下一个的输入)、model、reasoning_effort、default_isolation。生效模型与推理强度的解析顺序(高优先在前):spawn 时显式覆盖 → 角色默认 → persona 默认 → 父会话;隔离同理但前三步后兜底为 none 而非继承父会话。persona 找不到、无指令或指令文件不可读时,spawn 直接失败——配置错误要尽早暴露。自定义角色则用 [subagents.roles.researcher] 一类配置,可设 description、default_capability_mode、model、prompt_file。

# ~/.grok/config.toml —— 子代理相关配置一览
[subagents]
enabled = true

[subagents.personas.researcher]
instructions = "You are a thorough researcher. Always cite specific file paths."
description = "Deep investigator."

[subagents.personas.reviewer]
instructions = "Review code strictly against the project style guide."

[subagents.roles.researcher]
description = "Deep research agent"
default_capability_mode = "read-only"
model = "grok-4.6"          # 示例型号,以实际可用为准
prompt_file = ".grok/prompts/researcher.md"

[subagents.toggle]
explore = true              # 缺省即开启,可省略
plan = false                # 禁用 plan 子代理

[subagents.models]
explore = "grok-4.6"        # 把 explore 型路由到指定模型;不配则继承父模型

整套子代理也可整体关停:环境变量 GROK_SUBAGENTS=0,或 config 里 [subagents] enabled = false。与文件隔离相关的还有 MCP 继承:子代理默认继承父会话已连接的 MCP 服务器(含本地 stdio/HTTP 服务器与插件来源 agent),用 agent frontmatter 的 mcpInheritance 可控制为 all(默认)/none/named: [...]/except: [...];插件 agent 出于安全不能自声明 mcpServers、不能声明 hooks、不能设 permissionMode: bypassPermissions。需要改文件且怕冲突的任务,用 isolation: worktree 让子代理在独立 git worktree 里干活,改动隔离、结果里带 worktree 路径,再由 Grok 的 x.ai/git/worktree/* 扩展(含 apply 合并操作)把改动并回主工作区。

并行协作的典型场景与边界

官方给的推荐场景:父代理继续别的工作的同时,子代理去研究代码库;父代理写改动的同时,子代理并行跑测试;提交前让子代理先审一遍生成的改动;把彼此无依赖的独立任务分发出去。反面的「别用」清单同样重要:父代理自己顺手就能做的简单任务;需要与人紧密来回沟通的任务(子代理自主运行,不适合交互式往来);上下文搭建成本超过并行收益的任务。TUI 里可用 Ctrl+G 打开任务窗格看运行中与已完成的子代理、后台命令与各自状态,Ctrl+T 是独立的 todo 窗格,滚动区里子代理有紧凑的生命周期块(Subagent running: "任务名" (类型 · 模型)),回车可打开子代理完整转录的边框视图。

你现在应该掌握了什么:你应该能讲清子代理是「独立上下文窗口的并行子会话、完成回传总结」,知道 spawn_subagent 的核心参数(prompt/description/subagent_type/background/isolation/resume_from/cwd),识别三种内置类型(general-purpose 全能力、explore/plan 只读不编辑)与四档能力模式;记住「嵌套深度上限 1、只有顶层能派发」与「接力用 resume_from」;理解 persona 是解析期叠加的行为层而非 spawn 参数,会写 config.toml 里的 personas/roles/toggle/models 配置;并能按官方正反清单判断什么任务该委派、什么任务别委派。

Sessions 会话管理:让机器也能「接着上次聊」

会话是什么,存在哪里

无论是 TUI、无头模式还是 agent stdio,Grok 都会把每一次对话自动落盘为一条会话(session)。官方对会话的定义是「带完整历史的持久对话」,内容包括:全部用户提示与 Agent 响应、工具调用及其结果、TODO/任务列表状态、供 /rewind 撤销用的回退点、token 用量与轮次计数,以及(开启时)子代理会话。会话有唯一 ID(Grok 生成时是 UUIDv7,客户端也可用 -s 自供 ID),存放在 ~/.grok/sessions/ 下(GROK_HOME 可整体改基目录)。

磁盘布局按工作目录分组:~/.grok/sessions/<编码后的cwd>/<会话ID>/,目录里各文件各司其职——summary.json 是索引条目,记录总结、自动生成标题、模型 ID、创建与更新时间戳、消息计数、fork/恢复来源的父会话引用等;updates.jsonl 是权威对话日志,驱动 /resume 与会话恢复;chat_history.jsonl 是发给模型的原始聊天消息;另有 plan.json(任务列表状态)、rewind_points.jsonl、signals.json、feedback.jsonl、compaction_checkpoints/ 与 subagents/ 等。存储格式是追加写的 JSONL,天然支持增量写入、流式读取与逐行调试;grok sessions search 另维护本地 SQLite FTS5 索引做标题与提示词的快速关键词检索。长会话里磁盘大头是 updates.jsonl 与 chat_history.jsonl,压缩历史用 /compact。标题由模型自动生成:第一条 prompt 后即开始生成,之后在最初几轮由整段对话重新生成一次并冻结,避免标题老变让你找不到会话;手动 /rename 永远优先,自动生成不再覆盖,/rename --auto 可交还自动。

TUI 里的会话操作速览

会话操作命令不少,挑自动化上下文相关的说:/new(别名 /clear)清空上下文开新对话;/quit//exit 结束并退出,/home 只回欢迎屏;/delete 确认后永久删除当前会话历史;/resume 打开会话选择器,列出当前工作区的近期会话,输入即按标题过滤并同时全文检索对话内容,命中内容会出现在「Extended search results」区,按 Ctrl+/ 可跳过停顿立即搜索;/rewind(别名 /undo,或空闲时空 prompt 下 800 毫秒内连按两次 Esc)把对话回退到更早的回合并丢弃其后回合——注意只截断对话历史,磁盘上的文件改动原样保留;/compact [context] 压缩历史腾上下文窗口,接近上限时还会自动压缩,触发阈值由模型的 context_window 配置决定,可选 context 参数交代压缩时要保留什么;/session-info 查看当前会话详情(标题、会话 ID、工作目录、模型、鉴权方式、上下文窗口已用/总量百分比),c 键复制会话 ID、y 复制整块;/fork [--worktree|--no-worktree] [directive] 从当前对话副本分叉出一个平行 agent,可选 directive 作为新会话首条提示词。

无头模式:断点续传的正确姿势

无头模式下每次 grok -p 默认都是全新会话,要保持上下文跨调用就得靠会话参数——这是自动化多步任务的地基。官方把三兄弟讲得很清楚:

# 默认:每次全新会话
grok -p "Hello"

# 从 JSON 输出里捞会话 ID,作为后续调用的"接力棒"
grok -p "Hello" --output-format json | jq -r '.sessionId'
# 按 ID 续传指定会话(会话不存在会报错)
grok -p "Now check for security issues" --resume abc123

# 续传当前目录最近的会话
grok -p "Continue where we left off" -c

# -s 只用于"新建":必须传合法 UUID,且该 ID 在目标目录下未被占用
grok -p "hello" --session-id "$(uuidgen | tr '[:upper:]' '[:lower:]')" --output-format json

细节语义别记错:-r/--resume 按 ID 续传,值不是 ID 时按当前目录会话标题匹配(忽略大小写)——多个同标题时,唯一手动改过名的胜出,其余报错并列 ID;长得像 UUID 的值永远走 ID 路径,所以脚本里务必传 ID。-c/--continue 续传当前目录最近会话。-s/--session-id 只新建(非法 UUID 或 ID 已被占用都会报错),绝不续传;把 -s 和 -r/-c 组合使用时必须同时给 --fork-session,即把历史分叉进一个新 ID(可选的 -s 给子会话命名)。中断恢复的官方姿势是 grok -p "continue" --resume "<id>" 或 grok -p "continue" --continue——配合上一节讲的退出码 130/143,被打断的流水线可以安全续跑。

命令行侧还有两个管理子命令:grok sessions list(默认 20 条,--limit 50 可调,列出会话 ID、创建/更新日期、来源状态与摘要)与 grok sessions search "关键词"(本地 SQLite 索引加远端结果);grok usage <会话ID> [回合号] 打印持久化的 token 与费用(输出 JSON,costUsdTicks 按 1 美元 = 10 的 10 次方刻度计,除以 1e10 即美元),官方明说「看用量用它,别去翻会话文件」。磁盘体检用 grok du(别名 grok disk-usage),回收空间用 grok worktree gc --max-age 7d(先 --dry-run 预览),这些都是长跑自动化机器人的保养工具。

# 会话查询与恢复实战
grok sessions list --limit 10
grok sessions search "rate limit"

# 中断后的续跑
grok -p "continue" --resume "$(grok sessions list --limit 1 | ...)"   # 示意:正式脚本直接存 JSON 里的 sessionId

自动化里会话管理的三条心法

第一,ID 优先于标题:脚本一律从 --output-format json 的 sessionId 字段取 ID 传给 -r,标题匹配是给人用的(手动 /rename 之后很方便),对脚本是歧义源。第二,把多步任务串成一场会话:先让 Agent 记下关键约定(如「记住:秘密数字是 42」),后续调用 -r 接着问,上下文天然延续,不用每次把背景重敲一遍。第三,状态与账单都要有据可查:每步调用都留 JSON 输出与退出码,出问题按 ID 恢复现场,查账用 grok usage。这三条配合起来,一条本来需要人盯着的长流程,就变成了可审计、可重试、可恢复的流水线。

你现在应该掌握了什么:你应该能说明会话是「自动落盘的完整历史对话」,存储于 ~/.grok/sessions/<编码cwd>/<会话ID>/ 且 updates.jsonl 是权威日志;在无头模式里会用 -r 按 ID 续传、-c 续最近会话、-s 只新建 UUID 会话(组合续传需 --fork-session),会从 JSON 输出提取 sessionId 并传给后续调用;会用 grok sessions list/search 与 grok usage 两个子命令做查询与账单核对;理解 /resume、/rewind(不动磁盘文件)、/compact、/fork 在 TUI 里的定位,以及中断后 grok -p "continue" --resume <id> 的恢复套路。

Background Tasks 后台任务:把长任务从对话里解放出来

后台命令:跑着测试的同时继续写代码

AI 编程 Agent 遇到的最大时间杀手是长命令:构建要三分钟、测试套件要五分钟、编译要更久。若每次都得干等,Agent 与人的体验同样糟糕。Grok 的做法是让命令不阻塞对话:对 run_terminal_command 工具设 background: true 即可后台运行——它立即返回一个任务 ID,稍后用 get_command_or_subagent_output 取输出。工作流如下:Agent 带 background: true 调 run_terminal_command → 命令后台启动 → Agent 收到 task_id 留待引用 → 命令完成时对话里出现一条通知。

取输出的调用细节值得记住:get_command_or_subagent_output 的 task_ids 传列表(单个 ID 也是一元素数组,最多 20 个);timeout_ms 省略或传 0 表示非阻塞快照,传正数则等到完成——多个 ID 时等到全部完成;正数上限被钳制在 1 小时(3600000 毫秒),传输层 deadline 更短的主机可用 GROK_MAX_WAIT_BLOCK_MS 调整。一个微妙但重要的提示:若等待返回时子任务仍在跑,别杀它也别叫它停——完成时会自动唤醒父代理,需要新快照再轮询即可。终止任务用 kill_command_or_subagent(task_id):对 shell 进程发 SIGTERM 再 SIGKILL,对子代理发 Cancel 与 Shutdown。官方点名的典型用途:起 dev server 后继续写码、后台跑测试套件的同时修 bug、启动构建稍后查结果、长编译期间干别的活。

TUI 里还有一条快捷键:Ctrl+B 把正在前台运行的命令转后台——命令比预期久、想边跑边问别的问题、或启动后才发现是长任务时都用得上;其实在命令运行中直接发一条新消息也会把该命令后台化而不是杀掉它。任务跑完会收到通知。

/loop、monitor 与 scheduler:三种「周期/流式」武器

比单次后台命令更进一步的,是让 Agent 按节奏自主干活的三件套。官方用一句话划清界限:/loop 用于周期性检查,monitor 用于实时事件流,scheduler 是 /loop 底层的通用调度 API。先看三者的对比表:

机制 形态 适用 关键参数/语法
background 命令 一次长命令,立即返回 task_id 构建、测试、dev server run_terminal_command 的 background: true
/loop 按固定间隔重复执行提示词 轮询任务、周期检查、持续监控 /loop 5m 提示词,间隔 60s/5m/2h/1d(秒级最小 60)
monitor 工具 命令每行输出变一条通知 日志跟踪、文件监听、实时流 传 command + description,可 persistent: true
scheduler_* 底层调度 API 需要编程式控制的周期任务 scheduler_create/list/delete

/loop 的语法是 /loop [间隔] <提示词>:创建后立即先跑一次,之后按间隔重复;每次触发都产生一个新的 agent 回合;任务 7 天后自动过期,同时最多 50 个。示例:/loop 5m Check if the test suite passes and report any failures、/loop 2h Summarize new commits since the last check、/loop 60s Check if the dev server at localhost:3000 is responding——CI 状态盯梢这类场景正合适。

monitor 工具的思路是「把脚本的每一行输出变成对话里的一条通知」:你提供 shell 命令与一条短 description(会出现在每条通知里),Grok 把 stdout 与 stderr 合并进单个输出文件,文件每新增一行就投递一条通知,直到命令退出或你叫停。官方给的脚本准则极其实战,几乎每条都是血泪教训:管道里永远用 grep --line-buffered,否则管道缓冲能把事件延迟好几分钟;轮询循环里要容忍瞬时失败(curl ... || true),一次失败不该让 monitor 死掉;过滤要狠——每一行都会变成消息,绝不要把原始日志整条管道喂进去;轮询间隔匹配数据源节奏——远端 API 用 30 秒以上尊重限流,本地检查 0.5 到 1 秒即可;stdout 和 stderr 都会产生事件,不想当事件的就 2>/dev/null 重定向或过滤掉。若 monitor 产生的事件太多,Grok 会自动停掉它,重启时把过滤器收紧即可。persistent: true 的 monitor 会活到会话生命周期结束(PR 监控、日志跟踪、CI 状态盯梢),用 kill_command_or_subagent(task_id) 停。

# monitor 的正确打开方式:缓冲、容错、过滤三件套
tail -f /var/log/app.log | grep --line-buffered "ERROR"

# 轮询远端 API 的骨架:失败不致命、间隔尊重限流
last=$(date -u +%Y-%m-%dT%H:%M:%SZ)
while true; do
  now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
  gh api "repos/owner/repo/issues/123/comments?since=$last" \
    --jq '.[] | "\(.user.login): \(.body)"' || true
  last=$now
  sleep 30
done

最底层是 scheduler 工具三件套:scheduler_create 的关键参数有 interval("5m"/"2h"/"1d"/"60s" 这类字符串)、prompt(每次触发执行的提示词)、fire_immediately(创建时除按间隔外是否立即先跑一次,默认 false)、recurring(重复还是只跑一次,默认 true)、durable(跨会话持久化,默认 false);scheduler_list 列出全部活跃任务及其 ID、提示词、间隔与下次触发时间;scheduler_delete 按 ID 取消。一个容易被忽略的用法:recurring: false 加延迟间隔,等价于一次性延迟任务——官方 best practices 里专门提了这一点。

看得见的进度:任务窗格与状态行

交互界面为这一切配了仪表盘:Ctrl+G 开合任务窗格,单一视图列出运行中的子代理与进度、活跃后台任务与状态、monitor 与 /loop 任务(各带实时行数徽标)以及每个条目的任务 ID;Ctrl+; 切换的是提示词队列。更妙的是那条「仍在运行」状态行:只要有后台工作在跑,prompt 上方就常驻一行 ◎ 1 command · 2 monitors · 1 loop · 1 subagent still running,随各任务完成实时更新计数——任何一项完成/出事件/到点都能唤醒 Agent 开新回合,所以这行提示会一直挂到无事可做。当 Agent 阻塞在等后台工作(get_command_or_subagent_output)时,状态行会补一句 ◎ 1 command still running · send a message to interrupt,提示你发条消息即可打断等待、立即执行你的话。

组合拳的官方套路:dev server 后台起 + 继续实现功能;/loop 5m 持续跑测试只报新失败;monitor 盯应用日志里的 ERROR/WARN;/loop 2m 盯 GitHub Actions 运行状态、完成时汇报。最佳实践五条收尾:一次性长命令用 background;周期检查用 /loop;实时事件流用 monitor;延迟一次性任务用 scheduler_create 配 recurring: false;monitor 过滤器要收紧,正常命令里别用 sleep 循环轮询——等后台任务用 get_command_or_subagent_output 的 timeout_ms 才是正解。

你现在应该掌握了什么:你应该能区分四条后台路径的适用场景——background: true 跑一次性长命令、/loop 做周期检查、monitor 盯实时事件流、scheduler_create(配 recurring: false)做延迟一次性任务;会用 get_command_or_subagent_output 取输出(timeout_ms 省略/0 为快照、正数等到全部完成、上限 1 小时)与 kill_command_or_subagent 收尾;记住 monitor 脚本三铁律(grep --line-buffered、瞬时失败容错、过滤要狠)与远端轮询 30 秒起步的限流自觉;知道 Ctrl+B 前台转后台、Ctrl+G 任务窗格与「仍在运行」状态行的含义。

自动化集成实战:在 GitHub Actions 与 shell 里调度 grok

前六节是零件,这一节把它们装成两台能直接上生产的机器:一台是跑在 GitHub Actions 里的 PR 自动评审流水线,一台是本地 pre-commit 钩子脚本。两者都用无头模式做单发调用,用 JSON 输出做程序化解析,用会话 ID 做多步串联,再用工具过滤与权限规则上护栏。先看整体数据流(纯 ASCII 流程图,便于在任意终端与博客里渲染):

+-------------+      +------------------+      +------------------+      +---------+
|  git push   | -->  | GitHub Actions   | -->  | grok -p --yolo   | -->  | jq 解析 |
|  / PR 事件  |      | ubuntu-latest    |      | --output-format  |      | .text / |
+-------------+      | runner           |      | json             |      | .sessionId
                     +------------------+      +------------------+      +----+----+
                                                                              |
                                                            +-----------------v-----------------+
                                                            | 评审意见 -> review.md             |
                                                            | 失败则 job 退出非零 -> PR 标红     |
                                                            +-----------------------------------+

示例一:PR 自动代码评审(GitHub Actions)

思路:PR 的 pull_request 事件触发一个 job,runner 上先检出代码,再以无头模式让 grok 对本次改动做一轮「找 bug 与安全隐患」的评审,结果写进 review.md 并作为 job artifact 供人查阅。安装 grok CLI 的具体方式与版本策略官方 README 与安装文档为准(不同版本安装路径可能变化),workflow 里我们假设 runner 上 grok 已可用;鉴权用仓库 Secrets 里的 XAI_API_KEY。CI 环境没有浏览器,无头模式配 --yolo 让工具免确认执行——配合 --tools 白名单把 Agent 限定在只读工具上(read_file、grep、list_dir、run_terminal_cmd 按需放开),评审任务本就不该改文件;--rules 追加护栏约束措辞;--max-turns 给预算上限防失控;--no-auto-update 与 GROK_DISABLE_AUTOUPDATER=1 关掉更新检查,免得每次 CI 跑都慢半拍:

name: grok-pr-review
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0   # 需要完整历史用于 diff

      # grok CLI 的安装方式以官方 README / 安装文档为准,此处假定已可用
      - name: AI 代码评审(Grok headless)
        id: review
        env:
          XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
        run: |
          export GROK_DISABLE_AUTOUPDATER=1
          grok -p "Review the changes in this PR for bugs and security issues. \
Be concrete: cite file paths and suggest fixes. Reply in Chinese." \
            --output-format json \
            --tools "read_file,grep,list_dir,run_terminal_cmd,git_diff" \
            --max-turns 30 \
            --yolo \
            --no-auto-update \
            | jq -r '.text' > review.md
          # 顺手把会话 ID 存下来,事后可用 --resume 追问
          grok -p "hello" --output-format json | jq -r '.sessionId' > .session_id || true

      - name: 上传评审报告
        uses: actions/upload-artifact@v4
        with:
          name: grok-review
          path: review.md

几点工程细节说明:--tools 白名单里我们保留了 run_terminal_cmd(评审要跑构建或测试时用),真要不放行 shell 就把它从列表里摘掉;--yolo 只管免确认,危险动作仍要仰仗白名单与 --rules;jq -r '.text' 提取正文,sessionId 留档以便人肉追问同一场会话。评审意见想要「挂回 PR 评论」也很自然:把 review.md 内容用 gh pr comment 发回去即可,这里不做展开。想控制预算还可以给 --reasoning-effort medium 之类的档位(模型支持时生效)。

示例二:pre-commit 钩子与批量迁移脚本

本地守门员的经典写法来自官方文档的 CI/CD 示例:让 grok 审一遍暂存区改动,要求它「没问题就回 OK」,脚本用 grep 判定结果决定是否放行。官方原版语义是:回复以 OK 开头则通过,否则列出问题并让提交失败:

#!/bin/bash
# 示例二 a:pre-commit 钩子 —— 有问题就拦住提交
set -euo pipefail
export XAI_API_KEY="${XAI_API_KEY:-}"   # 已登录则自动用缓存凭据

RESULT=$(grok -p "Review staged changes for obvious bugs. Reply OK if fine, or list issues." \
  --yolo --output-format json | jq -r '.text')

echo "$RESULT" | grep -q "^OK" || { echo "$RESULT"; exit 1; }

批量任务则利用会话串联的威力:官方文档示范了对目录内每个文件各起一次 grok -p 的朴素循环,更省的是一场会话多步走——第一次调用把「迁移规则」讲清楚并拿回 sessionId,后续每次带 -r 续传,Agent 始终记得规则与已处理进度:

# 示例二 b:批量迁移 —— 一场会话串起 N 个文件
SESSION_ID=$(grok -p "我们正在把 src/ 下所有文件从 CommonJS 迁移到 ES modules。
规则:(1) import 语句置顶;(2) 导出统一用 export default/export function;
(3) 不改变运行时行为。先回复'规则已记下'并给出你的 sessionId 字段所在行。
我们逐文件进行,每轮我发一个文件路径。" \
  --output-format json | jq -r '.sessionId')

for file in src/*.js; do
  echo "== 处理 $file =="
  grok -p "请迁移文件:$file。完成后简要报告改动要点。" \
    --resume "$SESSION_ID" --yolo || echo "!! $file 处理失败,继续下一个"
done

把三件机制都用上了:sessionId 做上下文接力、--resume 续传、失败不中断整个循环(配合退出码 1 与 130/143 的区分,可事后按 ID 单独补跑失败项)。本地想用后台任务编排更长的流水线,把循环丢进 nohup/tmux 即可,Grok 侧的 /loop 与 scheduler 适合跑在常驻会话里——详见上一节。

批处理数据流(shell 视角)
+--------------+     +----------------------+     +----------------+
| 第一轮调用    | --> | JSON -> jq 提取       | --> | 后续调用        |
| grok -p 规则  |     | sessionId 写入变量    |     | grok -p -r $ID |
+--------------+     +----------------------+     +----------------+
                                                          |
                                 +------------------------v------------------------+
                                 | 每个文件一轮:Agent 带着完整规则上下文逐个迁移     |
                                 | 失败项记录退出码,全部跑完后再按 ID 补跑           |
                                 +-------------------------------------------------+

你现在应该掌握了什么:你应该能在 GitHub Actions 里搭出「检出 → grok -p --output-format json --yolo → jq 提取 → 产出评审文件/挂回 PR」的完整流水线,知道 CI 里要设 XAI_API_KEY、关更新检查、用白名单限工具、用 --max-turns 卡预算;会写「回复 OK 才放行」的 pre-commit 钩子;并掌握用 sessionId + --resume 把批量迁移串成一场带规则记忆的多步会话、失败项按退出码甄别后补跑的做法。安装与版本细节不确定处,一律以当前版本官方文档为准。

踩坑经验:官方文档反复强调的边界,都是前人踩过的坑

这一节把六章文档里最容易误读、误用、误信的细节集中成「❌ 错法 / ✅ 对法」清单,每条都对应文档原文,适合贴在终端旁边当备忘。

坑一:把 -s 当续传用。 ❌ 以为 grok -p "..." -s <旧会话ID> 能接着旧会话聊——新版里 -s 的隐藏 upsert 行为已移除,旧 ID 直接报「已被占用」。✅ -s 只负责新建且必须传合法 UUID;续传用 -r(按 ID/标题)或 -c(当前目录最近会话);想把历史分叉进新 ID 时 -s 才与 -r/-c 组合,且必须配 --fork-session。脚本永远优先传 ID,别赌标题匹配。

坑二:往 stdin 里管道喂提示词。 ❌ 写 echo "帮我审代码" | grok -p 期待它读 stdin——无头模式不读管道 stdin 进提示词。✅ 用命令替换 $(...)(如携带 git diff --staged)或 --prompt-file ./prompt.txt 传外部内容。

坑三:无浏览器环境裸跑。 ❌ 在 CI 容器里直接 grok -p,卡在登录或更新检查。✅ 设 XAI_API_KEY(console.x.ai 生成)或 grok login --device-auth 设备码登录;容器/CI 里顺手 GROK_DISABLE_AUTOUPDATER=1、加 --no-auto-update;更新提示走 stderr,别污染 stdout 的 JSON。若把 ~/.grok 只读挂载进容器,会话持久化会静默失败(本来就是临时态),属预期行为。

坑四:把 headless 专用参数带进 TUI,或反之。 ❌ 在 TUI 里传 --tools/--disallowed-tools/--max-turns,以为能限制当前会话——文档写明这些是仅无头参数,TUI 里只打警告然后忽略。✅ 记住工具过滤与轮次上限只在 -p 场景生效;--reasoning-effort、--allow/--deny、--permission-mode 才是两栖参数。

坑五:工具名想当然。 ❌ 写 --disallowed-tools "bash" 想禁 shell——内部 ID 是 run_terminal_cmd。✅ 用内部工具 ID;拦子代理用 Agent、Agent(explore)、Agent(explore, plan) 这类条目。同时给 --tools 与 --disallowed-tools 时黑名单优先。

坑六:在 plan mode 里以为 yolo 能改文件。 ❌ 开了 always-approve 就在规划阶段顺手让 Agent 改代码。✅ plan mode 的只读铁律独立于权限模式:除 plan.md 外一切编辑工具调用直接失败;yolo 只是垫在底下,批准退出规划后自动恢复。另注意 plan mode 拦的是编辑工具而非 shell 重定向,且子代理不受父会话规划闸门约束(general-purpose 子代理在父会话规划期也能改文件)。

坑七:子代理无限嵌套当「多级领导」。 ❌ 让子代理再去派生子代理——最大嵌套深度是 1,只有顶层会话能 spawn,子代理调用 spawn_subagent 会直接深度限制报错。✅ 需要接力时用 resume_from 把已完成子代理的转录、工具状态与模型继承给下一个,来源须已完成、同会话、同 agent 类型。

坑八:monitor/loop 脚本不做缓冲与过滤。 ❌ 把 tail -f app.log 原样喂给 monitor,或轮询脚本不带 || true——每行输出都是一条通知,原始日志几秒就能把会话淹了,Grok 会直接自动停掉 monitor。✅ 管道里 grep --line-buffered 保实时;瞬时失败要容忍(curl ... || true);过滤狠一点,只留你关心的行;远端 API 轮询间隔 30 秒起步尊重限流;不想当事件的输出用 2>/dev/null 处理掉。

坑九:拿 usage 字段当账单、把缺失当免费。 ❌ 见 total_cost_usd 缺席就以为这轮免费——缺席表示服务端未报告或不完整,从不代表免费;部分调用缺成本时 cost_is_partial 置真且所有成本浮点被整体省略,就是防止你加出假账单。✅ 精确对账用 total_cost_usd_ticks(整数刻度,1 美元 = 10 的 10 次方),或者直接 grok usage <sessionId> 读持久化账单;usage.input_tokens 只算未命中缓存的输入,别和总数混淆。

坑十:中断后不恢复、退出码不看。 ❌ 流水线被 Ctrl+C 打断就重头跑。✅ 记住退出码 0 成功 / 1 错误 / 130 SIGINT / 143 SIGTERM,CI 可据此区分「被打断」与「真失败」;中断时会话状态已保存到最后一个完成的工具调用、文件改动不回滚,用 grok -p "continue" --resume "<id>" 或 --continue 原地续跑。长会话记得主动 /compact(无头侧没有,就在 TUI 里做),历史文件 updates.jsonl/chat_history.jsonl 是磁盘大头。

你现在应该掌握了什么:你应该能把上述十类坑的错法/对法讲给别人听——-s 只新建、stdin 不进提示词、CI 鉴权三件套与更新检查关闭、headless 专用参数边界、工具内部 ID 与 Agent(...) 条目、plan mode 只读铁律的权限无关性、子代理深度上限 1 与 resume_from 接力、monitor 脚本三铁律、usage 缺失非免费的账单语义、中断退出码与原地恢复。遇到拿不准的新参数,回到官方文档 user-guide 对应章节核对,以当前版本官方文档为准。

总结:把 Grok Build 变成自动化流水线上的一等公民

通读六章 user-guide 之后,能沉淀下来的核心判断有以下几条:

  1. 无头模式是自动化的地基。 grok -p 把 Agent 变成「一条提示词进、一份可解析结果出」的管道单元,配合 --output-format json 取 sessionId 与正文、--tools/--disallowed-tools/--allow/--deny 上护栏、退出码做分支,任何 shell 或 CI 都能调度它。模型、安装、鉴权细节以当前版本官方文档为准。

  2. 按消费者选工作形态。 一次性自动化用无头模式;IDE/SDK/评测这类程序化双向服务用 Agent Mode(ACP JSON-RPC 长驻,stdio 或 WebSocket);人坐在终端前面对模糊大改造,先进 /plan 让 Agent 先探索后规划,批准了再动工。

  3. 子代理是「规模」的解药,但边界要敬畏。 独立上下文窗口的并行子会话让研究、测试、评审能同时进行而不挤占主上下文;explore/plan 内置类型只读不编辑,嵌套深度上限 1,接力用 resume_from,persona 是解析期叠加而非 spawn 参数。

  4. 会话是「韧性」的载体。 一切对话自动落盘,-r/-c/-s 语义分明(续传/最近/只新建),JSON 输出里的 sessionId 是脚本的接力棒;中断用退出码区分、用 grok -p "continue" --resume <id> 原地续跑,grok sessions/grok usage 负责查询与对账。

  5. 后台任务把「等待」还给机器。 background: true 跑一次性长命令、/loop 做周期检查、monitor 盯实时事件流、scheduler 做底层调度(recurring: false 即延迟一次性任务);脚本准则记牢三铁律:grep --line-buffered、瞬时失败容错、过滤要狠。

  6. 护栏与账单意识贯穿始终。 权限规则 deny 优先、plan mode 只读独立于权限模式、usage 缺失不代表免费、成本浮点可能整体省略——自动化的成熟度不取决于能放多开,而取决于知道边界在哪、账单怎么对。

这套机制组合起来的画面很清晰:PR 一到,CI 里无头模式的 grok 带着只读白名单审代码;仓库里跑着的常驻会话用 /loop 盯测试、用 monitor 盯日志;改动太模糊时,坐在终端前的人先 /plan 再动手,重活用子代理并行,干一半断电了,回来一条 --resume 接着聊。AI 编程 Agent 至此不再是「陪你写代码的对话窗」,而是流水线里一个随时可调度、可审计、可恢复的工程角色。

你现在应该掌握了什么:读完本文,你应当能够从零设计一条「grok 参与的自动化流水线」:选对工作形态(无头/Agent Mode/Plan Mode),用子代理并行、会话续传、后台任务处理长流程,用工具白名单与权限规则控制风险,用退出码与 usage 字段做可靠判定与成本核算;对文档中没覆盖或版本相关的细节,养成回官方仓库 docs/user-guide/ 查证的习惯,一切以当前版本官方文档为准。

参考资料

  • Grok Build 官方仓库(含本文带读的 user-guide 全文与安装/鉴权说明):https://github.com/xai-org/grok-build
  • Headless Mode and Scripting(14-headless-mode.md):无头模式参数表、输出格式、会话参数、退出码与环境变量
  • Agent mode (ACP) and IDE integration(15-agent-mode.md):grok agent 启动方式、ACP 生命周期、扩展方法、SDK 与兼容客户端
  • Subagents and Personas(16-subagents.md):spawn_subagent 参数、内置类型、personas/roles 配置、worktree 隔离
  • Session Management(17-sessions.md):会话存储布局、grok sessions/grok usage 子命令、rewind/compact/fork
  • Plan Mode(19-plan-mode.md):状态机、计划文件、审批键盘流、适用清单
  • Background Tasks and Monitoring(20-background-tasks.md):后台命令、/loop、monitor、scheduler 与任务窗格
  • Agent Client Protocol 规范:https://agentclientprotocol.com