生命不息,折腾不止。前两课教 Claude Code 怎么「原地把活干漂亮」,这一课教它怎么「没人盯着也照干」——无头模式,让它在 CI 流水线、定时任务里 24 小时自己打工。

Claude Code 实战系列开到第三课了。先给新来的朋友补个课:开篇那篇《从安装到命令行自动化搭子》已经把 claude -p 的梗概带过一嘴,第一课用 MCP 给它装「手脚」,第二课用 Hooks 立「规矩」;至于装机和接中转站这些基本盘,早在 8 月那篇《Claude Code 国内配置教程》里就铺好了,今儿不重复。

这课把「无头模式」(Headless)彻底讲透——从开篇里的「能用」讲到「上生产可靠地用」:权限白名单、三种输出格式、CI 流水线、常见踩坑一次收全。它把 Claude Code 从「一个你坐在终端前聊天的工具」,变成「一个能被脚本、定时任务、CI 流水线按需调用的正经编程接口」。跨过这一步,你才算真的把 AI 接进了自己的自动化世界。

一、为什么交互模式不够用

先说明白「无头」到底无的什么头。你平时敲 claude 进去的那个是交互式 TUI——有界面、会等你打字、动不动追着你问「允许我执行这个命令吗?」。体验很好,但它的前提永远是:有个人坐在屏幕前。

可软件工程里有一大堆活,恰恰是「没人守着、还得每天准时干」的:

  • 每天凌晨,自动扫前一天的服务日志,吐一份异常报告;
  • 每次有人提 PR,自动跑一遍代码 review,把意见贴回评论区;
  • 每次合并代码,自动更新 API 文档;
  • 每个周期,自动按清单生成发布说明。

这些场景的共同点是:触发是确定的、动作是重复的、人是不在场的。交互模式天生干不了,因为你不可能半夜三点爬起来敲 claude。

无头模式就是为这个生的。它把 Claude Code 变成一条「一次性的命令」:喂一条任务,它自己干完,把结果打到标准输出,然后退出,退出码还能反映成败。于是 shell、crontab、Jenkins、GitHub Actions 都能直接拿它当普通命令用。一句话:从「有人值守」到「无人值守」,这是 Claude Code 上生产的关键一步。

二、一条命令入门:claude -p

无头模式的核心就一个参数:-p(--print 的缩写)。看最基本的用法:

1
2
# 让它读当前仓库,总结最近 10 次提交改了什么
claude -p "总结这个分支最近 10 次提交改了什么"

-p 干的事就三样:不进交互界面、处理完这条 prompt、把最后结果打到 stdout 然后退出。没有 TTY、不会等你输入、退出码反映成败——脚本和 CI 要的它全给了。

环境变量跟交互模式共用一套(中转站接入 8 月那篇讲过,这里只补 CI 场景怎么写)。如果你走中转站,比如 ai.aklibk.com(Claude/GPT/Gemini 国内直连、免绑卡、人民币按量付费),CI 里长这样:

1
2
3
4
export ANTHROPIC_BASE_URL=https://ai.aklibk.com
export ANTHROPIC_API_KEY=你的中转站Key

claude -p "解释一下这个项目的架构"

小提醒:不同中转站对 base_url 的要求略有差异,有的要带 /v1 后缀,以你中转站后台给到的地址为准,别硬套。

想让它在同一个会话里接着聊,加 --continue(或 --resume 并传 session id)即可:

1
claude -p "继续刚才的分析,把结论整理成 3 条" --continue

三、让输出变成「可编程」:三种输出格式

无头模式如果只吐一大段人话,脚本还是没法接着用。真正让它值钱的是输出格式,Claude Code 给了三种:

1. text(默认):纯文本回答,适合自己看。

2. json:把结果包成结构化 JSON,带上会话 id、token 用量、花费等元数据,脚本用 jq 一扒就出来了:

1
2
3
claude -p "列出仓库里所有 TODO 注释" \
--bare --allowedTools "Read,Glob,Grep" \
--output-format json | jq -r '.result'

返回的 JSON 里,result 是正文,另外还带 session_id、usage、total_cost_usd(本次花了多少钱)这些元数据。CI 里想「每次跑贵不贵、报个账」,抓 total_cost_usd 就行。

3. stream-json:逐行输出事件流,适合边跑边看、实时推流:

1
2
claude -p "解释递归" --output-format stream-json --verbose --include-partial-messages \
| jq -r 'select(.type=="stream_event" and .event.delta.type?=="text_delta") | .event.delta.text'

再加个杀手锏 --json-schema:如果你连「结果长什么样」都要事先锁死,直接塞一段 JSON Schema,Claude 会把答案放进校验过的 structured_output 字段。以前靠正则硬抠 LLM 输出的苦,从这儿开始不用吃了:

1
2
3
claude -p "提取 auth.py 里所有函数名" \
--output-format json \
--json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}'

四、无人值守的安全三件套

人不在场,最怕的是「它自己放开手脚乱搞」。无头模式下没人会给权限弹窗点「允许」,所以权限必须事先定死。记住这三件套:

1. --allowedTools(工具白名单)——只准它用你列出的工具,其余一律不许碰,这是最重要的一条:

1
2
3
4
5
6
# 只读审查,啥都不能改
claude -p "审查代码" --allowedTools "Read,Glob,Grep"

# 允许改文件 + 跑测试和 lint,别的命令不许碰
claude -p "修 bug 并跑测试" \
--allowedTools "Read,Edit,Bash(npm test),Bash(npm run lint)"

看到 Bash(npm test) 这种写法没——它支持「工具 + 具体命令」的粒度,只放行 npm test、npm run lint 这两条,别的 bash 命令照样被拦。想反向操作,用 --disallowedTools 拉黑特定工具。

2. --max-turns(熔断器)——限制它最多迭代多少轮。Claude 偶尔会陷入「我再读个文件确认下」的死循环,不设上限,token 能烧到天亮:

1
claude -p "修这个 issue" --max-turns 40

3. --permission-mode(权限模式)——几档按需挑:

  • acceptEdits:允许它改文件,也自动放行 mkdir/touch/mv/cp 这类常见文件操作;
  • dontAsk:白名单和只读集之外的请求,一律直接拒绝并中止;
  • bypassPermissions:跳过权限检查、基本全放行(生产慎用)。

还有个 --dangerously-skip-permissions,字面意思就是「危险地跳过所有权限」,网上俗称 yolo 模式。写代码一时爽,生产环境千万别带着它上 CI。

最后补一个 CI 专用 flag --bare:跳过 hooks、skills、plugin、CLAUDE.md、MCP 自动发现,跑得又快又稳,要啥再单独加回来:

1
claude --bare -p "总结这个文件" --allowedTools "Read"

五、接进 GitHub Actions:一条自动 review 流水线

理论说完了,上真家伙。下面是最实用、上手最快的一条——PR 自动代码审查:每次有人开 PR 或推新代码,Claude 自动把 diff 审一遍,结论直接贴回评论区。

在仓库里建 .github/workflows/claude-review.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
name: Claude 自动代码审查
on:
pull_request:
types: [opened, synchronize]

jobs:
review:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
pull-requests: write
steps:
- name: 检出代码
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: 装 Claude Code
run: npm install -g @anthropic-ai/claude-code

- name: 跑无头审查
env:
ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
DIFF=$(git diff origin/main...HEAD)
claude -p "审查下面这段 diff,从代码健壮性、潜在安全漏洞、团队规范三个维度排查,输出 Markdown 报告。diff:$DIFF" \
--allowedTools "Read,Glob,Grep,Bash(npm test),Bash(npm run lint)" \
--output-format json \
--max-turns 20 > review.json

- name: 贴回评论区
if: always()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const out = JSON.parse(fs.readFileSync('review.json', 'utf8'));
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: '## Claude Code 审查报告\n\n' + out.result
});

ANTHROPIC_BASE_URL、ANTHROPIC_API_KEY 在仓库 Settings → Secrets 里配好,千万别明文写进 yaml。另外 Anthropic 官方也出了一键集成 anthropics/claude-code-action@v1,在仓库里敲 claude /install-github-app 就能自动装 GitHub App、存 Key、生成 workflow,想省事直接走官方件。

六、踩过的五个坑

无头模式上 CI,新手最容易栽这几个跟头,提前打预防针:

  1. 不设 --max-turns,token 烧穿:死循环真实存在,一定要设上限,step 里再套个 timeout-minutes 双保险。
  2. fork 仓库的 PR 读不到 Secret:外部贡献者提的 PR,GitHub 默认不给 workflow 注 Secret(防泄露),想跑审查得用 pull_request_target 并严格审核触发条件。
  3. runner 磁盘被打满:大仓库 + 多会话,临时文件动辄 5-10GB,提前 df -h 盯着。
  4. 递归触发:Claude 自己 push 会再触发新一轮 workflow,又拉起 Claude,无限套娃。加一句 if: github.actor != 'claude-bot' 挡住。
  5. 中转站 RPM 冲顶:多条流水线并发跑,请求速率上去容易被限。挑支持高并发的套餐,或错峰调度。

七、写在最后

无头模式是 Claude Code 从「玩具」到「工具」的分水岭。前面几课学的是「怎么让它干得更好」,这一课学的是「怎么让它自己上场」。两条腿都学会了,你就不是在「用 Claude Code」,而是在「部署 Claude Code」——把它当成一个能塞进任何自动化链条里的 AI 小工。

接 Claude/GPT/Gemini 这些国外模型,中转站 ai.aklibk.com 国内直连、免绑卡、人民币按量付费,跟无头模式天生一对:CI 里跑起来不心疼钱,跑多少算多少。

生命不息,折腾不止。下一课把 CLI 再往上拔一层——聊聊 Claude Agent SDK:用 Python / TypeScript 把 Claude Code 这套引擎直接塞进你自己的程序,做真正程序化的 Agent 流水线,而不是每次都得走命令行。