C.W.K.
Stream
Lesson 01 of 12 · published

LLM Evaluation in CI

~13 min · llm, eval, ci

Level 0Apprentice
0 XP0/101 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

Behavior is the regression target

For a service that wraps an LLM, the thing your tests should check is not 'did the API respond' (it almost always does). It's 'did the model produce the kind of answer we expect for our prompts'. That's an eval: a structured set of (input, expected behavior) pairs that you score against the model.

Eval shape

  • Dataset — N examples covering golden cases, edge cases, regressions you've fixed. Versioned in the repo.
  • Scorer — function that takes a model output and returns a number (0–1, or pass/fail). Common scorers: exact match, contains, regex, semantic similarity, rubric judge (LLM-as-judge), pairwise preference.
  • Threshold — minimum aggregate score to pass.

Where it runs in CI

  1. On every PR — fast smoke subset (10–20 examples).
  2. On every push to main — full eval (all examples).
  3. Nightly — full eval against the latest model versions to catch upstream changes.

Cost discipline

An eval with 200 examples × 3 model variants × 5 PRs/day burns API budget. Cache results keyed on (prompt SHA, model version, scorer); only re-run when one of those changed. The Eval Quest covers this in depth.

Code

Minimal eval job·yaml
  eval:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write   # for sticky PR comment
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v3
      - run: uv sync --all-extras --dev
      - name: Run eval
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          # Smoke subset on PRs, full on main
          if [[ '${{ github.event_name }}' == 'pull_request' ]]; then
            uv run python -m evals.run --suite smoke --threshold 0.85 --output evals/results.md
          else
            uv run python -m evals.run --suite full --threshold 0.85 --output evals/results.md
          fi
      - uses: marocchino/sticky-pull-request-comment@v2
        if: github.event_name == 'pull_request'
        with:
          path: evals/results.md

External links

Exercise

Pick one LLM-using path in your project. Write 5 (input, expected behavior) examples as a YAML or JSON dataset. Add an eval job to CI that runs them on every PR and comments the results.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.