Mingyu's Library主页
2026年8月18日

第 12 课:无头模式 `claude -p` 与 CI/CD 自动化

日期:2026-08-18 ・ 主题:把 Claude Code 从「你坐在终端前对话」变成「流水线里的一个可执行步骤」 前置课:第 9 课(权限与沙箱)、第 11 课(MCP 配置)。本课的每条技巧都假设你已经理解 --allowedTools 的含义。

无头模式的本质很简单:-p(--print)让 Claude Code 从交互式 REPL 变成一次性批处理调用——一个 prompt 进去,一个结果出来,进程退出。退出码 0 表示成功,非 0 表示出错,所以它能像任何 Unix 命令一样接进 &&、||、管道和 CI step。

下面四条由浅入深。


技巧 1(入门):用管道喂上下文,别用「请你去读文件」

新手最常见的浪费,是在无头模式里写「请读取 src/ 下的所有文件然后……」——这让 Agent 花几轮工具调用去做你一条 shell 命令就能做完的事。无头模式下,上下文应该由你从外面塞进去。

# 最小可用形态
claude -p "总结这段 diff 的风险点,只列出可能引入 bug 的改动"  < <(git diff HEAD~1)

# 更常用:用管道
git diff origin/main...HEAD | claude -p "以资深 reviewer 的身份 review 这段 diff,按严重程度排序输出"

# 把日志喂进去做故障定位
tail -n 500 /var/log/app.log | claude -p "找出这段日志里的异常模式,给出最可能的三个根因"

要点: - -p 后面跟的是任务描述,不是数据。数据走 stdin。 - 加 --append-system-prompt "你是……,输出必须为中文,不要写客套话" 可以稳定输出风格,比在 prompt 里反复叮嘱更可靠。

📖 Claude Code CLI 参考


技巧 2(进阶):选对输出格式,让结果可被程序消费

无头模式有三种输出格式,选错了后面全是正则地狱:

格式 参数 用在哪
纯文本 --output-format text(默认) 人看的报告、贴进 PR 评论
单个 JSON --output-format json 脚本要取结果 + 用量 + 耗时
流式 JSON --output-format stream-json 需要实时进度、长任务、要看中间工具调用
# 让结果结构化,并用 jq 取字段
result=$(git diff | claude -p "输出 JSON:{\"risk\":\"low|medium|high\",\"issues\":[...]}" \
  --output-format json)

echo "$result" | jq -r '.result' > review.md
echo "$result" | jq -r '.total_cost_usd'   # 这次调用花了多少钱

# 让 CI 根据风险等级决定是否卡住合并
risk=$(echo "$result" | jq -r '.result | fromjson | .risk')
[ "$risk" = "high" ] && exit 1

要点:JSON 模式的顶层字段包含 result(模型输出)、用量与成本信息。如果你要模型输出结构化内容,请在 prompt 里明确给出 JSON schema,再套一层 fromjson 解析——不要指望默认就有结构。

📖 Headless mode 与 CI/CD 实践


技巧 3(实战):在 GitHub Actions 里跑,权限收到最紧

官方提供 anthropics/claude-code-action,它把安装、鉴权、执行三件事包成一个 step。但默认放开权限是无头模式最大的坑:没有人在旁边点确认,--dangerously-skip-permissions 会让 Agent 在 CI 里为所欲为。

正确姿势是白名单 + 只读:

name: AI Code Review
on: pull_request

jobs:
  review:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write      # 只给写评论的权限,不给写代码
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }

      - name: Claude review
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          git diff origin/${{ github.base_ref }}...HEAD > /tmp/diff.txt
          claude -p "review 这段 diff,重点看:N+1 查询、未处理错误、注入风险、边界条件。用中文,按严重度分级。" \
            --allowedTools "Read,Grep,Glob" \
            --output-format text \
            < /tmp/diff.txt > /tmp/review.md

      - name: Post comment
        run: gh pr comment ${{ github.event.pull_request.number }} --body-file /tmp/review.md
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

要点: - --allowedTools "Read,Grep,Glob" —— 评审任务不需要 Bash 和 Write,别给。工具白名单是无头模式最有效的一道防线。 - 让 Claude 只产出文本,由后面的 step(gh pr comment)去做写操作。「模型思考、脚本执行」比「模型直接动手」在 CI 里安全一个数量级。 - 成本可控:一次 500 行 diff 的评审大约 20–45 秒、几分钱量级的 token 成本(具体随模型与上下文变化,建议自己用 total_cost_usd 实测一周再定预算)。

📖 claude-code-action(GitHub Actions 集成) ・ Headless CI/CD Playbook 2026


技巧 4(高阶):批量任务的 fan-out 与断点续跑

无头模式真正的威力在于批量:同一个 prompt 打到几十个目标上。但直接 for 循环串行跑会很慢,而无脑并行会打爆速率限制。折中做法是用 xargs -P 限流,并把结果落盘以便断点续跑。

#!/usr/bin/env bash
# 给仓库里每个模块生成/更新 README,失败可重跑
set -uo pipefail
mkdir -p .ai-out

gen() {
  mod="$1"
  out=".ai-out/$(echo "$mod" | tr '/' '_').md"
  [ -s "$out" ] && { echo "skip $mod"; return 0; }   # 已完成的跳过 → 断点续跑

  claude -p "为模块 $mod 写一份 README:用途、公开 API、一个最小示例。中文。" \
    --allowedTools "Read,Grep,Glob" \
    --output-format json 2>/dev/null \
    | jq -r '.result' > "$out" || { rm -f "$out"; echo "FAIL $mod" >&2; return 1; }
}
export -f gen

find src -maxdepth 2 -type d -name "*" | xargs -P 4 -I{} bash -c 'gen "$@"' _ {}

要点: - -P 4 控制并发,比无限并行友好得多;遇到 429 就降到 2。 - 失败即删除半成品(rm -f "$out"),否则「已存在就跳过」的续跑逻辑会把残缺文件当成功。 - 需要跨次调用保持上下文时,用 --resume <session-id> 或 --continue;但批量场景通常故意不共享上下文——每个任务独立才可并行、可重跑。 - 相关工程实践可参考 Anthropic 的 Agent 工程文章,把无头调用当作「一个确定性的函数」来设计。

📖 Anthropic Engineering Blog ・ Headless mode 教程


一句话总结

无头模式的心法是:把 Agent 当成一个吃 stdin、吐 stdout、有退出码的 Unix 命令。上下文你来喂,工具你来限,写操作交给后面的脚本——剩下的判断力才交给模型。

下节预告候选

git worktree 并行开发、成本控制与用量分析、调试工作流(--verbose 与 stream-json 排障)。