1. 为什么要把 Claude Skill 塞进 CI/CD 流水线
先说清楚 Claude Skill 是什么、能做什么、适合谁。Claude Skill 是 Anthropic 在 2025 年 10 月推出的能力封装机制,本质是把一段可复用的提示词、工作流程和代码规范写进.claude/skills/目录下的 markdown 文件,需要时用@skill 技能名调用。它和随手写提示词最大的区别在于:Skill 可以进 Git 仓库做版本管理,团队共享一套模板,不会因为对话轮次变长而失效。适合谁?适合那些每天要重复交代“记得加错误处理”“测试要覆盖边界条件”的后端、前端和测试开发同学。
但只在本地用 Skill,还是得靠人手动触发。真正让这件事产生质变的,是把它接进 CI/CD。你想想,代码审查和单元测试生成这两件事,恰好是“规则明确、重复度高、又必须做”的典型。每次 PR 提交,如果能让流水线自动跑一遍 AI 审查、自动生成缺失的单测用例,reviewer 打开 PR 时看到的不再是空白,而是一份带行号、带修复建议的结构化报告,人工 review 就能聚焦在架构和业务逻辑上。
我试过在一个中型 Node 项目里落地这套流程,最直观的变化是:以前 PR 里那些“这里可能空指针”“这个 SQL 拼接有注入风险”的低级问题,现在在提交后两分钟内就被自动标出来了。开发者自己就能改掉,不用等 reviewer 打回来。这篇文章就按 GitHub Action 场景,把 Skill 配置、工作流 YAML、触发规则、结果验证和排错一次讲透,中间用 TaoToken 统一 Key 和 API 通道接入,避免每个仓库到处散落密钥。
2. TaoToken 前置准备:统一 Key 与 API 通道
在把 Skill 接进 GitHub Action 之前,得先解决一个现实问题:CI 环境里怎么安全地调用模型。你不可能把某个厂商的原始 Key 硬编码在 workflow 文件里,也不希望每个仓库各配一套。TaoToken 在这里扮演的角色是统一入口——一个 Key、一个 Base URL,兼容 Anthropic 风格的接口,Claude Code、Cline、Codex 这类工具都能接。
先注册并拿到 Key。打开官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,完成账号注册后进入控制台。控制台地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,在左侧找到 API Keys 菜单,新建一个 Key。建议给 CI 单独建一个 Key,命名成github-action-ci,方便后续按用途区分和轮换。API Keys 页面直达:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。
拿到 Key 之后,记住两个地址。官网首页是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api ,注意 API 地址后面不加任何 UTM 参数,保持干净。这个 Base URL 就是后面所有配置里要填的ANTHROPIC_BASE_URL。
关于模型 ID,TaoToken 的模型对话页面可以查看当前可用的模型列表,地址是 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。在 CI 里做代码审查和单测生成,建议选一个代码能力强的模型,把它的 Model ID 记下来,后面写进配置。如果你打算长期在多个仓库跑 Agent 类任务,可以了解下 Coding Plan,地址是 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,它更适合高频、长期的编码场景。
接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,遇到接口细节问题可以对照查。如果你用的是 Claude Code 作为本地执行器,它的接入说明在 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=ClaudeCodeAnthropic&utm_campaign=rewrite ,里面写了 Base URL、Key、Model ID 三件套怎么填。
这里要强调一个安全原则:Key 绝对不能写进 workflow 文件明文。正确做法是存进 GitHub 仓库的 Secrets,在 workflow 里用${{ secrets.TAOTOKEN_API_KEY }}引用。下面进入具体配置。
3. 可复制配置:Skill 文件 + GitHub Action 工作流 YAML
这一节是全文的核心,所有片段都可以直接复制。先建目录结构,再写 Skill 文件,最后写 workflow。
3.1 目录结构
在项目根目录下创建如下结构:
.claude/ skills/ code-review/ SKILL.md test-gen/ SKILL.md .github/ workflows/ ai-review.yml.claude/skills/目录建议加入 Git 仓库,团队共享。.github/workflows/是 GitHub Action 的标准位置。
3.2 代码审查 Skill 配置
创建.claude/skills/code-review/SKILL.md,内容如下:
--- name: code-review description: 对代码变更进行多维度审查,涵盖安全、逻辑、性能、代码风格四个维度,输出结构化报告 --- # 代码审查专家 Skill 你是一位经验丰富的代码审查专家。收到代码后,请按以下标准进行系统化审查。 ## 审查维度 ### 1. 安全性 - SQL 注入风险(尤其检查字符串拼接的查询) - XSS 漏洞(用户输入是否经过转义) - 敏感信息泄露(API 密钥、密码是否硬编码) - 权限校验是否完整 ### 2. 逻辑正确性 - 空指针 / undefined 访问风险 - 边界条件处理(数组越界、除零、空集合) - 并发安全问题(竞态条件、死锁) - 异常处理是否完善 ### 3. 性能 - 不必要的循环嵌套 - N+1 查询问题 - 内存泄漏风险(事件监听未移除、定时器未清理) - 大数据量操作是否有分页或流式处理 ### 4. 代码质量 - 命名是否符合项目规范 - 函数是否过长(超过 50 行需拆解) - 重复代码是否存在 - 注释是否与代码一致 ## 输出格式 请按以下结构输出审查报告: ### 严重问题(P0 - 必须修复) (安全漏洞、逻辑错误等会导致线上故障的问题) ### 警告(P1 - 建议修复) (性能隐患、代码异味、可维护性问题) ### 建议(P2 - 可选) (优化建议、最佳实践参考) ### 亮点 (代码中值得肯定的部分) ## 审查原则 1. 所有判断必须有具体依据,引用代码行号 2. 每个问题附带修复建议,给出示例代码 3. 宁缺毋滥,避免刷屏式输出低质量建议 4. 不确定的地方标注“需要人工确认”3.3 单元测试生成 Skill 配置
创建.claude/skills/test-gen/SKILL.md:
--- name: test-gen description: 为给定函数或模块自动生成单元测试用例,覆盖正常路径、边界条件、异常情况 --- # 单元测试生成专家 Skill ## 角色定位 你是一位经验丰富的测试工程师,擅长编写高质量、可维护的单元测试。 ## 工作流程 ### Step 1: 分析待测代码 - 识别函数的所有输入参数及其类型 - 识别函数的返回值类型 - 识别函数内部的分支逻辑和依赖 ### Step 2: 设计测试用例 基于等价类划分和边界值分析法,覆盖以下场景: | 用例类型 | 覆盖内容 | 最少数量 | |---------|---------|---------| | 正常路径 | 函数设计场景下的典型输入 | 1-2 个 | | 边界条件 | 空值、零值、最大/最小值、数组首尾 | 2-3 个 | | 异常路径 | 无效输入、依赖失败、超时 | 2-3 个 | | 并发场景 | 如有状态,考虑竞态 | 按需 | ### Step 3: 生成测试代码 - 使用项目已有的测试框架(Jest/Vitest/JUnit/pytest) - 遵循 AAA 模式:Arrange(准备)、Act(执行)、Assert(断言) - 每个测试用例独立,不依赖执行顺序 - Mock 外部依赖,不发起真实网络请求 ### Step 4: 质量自查 - 每个用例是否有明确的预期结果? - 测试命名是否清晰描述场景? - 是否有冗余的测试用例? ## 注意事项 - 不要修改源代码,只生成测试文件 - 不确定的地方添加 @todo 注释标注 - 如果待测代码难以测试,在报告末尾给出重构建议3.4 GitHub Action 工作流 YAML
创建.github/workflows/ai-review.yml。这里用 TaoToken 的 Base URL 和 Key,通过环境变量注入:
name: AI Code Review and Test Gen on: pull_request: types: [opened, synchronize, reopened] branches: - main - develop permissions: contents: read pull-requests: write jobs: ai-review: runs-on: ubuntu-latest env: ANTHROPIC_BASE_URL: https://taotoken.net/api ANTHROPIC_API_KEY: ${{ secrets.TAOTOKEN_API_KEY }} ANTHROPIC_MODEL: ${{ secrets.TAOTOKEN_MODEL_ID }} steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Node uses: actions/setup-node@v4 with: node-version: '20' - name: Install Claude Code CLI run: npm install -g @anthropic-ai/claude-code - name: Get PR diff id: diff run: | git fetch origin ${{ github.base_ref }} git diff origin/${{ github.base_ref }}...HEAD > /tmp/pr.diff echo "diff_size=$(wc -l < /tmp/pr.diff)" >> $GITHUB_OUTPUT - name: Run code review skill if: steps.diff.outputs.diff_size > 0 run: | claude -p "@skill code-review 请审查以下 git diff 内容,按 SKILL.md 的输出格式给出报告:$(cat /tmp/pr.diff)" > /tmp/review.md - name: Run test gen skill if: steps.diff.outputs.diff_size > 0 run: | claude -p "@skill test-gen 针对本次变更涉及的核心函数生成单元测试用例,使用项目现有测试框架" > /tmp/tests.md - name: Post review comment uses: actions/github-script@v7 with: script: | const fs = require('fs'); const review = fs.readFileSync('/tmp/review.md', 'utf8'); const tests = fs.readFileSync('/tmp/tests.md', 'utf8'); const body = `## AI 代码审查报告\n\n${review}\n\n---\n\n## 单元测试生成建议\n\n${tests}`; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body: body });这段 YAML 的关键点:ANTHROPIC_BASE_URL指向 TaoToken 的 API 地址,ANTHROPIC_API_KEY和ANTHROPIC_MODEL从 Secrets 读取。触发规则是 PR 打开、同步、重新打开时跑,目标分支限定 main 和 develop。fetch-depth: 0是为了拿到完整的 git 历史,否则 diff 算不出来。
3.5 三件套配置对照
如果你用的是 Cline、Codex 或 Claude Code 本地调试,配置项对照如下:
| 配置项 | 值 | 说明 |
|---|---|---|
| Base URL | https://taotoken.net/api | 不加 UTM 参数 |
| API Key | 控制台新建的 Key | 存 Secrets,不写明文 |
| Model ID | 模型对话页查看 | 选代码能力强的 |
Codex 的auth.json里对应填base_url和api_key;Cline 的 MCP 配置里填baseUrl和apiKey。三件套缺一不可,尤其是 Model ID,填错会直接报模型不存在。
4. 验证请求与成功结果:一次提交后的完整链路
配置写完,得验证它真的能跑。这一节演示从提交到看到审查意见和单测用例的完整过程。
4.1 本地先验证 Skill 能跑通
在推 CI 之前,先在本地确认 Skill 和 TaoToken 通道是通的。设置环境变量:
export ANTHROPIC_BASE_URL=https://taotoken.net/api export ANTHROPIC_API_KEY=你的Key export ANTHROPIC_MODEL=你的ModelID然后写一个故意有问题的函数来测试。创建utils/calculator.ts:
export function divide(a: number, b: number): number { return a / b; }这个函数没处理除零,正好用来验证审查 Skill 能不能发现问题。运行:
claude -p "@skill code-review 请审查 utils/calculator.ts"预期输出里应该出现类似这样的内容:
### 严重问题(P0 - 必须修复) - utils/calculator.ts:2 - divide 函数未处理除数为 0 的情况,会导致返回 Infinity 或 NaN。 修复建议: if (b === 0) { throw new Error('除数不能为0'); }如果看到了带行号和修复建议的报告,说明 Skill 和 TaoToken 通道都正常。
4.2 验证单测生成
接着验证 test-gen:
claude -p "@skill test-gen 为 utils/calculator.ts 中的 divide 函数生成单元测试,使用 Jest 框架"预期输出:
import { divide } from './calculator'; describe('divide', () => { it('should return correct result when dividing positive numbers', () => { expect(divide(10, 2)).toBe(5); expect(divide(9, 3)).toBe(3); }); it('should handle negative numbers correctly', () => { expect(divide(-10, 2)).toBe(-5); expect(divide(10, -2)).toBe(-5); }); it('should handle division by 1', () => { expect(divide(100, 1)).toBe(100); }); it('should throw error when dividing by zero', () => { expect(() => divide(10, 0)).toThrow('除数不能为0'); }); });注意最后一条用例——它期望抛错,但当前实现并不会抛错。这恰好说明 test-gen 按边界条件设计了用例,能暴露实现缺陷。
4.3 推 CI 看真实效果
本地通了之后,把改动推到分支,开一个 PR。GitHub Action 会自动触发。在 Actions 标签页能看到AI Code Review and Test Gen这个 workflow 在跑。跑完后回到 PR 页面,底部会出现一条机器人评论,里面是完整的审查报告和单测建议。
实测下来,一个改动 200 行左右的 PR,从触发到评论出现大约两到三分钟。审查报告会按 P0/P1/P2 分级,单测建议会给出可直接粘贴的测试代码。reviewer 打开 PR 时,第一眼看到的就是这份报告,可以直接在评论里讨论具体问题。
4.4 触发规则说明
workflow 里的on.pull_request.types决定了什么时候跑。opened是新建 PR,synchronize是 PR 有新提交,reopened是重新打开。如果你只想在特定路径变更时跑,可以加paths过滤:
on: pull_request: types: [opened, synchronize] paths: - 'src/**' - 'utils/**'这样只有 src 和 utils 目录下的文件变更才会触发,避免改个 README 也跑一遍浪费额度。
5. 本篇常见错误排查
配置过程中最容易踩的坑集中在认证、代理和输出解析三块。下面按真实报错逐个拆。
5.1 401 Unauthorized
报错长这样:
Error: 401 Unauthorized - invalid api key原因通常是 Secrets 没配、名字写错,或者 Key 被撤销了。排查步骤:进仓库 Settings → Secrets and variables → Actions,确认TAOTOKEN_API_KEY存在且值正确。注意 workflow 里引用的是secrets.TAOTOKEN_API_KEY,名字必须完全一致,大小写敏感。如果 Key 是在控制台新建的,确认没有复制时带上多余空格。
5.2 local proxy failed / connection refused
报错:
Error: local proxy failed: connect ECONNREFUSED 127.0.0.1:xxxx这是本地调试时常见的。原因是你本地设了某个代理环境变量,但代理服务没启动。检查HTTP_PROXY、HTTPS_PROXY、ALL_PROXY这几个环境变量,如果指向本地端口而服务没跑,就会报这个。解决方法是清掉这些变量,或者确保代理服务正常运行。在 CI 环境里一般不会遇到,因为 GitHub runner 是干净环境。
5.3 reading choices 相关报错
报错:
Error: reading 'choices' of undefined这个通常出现在用 OpenAI 兼容格式调用时,响应结构不符合预期。TaoToken 的 API 基址是 https://taotoken.net/api ,如果你在代码里手动拼了/v1/chat/completions之类的路径,可能和实际接口不匹配。对照接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 确认请求路径和响应格式。用 Claude Code CLI 的话,它会自动处理,一般不会碰到这个。
5.4 OAuth 相关报错
报错:
Error: OAuth token expired or invalid如果你之前用 OAuth 方式登录过 Claude Code,本地可能残留了旧的凭证文件,优先级高于环境变量。检查~/.claude/目录下有没有credentials.json之类的文件,有的话先备份再删除,让环境变量生效。CI 环境里没有这个文件,所以不会受影响。
5.5 Model ID 填错
报错:
Error: model not found: xxxModel ID 必须和模型对话页列出的完全一致。去 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 复制准确的 ID,注意大小写和连字符。填进 Secrets 的TAOTOKEN_MODEL_ID后,workflow 里通过ANTHROPIC_MODEL引用。
5.6 diff 为空导致 Skill 不执行
workflow 里有个判断if: steps.diff.outputs.diff_size > 0,如果 diff 为空就跳过。如果发现 Action 跑了但没输出,先看Get PR diff这步的日志,确认 diff 文件是不是空的。常见原因是fetch-depth没设成 0,导致拿不到 base 分支的历史。
6. 把这条流水线用起来
配置和排错都过了一遍,最后说几个实际用下来的经验。Skill 文件一定要进 Git,团队共享一套,新人拉下来就能用,有改进就提 PR。审查和测试生成建议用不同的 Skill 从不同角度跑,不要让同一个 Skill 又写又审,容易产生盲点。CI 里的 Key 单独建一个,方便按用途轮换和审计。
如果你还没开始,建议先从 code-review 这一个 Skill 接进 CI,跑一周感受下 PR 里自动出现审查报告是什么体验。稳定之后再加 test-gen。TaoToken 的 Key 和 Base URL 配一次,后面所有仓库都能复用,省去到处散落密钥的麻烦。模型对话页面可以随时切换模型对比效果,接入文档遇到问题随时查。这条流水线搭好之后,代码审查和单测生成这两件苦差事,就真的变成提交后自动完成的事了。