К основному содержанию
скилл CX Codex

promptfoo-evaluation — скилл для Claude Code: тесты промптов

Скилл помогает настроить и запустить оценку LLM через Promptfoo: конфиг promptfooconfig.yaml, свои проверки на Python, судейство моделью по рубрике.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add daymade/claude-code-skills --skill promptfoo-evaluation -a claude-code
Открыть на GitHub

Что делает promptfoo-evaluation

Скилл ведёт агента по структуре проекта Promptfoo: главный конфиг, папка prompts/ с текстовыми и чат-промптами в JSON, tests/cases.yaml с тест-кейсами и scripts/ для своих проверок на Python. Запуск идёт через npx promptfoo@latest init, eval и view — последняя команда открывает результаты в браузере.

Проверки бывают простые и составные. Кроме встроенных contains, equals, regex и latency можно написать функцию на Python: она получает вывод и контекст, возвращает pass, score и причину. Для оценки качества текста есть llm-rubric — судья-модель со своим порогом, например threshold: 0.7.

Если вы ходите в модели через релей или прокси, у каждой проверки llm-rubric должен быть свой provider с apiBaseUrl: настройка основного провайдера не наследуется, иначе судья уйдёт на стандартный эндпоинт и получит 401. Ещё одна частая ловушка — пути file:// считаются от места promptfooconfig.yaml, а не от того YAML, где они записаны. И maxConcurrency работает только внутри commandLineOptions.

Какие задачи решает

  • сравнить ответы двух моделей на одном наборе промптов
  • написать свою проверку вывода на Python и вернуть score
  • поставить оценку тексту моделью-судьёй по рубрике
  • собрать тест-кейсы в отдельный YAML с переменными из файлов
  • настроить прогон через релей с apiBaseUrl

Примеры запросов агенту

  • настрой Promptfoo для моего промпта и запусти eval
  • добавь в конфиг проверку llm-rubric с порогом 0.7
  • почему мой llm-rubric падает с 401 при работе через прокси?

Как установить promptfoo-evaluation

1Через Skills CLI (Vercel)

npx skills add daymade/claude-code-skills --skill promptfoo-evaluation -a claude-code

2Вручную: положить файлы

~/.claude/skills/promptfoo-evaluation/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/promptfoo-evaluation/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о promptfoo-evaluation

Как установить promptfoo-evaluation в Claude?

Выполните npx skills add daymade/claude-code-skills --skill promptfoo-evaluation -a claude-code. После этого скилл лежит в ~/.claude/skills/promptfoo-evaluation/SKILL.md.

Что делает promptfoo-evaluation?

Помогает агенту собрать конфиг Promptfoo, описать промпты и тест-кейсы, добавить проверки на Python или судейство моделью по рубрике и прогнать оценку.

Почему llm-rubric возвращает 401 при работе через прокси?

Основной провайдер не передаёт свой apiBaseUrl проверкам. Каждой llm-rubric нужен собственный блок provider с apiBaseUrl, иначе судья обратится к стандартному эндпоинту.

Исходный текст

SKILL.md · MIT
---
name: promptfoo-evaluation
description: Configures and runs LLM evaluation using Promptfoo framework. Use when setting up prompt testing, creating evaluation configs (promptfooconfig.yaml), writing Python custom assertions, implementing llm-rubric for LLM-as-judge, or managing few-shot examples in prompts. Triggers on keywords like "promptfoo", "eval", "LLM evaluation", "prompt testing", or "model comparison".
---

# Promptfoo Evaluation

## Overview

This skill provides guidance for configuring and running LLM evaluations using [Promptfoo](https://www.promptfoo.dev/), an open-source CLI tool for testing and comparing LLM outputs.

## Quick Start

```bash
# Initialize a new evaluation project
npx promptfoo@latest init

# Run evaluation
npx promptfoo@latest eval

# View results in browser
npx promptfoo@latest view
```

## Configuration Structure

A typical Promptfoo project structure:

```
project/
├── promptfooconfig.yaml    # Main configuration
├── prompts/
│   ├── system.md           # System prompt
│   └── chat.json           # Chat format prompt
├── tests/
│   └── cases.yaml          # Test cases
└── scripts/
    └── metrics.py          # Custom Python assertions
```

## Core Configuration (promptfooconfig.yaml)

```yaml
# yaml-language-server: $schema=https://promptfoo.dev/config-schema.json
description: "My LLM Evaluation"

# Prompts to test
prompts:
  - file://prompts/system.md
  - file://prompts/chat.json

# Models to compare
providers:
  - id: anthropic:messages:claude-sonnet-4-6
    label: Claude-Sonnet-4.6
  - id: openai:gpt-4.1
    label: GPT-4.1

# Test cases
tests: file://tests/cases.yaml

# Concurrency control (MUST be under commandLineOptions, NOT top-level)
commandLineOptions:
  maxConcurrency: 2

# Default assertions for all tests
defaultTest:
  assert:
    - type: python
      value: file://scripts/metrics.py:custom_assert
    - type: llm-rubric
      value: |
        Evaluate the response quality on a 0-1 scale.
      threshold: 0.7

# Output path
outputPath: results/eval-results.json
```

## Prompt Formats

### Text Prompt (system.md)

```markdown
You are a helpful assistant.

Task: {{task}}
Context: {{context}}
```

### Chat Format (chat.json)

```json
[
  {"role": "system", "content": "{{system_prompt}}"},
  {"role": "user", "content": "{{user_input}}"}
]
```

### Few-Shot Pattern

Embed examples directly in prompt or use chat format with assistant messages:

```json
[
  {"role": "system", "content": "{{system_prompt}}"},
  {"role": "user", "content": "Example input: {{example_input}}"},
  {"role": "assistant", "content": "{{example_output}}"},
  {"role": "user", "content": "Now process: {{actual_input}}"}
]
```

## Test Cases (tests/cases.yaml)

```yaml
- description: "Test case 1"
  vars:
    system_prompt: file://prompts/system.md
    user_input: "Hello world"
    # Load content from files
    context: file://data/context.txt
  assert:
    - type: contains
      value: "expected text"
    - type: python
      value: file://scripts/metrics.py:custom_check
      threshold: 0.8
```

## Python Custom Assertions

Create a Python file for custom assertions (e.g., `scripts/metrics.py`):

```python
def get_assert(output: str, context: dict) -> dict:
    """Default assertion function."""
    vars_dict = context.get('vars', {})

    # Access test variables
    expected = vars_dict.get('expected', '')

    # Return result
    return {
        "pass": expected in output,
        "score": 0.8,
        "reason": "Contains expected content",
        "named_scores": {"relevance": 0.9}
    }

def custom_check(output: str, context: dict) -> dict:
    """Custom named assertion."""
    word_count = len(output.split())
    passed = 100 <= word_count <= 500

    return {
        "pass": passed,
        "score": min(1.0, word_count / 300),
        "reason": f"Word count: {word_count}"
    }
```

**Key points:**
- Default function name is `get_assert`
- Specify function with `file://path.py:function_name`
- Return `bool`, `float` (score), or `dict` with pass/score/reason
- Access variables via `context['vars']`

## LLM-as-Judge (llm-rubric)

```yaml
assert:
  - type: llm-rubric
    value: |
      Evaluate the response based on:
      1. Accuracy of information
      2. Clarity of explanation
      3. Completeness

      Score 0.0-1.0 where 0.7+ is passing.
    threshold: 0.7
    provider: openai:gpt-4.1  # Optional: override grader model
```

**When using a relay/proxy API**, each `llm-rubric` assertion needs its own `provider` config with `apiBaseUrl`. Otherwise the grader falls back to the default Anthropic/OpenAI endpoint and gets 401 errors:

```yaml
assert:
  - type: llm-rubric
    value: |
      Evaluate quality on a 0-1 scale.
    threshold: 0.7
    provider:
      id: anthropic:messages:claude-sonnet-4-6
      config:
        apiBaseUrl: https://your-relay.example.com/api
```

**Best practices:**
- Provide clear scoring criteria
- Use `threshold` to set minimum passing score
- Default grader uses available API keys (OpenAI → Anthropic → Google)
- **When using relay/proxy**: every `llm-rubric` must have its own `provider` with `apiBaseUrl` — the main provider's `apiBaseUrl` is NOT inherited

## Common Assertion Types

| Type | Usage | Example |
|------|-------|---------|
| `contains` | Check substring | `value: "hello"` |
| `icontains` | Case-insensitive | `value: "HELLO"` |
| `equals` | Exact match | `value: "42"` |
| `regex` | Pattern match | `value: "\\d{4}"` |
| `python` | Custom logic | `value: file://script.py` |
| `llm-rubric` | LLM grading | `value: "Is professional"` |
| `latency` | Response time | `threshold: 1000` |

## File References

All `file://` paths are resolved relative to `promptfooconfig.yaml` location (NOT the YAML file containing the reference). This is a common gotcha when `tests:` references a separate YAML file — the `file://` paths inside that test file still resolve from the config root.

```yaml
# Load file content as variable
vars:
  content: file://data/input.txt

# Load prompt from file
prompts:
  - file://prompts/main.md

# Load test cases from file
tests: file://tests/cases.yaml

# Load Python assertion
assert:
  - type: python
    value: file://scripts/check.py:validate
```

## Running Evaluations

```bash
# Basic run
npx promptfoo@latest eval

# With specific config
npx promptfoo@latest eval --config path/to/config.yaml

# Output to file
npx promptfoo@latest eval --output results.json

# Filter tests
npx promptfoo@latest eval --filter-metadata category=math

# View results
npx promptfoo@latest view
```

## Relay / Proxy API Configuration

When using an API relay or proxy instead of direct Anthropic/OpenAI endpoints:

```yaml
providers:
  - id: anthropic:messages:claude-sonnet-4-6
    label: Claude-Sonnet-4.6
    config:
      max_tokens: 4096
      apiBaseUrl: https://your-relay.example.com/api  # Promptfoo appends /v1/messages

# CRITICAL: maxConcurrency MUST be under commandLineOptions (NOT top-level)
commandLineOptions:
  maxConcurrency: 1  # Respect relay rate limits
```

**Key rules:**
- `apiBaseUrl` goes in `providers[].config` — Promptfoo appends `/v1/messages` automatically
- `maxConcurrency` must be under `commandLineOptions:` — placing it at top level is silently ignored
- When using relay with LLM-as-judge, set `maxConcurrency: 1` to avoid concurrent request limits (generation + grading share the same pool)
- Pass relay token as `ANTHROPIC_API_KEY` env var

## Troubleshooting

**Python not found:**
```bash
export PROMPTFOO_PYTHON=python3
```

**Large outputs truncated:**
Outputs over 30000 characters are truncated. Use `head_limit` in assertions.

**File not found errors:**
All `file://` paths resolve relative to `promptfooconfig.yaml` location.

**maxConcurrency ignored (shows "up to N at a time"):**
`maxConcurrency` must be under `commandLineOptions:`, not at the YAML top level. This is a common mistake.

**LLM-as-judge returns 401 with relay API:**
Each `llm-rubric` assertion must have its own `provider` with `apiBaseUrl`. The main provider config is not inherited by grader assertions.

**HTML tags in model output inflating metrics:**
Models may output `<br>`, `<b>`, etc. in structured content. Strip HTML in Python assertions before measuring:
```python
import re
clean_text = re.sub(r'<[^>]+>', '', raw_text)
```

## Echo Provider (Preview Mode)

Use the **echo provider** to preview rendered prompts without making API calls:

```yaml
# promptfooconfig-preview.yaml
providers:
  - echo  # Returns prompt as output, no API calls

tests:
  - vars:
      input: "test content"
```

**Use cases:**
- Preview prompt rendering before expensive API calls
- Verify Few-shot examples are loaded correctly
- Debug variable substitution issues
- Validate prompt structure

```bash
# Run preview mode
npx promptfoo@latest eval --config promptfooconfig-preview.yaml
```

**Cost:** Free - no API tokens consumed.

## Advanced Few-Shot Implementation

### Multi-turn Conversation Pattern

For complex few-shot learning with full examples:

```json
[
  {"role": "system", "content": "{{system_prompt}}"},

  // Few-shot Example 1
  {"role": "user", "content": "Task: {{example_input_1}}"},
  {"role": "assistant", "content": "{{example_output_1}}"},

  // Few-shot Example 2 (optional)
  {"role": "user", "content": "Task: {{example_input_2}}"},
  {"role": "assistant", "content": "{{example_output_2}}"},

  // Actual test
  {"role": "user", "content": "Task: {{actual_input}}"}
]
```

**Test case configuration:**

```yaml
tests:
  - vars:
      system_prompt: file://prompts/system.md
      # Few-shot examples
      example_input_1: file://data/examples/input1.txt
      example_output_1: file://data/examples/output1.txt
      example_input_2: file://data/examples/input2.txt
      example_output_2: file://data/examples/output2.txt
      # Actual test
      actual_input: file://data/test1.txt
```

**Best practices:**
- Use 1-3 few-shot examples (more may dilute effectiveness)
- Ensure examples match the task format exactly
- Load examples from files for better maintainability
- Use echo provider first to verify structure

## Long Text Handling

For Chinese/long-form content evaluations (10k+ characters):

**Configuration:**

```yaml
providers:
  - id: anthropic:messages:claude-sonnet-4-6
    config:
      max_tokens: 8192  # Increase for long outputs

defaultTest:
  assert:
    - type: python
      value: file://scripts/metrics.py:check_length
```

**Python assertion for text metrics:**

```python
import re

def strip_tags(text: str) -> str:
    """Remove HTML tags for pure text."""
    return re.sub(r'<[^>]+>', '', text)

def check_length(output: str, context: dict) -> dict:
    """Check output length constraints."""
    raw_input = context['vars'].get('raw_input', '')

    input_len = len(strip_tags(raw_input))
    output_len = len(strip_tags(output))

    reduction_ratio = 1 - (output_len / input_len) if input_len > 0 else 0

    return {
        "pass": 0.7 <= reduction_ratio <= 0.9,
        "score": reduction_ratio,
        "reason": f"Reduction: {reduction_ratio:.1%} (target: 70-90%)",
        "named_scores": {
            "input_length": input_len,
            "output_length": output_len,
            "reduction_ratio": reduction_ratio
        }
    }
```

## Real-World Example

**Project:** Chinese short-video content curation from long transcripts

**Structure:**
```
tiaogaoren/
├── promptfooconfig.yaml          # Production config
├── promptfooconfig-preview.yaml  # Preview config (echo provider)
├── prompts/
│   ├── tiaogaoren-prompt.json   # Chat format with few-shot
│   └── v4/system-v4.md          # System prompt
├── tests/cases.yaml              # 3 test samples
├── scripts/metrics.py            # Custom metrics (reduction ratio, etc.)
├── data/                         # 5 samples (2 few-shot, 3 eval)
└── results/
```

**See:** `./tiaogaoren/` (example project root) for full implementation.

## Resources

For detailed API reference and advanced patterns, see [references/promptfoo_api.md](references/promptfoo_api.md).

© daymade · MIT · daymade/claude-code-skills

Похожие скиллы

Всё по теме «Тесты и QA» →
arize-evaluator arize Скилл для работы с оценщиками Arize: создаёт LLM-судью или детерминированный код-оценщик и прогоняет их по спанам и экспериментам. CX Codex +1 3 100установок agent-evaluation mlflow Скилл для системной оценки LLM-агентов через MLflow: датасеты, скореры, трейсинг. Пригодится, когда агент ошибается с выбором инструментов или даёт неполные ответы. CX Codex +1 1 100установок llm-evaluation Seth Hobson Скилл для оценки качества LLM-приложений: автоматические метрики, человеческая разметка, LLM-as-judge и бенчмарки. Пригодится, когда нужно сравнить модели или поймать регрессию до деплоя. CX Codex 12 755установок agent-evals devops-skills Скилл собирает автотесты для ИИ-агентов: golden-датасеты, рубрики оценки и регрессионные гейты в CI. Пригодится, когда меняешь промпты и хочешь видеть, что стало хуже. CX Codex +1 340установок eval-harness-first Seth Hobson Собирает оценочный контур до начала fine-tuning: goldens, graders, калибровку judge и baseline базовой модели. Нужен для контроля регрессий. CX Codex 2 600установок write-judge-prompt ai-evals-course Помогает собрать бинарный Pass/Fail оценщик для одного субъективного сбоя: тона, верности ответа, релевантности. Пригодится, когда кодом такое не проверить. CX Codex +1 1 100установок

Из того же набора64

github-ops Управляет состоянием GitHub через gh и API, а затем отдельно проверяет результат. Нужен для пул-реквестов, задач, репозиториев и прав доступа. CX Codex +1 1 500установок developing-ios-apps Скилл для сборки и выкладки iOS и macOS приложений: XcodeGen, SwiftUI, SPM, подпись, нотаризация и CI. Пригодится, когда Xcode падает на подписи или rpath. CX Codex +1 1 500установок meeting-minutes-taker Превращает сырые стенограммы встреч в структурированные протоколы с цитатами, выявляет спикеров и объединяет варианты без потери деталей. CX Codex +1 1 500установок fact-checker Скилл находит проверяемые утверждения в документе, сверяет их с официальными источниками и предлагает правки. Пригодится, когда в тексте устарели версии, цифры или характеристики моделей. CX Codex +1 1 500установок skills-search Ищет скиллы сначала в настроенных локальных репозиториях, затем в CCPM. Помогает найти, проверить, установить или обновить нужный скилл. CX Codex +1 1 400установок statusline-generator Ставит и чинит статусную строку Claude: путь, ветка git, модель и токены. Плюс проверка, что скрипт вообще запускается. CX Codex +1 1 400установок transcript-fixer Правит ошибки ASR/STT в расшифровках: омофоны, искажённые термины, имена, смешанный китайско-английский текст. Пригодится для заметок со встреч, лекций и интервью. CX Codex +1 1 200установок capture-screen Скилл для программного захвата скриншотов на macOS: находит окна по ID, управляет видом через AppleScript и снимает кадр через screencapture. CX Codex +1 1 000установок

Спонсоры проекта

Компании, которые помогают держать аналитику открытой