🤖 第四课:AI 分析 Issue — 第一个 AI 驱动的工作流

预计完成时间:30 分钟  |  难度:中级  |  类型:实践

📌 前置要求:本课假设你已理解 第 1 课 的标签触发、第 2 课 的状态机,以及 第 3 课 的 PAT 概念。

本课目标

在本课结束时,你将在自己的仓库中部署一个真实工作的 AI 驱动工作流:

这是你从"看懂别人的工作流"到"自己的仓库跑 AI"的关键一步


你会看到什么

部署完成后,你的仓库行为如下:

┌──────────────────────────────────────────────────┐ │ 1. 你在 Issue 上添加 agent:analyze 标签 │ └─────────────────┬────────────────────────────────┘ │ ┌─────────────────▼────────────────────────────────┐ │ 2. GitHub Actions 自动触发(几秒内) │ └─────────────────┬────────────────────────────────┘ │ ┌─────────────────▼────────────────────────────────┐ │ 3. Claude Code 读取 Issue 内容 │ │ 分析需求、拆解任务、提出技术方案 │ └─────────────────┬────────────────────────────────┘ │ ┌─────────────────▼────────────────────────────────┐ │ 4. 分析结果自动发布为 Issue 评论 │ │ 标签从 agent:analyze 变为 agent:analyzed │ └──────────────────────────────────────────────────┘

新概念:Agent-Runner 契约

在开始写 YAML 之前,需要理解工作流和 AI 之间如何协作。sandcastle 和 course-video-manager 都遵循同一条设计铁律

🔑 核心原则:工作流(orchestrator)拥有所有 GitHub 写操作——贴标签、发评论、推代码、开关 Issue。AI 代理只输出文件。这个"接缝"让 AI 跑腿可以任意替换,也让系统可以测试。

具体的"合同条款":

方向机制示例
输入 → AI 环境变量 + 预获取的上下文 ISSUE_NUMBER=42ISSUE_TITLE=登录系统、已 checkout 的代码仓库
AI → 输出 写入约定文件到 OUTPUT_DIR analysis.md(分析报告)、failure_reason.txt(失败原因)
工作流 → GitHub 读取输出文件,调用 gh CLI 操作 gh issue comment "$N" --body-file analysis.md
💡 为什么这样设计?AI 可能产生幻觉、格式错误或中途崩溃。如果 AI 直接操作 GitHub 而崩溃,仓库状态就乱了。把"输出"和"生效"分离,工作流可以先验证输出再执行,失败了也有清晰的回滚路径。

第一步:创建 Secret

Claude Code 在 CI 中运行需要一个认证凭证——这个 token 来自 Anthropic(Claude 的开发商),而不是 GitHub。完整链路分两段:

1-A:获取 Claude Code OAuth Token

在你的本地终端执行:

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 值。

📝 备选:也可以在 Anthropic Console 生成 API key,但 OAuth token 更适合 Claude Code 在 CI 中的使用场景。

1-B:存入 GitHub Repository Secret

  1. 进入你的仓库 → SettingsSecrets and variablesActions
  2. 点击 New repository secret
  3. Name: CLAUDE_CODE_OAUTH_TOKEN
  4. Value: 粘贴刚才从 ~/.claude/credentials.json 中复制的 token
  5. 点击 Add secret
⚠️ 常见误区:CLAUDE_CODE_OAUTH_TOKEN 不是 GitHub Personal Access Token,不能在 github.com/settings/tokens 中生成。那是 GitHub 自己的 token 页面,和 Anthropic / Claude 无关。这里你只是把 Anthropic 颁发的 token"存进"GitHub 的保险箱(Secret),来源和存放是两回事。

第二步:理解工作流 YAML

下面是完整的工作流文件。先通读一遍,然后我们逐段拆解:

# .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\` 标签重试。"

逐段拆解

阶段 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 课学到的状态机模式。不同的是这里用了 shell 层的 || true 来处理标签可能不存在的情况——如果 Issue 上没有 agent:blockedgh issue edit --remove-label 会报错退出(非零状态码),|| true 让它"忽略这个错误,继续执行"。

📎 回顾:agent:in-progress 在这里既是状态标记,也是一把。如果有人手滑同时加了两遍 agent:analyzeconcurrency 会让第二次运行排队等待(因为 cancel-in-progress: false)。

阶段 2:环境准备

- 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 命令。

阶段 3:获取上下文

- 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 提供的临时目录,工作流结束后自动清理。

阶段 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"

这一段的要点:

元素含义
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 诱导)修改代码。后面的课会逐步放开权限——但先从只读开始,建立信心。

阶段 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 -

这里体现了 Agent-Runner 契约的精髓:AI 只输出文件,工作流负责发布。{ ... } | gh issue comment --body-file - 把 AI 的分析结果加上标题头,通过管道传给 gh CLI 发布。AI 从头到尾没有碰过 GitHub token。

阶段 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 "❌ ..."

状态机收尾——第 2 课的模式在这里完整落地:

agent:analyze → agent:in-progress → agent:analyzed (✅ 成功) → agent:blocked (❌ 失败)

注意 agent:in-progress 两个分支都会移除——

失败路径用独立 step 确保锁不会被遗留。在 sandcastle 中这是用 always() 一步完成的,但拆成两个分支让你更清楚地看到成功和失败两条路径。

第三步:部署到你的仓库

  1. 在你的仓库中创建 .github/workflows/agent-analyze.yml
  2. 将上面完整的 YAML 内容粘贴进去
  3. 确认 CLAUDE_CODE_OAUTH_TOKEN secret 已创建(第一步)
  4. 预先创建三个标签:agent:analyzeagent:in-progressagent:analyzedagent:blocked
  5. 提交并推送到默认分支
  6. 创建一个测试 Issue,添加 agent:analyze 标签
  7. 观察 Actions 面板 → 等待完成 → 查看 Issue 评论
🎉 如果看到 AI 的评论出现在 Issue 下:恭喜!你已经从"看懂别人的工作流"跨越到了"自己的仓库跑 AI"。这个看似简单的工作流,实际上已经包含了你在三个课程中学到的所有核心模式:标签触发、状态机、优雅降级、Agent-Runner 契约。

✏️ 诊断性测验

1. Agent-Runner 契约中,AI 代理不能做什么?

A. 读取仓库中的文件
B. 将分析结果写入文件
C. 直接调用 gh CLI 给 Issue 发评论
D. 通过环境变量接收 Issue 编号

2. --allowedTools Read,Grep,Glob 的作用是什么?

A. 让 Claude Code 运行得更快
B. 限制 AI 只能做只读操作,防止意外修改代码
C. 指定 Claude Code 使用哪个编程语言
D. 开启多线程分析模式

3. 为什么 gh issue edit --remove-label 后面加 || true

A. 这是语法要求,每个 gh 命令都必须加
B. 防止标签不存在时命令返回非零退出码导致 step 失败
C. 让命令执行得更快
D. 强制在标签不存在时创建一个新标签


🎯 总结:你学到了什么

新知识具体内容
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.mdgh issue comment --body-file - 发布。
安全渐进 从只读权限(--allowedTools Read,Grep,Glob)开始,建立信心后再逐步放开。
🚀 下一步:部署成功后,下一步可以:
  1. 改进 prompt:让 AI 的分析更贴合你的项目(在 prompt 中加入项目 README 或架构文档)
  2. 放开权限:给 AI 添加 WriteEdit 权限,让它可以修改代码
  3. 接入实现:参考 sandcastle 的 implement 工作流,让 AI 真正实现代码变更

推荐进一步阅读


← 第 3 课:高级模式 第 4 课 第 5 课:从 YAML 到 TypeScript →

💬 有问题?部署过程中遇到任何报错——无论是 YAML 语法、权限问题还是 Claude Code 行为不符合预期——直接向你的 AI 导师提问。