用 Litefuse 追踪 Pi
Pi 是一个极简的终端编码 Agent。本集成是一个 Pi 扩展,安装在 ~/.pi/agent/extensions/litefuse/ —— 单个 TypeScript 文件,零 npm 依赖。Pi 在进程内加载它,扩展订阅生命周期事件(agent_start、before_provider_request、message_end、tool_execution_start/end、agent_end),为每个用户回合生成一条 Litefuse trace,使用真实的逐事件时间戳。
Span 以裸 OTLP/HTTP JSON 形式、用 Node 内置 fetch 直接发送 —— 没有 SDK、没有 node_modules、不需要构建步骤(Pi 直接加载 TypeScript)。
给 AI —— 自动安装
如果你正在和 Pi 对话,把下面这段 prompt 粘贴过去,Agent 会端到端完成整个安装:
Read https://litefuse.ai/SKILL.md and follow the instructions to install and configure Litefuse for Pi.
skill 会向你索取 Litefuse 的 API Key(如果还没有账号,会引导你注册),然后在本地完成全部配置。如果你想手工一步步配置,请继续往下看。
会捕获哪些数据
| 数据 | 捕获方式 | 备注 |
|---|---|---|
| 用户 prompt | trace input | 图片 block 数量记入 metadata |
| 每次 LLM API 调用 | generation observation | plan (n tools) #N / response / think #N,按模型这一步做了什么命名 |
| thinking / text / toolCall block | generation output | 保留块结构,包含 reasoning 文本 |
| 首 token 时间 | generation 的 completion_start_time | 来自第一个流式 delta —— 驱动 Litefuse 的 TTFT 指标 |
| 采样参数 | generation 的 model_parameters | temperature / max_tokens / top_p,provider payload 携带时采集 |
token 用量(input、output、cache_read_input_tokens、cache_creation_input_tokens) | generation 的 usage_details | Anthropic 风格 key,Litefuse 成本映射可用 |
| 模型名 + provider 成本 | generation 的 model + cost_details | 有 Pi 自带成本数据就用,否则 Litefuse 按价格表计算 |
| 工具执行 | tool observation | tool: bash (grep) #N、tool: read (index.ts) #N —— 名字带关键信息,完整参数在 input |
| 子 agent(single / parallel / chain) | 子树 | tool (n subagents) #N → subagent 容器 → 子 agent 自己的 plan/tool/response 步骤;子成本汇入父 trace |
| 工具错误 | tool observation,level=ERROR | 带 status_message 预览 |
| LLM 错误 / 中止 | generation level=ERROR / WARNING | 来自 stopReason,metadata 带 HTTP 状态码与 request id |
| 上下文压缩 | event observation | 解释下一次调用 input token 骤降 |
| 上下文窗口水位 | trace metadata agent_context_usage | 回合结束时的 tokens / window / percent |
| 会话分组 | trace 的 session_id | Pi 会话 UUID;续接会话 Turn 编号接着数 |
| 用户身份 | trace 的 user_id | $LITEFUSE_USER_ID,回退到系统用户名 |
| 标签 | trace 的 tags | pi-agent + model:<name> |
Trace 结构
一个委派了子 agent 的典型回合产生的 trace 形如:
Pi Agent — Turn 1 (AGENT root —— 真实回合时长)
├── plan (1 tool) #1 (generation,usage_details,真实延迟)
├── tool: bash (grep) #2 (tool)
├── plan (1 tool) #3 (generation)
├── tool (1 subagent) #4 (tool —— 父进程视角的委派)
│ └── subagent (AGENT 容器 —— 子 pi 进程)
│ ├── plan (1 tool) #1 (容器内局部编号)
│ ├── tool: ls (demo-src) #2
│ └── subagent response (generation —— 子 agent 的最终回答)
└── response (generation —— 最终回答,带自己的 usage)设计说明:
- Generation 按”模型这一步做了什么”命名,而不是用哪个模型(
plan (2 tools)、response、think)—— 模型名是 generation 的model属性,换模型不会破坏看板。名字在消息完整后一次定型,绝不中途改名。 response就是最后一次 LLM 调用本身。 Pi 的 agent 循环在 assistant 消息不含工具调用时结束 —— 那条消息就是最终回答,因此它携带真实的 token 用量与延迟,不再额外发一条收尾 observation。- 每个 agent 容器一个步骤计数器:
#N由 generation 和 tool 按时间顺序共用。tool 的 metadataagent_plan_step指向发起它的 plan 的agent_step_index—— join 条件是tool.agent_plan_step == generation.agent_step_index。每个子 agent 容器从#1重新计数,层级由树结构表达。 - 子 agent 上下文经环境变量传播:
subagent工具开始执行时,扩展导出LITEFUSE_TRACEPARENT=00-<traceId>-<toolSpanId>-01。spawn 出的子 Pi 进程继承该变量,启动时检测到即加入父 trace 形成子树,而不是各开各的 trace —— 嵌套委派递归成立。工具 span 与容器的时长差还顺带暴露了委派的真实开销(进程启动、运行时加载)。 - 每个 span 恰好发送一次、在它结束时 —— OTel 的 span 不可变;没有临时发送、没有 upsert。trace header(name / session / user / input / tags)随每个 span 携带,所以第一个 observation(通常是几秒内完成的
plan #1)一完成,trace 就出现在 Litefuse 里 —— 长回合执行中即可见。 - 真实 wall-clock 时间戳,来自每个事件触发的时刻 —— 时间线反映真实的 LLM 延迟、工具耗时与间隔。
- 打平的
agent_*metadata:所有 Pi 专属字段都是带统一前缀的顶层 metadata key(agent_step_index、agent_plan_step、agent_duration_ms……)—— 同一个看板过滤条件对所有 Litefuse agent 集成通用。稀疏存储:没有值的字段完全不出现,不用null占位。 - Fail-open:任何意外错误写入
~/.pi/agent/litefuse.log后静默吞掉 —— 扩展永不阻塞 Pi 的主循环。不可达的目标直接跳过。
快速开始
前置条件
- 已安装 Pi —— 用
pi --version检查。 - 在 https://litefuse.cloud 创建一个 Litefuse 项目,拿到 public + secret key。
没有其他依赖:扩展是只用 Node 内置模块的单文件,Pi 直接加载 TypeScript。
下载扩展
mkdir -p ~/.pi/agent/extensions/litefuse
curl -fsSL https://litefuse.ai/integrations/pi/index.ts \
-o ~/.pi/agent/extensions/litefuse/index.ts源码也在同一 URL 上 —— 部署前可以先读一遍。
配置凭据
创建 ~/.pi/agent/litefuse-targets.json(不需要改 shell 配置文件):
cat > ~/.pi/agent/litefuse-targets.json <<'EOF'
[
{
"publicKey": "pk-lf-xxx",
"secretKey": "sk-lf-xxx",
"baseUrl": "https://litefuse.cloud",
"environment": "production"
}
]
EOF
chmod 600 ~/.pi/agent/litefuse-targets.json把占位值替换成真实值。也可以改用环境变量 LITEFUSE_PUBLIC_KEY / LITEFUSE_SECRET_KEY / LITEFUSE_BASE_URL(同名 LANGFUSE_* 作为 fallback 也接受)。targets 文件支持多个目标 —— 同一份 trace 会写入每一个 Litefuse 实例,各目标可配置独立的 environment。
验证
PI_LITEFUSE_DEBUG=true pi --no-session -p "Reply with exactly: ok"
tail -3 ~/.pi/agent/litefuse.log
# 期望: "extension loaded, 1 target(s): https://litefuse.cloud"
# + "turn complete session=... turn=1 api_calls=1 tool_calls=0"打开 https://litefuse.cloud —— 最新一条 trace 名为 Pi Agent — Turn 1,结构如上所示。
正在运行的 Pi 交互会话在启动时加载扩展:在 Pi 里执行 /reload(或新开会话)才会生效。
环境变量
LITEFUSE_* 变量优先;同名 LANGFUSE_* 变量作为生态兼容的 fallback 也接受。
| 变量 | 必填 | 说明 |
|---|---|---|
LITEFUSE_PUBLIC_KEY | 是* | Litefuse 项目 public key(pk-lf-...)。 |
LITEFUSE_SECRET_KEY | 是* | Litefuse 项目 secret key(sk-lf-...)。 |
LITEFUSE_BASE_URL | 否 | 默认 https://litefuse.cloud。别名 LITEFUSE_HOST。 |
LITEFUSE_TRACING_ENVIRONMENT | 否 | env 配置目标的 trace environment。默认 production。 |
LITEFUSE_USER_ID | 否 | 覆盖 trace 的 user_id。回退到系统用户名。 |
LITEFUSE_EXTRA_TARGETS | 否 | 额外目标的 JSON 数组(与 targets 文件同构)。 |
PI_LITEFUSE_DEBUG | 否 | 设为 "true" 启用 ~/.pi/agent/litefuse.log 的详细日志。 |
PI_LITEFUSE_MAX_CHARS | 否 | span 输入/输出的截断阈值(字符数)。默认 1000000(约 1MB 文本)。 |
LITEFUSE_TRACEPARENT | — | 扩展为子 agent 进程自动设置(W3C 格式)。不要手工设置。 |
* 仅在不使用 ~/.pi/agent/litefuse-targets.json 时必填。targets 文件里的凭据无需任何环境变量即可工作。
Trace metadata 参考
所有 Pi 专属字段都是带 agent_ 前缀的打平顶层 metadata key —— 所有 Litefuse agent 集成共用同一套 key,同样的过滤器和看板处处可用。Litefuse 标准字段(sessionId、userId、tags)通过 OTel 属性保持在 trace 级。稀疏存储:没有值的字段完全不出现,绝不用 null。
Trace 级:
agent_turn_number、agent_session_id、agent_cwd、agent_model、agent_provideragent_api_calls、agent_tool_calls、agent_steps、agent_message_count、agent_duration_ms—— 回合统计agent_context_usage—— 回合结束时的{tokens, contextWindow, percent}agent_thinking_level—— 用户调整过时出现agent_image_blocks、agent_prompt_truncated、agent_prompt_orig_len—— 适用时出现
Generation observation:
agent_step_index—— 名字里的#Nagent_provider、agent_api、agent_stop_reasonagent_api_duration_ms、agent_time_to_first_token_msagent_http_status、agent_request_id、agent_retry_after—— 来自 provider 的 HTTP 响应agent_tool_call_count、agent_thinking_charsagent_input_truncated、agent_output_truncated、agent_output_orig_len—— 仅截断时出现
Tool observation:
agent_tool_name、agent_tool_call_idagent_step_index—— 工具自己的#Nagent_plan_step—— 发起本工具的 plan generation 的agent_step_indexagent_duration_ms、agent_is_erroragent_details—— Pi 的结构化工具结果细节,≤ 2 KB 时以对象内嵌;过大则整体省略并记agent_details_omitted_len(subagent 的 details 内嵌完整子历史,子树已完整保存)
Subagent 容器:
agent_subagent: true,外加与 trace 级相同的单次运行统计(agent_api_calls、agent_tool_calls、agent_steps、agent_duration_ms)
工作原理
扩展订阅 Pi 的进程内扩展事件:
| 事件 | 用途 |
|---|---|
session_start | 会话 id;从已有 user 消息数恢复 Turn 编号 |
before_agent_start | 捕获用户 prompt |
agent_start | 开启回合:新 traceId、root span id、步骤计数器 |
before_provider_request | generation 开始时间、模型、请求 messages、采样参数 |
message_update | 首个流式 token → completion_start_time(TTFT) |
after_provider_response | HTTP 状态码、request id、retry-after 头 |
message_end | 发出 generation span:按内容命名,带 usage + 成本 |
tool_execution_start / tool_execution_end | tool span;导出 subagent traceparent |
session_compact | 上下文压缩 event observation |
agent_end | 发出 root span:trace output、回合统计、上下文水位;flush |
session_shutdown | 防御性关闭被中断的回合;最终 flush |
每个完成的 span 立即以 OTLP/HTTP JSON POST 到 <baseUrl>/api/public/otel/v1/traces(Basic auth)—— fire-and-forget 加超时,回合结束时 flush。每个 span 只发送一次;执行中的步骤在完成时变为可见。
对于子 agent,Pi 的 subagent 扩展会 spawn 子 pi 进程。父进程的 Litefuse 扩展在 subagent 工具运行期间导出 LITEFUSE_TRACEPARENT;子进程的扩展(同一个文件,由子进程加载)检测到它后,抑制自己的 trace header,并把容器 span 挂在 subagent 工具 span 下。嵌套委派按同样方式递归。一个已知暂态:live 视图中子 agent 的步骤可能先于其容器出现(容器在子进程结束时才发送)—— 回合结束后树完整且嵌套正确。
故障排查
Litefuse 中没有出现 trace。 查看扩展日志:
tail -20 ~/.pi/agent/litefuse.log日志为空说明扩展没加载 —— 确认文件在 ~/.pi/agent/extensions/litefuse/index.ts,且启动 Pi 时没有加 --no-extensions。文件存在但没有 extension loaded 行?扩展没找到凭据:检查 ~/.pi/agent/litefuse-targets.json 或 LITEFUSE_* 环境变量。
改完凭据后 trace 停了。 Pi 交互会话在启动时加载扩展 —— 在 Pi 里执行 /reload 或新开会话。
工具调用有了但 subagent 子树缺失。 子进程连不上 Litefuse,或没有运行本扩展。确认 subagent spawn 的是普通 pi(默认即是),并在 ~/.pi/agent/litefuse.log 里找子进程的日志行(session=ephemeral)。
totalCost 是 0。 你的 Litefuse 项目没有该模型的价格条目(Pi 的自定义模型 id 不会匹配默认价格表)。在 Litefuse UI 的 Settings → Models 里添加,或依赖 Pi 自带的成本数据 —— 模型在 Pi 注册表中时扩展会以 cost_details 转发。
启用调试日志:
PI_LITEFUSE_DEBUG=true pi
# 每次发送 / 跳过 / 错误都会写入 ~/.pi/agent/litefuse.log本地 + 云端双写: 在 litefuse-targets.json 里加第二个条目(例如本地自托管实例配 "environment": "test")。不可达的目标会被静默跳过,永不阻塞 Pi。