独立开发者的 CI/CD 选型:GitHub Actions 跑 AI 推理任务该怎么配

AI3小时前发布 beixibaobao
5 0 0

独立开发者的 CI/CD 选型:GitHub Actions 跑 AI 推理任务该怎么配

一、AI 推理跑在 CI 里?看似荒谬实则必要

CI(持续集成)的传统职责是跑测试、lint、构建。放在 AI 场景下,你可能会问:为什么要在 CI 里跑推理任务?

答案是评估(Evaluation)。你改了一行 Prompt,怎么知道这次修改让回答质量提升了还是下降了?你升级了模型版本,怎么确认旧用例的准确率没有退化?你新增了一个工具调用的逻辑,怎么验证边界 case 没有被破坏?

这些都依赖于 AI 推理的自动化评估。如果每次发布前都靠人肉测试——"把 50 个标准问题发给 AI,手动对比回答质量"——那你的发布节奏会被严重拖慢。

在 CI 中自动运行推理评估是最优解。但这里有三个挑战:API 调用的成本控制、推理时间的等待策略、以及评估结果的判读机制。

graph TB
    A[PR 提交] --> B[Lint + Unit Test]
    B -->|通过| C{AI Eval 触发?}
    C -->|diff 包含 Prompt 变更| D[启动 AI 评估]
    C -->|diff 不含 Prompt 变更| E[跳过评估]
    D --> F[运行标准用例集<br/>20-50 个测试]
    F --> G[收集结果 + Token 消耗]
    G --> H{评分 ≥ 阈值?}
    H -->|是| I[CI 通过]
    H -->|否| J[CI 失败 + 详细报告]
    I --> K[记录指标变化]
    style J fill:#ff6b6b,color:#fff
    style I fill:#51cf66,color:#fff
    style E fill:#868e96,color:#fff

本文将设计一套低成本、高效率的 AI 推理 CI 流程,覆盖触发策略、成本控制和结果判读。

二、触发策略:不是每次提交都值得跑 AI 评估

AI 推理的 CI 不能在每次 push 时都运行。原因很简单:API 调用花钱,推理需要时间。一次完整的评估可能需要 5-10 分钟和 $0.5-2 的成本。

触发策略应该是条件性的:

条件 是否触发 原因
prompts/ 目录有变更 触发 Prompt 变更直接影响输出质量
models/ 或模型配置变更 触发 模型切换可能引入回归
eval/ 或测试用例变更 触发 评估体系本身需要验证
只改了文档、注释、CSS 跳过 不影响推理质量
PR 是 Draft 跳过 WIP 不需要评估

GitHub Actions 的 paths 过滤可以实现:

on:
  pull_request:
    paths:
      - 'prompts/**'
      - 'src/models/**'
      - 'eval/**'
      - 'config/ai.json'

更进一步,可以在 workflow 中动态判断 diff 内容,决定是否运行全部用例还是抽样运行。

三、完整 CI 配置与评估脚本

GitHub Actions Workflow:

name: AI Eval
on:
  pull_request:
    paths:
      - 'prompts/**'
      - 'src/models/**'
      - 'eval/**'
jobs:
  eval:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - name: Install dependencies
        run: npm ci
      - name: Run AI Eval
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: node scripts/ai-eval.js
      - name: Upload Eval Report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: eval-report
          path: eval-results.json

评估脚本:

// scripts/ai-eval.js
const fs = require('fs');
const path = require('path');
const OpenAI = require('openai');
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// 加载测试用例
function loadTestCases() {
  const evalDir = path.join(__dirname, '..', 'eval');
  const cases = [];
  for (const file of fs.readdirSync(evalDir)) {
    if (!file.endsWith('.json')) continue;
    const content = JSON.parse(fs.readFileSync(path.join(evalDir, file), 'utf-8'));
    cases.push(...content);
  }
  return cases;
}
// 评分函数:比较 AI 输出与期望答案
function scoreResponse(expected, actual) {
  const lowerActual = actual.toLowerCase();
  const lowerExpected = expected.toLowerCase();
  // 精确匹配
  if (lowerActual === lowerExpected) return 1.0;
  // 包含匹配
  if (lowerActual.includes(lowerExpected) || lowerExpected.includes(lowerActual)) {
    return 0.8;
  }
  // 关键词匹配
  const keywords = lowerExpected.split(/s+/).filter(w => w.length > 3);
  const hitCount = keywords.filter(kw => lowerActual.includes(kw)).length;
  if (keywords.length === 0) return 0.5;
  return Math.min(hitCount / keywords.length, 0.7);
}
async function runEval() {
  const testCases = loadTestCases();
  const results = {
    timestamp: new Date().toISOString(),
    total: testCases.length,
    passed: 0,
    failed: 0,
    totalTokens: 0,
    totalCost: 0,
    details: [],
  };
  // 串行运行,避免触发 API 速率限制
  for (const tc of testCases) {
    try {
      const start = Date.now();
      const response = await openai.chat.completions.create({
        model: 'gpt-4o-mini',
        messages: tc.messages,
        temperature: 0,
        max_tokens: 500,
      });
      const actual = response.choices[0].message.content;
      const score = scoreResponse(tc.expected, actual);
      const usage = response.usage;
      results.totalTokens += usage.total_tokens;
      // gpt-4o-mini 定价: $0.15/1M input, $0.60/1M output
      const cost = (usage.prompt_tokens * 0.15 / 1_000_000) + 
                   (usage.completion_tokens * 0.60 / 1_000_000);
      results.totalCost += cost;
      const passed = score >= tc.threshold;
      if (passed) {
        results.passed++;
      } else {
        results.failed++;
      }
      results.details.push({
        name: tc.name,
        passed,
        score,
        threshold: tc.threshold,
        expected: tc.expected.substring(0, 100),
        actual: actual.substring(0, 200),
        tokens: usage.total_tokens,
        cost: cost.toFixed(4),
        durationMs: Date.now() - start,
      });
      console.log(`${passed ? '✓' : '✗'} ${tc.name}: ${score.toFixed(2)}/${tc.threshold} (${(Date.now() - start)}ms, $${cost.toFixed(4)})`);
    } catch (err) {
      results.failed++;
      results.details.push({
        name: tc.name,
        passed: false,
        error: err.message,
      });
      console.error(`✗ ${tc.name}: ERROR - ${err.message}`);
    }
  }
  // 写入结果
  fs.writeFileSync('eval-results.json', JSON.stringify(results, null, 2));
  // 总结
  console.log(`n=== AI Eval Summary ===`);
  console.log(`Total: ${results.total}, Passed: ${results.passed}, Failed: ${results.failed}`);
  console.log(`Total Tokens: ${results.totalTokens}, Cost: $${results.totalCost.toFixed(4)}`);
  // CI 失败条件:通过率 < 80%
  const passRate = results.total > 0 ? results.passed / results.total : 0;
  if (passRate < 0.8) {
    console.error(`Pass rate ${(passRate * 100).toFixed(1)}% is below threshold 80%`);
    process.exit(1);
  }
}
runEval().catch((err) => {
  console.error('Eval failed:', err);
  process.exit(1);
});

测试用例格式(eval/basic_qa.json):

[
  {
    "name": "天气查询-晴天",
    "messages": [
      {"role": "system", "content": "你是天气助手。用中文回答。"},
      {"role": "user", "content": "今天北京天气怎么样?"}
    ],
    "expected": "北京",
    "threshold": 0.5
  }
]

四、成本优化与工程权衡

成本优化手段

  • 使用 gpt-4o-mini 而非 gpt-4o 跑评估。评估的目的是验证 Prompt 变更的效果,mini 模型的判断力和 full 模型高度相关
  • 对非 PR,只在 main 分支的定时任务中运行全量评估(如每天凌晨一次)
  • 使用 temperature: 0 保证输出的一致性,减少重复运行
  • 和 Prompt 无关的 PR,可以通过 paths filter 跳过 AI 评估

CI 超时风险:AI 推理可能因为 API 延迟而超时。设置 timeout-minutes: 15,如果超时视为评估失败。

不适用场景

  • 评估需要人工判断的开放性问题(如创意写作质量)
  • LLM 调用量极小(< 100 次/天)的项目——人工检查更经济
  • 模型变更频率极低(月级)——不需要 CI 集成

五、总结

AI 推理 CI 的核心价值是:用自动化评估替代人工质检,让每次 Prompt 变更都经过客观验证。

落地路径:先建立 20-30 个标准测试用例覆盖核心场景;然后在 CI 中配置 paths 过滤,只在 Prompt 相关文件变更时触发;接着用 gpt-4o-mini 控制成本;最后设定通过率阈值(80%)作为 CI 的通过条件。

独立开发者最宝贵的资源是时间。让 CI 替你盯着质量,你才能专注于创造价值。少即是多——让 CI 做它擅长的事,你做你擅长的事。

© 版权声明

相关文章