预计完成时间:30 分钟 | 难度:中级 | 类型:实践
在本课结束时,你将在自己的仓库中部署一个真实工作的 AI 驱动工作流:
agent:analyze 标签这是你从"看懂别人的工作流"到"自己的仓库跑 AI"的关键一步。
部署完成后,你的仓库行为如下:
在开始写 YAML 之前,需要理解工作流和 AI 之间如何协作。sandcastle 和 course-video-manager 都遵循同一条设计铁律:
具体的"合同条款":
| 方向 | 机制 | 示例 |
|---|---|---|
| 输入 → AI | 环境变量 + 预获取的上下文 | ISSUE_NUMBER=42、ISSUE_TITLE=登录系统、已 checkout 的代码仓库 |
| AI → 输出 | 写入约定文件到 OUTPUT_DIR |
analysis.md(分析报告)、failure_reason.txt(失败原因) |
| 工作流 → GitHub | 读取输出文件,调用 gh CLI 操作 |
gh issue comment "$N" --body-file analysis.md |
Claude Code 在 CI 中运行需要一个认证凭证——这个 token 来自 Anthropic(Claude 的开发商),而不是 GitHub。完整链路分两段:
在你的本地终端执行:
claude login
这会弹出浏览器,跳转到 Anthropic 的 OAuth 授权页面。授权完成后,Claude Code 将 token 保存在本地。用以下命令查看这个 token:
# macOS / Linux
cat ~/.claude/credentials.json
# Windows(PowerShell)
Get-Content "$env:USERPROFILE\.claude\credentials.json"
复制输出中的 OAuth token 值。
CLAUDE_CODE_OAUTH_TOKEN~/.claude/credentials.json 中复制的 tokenCLAUDE_CODE_OAUTH_TOKEN 不是 GitHub Personal Access Token,不能在 github.com/settings/tokens 中生成。那是 GitHub 自己的 token 页面,和 Anthropic / Claude 无关。这里你只是把 Anthropic 颁发的 token"存进"GitHub 的保险箱(Secret),来源和存放是两回事。
下面是完整的工作流文件。先通读一遍,然后我们逐段拆解:
# .github/workflows/agent-analyze.yml
name: Agent Analyze Issue
on:
issues:
types: [labeled]
jobs:
analyze:
if: github.event.label.name == 'agent:analyze'
runs-on: ubuntu-latest
timeout-minutes: 10
concurrency:
group: agent-analyze-issue-${{ github.event.issue.number }}
cancel-in-progress: false
permissions:
issues: write
contents: read
env:
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_TITLE: ${{ github.event.issue.title }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
steps:
# ── 阶段 1: 标签转换 ──
- name: Transition to in-progress
run: |
gh issue edit "$ISSUE_NUMBER" --remove-label "agent:analyze" || true
gh issue edit "$ISSUE_NUMBER" --remove-label "agent:blocked" || true
gh issue edit "$ISSUE_NUMBER" --add-label "agent:in-progress"
# ── 阶段 2: 准备环境 ──
- uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
# ── 阶段 3: 获取上下文 ──
- name: Fetch issue body
run: |
gh issue view "$ISSUE_NUMBER" --json title,body,labels \
> "$RUNNER_TEMP/issue-context.json"
# ── 阶段 4: AI 分析 ──
- name: Run AI analysis
id: analyze
env:
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
run: |
claude -p "$(cat <<'PROMPT'
你是一个软件架构分析助手。下面是一个 GitHub Issue 的 JSON 数据。
请阅读这个 Issue 并用中文做以下分析:
1. **需求理解**:用 2-3 句话总结 Issue 想解决的问题
2. **技术方案**:提出 1-2 个可行的技术实现思路
3. **潜在风险**:指出实现中需要注意的 1-3 个风险点
4. **建议的下一步**:给出具体的行动建议
请直接输出分析结果,不要做其他操作。
PROMPT
)" --output-format text --allowedTools Read,Grep,Glob \
> "$RUNNER_TEMP/analysis.md"
# ── 阶段 5: 发布结果 ──
- name: Post analysis as comment
if: success()
run: |
{
echo "## 🤖 AI 分析结果"
echo
echo "> 由 Claude Code 自动生成 | Issue #$ISSUE_NUMBER"
echo
cat "$RUNNER_TEMP/analysis.md"
} | gh issue comment "$ISSUE_NUMBER" --body-file -
# ── 阶段 6: 标签收尾 ──
- name: Mark analyzed
if: success()
run: |
gh issue edit "$ISSUE_NUMBER" --remove-label "agent:in-progress" || true
gh issue edit "$ISSUE_NUMBER" --add-label "agent:analyzed"
- name: Mark blocked on failure
if: failure()
run: |
gh issue edit "$ISSUE_NUMBER" --add-label "agent:blocked" || true
gh issue edit "$ISSUE_NUMBER" --remove-label "agent:in-progress" || true
gh issue comment "$ISSUE_NUMBER" \
--body "❌ AI 分析失败。请检查 [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) 的日志。修复后重新添加 \`agent:analyze\` 标签重试。"
- name: Transition to in-progress
run: |
gh issue edit "$ISSUE_NUMBER" --remove-label "agent:analyze" || true
gh issue edit "$ISSUE_NUMBER" --remove-label "agent:blocked" || true
gh issue edit "$ISSUE_NUMBER" --add-label "agent:in-progress"
这是你在第 2 课学到的状态机模式。不同的是这里用了 shell 层的 || true 来处理标签可能不存在的情况——如果 Issue 上没有 agent:blocked,gh issue edit --remove-label 会报错退出(非零状态码),|| true 让它"忽略这个错误,继续执行"。
agent:in-progress 在这里既是状态标记,也是一把锁。如果有人手滑同时加了两遍 agent:analyze,concurrency 会让第二次运行排队等待(因为 cancel-in-progress: false)。
- uses: actions/checkout@v4
with:
fetch-depth: 1 # 只拉最新 commit,分析 Issue 不需要完整历史
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
三个标准步骤。注意 fetch-depth: 1 —— AI 分析 Issue 不需要 git 历史,拉最少就够了(回顾第 3 课的 fetch-depth 讨论)。
npm install -g @anthropic-ai/claude-code 全局安装 Claude Code CLI,之后就可以直接执行 claude 命令。
- name: Fetch issue body
run: |
gh issue view "$ISSUE_NUMBER" --json title,body,labels \
> "$RUNNER_TEMP/issue-context.json"
用 gh issue view --json 把 Issue 数据导出为 JSON 文件。这样做的好处是:AI 可以在后续步骤中读取这个文件获取完整上下文,而不是依赖可能截断的环境变量。
$RUNNER_TEMP 是 GitHub Actions 提供的临时目录,工作流结束后自动清理。
- name: Run AI analysis
id: analyze
env:
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
run: |
claude -p "$(cat <<'PROMPT'
你是一个软件架构分析助手。下面是一个 GitHub Issue 的 JSON 数据。
请阅读这个 Issue 并用中文做以下分析:
1. **需求理解**:用 2-3 句话总结 Issue 想解决的问题
2. **技术方案**:提出 1-2 个可行的技术实现思路
3. **潜在风险**:指出实现中需要注意的 1-3 个风险点
4. **建议的下一步**:给出具体的行动建议
请直接输出分析结果,不要做其他操作。
PROMPT
)" --output-format text --allowedTools Read,Grep,Glob \
> "$RUNNER_TEMP/analysis.md"
这一段的要点:
| 元素 | 含义 |
|---|---|
claude -p "..." |
以非交互模式运行 Claude Code,-p = print(直接输出结果)。这是 CI 中使用 Claude Code 的标准方式。 |
cat <<'PROMPT' ... PROMPT |
heredoc 语法:将多行文本作为字符串传给 cat,再被 $() 捕获为 claude -p 的参数。'PROMPT' 加引号防止 shell 在 heredoc 内部做变量展开。 |
--output-format text |
输出纯文本(默认可能是流式 JSON),方便存入文件。 |
--allowedTools Read,Grep,Glob |
限制 Claude Code 只能做只读操作——读文件、搜索、列出文件。不能写代码、不能执行命令。这是安全保障。 |
> "$RUNNER_TEMP/analysis.md" |
AI 输出重定向到文件。这就是 Agent-Runner 契约中的"输出文件"。 |
--allowedTools 是本课的安全边界。只给 AI 读权限,确保它不会意外(或被恶意 Issue 诱导)修改代码。后面的课会逐步放开权限——但先从只读开始,建立信心。
- name: Post analysis as comment
if: success()
run: |
{
echo "## 🤖 AI 分析结果"
echo
echo "> 由 Claude Code 自动生成 | Issue #$ISSUE_NUMBER"
echo
cat "$RUNNER_TEMP/analysis.md"
} | gh issue comment "$ISSUE_NUMBER" --body-file -
这里体现了 Agent-Runner 契约的精髓:AI 只输出文件,工作流负责发布。{ ... } | gh issue comment --body-file - 把 AI 的分析结果加上标题头,通过管道传给 gh CLI 发布。AI 从头到尾没有碰过 GitHub token。
# 成功路径
- name: Mark analyzed
if: success()
run: |
gh issue edit "$ISSUE_NUMBER" --remove-label "agent:in-progress" || true
gh issue edit "$ISSUE_NUMBER" --add-label "agent:analyzed"
# 失败路径
- name: Mark blocked on failure
if: failure()
run: |
gh issue edit "$ISSUE_NUMBER" --add-label "agent:blocked" || true
gh issue edit "$ISSUE_NUMBER" --remove-label "agent:in-progress" || true
gh issue comment "$ISSUE_NUMBER" --body "❌ ..."
状态机收尾——第 2 课的模式在这里完整落地:
注意 agent:in-progress 两个分支都会移除——
always() 一步完成的,但拆成两个分支让你更清楚地看到成功和失败两条路径。
.github/workflows/agent-analyze.ymlCLAUDE_CODE_OAUTH_TOKEN secret 已创建(第一步)agent:analyze、agent:in-progress、agent:analyzed、agent:blockedagent:analyze 标签1. Agent-Runner 契约中,AI 代理不能做什么?
2. --allowedTools Read,Grep,Glob 的作用是什么?
3. 为什么 gh issue edit --remove-label 后面加 || true?
| 新知识 | 具体内容 |
|---|---|
| Agent-Runner 契约 | AI 通过环境变量接输入 + 写入文件输出 → 工作流读取文件操作 GitHub。这层"接缝"让系统可测试、可替换。 |
| Claude Code in CI | npm install -g @anthropic-ai/claude-code 安装 → claude -p "..." 非交互执行 → --allowedTools 限制权限。 |
| 凭证管理 | CLAUDE_CODE_OAUTH_TOKEN 存在 GitHub Secrets 中,通过 ${{ secrets... }} 传入环境变量。 |
| 输出收集模式 | AI 输出 → $RUNNER_TEMP/analysis.md → gh issue comment --body-file - 发布。 |
| 安全渐进 | 从只读权限(--allowedTools Read,Grep,Glob)开始,建立信心后再逐步放开。 |
Write 和 Edit 权限,让它可以修改代码--print、--allowedTools、--output-format 等 CLI 参数的完整说明