<!-- title: 无头模式接CI造评审机器人 -->

## 概述

本教程教你用 Claude Code 的 **headless 无头模式**（`claude -p`）搭配 **GitHub Actions**，从零搭一个「PR 一开就自动评审、自动贴评论」的机器人。学完你能做到三件事：在脚本里非交互地驱动 Claude、把 `claude -p` 包成可复现的 CI 步骤、用官方 `claude-code-action` 让团队成员在评论区敲一句 `@claude` 就能召唤评审。适合已经有 CI 基础、想把 AI 评审常态化接入研发流水线的后端/DevOps 工程师。

---

## 前置条件

- **仓库权限**：你是目标 GitHub 仓库的 admin（要装 App、加 Secret、写 workflow）。
- **Claude Code v2.1 及以上**：本地能跑 `claude --version`。本文涉及 `--bare`、`--json-schema`、流式事件等较新特性，建议直接升到最新。
- **一把 `ANTHROPIC_API_KEY`**：在 [console.anthropic.com](https://console.anthropic.com) 申请，注意别提交进仓库。
- **基础栈**：看得懂 YAML、会在终端管道命令（`|`、`>`）、装了 `jq`（用于解析 JSON 输出）。

---

## 第 1 步：吃透 headless 模式的四个开关

交互式的 `claude` 是聊天框；headless 模式把它变成一个**可以管道喂入、可以结构化吐出**的命令行工具。加 `-p`（或 `--print`）即进入非交互模式：

```bash
claude -p "What does the auth module do?"
```

真正决定 CI 行为的是它后面那几个开关，先记住这张表：

| 开关 | 作用 | CI 里为什么关键 |
| --- | --- | --- |
| `--bare` | 跳过自动加载 hooks/skills/plugins/MCP/CLAUDE.md | 在干净 runner 上保证「每台机器结果一致」，将来会成为 `-p` 的默认 |
| `--output-format` | `text` / `json` / `stream-json` | 脚本要拿 session_id、cost 就得用 `json` |
| `--allowedTools` | 自动批准工具，用权限规则语法 | 锁死能干什么，比如只允许 `Bash(git diff *)` |
| `--permission-mode` | `dontAsk` / `acceptEdits` | `dontAsk` 适合锁定 CI：不在白名单里的一律拒绝 |

一条把四件事都用上的「CI 体感」命令长这样：

```bash
claude --bare -p "Review this diff for bugs" \
  --output-format json \
  --allowedTools "Read" \
  --permission-mode dontAsk
```

**预期结果**：终端打印一段 JSON，里面有 `result`（正文）、`session_id`、`total_cost_usd`。整个过程不弹任何权限提示——因为 `dontAsk` + 只开了 `Read`，碰任何写操作它会直接中止。

**常见问题**：
- *报「需要登录 / 找不到凭证」？* bare 模式跳过 OAuth 和 keychain，**必须**用环境变量 `ANTHROPIC_API_KEY`，或在 `--settings` 里配 `apiKeyHelper`。
- *想让它读项目里的 `CLAUDE.md`？* bare 模式默认不读，需要用 `--append-system-prompt-file CLAUDE.md` 显式喂进去。

---

## 第 2 步：本地先把评审跑通，再上云

上 CI 之前先在本地把管道打通，能省下大量「到底哪一步炸了」的排查时间。核心思路是把 `git diff` 通过 **stdin** 喂给 Claude，这样它连 Bash 读文件的权限都不用给：

```bash
git diff main | claude -p \
  'You are a senior reviewer. For each real problem in this diff,
   print `file:line` then a one-line issue. Return nothing if clean.' \
  --output-format json | jq -r '.result'
```

为什么用管道而不是让 Claude 自己 `git diff`？因为管道意味着 **Claude 不需要 Bash 权限就能拿到 diff**，权限面缩到最小——这正是 CI 安全的第一原则。

把上面这条命令存成 `package.json` 脚本，团队任何人本地都能复现同一套评审口径：

```json
{
  "scripts": {
    "review:local": "git diff main | claude -p \"you are a typo linter. for each typo in this diff, report filename:line on one line and the issue on the next. return nothing else.\""
  }
}
```

**预期结果**：跑 `npm run review:local`，要么空输出（干净），要么逐行列出问题。

**常见问题**：
- *diff 太大被截断？* 自 v2.1.128 起，piped stdin 上限 **10MB**。超了会非零退出。解法是把大 diff 写进文件，prompt 里引用路径，让 Claude 用 `Read` 分段读。

---

## 第 3 步：装 GitHub App、配 Secret

`@claude` 之所以能在 PR 里「说话」，靠的是装在仓库上的 **Claude GitHub App**。最快的方式是在本地 Claude Code 终端里跑：

```
/install-github-app
```

它会交互式地帮你：把 App 装到仓库 → 生成 workflow 骨架 → 把 API key 写进仓库 Secret（名字固定为 `ANTHROPIC_API_KEY`）。注意：

- 你必须是仓库 **admin** 才能装 App、加 Secret。
- App 会申请 Contents / Issues / Pull requests 的**读写**权限（要能改文件、回 issue、推 commit）。
- 这条快速通道**只对直连 Claude API 的用户**开放；用 Amazon Bedrock 或 Vertex 的走手动 OIDC 配置。

手动装的话：去 [github.com/apps/claude](https://github.com/apps/claude) 装 App → Settings → Secrets 里加 `ANTHROPIC_API_KEY` → 把官方 [`examples/claude.yml`](https://github.com/anthropics/claude-code-action/blob/main/examples/claude.yml) 拷进 `.github/workflows/`。

**常见问题**：
- *评论里 `@claude` 没反应？* 九成是用了 `/claude`（斜杠）而非 `@claude`；或者 App 没装、workflow 没启用、Secret 没配。

---

## 第 4 步：第一条 workflow——响应 `@claude` 召唤

装好 App 后，最小可用的 workflow 就是「监听评论、识别 `@claude`、交给 action」：

```yaml
name: Claude Code
on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]

jobs:
  claude:
    runs-on: ubuntu-latest
    steps:
      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
```

提交它，然后在任意 PR 评论里写 `@claude 这个函数有竞态吗？`，几秒后 Claude 就会以机器人身份回评。

> **升级注意（v1.0 GA 有 breaking change）**：如果你用的是旧的 `@beta`，必须改：`@beta`→`@v1`、删掉 `mode:`、`direct_prompt`→`prompt`、`max_turns`/`model`/`allowed_tools` 等全部搬进 `claude_args`。下面是迁移对照：

| 旧 beta 输入 | 新 v1 输入 |
| --- | --- |
| `mode: "tag"` | （移除，自动检测） |
| `direct_prompt` | `prompt` |
| `custom_instructions` | `claude_args: --append-system-prompt` |
| `max_turns` / `model` / `allowed_tools` | `claude_args: --max-turns` / `--model` / `--allowedTools` |

**常见问题**：
- *Claude 自己 commit 后 CI 没触发？* 因为 Actions 用户触发的 push 默认不跑后续 workflow。改用 **GitHub App**（或自建 App）身份提交即可，App 身份的 push 会正常触发 CI。

---

## 第 5 步：升级成「PR 一开就评审」的机器人

被动等 `@claude` 还不够省事。真正的机器人是 **PR 一开 / 一更新就自动跑评审并贴评论**。把触发器换成 `pull_request`，用 `prompt` 驱动：

```yaml
name: PR Auto Review
on:
  pull_request:
    types: [opened, synchronize]

permissions:
  contents: read
  pull-requests: write   # 要能贴评审评论

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0   # 让 Claude 能看到完整 diff 上下文

      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          prompt: |
            Review the changes in this pull request.
            Focus on: correctness bugs, security, and obvious logic errors.
            Post your findings as a review comment. Be concise.
          claude_args: |
            --max-turns 10
            --model claude-sonnet-4-6
            --allowedTools "Read,Bash(git diff *),Bash(git log *)"
```

这里有几个值得抠的点：

- `actions/checkout` + `fetch-depth: 0`：浅克隆拿不到 `main` 分支做 diff，必须拉完整历史。
- `claude_args` 直接透传给底层 `claude`，所以第 1、2 步学到的 `--allowedTools`、`--permission-mode`、`--bare` 都能在这用。
- `--max-turns 10` 是**成本刹车**：防止模型在一个 PR 上无限探索烧钱。

**预期结果**：开 PR → Actions 跑起来 → 约 30–90 秒后 PR 上出现一条机器人的评审评论，列出 0~N 条问题。

**常见问题**：
- *评审跑超时？* 设 workflow 级 `timeout-minutes`，并用 `--max-turns` 收紧。
- *多个 PR 同时来炸 runner？* 加并发控制：
  ```yaml
  concurrency:
    group: pr-review-${{ github.event.pull_request.number }}
    cancel-in-progress: true
  ```

---

## 第 6 步：用 Skill + CLAUDE.md 固化团队评审口径

每个项目对「什么叫问题」定义不同。两件工具能让评审稳定下来：

**1. 仓库根的 `CLAUDE.md`** 写死红线，例如「禁止提交 `console.log`、禁止 any 类型、所有新 API 必须有单测」。Claude 评审/改码时都会读它。

**2. 把评审流程做成 Skill**，放在 `.claude/skills/code-review/SKILL.md`，然后在 workflow 里用 `prompt` 调用 `/code-review`：

```yaml
      - uses: actions/checkout@v4          # 跑 skill 前必须 checkout
      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          prompt: "/code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
```

官方甚至提供了现成的 `code-review` 插件，**每个 PR 自动评审、无需 `@claude` 触发**：

```yaml
      - 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 ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
```

**常见问题**：
- *Skill 没生效？* 仓库内 Skill 必须**先 `actions/checkout`** action 才能读到 `.claude/skills/`；插件 Skill 要先在 `plugin_marketplaces` + `plugins` 里装好。

---

## 第 7 步：结构化输出，让评审结果可被下游消费

纯文本评论是给人看的；如果你想拿评审结果**自动打 label、卡 merge、汇总到日报**，就要让 Claude 吐结构化数据。`--output-format json` + `--json-schema` 做到这件事：

```bash
claude -p "Analyze this PR diff for severity" \
  --output-format json \
  --json-schema '{
    "type":"object",
    "properties":{
      "severity":{"type":"string","enum":["blocker","warning","nit"]},
      "issues":{"type":"array","items":{"type":"object",
        "properties":{"file":{"type":"string"},"line":{"type":"integer"},
        "problem":{"type":"string"}},
        "required":["file","line","problem"]}}
    },
    "required":["severity","issues"]
  }' | jq '.structured_output'
```

结果落在 `structured_output` 字段，是一个严格符合 schema 的对象。后续脚本就能 `jq -r '.structured_output.severity'` 判断要不要给 PR 打 `do-not-merge` 标签。

此外，`json` 输出还带 `total_cost_usd` 和按模型拆分的费用——可以直接喂进你的成本看板，不用再去翻用量页。

**常见问题**：
- *schema 校验失败反复重试？* 把 schema 写严（枚举、required 列全），别留模糊字段；模型越明确，单次出对的概率越高。

---

## 第 8 步：成本、并发与失控防护

云端跑 AI 是会真金白银烧钱的，三道闸必须拉上：

1. **轮次闸**：`--max-turns`（默认 10）。一个 PR 最多让模型转几圈，超了强制停。
2. **超时闸**：workflow 级 `timeout-minutes: 10`，挡住卡死的 agent。
3. **并发闸**：`concurrency` 限制同一 PR 的重复评审，避免「推一下就重跑一遍」。

还有个容易踩的坑：**后台任务**。如果某次 `claude -p` 里 Claude 启动了 dev server 之类的后台 Bash 任务，Claude 返回后这个 shell 会被**约 5 秒后终止**（v2.1.163 之前会无限挂住进程，导致 CI 永不结束）。后台子代理/工作流则会被等到完成，上限默认 10 分钟，可用环境变量 `CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS` 调整、设 `0` 表示不限。

---

## 完整实战：开箱即用的 PR 评审机器人

把前面八步捏成一个文件 `.github/workflows/pr-review.yml`，提交即用。它会：开/更新 PR 时自动评审、按 schema 出结构化结果、严重程度达 blocker 就打标签、整个流程受并发与超时保护。

```yaml
name: PR Auto Review

on:
  pull_request:
    types: [opened, synchronize]

permissions:
  contents: read
  pull-requests: write

concurrency:
  group: pr-review-${{ github.event.pull_request.number }}
  cancel-in-progress: true

jobs:
  review:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Run Claude review
        id: claude
        uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          prompt: |
            Review this PR. Output JSON via your schema.
            Post a human-readable summary as a review comment.
          claude_args: |
            --max-turns 10
            --model claude-sonnet-4-6
            --allowedTools "Read,Bash(git diff *),Bash(git log *)"
            --append-system-prompt-file CLAUDE.md
            --output-format json

      - name: Label blockers
        if: steps.claude.outputs.structured_output.severity == 'blocker'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh pr edit ${{ github.event.pull_request.number }} \
            --add-label "do-not-merge"
          gh pr review ${{ github.event.pull_request.number }} \
            --request-changes --body "Claude 发现 blocker，请处理后再合。"
```

跑起来的效果：PR 一开 → runner checkout 全历史 → Claude 读 diff + `CLAUDE.md` 红线 → 评审评论自动贴上 → 若是 blocker，PR 被打 `do-not-merge` 并 request changes。整个过程无人值守，且每一步都可复现、可追溯。

---

## 工作流程图

```mermaid
flowchart TD
    A[开发者开/更新 PR] --> B[GitHub 触发 workflow]
    B --> C[Actions Runner 启动]
    C --> D[checkout 全历史]
    D --> E[claude-code-action@v1]
    E --> F[claude -p headless 模式]
    F --> G[读 diff + CLAUDE.md 红线]
    G --> H{--output-format json}
    H --> I[result 评审正文]
    H --> J[structured_output 严重程度]
    I --> K[贴评审评论到 PR]
    J --> L{severity == blocker?}
    L -- 是 --> M[打 do-not-merge 标签<br/>request changes]
    L -- 否 --> N[流程结束]
    M --> N
    K --> N
```

---

## 延伸阅读

- **[Run Claude Code programmatically](https://code.claude.com/docs/en/headless)** — 官方 headless 模式完整参考，含 Agent SDK 的 Python/TypeScript 封装。
- **[Claude Code GitHub Actions](https://code.claude.com/docs/en/github-actions)** — 官方 action 的全部参数、Bedrock/Vertex 接法、安全文档链接。
- **[anthropics/claude-code-action](https://github.com/anthropics/claude-code-action)** — action 源码与 examples 目录，最佳的学习样本。
- **[Hooks reference](https://code.claude.com/docs/en/hooks)** — 想在本地评审前后再插一道护栏（比如 PostToolUse 自动跑测试），从这里入手。
- **[GitHub Code Review](https://code.claude.com/docs/en/code-review)** — 不想自己写 workflow？官方的免触发自动评审服务。

---

## 训练营适用性评估

- **适合类型**：DevOps / 平台工程 / 后端方向的进阶训练营；也可作为「AI 研发效能」主题的实战课。要求学员已熟悉 Git 与 GitHub Actions 基础。
- **教学时长**：建议 **3–4 学时**。1 学时讲透 headless 四开关 + 本地管道（第 1–2 步）；1 学时搭 App/Secret 与最小 workflow（第 3–4 步）；1 学时升级为自动评审机器人并调通结构化输出（第 5–7 步）；剩余时间做成本治理与排错演练。
- **考核点**：能独立产出一个开 PR 即评审、blocker 自动卡合并的 workflow，并能解释 `--bare`、`--allowedTools`、`concurrency` 各自挡住了哪类风险。
- **延伸作业**：把同一套管道接到 GitLab CI/CD，或改造成「定时巡检 main 分支、出日报」的 cron 机器人（`on: schedule`）。

---

## 参考来源

1. [Run Claude Code programmatically（官方 headless 文档）](https://code.claude.com/docs/en/headless) — `-p`/`--bare`/`--output-format`/`--json-schema` 等核心参数与流式事件的权威说明。
2. [Claude Code GitHub Actions（官方文档）](https://code.claude.com/docs/en/github-actions) — `claude-code-action@v1` 的安装、参数、v1.0 GA 迁移与 Bedrock/Vertex 接法。
3. [anthropics/claude-code-action（官方仓库）](https://github.com/anthropics/claude-code-action) — action 源码、examples/claude.yml 样例与安全文档。
4. [Run Claude Code in CI（ClaudeFolio 实践指南）](https://claudefolio.com/guides/claude-code-in-ci-headless-sdk-github-actions) — headless CLI、Agent SDK、GitHub Actions 三种入口的横向对比与选型建议。
5. [Hooks reference（官方钩子参考）](https://code.claude.com/docs/en/hooks) — 配合本地评审做 PreToolUse/PostToolUse 护栏的事件与 schema 参考。
ent Reference — cc-wrap (badlogic)](https://github.com/badlogic/cc-wrap/blob/main/docs/claude-stream-json.md) — `stream-json` 全事件类型与字段的最全第三方参考
4. [Using Claude Code Programmatically — JacobFV Gist](https://gist.github.com/JacobFV/2c4a75bc6a835d2c1f6c863cfcbdfa5a) — `-p`/`--output-format` 无头管道的实战指南
5. [Claude Code Permission Modes: which one should your CI use? — backgroundclaude](https://backgroundclaude.com/blog/permission-modes) — CI 场景下 `auto`/`dontAsk`/`bypassPermissions` 选型决策框架

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
