340 lines
17 KiB
Markdown
340 lines
17 KiB
Markdown
# 测试 API 参考
|
||
|
||
> 自动从 pixie 源代码文档字符串生成。
|
||
> 请勿手动编辑——请运行 `uv run python scripts/generate_skill_docs.py`。
|
||
|
||
pixie.evals —— LLM 应用程序的评估框架。
|
||
|
||
公开 API: - `Evaluation` —— 单次评估器运行的结果数据类 - `Evaluator` —— 评估可调用对象的协议 - `evaluate` —— 针对一个可评估对象运行一个评估器 - `run_and_evaluate` —— 评估来自 MemoryTraceHandler 的跨度 - `assert_pass` —— 带通过/失败条件的批量评估 - `assert_dataset_pass` —— 加载数据集并运行 assert_pass - `EvalAssertionError` —— 当 assert_pass 失败时抛出 - `capture_traces` —— 用于内存中追踪捕获的上下文管理器 - `MemoryTraceHandler` —— 收集跨度的 InstrumentationHandler - `ScoreThreshold` —— 可配置的通过标准 - `last_llm_call` / `root` —— 追踪到可评估对象的辅助函数 - `DatasetEntryResult` —— 单个数据集条目的评估结果 - `DatasetScorecard` —— 带非统一评估器的按数据集评分卡 - `generate_dataset_scorecard_html` —— 将评分卡渲染为 HTML - `save_dataset_scorecard` —— 将评分卡 HTML 写入磁盘
|
||
|
||
预置评估器(autoevals 适配器): - `AutoevalsAdapter` —— 任意 autoevals `Scorer` 的通用包装器 - `LevenshteinMatch` —— 编辑距离字符串相似度 - `ExactMatch` —— 精确值比较 - `NumericDiff` —— 归一化数值差异 - `JSONDiff` —— 结构化 JSON 比较 - `ValidJSON` —— JSON 语法 / Schema 验证 - `ListContains` —— 列表重叠 - `EmbeddingSimilarity` —— 嵌入余弦相似度 - `Factuality` —— LLM 事实准确性检查 - `ClosedQA` —— 闭卷问答评估 - `Battle` —— 两两对比 - `Humor` —— 幽默检测 - `Security` —— 安全漏洞检查 - `Sql` —— SQL 等价性 - `Summary` —— 摘要质量 - `Translation` —— 翻译质量 - `Possible` —— 可行性检查 - `Moderation` —— 内容审核 - `ContextRelevancy` —— RAGAS 上下文相关性 - `Faithfulness` —— RAGAS 忠实度 - `AnswerRelevancy` —— RAGAS 答案相关性 - `AnswerCorrectness` —— RAGAS 答案正确性
|
||
|
||
## 数据集 JSON 格式
|
||
|
||
数据集是一个 JSON 对象,包含以下顶层字段:
|
||
|
||
```json
|
||
{
|
||
"name": "customer-faq",
|
||
"runnable": "pixie_qa/run_app.py:AppRunnable",
|
||
"evaluators": ["Factuality"],
|
||
"entries": [
|
||
{
|
||
"input_data": { "question": "Hello" },
|
||
"description": "Basic greeting",
|
||
"eval_input": [{ "name": "input", "value": "Hello" }],
|
||
"expectation": "A friendly greeting that offers to help",
|
||
"evaluators": ["...", "ClosedQA"]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 条目结构
|
||
|
||
所有字段均在每个条目上作为顶层字段(扁平结构——无嵌套):
|
||
|
||
```
|
||
entry:
|
||
├── input_data (必需)—— Runnable.run() 的参数
|
||
├── eval_input (可选)—— {"name": ..., "value": ...} 对象的列表(默认:[])
|
||
├── description (必需)——测试用例的人类可读标签
|
||
├── expectation (可选)——用于基于比较的评估器的参考值
|
||
├── eval_metadata (可选)——用于自定义评估器的额外逐条目数据
|
||
└── evaluators (可选)——此条目的评估器名称
|
||
```
|
||
|
||
### 字段参考
|
||
|
||
- `runnable`(必需):驱动评估期间应用的 `Runnable` 子类的 `filepath:ClassName` 引用。
|
||
- `evaluators`(数据集级别,可选):默认评估器名称——应用于每个未声明自己的 `evaluators` 的条目。
|
||
- `entries[].input_data`(必需):作为 Pydantic 模型传递给 `Runnable.run()` 的关键字参数。键必须与 `run(args: T)` 中使用的 Pydantic 模型的字段匹配。
|
||
- `entries[].description`(必需):测试用例的人类可读标签。
|
||
- `entries[].eval_input`(可选,默认 `[]`):`{"name": ..., "value": ...}` 对象的列表。用于填充包装输入注册表——应用中的 `wrap(purpose="input")` 调用返回按 `name` 键控的注册表值。运行器在构建 `Evaluable` 时会自动前置 `input_data`。
|
||
- `entries[].expectation`(可选):针对基于比较的评估器的简洁预期描述。应描述正确输出的样子,**而不是**逐字复制输出。对追踪使用 `pixie format` 以查看实际输出形状,然后编写更短的描述。
|
||
- `entries[].eval_metadata`(可选):用于自定义评估器的额外逐条目数据——例如,预期的工具名称、布尔标志、阈值。在评估器中通过 `evaluable.eval_metadata` 访问。
|
||
- `entries[].evaluators`(可选):行级别的评估器覆盖。规则:
|
||
- 省略 → 条目继承数据集级别的 `evaluators`。
|
||
- `["...", "ClosedQA"]` → 数据集默认值 **加上** ClosedQA。
|
||
- `["OnlyThis"]`(无 `"..."`)→ **仅** OnlyThis,不包含默认值。
|
||
|
||
## 评估器名称解析
|
||
|
||
在数据集 JSON 中,评估器名称按如下方式解析:
|
||
|
||
- **内置名称**(裸名称,如 `"Factuality"`、`"ExactMatch"`)会自动解析为 `pixie.{Name}`。
|
||
- **自定义评估器**使用 `filepath:callable_name` 格式(例如 `"pixie_qa/evaluators.py:my_evaluator"`)。
|
||
- 自定义评估器引用指向模块级别的可调用对象——类(自动实例化)、工厂函数(如果零参数则调用)、评估器函数(按原样使用)或预实例化的可调用对象(例如 `create_llm_evaluator` 的结果——按原样使用)。
|
||
|
||
## CLI 命令
|
||
|
||
| 命令 | 描述 |
|
||
| ------------------------------------------- | ----------------------------- |
|
||
| `pixie test [path] [-v] [--no-open]` | 对数据集文件运行评估测试 |
|
||
| `pixie dataset create <name>` | 创建一个新的空数据集 |
|
||
| `pixie dataset list` | 列出所有数据集 |
|
||
| `pixie dataset save <name> [--select MODE]` | 将跨度保存到数据集中 |
|
||
| `pixie dataset validate [path]` | 验证数据集 JSON 文件 |
|
||
| `pixie analyze <test_run_id>` | 生成分析和建议 |
|
||
|
||
---
|
||
|
||
## 类型
|
||
|
||
### `Evaluable`
|
||
|
||
```python
|
||
class Evaluable(TestCase):
|
||
eval_output: list[NamedData] # wrap(purpose="output") + wrap(purpose="state") 值
|
||
# 继承自 TestCase:
|
||
# eval_input: list[NamedData] # 来自数据集条目中的 eval_input
|
||
# expectation: JsonValue | _Unset # 来自数据集条目中的 expectation
|
||
# eval_metadata: dict[str, JsonValue] | None # 来自数据集条目中的 eval_metadata
|
||
# description: str | None
|
||
```
|
||
|
||
评估器的数据载体。使用实际输出扩展 `TestCase`。
|
||
|
||
- `eval_input` —— 从条目的 `eval_input` 字段加上 `input_data`(由运行器前置)填充的 `list[NamedData]`。始终至少包含一个条目。
|
||
- `eval_output` —— 包含运行期间捕获的所有 `wrap(purpose="output")` 和 `wrap(purpose="state")` 值的 `list[NamedData]`。每个条目都有 `.name`(str)和 `.value`(JsonValue)。使用 `_get_output(evaluable, "name")` 按名称查找。
|
||
- `eval_metadata` —— 来自条目 `eval_metadata` 字段的 `dict[str, JsonValue] | None`
|
||
- `expected_output` —— 来自数据集的预期文本(如果未提供则为 `UNSET`)
|
||
|
||
属性:
|
||
eval_input:命名输入数据项(来自数据集 + 运行器前置的 input_data)。始终非空。
|
||
eval_output:命名输出数据项(来自运行期间的 wrap 调用)。
|
||
每个条目都有 `.name`(str)和 `.value`(JsonValue)。
|
||
包含所有 `wrap(purpose="output")` 和 `wrap(purpose="state")` 值。
|
||
eval_metadata:补充元数据(不存在时为 `None`)。
|
||
expected_output:用于评估的预期/参考输出。
|
||
默认为 `UNSET`(未提供)。可被显式
|
||
设置为 `None`,表示"没有预期输出"。
|
||
|
||
### 测试时 `wrap()` 如何映射到 `Evaluable` 字段
|
||
|
||
当 `pixie test` 运行数据集条目时,应用中的 `wrap()` 调用会填充评估器接收的 `Evaluable`:
|
||
|
||
| 应用代码中的 `wrap()` 调用 | Evaluable 字段 | 类型 | 在评估器中的访问方式 |
|
||
| --------------------------------------------- | --------------- | ----------------- | --------------------------------------------------------- |
|
||
| `wrap(data, purpose="input", name="X")` | `eval_input` | `list[NamedData]` | 从数据集条目的 `eval_input` 预填充 |
|
||
| `wrap(data, purpose="output", name="X")` | `eval_output` | `list[NamedData]` | `_get_output(evaluable, "X")` —— 参见下面的辅助函数 |
|
||
| `wrap(data, purpose="state", name="X")` | `eval_output` | `list[NamedData]` | `_get_output(evaluable, "X")` —— 与输出相同的列表 |
|
||
| (来自数据集条目 `expectation`) | `expected_output` | `str \| None` | `evaluable.expected_output` |
|
||
| (来自数据集条目 `eval_metadata`) | `eval_metadata` | `dict \| None` | `evaluable.eval_metadata` |
|
||
|
||
**关键理解**:`purpose="output"` 和 `purpose="state"` 的 wrap 值最终都作为 `NamedData` 条目进入 `eval_output`。没有单独的 `captured_output` 或 `captured_state` 字典。使用下面的辅助函数按 wrap 名称查找值:
|
||
|
||
```python
|
||
def _get_output(evaluable: Evaluable, name: str) -> Any:
|
||
"""Look up a wrap value by name from eval_output."""
|
||
for item in evaluable.eval_output:
|
||
if item.name == name:
|
||
return item.value
|
||
return None
|
||
```
|
||
|
||
**`eval_metadata`** 用于向评估器传递额外的逐条目数据,这些数据既不是输入数据也不是输出——例如,预期的工具名称、布尔标志、阈值。定义为条目的顶层字段,通过 `evaluable.eval_metadata` 访问。
|
||
|
||
**完整的自定义评估器示例**(工具调用检查 + 数据集条目):
|
||
|
||
```python
|
||
from pixie import Evaluation, Evaluable
|
||
|
||
def _get_output(evaluable: Evaluable, name: str) -> Any:
|
||
"""Look up a wrap value by name from eval_output."""
|
||
for item in evaluable.eval_output:
|
||
if item.name == name:
|
||
return item.value
|
||
return None
|
||
|
||
def tool_call_check(evaluable: Evaluable, *, trace=None) -> Evaluation:
|
||
expected = evaluable.eval_metadata.get("expected_tool") if evaluable.eval_metadata else None
|
||
actual = _get_output(evaluable, "function_called")
|
||
if expected is None:
|
||
return Evaluation(score=1.0, reasoning="No expected_tool specified")
|
||
match = str(actual) == str(expected)
|
||
return Evaluation(
|
||
score=1.0 if match else 0.0,
|
||
reasoning=f"Expected {expected}, got {actual}",
|
||
)
|
||
```
|
||
|
||
对应的数据集条目:
|
||
|
||
```json
|
||
{
|
||
"input_data": { "user_message": "I want to end this call" },
|
||
"description": "User requests call end after failed verification",
|
||
"eval_input": [{ "name": "user_input", "value": "I want to end this call" }],
|
||
"expectation": "Agent should call endCall tool",
|
||
"eval_metadata": {
|
||
"expected_tool": "endCall",
|
||
"expected_call_ended": true
|
||
},
|
||
"evaluators": ["...", "pixie_qa/evaluators.py:tool_call_check"]
|
||
}
|
||
```
|
||
|
||
### `Evaluation`
|
||
|
||
```python
|
||
Evaluation(score: 'float', reasoning: 'str', details: 'dict[str, Any]' = <factory>) -> None
|
||
```
|
||
|
||
单个评估器应用于单个测试用例的结果。
|
||
|
||
属性:
|
||
score:介于 0.0 和 1.0 之间的评估分数。
|
||
reasoning:人类可读的解释(必需)。
|
||
details:任意的 JSON 可序列化元数据。
|
||
|
||
### `ScoreThreshold`
|
||
|
||
```python
|
||
ScoreThreshold(threshold: 'float' = 0.5, pct: 'float' = 1.0) -> None
|
||
```
|
||
|
||
通过标准:在所有评估器上,_pct_ 比例的输入必须得分 >= _threshold_。
|
||
|
||
属性:
|
||
threshold:单次评估必须达到的最低分数。
|
||
pct:必须通过的测试用例输入比例(0.0–1.0)。
|
||
|
||
## 评估函数
|
||
|
||
### `pixie.run_and_evaluate`
|
||
|
||
```python
|
||
pixie.run_and_evaluate(evaluator: 'Callable[..., Any]', runnable: 'Callable[..., Any]', eval_input: 'Any', *, expected_output: 'Any' = <object object at 0x7788c2ad5c80>, from_trace: 'Callable[[list[ObservationNode]], Evaluable] | None' = None) -> 'Evaluation'
|
||
```
|
||
|
||
在捕获追踪的同时运行 _runnable(eval_input)_,然后进行评估。
|
||
|
||
结合了 `_run_and_capture` 和 `evaluate` 的便捷包装器。
|
||
runnable 仅被调用一次。
|
||
|
||
参数:
|
||
evaluator:评估器可调用对象(同步或异步)。
|
||
runnable:要测试的应用函数。
|
||
eval_input:传递给 _runnable_ 的单个输入。
|
||
expected_output:合并到 evaluable 中的可选预期值。
|
||
from_trace:用于从追踪树中选择特定跨度进行评估的可选可调用对象。
|
||
|
||
返回:
|
||
`Evaluation` 结果。
|
||
|
||
抛出:
|
||
ValueError:如果执行期间未捕获到任何跨度。
|
||
|
||
### `pixie.assert_pass`
|
||
|
||
```python
|
||
pixie.assert_pass(runnable: 'Callable[..., Any]', eval_inputs: 'list[Any]', evaluators: 'list[Callable[..., Any]]', *, evaluables: 'list[Evaluable] | None' = None, pass_criteria: 'Callable[[list[list[Evaluation]]], tuple[bool, str]] | None' = None, from_trace: 'Callable[[list[ObservationNode]], Evaluable] | None' = None) -> 'None'
|
||
```
|
||
|
||
在多个输入上对 runnable 运行评估器。
|
||
|
||
对于每个输入,通过 `_run_and_capture` 运行一次 runnable,
|
||
然后通过 `asyncio.gather` 并发地使用每个评估器进行评估。
|
||
|
||
结果矩阵的形状为 `[eval_inputs][evaluators]`。
|
||
如果未满足通过标准,则抛出携带该矩阵的 :class:`EvalAssertionError`。
|
||
|
||
当提供了 `evaluables` 时,行为取决于每个条目是否已填充了 `eval_output`:
|
||
|
||
- **eval_output 为 None** —— 通过 `run_and_evaluate` 调用 `runnable` 以从追踪生成输出,并且来自 evaluable 的 `expected_output` 会合并到结果中。
|
||
- **eval_output 不为 None** —— 直接使用该 evaluable(该条目不调用 runnable)。
|
||
|
||
参数:
|
||
runnable:要测试的应用函数。
|
||
eval_inputs:输入列表,每个都传递给 _runnable_。
|
||
evaluators:评估器可调用对象列表。
|
||
evaluables:可选的 `Evaluable` 项列表,每个输入对应一个。
|
||
当提供时,它们的 `expected_output` 会被转发到
|
||
`run_and_evaluate`。必须与 _eval_inputs_ 具有相同的长度。
|
||
pass_criteria:接收结果矩阵,返回
|
||
`(passed, message)`。默认为 `ScoreThreshold()`。
|
||
from_trace:可选的跨度选择器,转发到
|
||
`run_and_evaluate`。
|
||
|
||
抛出:
|
||
EvalAssertionError:当未满足通过标准时。
|
||
ValueError:当 _evaluables_ 长度与 _eval_inputs_ 不匹配时。
|
||
|
||
### `pixie.assert_dataset_pass`
|
||
|
||
```python
|
||
pixie.assert_dataset_pass(runnable: 'Callable[..., Any]', dataset_name: 'str', evaluators: 'list[Callable[..., Any]]', *, dataset_dir: 'str | None' = None, pass_criteria: 'Callable[[list[list[Evaluation]]], tuple[bool, str]] | None' = None, from_trace: 'Callable[[list[ObservationNode]], Evaluable] | None' = None) -> 'None'
|
||
```
|
||
|
||
按名称加载数据集,然后对其条目运行 `assert_pass`。
|
||
|
||
这是一个便捷包装器,它:
|
||
|
||
1. 从 `DatasetStore` 加载数据集。
|
||
2. 从每个条目中提取 `eval_input` 作为 runnable 输入。
|
||
3. 使用完整的 `Evaluable` 项(它们携带 `expected_output`)
|
||
作为评估对象。
|
||
4. 委托给 `assert_pass`。
|
||
|
||
参数:
|
||
runnable:要测试的应用函数。
|
||
dataset_name:要加载的数据集的名称。
|
||
evaluators:评估器可调用对象列表。
|
||
dataset_dir:数据集存储的覆盖目录。
|
||
当为 `None` 时,从 `PixieConfig.dataset_dir` 读取。
|
||
pass_criteria:接收结果矩阵,返回
|
||
`(passed, message)`。
|
||
from_trace:可选的跨度选择器,转发到
|
||
`assert_pass`。
|
||
|
||
抛出:
|
||
FileNotFoundError:如果不存在名为 _dataset_name_ 的数据集。
|
||
EvalAssertionError:当未满足通过标准时。
|
||
|
||
## 追踪辅助函数
|
||
|
||
### `pixie.last_llm_call`
|
||
|
||
```python
|
||
pixie.last_llm_call(trace: 'list[ObservationNode]') -> 'Evaluable'
|
||
```
|
||
|
||
在追踪树中找到具有最新 `ended_at` 的 `LLMSpan`。
|
||
|
||
参数:
|
||
trace:追踪树(根 `ObservationNode` 实例的列表)。
|
||
|
||
返回:
|
||
包装最近结束的 `LLMSpan` 的 `Evaluable`。
|
||
|
||
抛出:
|
||
ValueError:如果追踪中不存在 `LLMSpan`。
|
||
|
||
### `pixie.root`
|
||
|
||
```python
|
||
pixie.root(trace: 'list[ObservationNode]') -> 'Evaluable'
|
||
```
|
||
|
||
将第一个根节点的跨度作为 `Evaluable` 返回。
|
||
|
||
参数:
|
||
trace:追踪树(根 `ObservationNode` 实例的列表)。
|
||
|
||
返回:
|
||
包装第一个根节点跨度的 `Evaluable`。
|
||
|
||
抛出:
|
||
ValueError:如果追踪为空。
|
||
|
||
### `pixie.capture_traces`
|
||
|
||
```python
|
||
pixie.capture_traces() -> 'Generator[MemoryTraceHandler, None, None]'
|
||
```
|
||
|
||
安装 `MemoryTraceHandler` 并返回它的上下文管理器。
|
||
|
||
调用 `init()`(如果已初始化则为空操作),然后通过 `add_handler()` 注册该处理器。退出时,处理器被移除,交付队列被刷新,以便所有跨度在 `handler.spans` 上可用。
|