无头模式与 CI:让 Claude 在流水线里干活
不开交互界面,用 claude -p 把 Claude Code 当成一个命令行工具嵌进脚本和 CI;掌握输出格式、权限与 --bare,接入 GitHub Actions 自动评审 PR,并了解什么时候该换成 Agent SDK。
学完这一课,你能
- 能用 claude -p 配合 --output-format 和 --json-schema 在脚本里拿到可解析的结果
- 能说清无头模式下权限怎么生效,以及为什么 CI 里推荐 --bare
- 能用 claude-code-action 或纯脚本两种方式,在 GitHub Actions 里自动评审 PR
- 能判断一个自动化需求该用 claude -p 还是 Agent SDK
团队五个人,每天十几个 PR。真正细看的评审只能顾上核心模块,其余的大多是扫一眼就点通过。结果上线后才发现:某个接口漏了 await,某个配置里明文写了测试环境的密码。
如果每个 PR 一提交,就有一个 Claude 先过一遍、把明显的问题写成评论,人类评审就能把精力留给真正需要判断的地方。这一课就做这件事:让 Claude Code 脱离交互界面,在脚本和 CI 里自动运行。
无头模式:claude -p
给 claude 加上 -p(全称 --print),它就不再打开交互界面,而是执行完一次任务、把结果打印到标准输出后退出,像 grep、jq 一样可以放进管道和脚本。成功时退出码为 0,失败时非 0,脚本可以据此判断。
它会读标准输入,所以最自然的用法是「把东西喂给它」:
# 把构建日志喂进去,把解释写进文件
cat build.log | claude -p "用三句话说明这次构建失败的根本原因" > why.txt
# 根据暂存区的改动生成一条中文提交信息
git diff --cached | claude -p "根据这段 diff 写一条符合 Conventional Commits 的提交信息,只输出这一行"
输出格式用 --output-format 控制:
| 格式 | 得到什么 | 适合 |
|---|---|---|
text(默认) |
纯文本 | 直接给人看、写进文件 |
json |
一个 JSON 对象,正文在 result,另有 session_id、费用估算 total_cost_usd 等元数据 |
脚本解析、记录花费 |
stream-json |
每行一个 JSON 事件,实时输出(需加 --verbose) |
自己做界面、实时展示进度 |
需要固定结构时,再加 --json-schema 传入一个 JSON Schema,结果会出现在 structured_output 字段里。比如把一批用户反馈分类:
cat feedback.txt | claude -p "把每条反馈归类为 bug、需求或咨询" \
--output-format json \
--json-schema '{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string"},"kind":{"type":"string","enum":["bug","需求","咨询"]}},"required":["text","kind"]}}},"required":["items"]}' \
| jq '.structured_output.items'
如果 Claude 拒绝了任务或没能产出合法结构,structured_output 字段会缺失,脚本使用前要先判断它在不在。
多轮任务可以用 --continue 接着最近一次对话,或者先从 JSON 里取出 session_id,再用 --resume 指定接哪一次。
无头模式下的权限
交互模式里,Claude 遇到需要批准的操作会停下来问你。无头模式没人可问,规则是这样的:
- 不设置权限模式时,
-p运行在 Manual 模式(也就是默认模式,v2.1.302 起明确如此),需要批准的调用一律被拒绝。读取工作目录里的文件这类本就不需要批准的操作照常进行。 - 用
--allowedTools预先放行,语法和权限规则一样(见第 3 课)。例如--allowedTools "Read,Bash(git diff *),Bash(git log *)"。注意Bash(git diff *)里星号前的空格不能省,否则git diff-index也会被匹配。 - 也可以用
--permission-mode设一个整体基线:acceptEdits自动接受文件编辑,dontAsk拒绝所有本该询问的调用,适合锁得很死的 CI。 - 给它设上限:
--max-turns限制轮数,--max-budget-usd限制估算花费,CI 里两个都建议加。
接入 GitHub Actions
官方提供了 anthropics/claude-code-action,它基于 Agent SDK,在 GitHub 的工作流里运行 Claude Code。最快的接入方式是在本地仓库里启动 claude,运行 /install-github-app:它会安装 Claude GitHub App、把凭据存成仓库 secret、推一个带工作流文件的分支并帮你打开 PR,合并即生效。前提是装好并登录了 GitHub CLI(gh auth login)、你对仓库有管理员权限,且仓库在 github.com 上。
想手动配置,就三步:安装 Claude GitHub App;在仓库 secret 里放 ANTHROPIC_API_KEY(API 计费),或用 claude setup-token 生成的 CLAUDE_CODE_OAUTH_TOKEN(用订阅额度);把工作流文件放进 .github/workflows/。
这个 Action 有两种工作方式,取决于你有没有写 prompt:
| 模式 | 触发条件 | 结果在哪 |
|---|---|---|
交互模式(不写 prompt) |
有人在 issue 或 PR 评论里 @claude | 在原 issue 或 PR 下回复评论 |
自动化模式(写了 prompt) |
工作流事件本身,如 PR 打开、定时任务 | 默认在工作流日志里,提示词要求且有发评论的工具时才会发到 PR |
其他常用参数:claude_args 透传任意命令行参数(如 --max-turns、--model、--allowedTools),settings 传入 settings JSON,trigger_phrase 改触发词。还在用 @beta 的旧工作流要迁到 @v1:去掉 mode,把 direct_prompt 改成 prompt,把 max_turns、model 这类选项挪进 claude_args。
实操:在 CI 里自动评审 PR
方案 A:官方 Action + 代码评审插件(推荐 GitHub 用户)
这是官方示例里的做法:安装 code-review 插件,在 PR 打开或更新时运行它的评审 Skill,把问题以行内评论的形式发在 PR 上。新建 .github/workflows/claude-review.yml:
name: Claude 代码评审
on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
jobs:
review:
runs-on: ubuntu-latest
timeout-minutes: 15 # 防止跑飞
permissions:
contents: read
pull-requests: read
issues: read
id-token: write # Action 用 GitHub App 认证时需要
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
plugin_marketplaces: "https://github.com/anthropics/claude-code.git"
plugins: "code-review@claude-code-plugins"
prompt: "/code-review:code-review --comment ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
claude_args: '--allowedTools "mcp__github_inline_comment__create_inline_comment"'
--comment 决定评审发到 PR 上,去掉它就只写进运行日志;claude_args 那一行不能删,Action 只有在 --allowedTools 点名时才会启动发行内评论的工具。草稿 PR、已关闭的 PR 和已有 Claude 评论的 PR 会被跳过。如果你连工作流都不想维护,官方还有一个托管的 Code Review 功能可以了解。
方案 B:纯脚本版(想完全掌控提示词,或用在其他 CI)
核心思路:让 Claude 只负责产出文字,发评论这一步交给脚本。Claude 手里没有任何写权限,即使 diff 里藏了恶意指令,它也做不了什么。
name: AI 评审(脚本版)
on:
pull_request:
types: [opened, synchronize]
permissions:
contents: read
pull-requests: write
concurrency:
group: ai-review-${{ github.event.pull_request.number }}
cancel-in-progress: true # 连续推送时只保留最新一次
jobs:
review:
# 只评审本仓库分支发起的 PR;fork 来的 PR 拿不到 secrets
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- name: 安装 Claude Code
run: |
curl -fsSL https://claude.ai/install.sh | bash
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: 生成评审并发评论
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
gh pr diff "$PR_NUMBER" > pr.diff
claude --bare -p "你是资深代码评审。下面是一个 PR 的 diff,请用中文列出:1. 可能的 bug 和安全问题(附文件和行号);2. 明显的性能问题;3. 缺失的测试。没有问题就回复「未发现明显问题」。不要逐行复述 diff。" \
--max-turns 5 --max-budget-usd 1 \
--output-format json < pr.diff > review.json
jq -r '.result' review.json > review.md
gh pr comment "$PR_NUMBER" --body-file review.md
几个设计上的讲究:
- PR 编号通过
env传进脚本,而不是把github.event.pull_request.title这类别人能控制的文字直接拼进run,避免命令注入。 --bare让 PR 里如果夹带了.claude/settings.json或.mcp.json,也不会被加载执行。--max-turns、--max-budget-usd、timeout-minutes、concurrency四道闸,防止一个 PR 烧掉一大笔钱。- 换成 GitLab CI 或公司内部的 Jenkins,只要把
gh pr diff和gh pr comment换成对应平台的命令,主体不用改。
$ curl -fsSL https://claude.ai/install.sh | bash# 安装输出省略$ gh pr diff 128 > pr.diff$ claude --bare -p "你是资深代码评审……" --max-turns 5 --output-format json < pr.diff > review.json$ jq -r '.result' review.json 1. src/api/order.ts:42 调用 fetchStock() 时缺少 await,库存校验会被跳过 2. config/test.yaml:7 明文写入了数据库密码,建议改为读取环境变量 3. 新增的 calcDiscount() 没有对应的单元测试$ gh pr comment 128 --body-file review.md# 输出为示意,实际内容取决于你的 diff 请为这个仓库添加一个 GitHub Actions 工作流,在 PR 打开和更新时用 claude -p 自动评审,要求:
- 文件放在 .github/workflows/ai-review.yml,参考「方案 B 纯脚本版」的结构:用 –bare,Claude 只输出文字,发评论由 gh pr comment 完成;
- 评审提示词结合这个仓库的技术栈来写(先读 package.json 或其他依赖文件判断),用中文输出,按「必须修 / 建议修 / 可以考虑」三级分类;
- 只对本仓库分支发起的 PR 运行,加上 timeout-minutes、concurrency、–max-turns 和 –max-budget-usd;
- 不要把任何 PR 标题、正文之类的外部文字直接拼进 run 脚本;
- 写完后告诉我需要在仓库 Settings 里添加哪个 secret,以及怎么开一个测试 PR 验证。不要替我提交或推送。
Agent SDK:需要更多控制时
claude -p 适合「一问一答」式的自动化。如果你要把 Claude Code 的能力嵌进自己的 Python 或 TypeScript 程序,比如做一个内部的「工单自动修复机器人」,需要在每次工具调用时自己决定放不放行、把过程实时显示在自己的界面上,就该用 Agent SDK。它提供和 Claude Code 相同的工具、智能体循环和上下文管理,并支持 Hooks、子智能体、MCP、权限和会话。
安装:Python 用 pip install claude-agent-sdk,TypeScript 用 npm install @anthropic-ai/claude-agent-sdk。最小的 Python 例子:
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def main():
async for message in query(
prompt="检查 utils/date.py 中可能导致崩溃的时区处理问题并修复",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Glob"],
permission_mode="acceptEdits",
),
):
if isinstance(message, ResultMessage):
print("完成:", message.subtype)
asyncio.run(main())
query 返回一个异步迭代器,Claude 每思考一步、每调用一次工具,你都会收到一条消息。认证用 Claude Console 的 API key;官方明确规定,未经许可,第三方开发者不能在自己的产品里让用户用 claude.ai 账号登录或使用其订阅额度。选择上可以这样记:脚本和 CI 用 claude -p,要做成产品或需要精细控制时用 SDK。
你在 CI 里运行 claude -p 「修复 lint 错误」,没有加任何权限相关参数。日志显示 Claude 想编辑文件,但最后什么都没改。最可能的原因是?
一个开源项目想用 Claude 自动评审外部贡献者的 PR。下面哪个做法风险最大?
动手清单
0/5参考来源
- Run Claude Code programmatically(Claude Code 官方文档 · 无头模式)
- Claude Code GitHub Actions(Claude Code 官方文档)
- CLI reference(Claude Code 官方文档)
- Agent SDK overview(Claude Code 官方文档)
- Agent SDK quickstart(Claude Code 官方文档)
- Set up Claude Code(Claude Code 官方文档 · 安装)
工具更新很快,若与官方文档不一致,以官方为准。
学完了吗?
标记完成会记录在本机浏览器,计入你的学习进度和连续天数。