Files
2026-07-13 21:35:56 +08:00

222 lines
9.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Wrap API 参考文档
> 自动从 pixie 源代码文档字符串生成。
> 请勿手动编辑——运行 `uv run python scripts/generate_skill_docs.py`。
`pixie.wrap` —— 面向数据的观测 API。
`wrap()` 在处理管道的命名点观测一个数据值或可调用对象。其行为取决于当前模式:
- **无操作模式**(追踪已禁用,无 eval 注册表):直接返回 `data`,不做任何改变。
- **追踪模式**(在 `pixie trace` 期间):写入追踪文件并发出一个 OTel 事件(如果 span 处于活动状态则通过 span event,否则通过 OTel logger),然后返回 `data` 不做改变(或者包装一个可调用对象,使得事件在调用时触发)。
- **评估模式**(eval 注册表已激活):为 `purpose="input"` 注入依赖数据,为 `purpose="output"` / `purpose="state"` 捕获输出/状态。
---
## CLI 命令
| 命令 | 描述 |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `pixie trace --runnable <filepath:ClassName> --input <kwargs.json> --output <file.jsonl>` | 使用 JSON 文件中的 kwargs 运行一次 Runnable 并写入追踪文件。`--input` 是一个**文件路径**(非内联 JSON)。 |
| `pixie format --input <trace.jsonl> --output <dataset_entry.json>` | 将追踪文件转换为格式化数据集条目模板。展示 `input_data``eval_input``eval_output`(实际捕获的输出)。 |
| `pixie trace filter <file.jsonl> --purpose input` | 仅打印匹配指定 purpose 的 wrap 事件。每个匹配事件输出一行 JSON。 |
---
## 类
### `pixie.Runnable`
```python
class pixie.Runnable(Protocol[T]):
@classmethod
def create(cls) -> Runnable[Any]: ...
async def setup(self) -> None: ...
async def run(self, args: T) -> None: ...
async def teardown(self) -> None: ...
```
供评估框架使用的结构化 Runnable 协议。`T``pydantic.BaseModel` 的子类,其字段与数据集 JSON 中的 `input_data` 键对应。
生命周期:
1. `create()` —— 类方法,用于构造并返回一个 runnable 实例。
2. `setup()` —— **异步**,在首次 `run()` 调用之前**仅调用一次**。在此初始化共享资源(例如 `TestClient`、数据库连接)。可选——具有默认的空操作实现。
3. `run(args)` —— **异步**,对**每个数据集条目并发调用**(最多并行 4 个条目)。`args` 是根据 `input_data` 构建的经过验证的 Pydantic 模型。调用应用程序的真实入口点。
4. `teardown()` —— **异步**,在最后一次 `run()` 调用之后**仅调用一次**。释放在 `setup()` 中获取的任何资源。可选——具有默认的空操作实现。
`setup()``teardown()` 具有默认的空操作实现;仅在需要共享资源时才需重写它们。
**并发性**`run()` 通过 `asyncio.gather` 并发调用。你的实现**必须是并发安全的**。如果它使用了共享的可变状态(例如 SQLite 连接、内存缓存、文件句柄),请使用 `asyncio.Semaphore``asyncio.Lock` 进行保护:
```python
class AppRunnable(pixie.Runnable[AppArgs]):
_sem: asyncio.Semaphore
@classmethod
def create(cls) -> "AppRunnable":
inst = cls()
inst._sem = asyncio.Semaphore(1) # serialise DB access
return inst
async def run(self, args: AppArgs) -> None:
async with self._sem:
await call_app(args.message)
```
常见的并发陷阱:
- **SQLite**:对并发写入不安全——使用 `Semaphore(1)` 或启用 WAL 模式的 `aiosqlite`
- **全局可变状态**:在 `run()` 中修改的模块级 dict/list 需要保护。
- **速率受限的 API**:添加信号量以避免 429 错误。
**导入解析**:项目根目录(即调用 `pixie test` / `pixie trace` 的目录)会在加载 runnable 和 evaluator 之前自动添加到 `sys.path` 中。这意味着你的 runnable 可以使用常规的 `import` 语句来引用项目模块(例如 `from app import service`)。
**示例**
```python
# pixie_qa/run_app.py
from pydantic import BaseModel
import pixie
class AppArgs(BaseModel):
user_message: str
class AppRunnable(pixie.Runnable[AppArgs]):
@classmethod
def create(cls) -> "AppRunnable":
return cls()
async def run(self, args: AppArgs) -> None:
from myapp import handle_request
await handle_request(args.user_message)
```
**Web 服务器示例**(使用异步 HTTP 客户端):
```python
import httpx
from pydantic import BaseModel
import pixie
class AppArgs(BaseModel):
user_message: str
class AppRunnable(pixie.Runnable[AppArgs]):
_client: httpx.AsyncClient
@classmethod
def create(cls) -> "AppRunnable":
return cls()
async def setup(self) -> None:
self._client = httpx.AsyncClient(base_url="http://localhost:8000")
async def run(self, args: AppArgs) -> None:
await self._client.post("/chat", json={"message": args.user_message})
async def teardown(self) -> None:
await self._client.aclose()
```
---
## 函数
### `pixie.wrap`
```python
pixie.wrap(data: 'T', *, purpose: "Literal['input', 'output', 'state']", name: 'str', description: 'str | None' = None) -> 'T'
```
在处理管道的某个点观测一个数据值或数据提供者可调用对象。
`data` 可以是一个纯值,也可以是一个产生值的可调用对象。两种情况下返回类型均为 `T` —— 在无操作模式或追踪模式下,调用者取回与传入时完全相同的类型。
在评估模式下且 `purpose="input"` 时,返回值(或可调用对象)会被替换为反序列化后的注册表值。当 `data` 为可调用对象时,返回的包装器会忽略原始函数,每次调用都返回注入的值;在所有其他模式下,返回的可调用对象会包装原始函数并添加追踪或捕获行为。
参数:
data:一个数据值或数据提供者可调用对象。
purpose:数据点的分类:- "input":来自外部依赖的数据(数据库记录、API 响应)- "output":发往外部系统或用户的数据 - "state":用于评估的中间状态(路由决策等)
name:此数据点的唯一标识符。用作 eval 注册表中的键以及追踪日志中的键。
description:可选的人类可读描述,说明此数据是什么。
返回:
原始数据不做改变(追踪/无操作模式),或注册表值(评估模式下且 purpose="input")。当 `data` 为可调用对象时,返回值也是可调用的。
---
## 错误类型
### `WrapRegistryMissError`
```python
WrapRegistryMissError(name: 'str') -> 'None'
```
当在 eval 注册表中未找到 wrap(purpose="input") 的名称时抛出。
### `WrapTypeMismatchError`
```python
WrapTypeMismatchError(name: 'str', expected_type: 'type', actual_type: 'type') -> 'None'
```
当反序列化后的注册表值与期望类型不匹配时抛出。
---
## 追踪文件工具函数
用于 wrap 日志条目的 Pydantic 模型及 JSONL 加载工具函数。
`WrapLogEntry` 是记录在 JSONL 追踪文件中的单个 `wrap()` 事件的类型化表示。代码库中有多个地方会加载这些对象——`pixie trace filter` CLI、数据集加载器和验证脚本——因此它们共享同一个模型。
### `pixie.WrapLogEntry`
```python
pixie.WrapLogEntry(*, type: str = 'wrap', name: str, purpose: str, data: Any, description: str | None = None, trace_id: str | None = None, span_id: str | None = None) -> None
```
记录到 JSONL 追踪文件中的单个 wrap() 事件。
属性:
type:对于 wrap 事件,始终为 `"wrap"`
namewrap 点名称(与 `wrap(name=...)` 匹配)。
purpose`"input"``"output"``"state"` 之一。
data:序列化后的数据(jsonpickle 字符串)。
description:可选的人类可读描述。
trace_idOTel 追踪 ID(如有)。
span_idOTel span ID(如有)。
### `pixie.load_wrap_log_entries`
```python
pixie.load_wrap_log_entries(jsonl_path: 'str | Path') -> 'list[WrapLogEntry]'
```
从 JSONL 文件加载所有 wrap 日志条目。
跳过非 wrap 行(例如 `type=llm_span`)及格式错误的行。
参数:
jsonl_pathJSONL 追踪文件的路径。
返回:
:class:`WrapLogEntry` 对象的列表。
### `pixie.filter_by_purpose`
```python
pixie.filter_by_purpose(entries: 'list[WrapLogEntry]', purposes: 'set[str]') -> 'list[WrapLogEntry]'
```
按 purpose 过滤 wrap 日志条目。
参数:
entrieswrap 日志条目列表。
purposes:要包含的 purpose 值集合。
返回:
过滤后的列表。