一、问题:交互式 Claude 没法挂到流水线上
人工 Code Review 跟不上 PR 速度,交互式调用 Claude 又没法自动化,怎么办?我们团队前阵子被这事卡住------夜里合了十几条 PR,第二天早会一堆"谁来 Review"的尴尬。
顺便一提,我维护的雷达鸭 App(收录中国一人公司赚钱案例,华为应用市场 + 微信小程序,Uni-app + arkTS + UniCloud)攒到一定体量后,PR 审查负担也压上来了,这才逼着我去翻 Claude Code 的 Headless 模式。
核心转变一句话:把"人确认"换成"参数约束"。交互式下,工具要不要执行、文件要不要改,都是人按回车;Headless 下,所有决策都靠参数预先约束好,Claude 自己跑完输出结果。
最朴素的调用:
bash
# 最基本的 Headless 调用
claude -p "分析 src/ 目录的安全漏洞"
# 等价的完整形式
claude --print "分析 src/ 目录的安全漏洞"
-p 就是 --print 的短写,跑完直接退出,不进交互式 REPL。这就是无人值守的起点。
二、4 维参数:把交互式的"确认"翻译成约束
Headless 不是简单加个 -p,而是要把交互式里那些隐式约定全显式化。Claude Code 给了 4 个维度:
bash
# 维度一:输出格式
claude -p "列出主要问题" --output-format text # 纯文本,人看
claude -p "审查 PR" --output-format json # 结构化,机器解析
claude -p "分析代码" --output-format stream-json # 流式,实时监控
# 维度二:成本与规模护栏
claude -p "审查代码" --max-turns 5 # 最多 5 轮工具调用
claude -p "分析日志" --max-budget-usd 0.50 # 单次硬上限 0.5 美元
# 维度三:安全边界
claude -p "审查代码变更" --allowedTools "Read,Grep,Glob" # 只读,最安全
claude -p "生成文档" --disallowedTools "Bash" # 黑名单禁 Bash
claude -p "检查 git 历史" \
--allowedTools "Read,Grep,Glob,Bash(git log *),Bash(git diff *)" # 细粒度白名单
# 维度四:执行控制
claude -p "检查代码格式" --model claude-haiku-4-5
claude -p "安全漏洞分析" --model claude-sonnet-4-6
claude -p "代码审查" --model claude-sonnet-4-6 --fallback-model haiku # 主模型过载自动切
text 输出适合人看;json 给 CI 后续步骤消费;stream-json 一行一个事件,适合长任务实时盯。我们做 PR 审查统一用 json,因为后面要用 jq 提取 result 字段、用 total_cost_usd 算账。
json 输出长这样:
json
{
"type": "result",
"subtype": "success",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"is_error": false,
"duration_ms": 12345,
"num_turns": 5,
"total_cost_usd": 0.0342,
"usage": {
"input_tokens": 5000,
"output_tokens": 1500,
"cache_read_input_tokens": 3000
},
"result": "发现 2 个 Critical 问题:\n1. SQL 注入风险..."
}
session_id 留着后面会话管理用,total_cost_usd 直接是这次审查花了几美分。我们跑了两周,平均一条 PR 审查成本在 0.03~0.08 美元之间------比之前请同事加班喝的咖啡便宜多了。
三、Unix 管道:Claude 当命令行工具用
Headless 模式下 Claude 就是个标准 stdin/stdout 程序,能塞进任何 Unix 管道:
bash
# 分析日志文件
cat server.log | claude -p "找出所有 500 错误并总结根因"
# 分析 git 变更
git diff HEAD~1 | claude -p "总结这次提交的变更,格式为 Conventional Commits"
# Claude 分析 → jq 提取 → 发邮件
claude -p "检查是否有安全漏洞" --output-format json | \
jq -r '.result' | \
mail -s "安全扫描报告" security@company.com
# grep 预过滤 → Claude 深度分析
grep -r "TODO" src/ | claude -p "将这些 TODO 按优先级分类"
末尾那个例子是我们日常在用的------先 grep 把范围收窄,再喂给 Claude 分类,比让 Claude 自己 Grep 整个仓库省 token 也省时间。
四、GitHub Actions 两种接法
官方给了两条路。
方式一:官方 Action,起步首选
yaml
# .github/workflows/claude-review.yml
name: Claude PR Review
on:
pull_request:
types: [opened, synchronize, reopened]
issue_comment:
types: [created]
# 同一 PR 的新提交取消旧的运行------避免浪费
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
review:
runs-on: ubuntu-latest
if: |
github.event_name == 'pull_request' ||
contains(github.event.comment.body, '@claude')
permissions:
contents: read
pull-requests: write
issues: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 完整历史,用于 diff 分析
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
审查这个 PR 的代码变更,重点检查:
1. 安全漏洞(SQL 注入、XSS、敏感信息硬编码)
2. 未处理的错误情况和边界条件
3. 性能问题(N+1 查询、内存泄漏风险)
claude_args: >-
--allowedTools "Read,Grep,Glob"
--max-turns 10
--model claude-sonnet-4-6
十几行 YAML 跑起来,PR 一开自动评论。concurrency 那段必加------同一 PR 连推几个 commit,旧的运行会被自动取消,不然账单会很难看。
方式二:直接 CLI,精细控制
需要自己处理变更文件列表、自己写评论、自己卡门禁的场景,用直接 CLI:
yaml
name: Advanced AI Review
on:
pull_request:
types: [opened, synchronize]
paths: ['src/**'] # 只在 src 目录变更时运行
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
- name: Get changed files
id: changed
run: |
FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
echo "files=$(echo "$FILES" | tr '\n' ' ')" >> $GITHUB_OUTPUT
echo "count=$(echo "$FILES" | wc -l)" >> $GITHUB_OUTPUT
- name: Run Claude Review
id: review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
run: |
claude -p "审查以下 PR 变更文件:
${{ steps.changed.outputs.files }}
重点检查安全漏洞、逻辑错误、性能问题。
对每个问题给出文件名和行号。" \
--output-format json \
--max-turns 10 \
--max-budget-usd 0.50 \
--model claude-sonnet-4-6 \
--allowedTools "Read,Grep,Glob" > review.json
RESULT=$(jq -r '.result' review.json)
COST=$(jq -r '.total_cost_usd' review.json)
echo "cost=$COST" >> $GITHUB_OUTPUT
echo "result<<EOF" >> $GITHUB_OUTPUT
echo "$RESULT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Post Review Comment
uses: actions/github-script@v7
with:
script: |
const cost = '${{ steps.review.outputs.cost }}';
const result = `${{ steps.review.outputs.result }}`;
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## 🤖 Claude Code Review\n\n${result}\n\n---\n*Cost: $${cost} | Automated by Claude Code*`
});
- name: Gate on critical issues
run: |
RESULT="${{ steps.review.outputs.result }}"
if echo "$RESULT" | grep -qi "critical"; then
echo "::error::Critical issues found"
exit 1
fi
末尾那段 Gate on critical issues 是真有用的------评审里出现 critical 字样直接让 CI 失败,比让人盯着看评论靠谱。
两种方式的取舍:起步用方式一,几分钟接上;要卡门禁、要拿 total_cost_usd 做账、要把结果写到外部系统,就上方式二。我们团队最终留了方式二,因为要把每次审查成本塞进 Grafana。
五、多阶段 CI 管道:分析 → 修复 → 验证
单步审查只是开始。真正省事的是把 Claude 拆成多个 job,按阶段跑、按模型分级:
yaml
name: Multi-Stage AI Pipeline
on:
pull_request:
types: [opened, synchronize]
jobs:
# 阶段一:快速格式检查(用便宜的模型)
lint-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Format Check
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
run: |
npx @anthropic-ai/claude-code -p "检查 src/ 下的代码是否符合项目编码规范" \
--model claude-haiku-4-5 \
--max-turns 3 \
--max-budget-usd 0.05 \
--allowedTools "Read,Grep,Glob" \
--output-format text
# 阶段二:深度安全审查(用最强模型)
security-scan:
runs-on: ubuntu-latest
needs: lint-check # 格式检查通过后再做安全扫描
steps:
- uses: actions/checkout@v4
- name: Security Scan
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
run: |
npx @anthropic-ai/claude-code -p "深度安全扫描,重点检查 OWASP Top 10" \
--model claude-sonnet-4-6 \
--append-system-prompt "你是应用安全专家,只关注安全漏洞。" \
--max-turns 10 \
--max-budget-usd 1.00 \
--allowedTools "Read,Grep,Glob" \
--output-format json \
--json-schema '{"type":"object","properties":{"vulnerabilities":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"description":{"type":"string"},"file":{"type":"string"}},"required":["severity","description"]}}},"required":["vulnerabilities"]}' \
> security-report.json
阶段一用 Haiku(便宜、快),阶段二才上 Sonnet(贵、准)。needs: lint-check 让安全扫描等格式检查通过再跑------格式都过不了的 PR 没必要烧 Sonnet 的钱。这种"分级模型 + 阶段依赖"组合下来,我们一条 PR 的平均成本从单一 Sonnet 全量审查的 ~0.15 美元压到了 ~0.06 美元。
六、流式输出和会话管理
流式输出盯长任务
stream-json 一行一个事件,配 while read 能实时看 Claude 在干啥:
bash
claude -p "分析整个 src/ 目录的架构问题" \
--output-format stream-json \
--allowedTools "Read,Grep,Glob" | while read -r line; do
TYPE=$(echo "$line" | jq -r '.type // ""')
case "$TYPE" in
"system") echo "[INIT] 模型: $(echo "$line" | jq -r '.model // "unknown"')" ;;
"assistant") TEXT=$(echo "$line" | jq -r '.message.content[0].text // ""'); [ -n "$TEXT" ] && echo "[CLAUDE] $TEXT" ;;
"result") COST=$(echo "$line" | jq -r '.total_cost_usd // 0'); TURNS=$(echo "$line" | jq -r '.num_turns // 0'); echo "[DONE] 完成!$TURNS 轮,费用: \$$COST" ;;
esac
done
我们把这个管道接到 CI 日志里,长任务不再是黑盒等 5 分钟------能实时看到 Claude 读到哪个文件、下了什么结论。
会话管理跨步骤复用上下文
多阶段管道每个 job 是独立容器,默认上下文丢光。--resume 接 session_id 能把上一轮分析接上:
bash
# 第一次调用:分析代码结构
RESULT=$(claude -p "分析 src/ 的模块依赖关系" \
--output-format json \
--allowedTools "Read,Grep,Glob")
# 提取 session_id
SESSION_ID=$(echo "$RESULT" | jq -r '.session_id')
# 第二次调用:在上一轮分析的基础上继续
claude -p "基于刚才的分析,指出循环依赖问题" \
--resume "$SESSION_ID" \
--output-format text
# 继续当前目录最近的会话(不需要知道 session_id)
claude -p "继续刚才的分析" --continue
# 创建分支会话(保留原始会话不变)
claude -p "如果改用微服务架构呢?" --resume "$SESSION_ID" --fork-session
# 禁用会话持久化(适合完全无状态的 CI 场景)
claude -p "一次性分析" --no-session-persistence
--continue 不用记 session_id,直接续最近的;--fork-session 适合"假设性提问"------分支会话不影响主线。完全无状态的 CI 用 --no-session-persistence,跑完不落盘。
七、五层安全防线
让 Claude 半夜自己跑,最让人睡不着的就是"它会不会偷偷 rm -rf"。书里给的五层防线我们一层没少:
层一:最小权限 --------allowedTools 把工具收成白名单,审查任务只给 Read,Grep,Glob,连 Edit 都不给。
层二:Secrets 管理 ------API Key 一律走 ${{ secrets.ANTHROPIC_API_KEY }},绝不硬编码。硬编码会原样出现在 Actions 日志里,谁都能看。
层三:容器隔离------只读文件系统 + 无网络:
yaml
jobs:
review:
runs-on: ubuntu-latest
container:
image: node:20
options: --read-only --tmpfs /tmp --network none
--network none 直接断网,Claude 想外传数据也没出口。
层四:成本防护------路径过滤 + 并发限制 + 单次预算兜底:
yaml
on:
pull_request:
paths:
- 'src/**'
- '!src/**/*.test.*' # 排除测试文件
concurrency:
group: claude-${{ github.event.pull_request.number }}
cancel-in-progress: true
加上 --max-budget-usd 0.50,单次跑超 0.5 美元自动停。
层五:审计日志 ------JSON 输出天然带审计字段,tee 落盘:
bash
claude -p "任务" --output-format json | tee -a /var/log/claude-audit.jsonl
事后谁、什么时候、花了多少钱、跑了多少轮,全在日志里。
八、CI 环境变量与完整模板
CI 里这几个环境变量必设:
bash
ANTHROPIC_API_KEY=sk-ant-... # API 密钥
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 # 一个变量关掉自动更新、遥测、错误上报
DISABLE_AUTOUPDATER=1 # 禁用自动更新(CI 中必须)
DISABLE_TELEMETRY=1 # 禁用遥测
DISABLE_ERROR_REPORTING=1 # 禁用错误上报
BASH_DEFAULT_TIMEOUT_MS=120000 # Bash 默认超时 2 分钟
CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000 # 最大输出 token
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 这个一定要加------不然 CI 跑着跑着 Claude 自己开始检查更新,日志里一堆噪音。
一个能直接抄的完整模板:
yaml
- name: Run Claude Analysis
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
BASH_DEFAULT_TIMEOUT_MS: "300000"
run: |
npx @anthropic-ai/claude-code -p "$PROMPT" \
--output-format json \
--max-turns 10 \
--max-budget-usd 1.00 \
--allowedTools "Read,Grep,Glob" \
--model claude-sonnet-4-6 \
--fallback-model haiku \
--append-system-prompt "遵循项目 CLAUDE.md 中的审查规范。"
--fallback-model haiku 是个容易被忽略的小细节------主模型过载时自动切 Haiku,不至于让整条管道挂在 429 上。
九、判断
跑了两周,我们的结论是:Headless 模式不是"凑合用"的替代品,是 CI 集成的正解。交互式适合人盯着改代码,Headless 适合挂流水线------两个场景,两套约束逻辑。
反正我们以后的 PR 审查默认走 Claude + GitHub Actions,人只看 critical 项。半夜合的 PR,第二天早会前报告已经躺在评论里了。
关于作者:雷达鸭 App 独立开发者,10+ 年软件开发经验,软件设计师、人工智能应用工程师,专注鸿蒙 ArkTS + Web 前端,探索 AI 自动化。
版权声明:本文采用 MIT 协议开源,转载请注明出处。