# 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 --input --output ` | 使用 JSON 文件中的 kwargs 运行一次 Runnable 并写入追踪文件。`--input` 是一个**文件路径**(非内联 JSON)。 | | `pixie format --input --output ` | 将追踪文件转换为格式化数据集条目模板。展示 `input_data`、`eval_input` 和 `eval_output`(实际捕获的输出)。 | | `pixie trace filter --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"`。 name:wrap 点名称(与 `wrap(name=...)` 匹配)。 purpose:`"input"`、`"output"` 或 `"state"` 之一。 data:序列化后的数据(jsonpickle 字符串)。 description:可选的人类可读描述。 trace_id:OTel 追踪 ID(如有)。 span_id:OTel 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_path:JSONL 追踪文件的路径。 返回: :class:`WrapLogEntry` 对象的列表。 ### `pixie.filter_by_purpose` ```python pixie.filter_by_purpose(entries: 'list[WrapLogEntry]', purposes: 'set[str]') -> 'list[WrapLogEntry]' ``` 按 purpose 过滤 wrap 日志条目。 参数: entries:wrap 日志条目列表。 purposes:要包含的 purpose 值集合。 返回: 过滤后的列表。