关于 promptfoo-evaluation
Promptfoo Evaluation 是一项使用开源命令行工具 Promptfoo 配置和运行大模型评测的技能,涵盖提示词格式、测试用例、断言、自定义 Python 检查及配置排错。
主要功能
- 01配置文本和聊天提示词
- 02定义测试用例和文件输入
- 03使用内置及自定义断言
- 04通过 LLM 评分标准评测输出
- 05用 echo 预览渲染后的提示词
适用场景
- 01比较大模型在不同测试用例中的输出
- 02按自定义标准检查回答质量
- 03调用 API 前预览提示词渲染
- 04排查评测配置问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ daymade/ claude-code-skills --skill promptfoo-evaluation安装后在 Claude Code 中输入/promptfoo-evaluation确认已加载。
附带文件references/promptfoo_api.mdscripts/metrics.py
详细介绍
Promptfoo 评估
概述
本技能提供使用 Promptfoo 配置和运行大模型评估的指南。Promptfoo 是一款开源命令行工具,用于测试和比较大模型的输出。
快速开始
# Initialize a new evaluation project
npx promptfoo@latest init
# Run evaluation
npx promptfoo@latest eval
# View results in browser
npx promptfoo@latest view
配置结构
典型的 Promptfoo 项目结构:
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
核心配置(promptfooconfig.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
提示词格式
文本提示词(system.md)
You are a helpful assistant.
Task: {{task}}
Context: {{context}}
聊天格式(chat.json)
[
{"role": "system", "content": "{{system_prompt}}"},
{"role": "user", "content": "{{user_input}}"}
]
Few-Shot 模式
将示例直接嵌入提示词中,或使用包含 assistant 消息的聊天格式:
[
{"role": "system", "content": "{{system_prompt}}"},
{"role": "user", "content": "Example input: {{example_input}}"},
{"role": "assistant", "content": "{{example_output}}"},
{"role": "user", "content": "Now process: {{actual_input}}"}
]
测试用例(tests/cases.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 自定义断言
创建一个 Python 文件来定义自定义断言(例如,scripts/metrics.py):
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}"
}
要点:
- 默认函数名为
get_assert - 使用
file://path.py:function_name指定函数 - 返回
bool、float(分数)或包含 pass/score/reason 的dict - 通过
context['vars']访问变量
LLM-as-Judge(llm-rubric)
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
使用中继/代理 API 时,每个 llm-rubric 断言都需要各自的 provider 配置,并设置 apiBaseUrl。否则,评分器会退回到默认的 Anthropic/OpenAI 端点,并出现 401 错误:
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
最佳实践:
- 提供清晰的评分标准
- 使用
threshold设置最低及格分数 - 默认评分器使用可用的 API 密钥(OpenAI → Anthropic → Google)
- 使用中继/代理时:每个
llm-rubric都必须有自己的provider和apiBaseUrl——不会继承主 provider 的apiBaseUrl
常见断言类型
| 类型 | 用途 | 示例 |
|---|---|---|
contains | 检查子字符串 | value: "hello" |
icontains | 不区分大小写 | value: "HELLO" |
equals | 精确匹配 | value: "42" |
regex | 模式匹配 | value: "\\d{4}" |
python | 自定义逻辑 | value: file://script.py |
llm-rubric | 大模型评分 | value: "Is professional" |
latency | 响应时间 | threshold: 1000 |
文件引用
所有 file:// 路径都相对于 promptfooconfig.yaml 所在位置解析(而非包含该引用的 YAML 文件)。当 tests: 引用了单独的 YAML 文件时,这一点很容易让人忽略——该测试文件中的 file:// 路径仍以配置根目录为基准解析。
# 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
运行评估
# 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
中继/代理 API 配置
使用 API 中继或代理,而非直接连接 Anthropic/OpenAI 端点时:
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
主要规则:
apiBaseUrl应放在providers[].config中——Promptfoo 会自动追加/v1/messagesmaxConcurrency必须放在commandLineOptions:下——放在顶层会被静默忽略- 使用中继进行 LLM-as-Judge 时,将
maxConcurrency: 1设为 1,以避免并发请求限制(生成和评分共用同一个池) - 将中继令牌作为
ANTHROPIC_API_KEY环境变量传入
故障排查
找不到 Python:
export PROMPTFOO_PYTHON=python3
较大的输出被截断:
超过 30000 个字符的输出会被截断。请在断言中使用 head_limit。
找不到文件错误:
所有 file:// 路径都相对于 promptfooconfig.yaml 所在位置解析。
maxConcurrency 被忽略(显示“每次最多 N 个”):
maxConcurrency 必须放在 commandLineOptions: 下,而不是 YAML 顶层。这是一个常见错误。
LLM-as-Judge 使用中继 API 时返回 401:
每个 llm-rubric 断言都必须有自己的 provider 和 apiBaseUrl。评分器断言不会继承主 provider 配置。
模型输出中的 HTML 标签导致指标虚高:
模型可能会在结构化内容中输出 <br>、<b> 等标签。在进行测量前,请在 Python 断言中移除 HTML:
import re
clean_text = re.sub(r'<[^>]+>', '', raw_text)
Echo Provider(预览模式)
使用 echo provider 预览渲染后的提示词,而不发起 API 调用:
# promptfooconfig-preview.yaml
providers:
- echo # Returns prompt as output, no API calls
tests:
- vars:
input: "test content"
适用场景:
- 在发起耗费较高的 API 调用前预览提示词渲染结果
- 验证 Few-shot 示例是否正确加载
- 调试变量替换问题
- 验证提示词结构
# Run preview mode
npx promptfoo@latest eval --config promptfooconfig-preview.yaml
费用: 免费——不会消耗 API 令牌。
高级少样本实现
多轮对话模式
对于包含完整示例的复杂少样本学习:
[
{"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}}"}
]
测试用例配置:
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
最佳实践:
- 使用 1-3 个少样本示例(更多示例可能会降低效果)
- 确保示例与任务格式完全一致
- 从文件加载示例,以便更好地维护
- 先使用 echo provider 验证结构
长文本处理
对于中文/长篇内容评估(10k+ 字符):
配置:
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 断言:
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
}
}
实际示例
项目: 基于长篇转录文本整理中文短视频内容
结构:
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/
参见: ./tiaogaoren/(示例项目根目录),查看完整实现。
资源
如需详细的 API 参考和高级模式,请参见 references/promptfoo_api.md。