用 Litefuse 追踪 Claude Code
Claude Code 是 Anthropic 的终端编码 agent。本集成利用 Claude Code 的 Stop hook 把每个对话回合发送到 Litefuse——不改 Claude Code 源码、不装 SDK、不建虚拟环境。hook 是一个零依赖(纯标准库)的单文件 Python 脚本,直接把 span 发往 Litefuse 的 OTLP 端点。
hook 解析 Claude Code 的会话 transcript(~/.claude/projects/ 下的 JSONL 文件),每个用户回合生成一条 trace:每次 LLM API 调用一个 generation、每次工具执行一个 tool observation、每次子 agent 委派一棵完整子树。
给 AI —— 自动安装
如果你此刻正在和 Claude Code 对话,粘贴这句话,agent 会端到端完成安装:
Read https://litefuse.ai/SKILL.md and follow the instructions to install and configure Litefuse for Claude Code.
skill 会向你索取 Litefuse API Key(没有账号会引导注册),然后就地完成全部配置。想手动一步步装,继续往下看。
采集内容
| 数据 | 形式 | 说明 |
|---|---|---|
| 用户 prompt | trace input | 文本;含图片时在 metadata 留 block 摘要 |
| 每次 LLM API 调用 | generation observation | plan (n tools) #N / response / think #N,按模型行为命名;输出保留 thinking / text / tool_use 块结构 |
| 工具执行(输入+输出) | tool observation | tool: bash (git) #N——名字带关键信息,完整参数在 input |
子 agent(Agent / Task 工具) | 子树 | tool (1 subagent) #N → subagent 容器 → 子 agent 自己的 plan/tool/response 步骤,从子 transcript 解析;子 agent 成本汇入父 trace。支持递归。 |
| Token 用量 | generation 的 usage_details | Anthropic 风格 key(input / output / cache_read_input_tokens / cache_creation_input_tokens);每次 API 调用只附一次,总量不虚增 |
| 模型名 | generation 的 model 属性 | Litefuse 据此计算成本 |
| API 错误 / 重试 / 限流 | event observation,level=ERROR | 来自 Claude Code 的 system 行 |
| 上下文压缩 | context compaction event | 解释下一次调用 input token 骤降 |
工具失败(is_error=true) | tool observation,level=ERROR | 带 status_message 预览 |
| 被中断的回合 | 根 span level=WARNING | 没有以最终 response 收尾的回合 |
| 会话分组 | trace session_id | Claude Code 会话 UUID |
| 用户身份 | trace user_id | $LITEFUSE_USER_ID,回退到系统用户名 |
| 环境上下文 | trace metadata(agent_*) | cwd、git 分支、Claude Code 版本、权限模式、回合统计 |
Trace 结构
一个含子 agent 委派的回合产出如下结构的 trace(真实示例):
Claude Code — Turn 4 (AGENT 根 span,携带 trace header)
├── plan (1 tool) #1 (generation——usage、真实延迟)
├── tool (1 subagent) #2 (tool——父进程视角的委派)
│ └── subagent (AGENT 容器——从子 transcript 解析)
│ ├── plan (2 tools) #1 (容器内编号从 #1 重计)
│ ├── tool: bash (wc) #2
│ ├── tool: read (architecture.md) #3
│ └── subagent response (generation——子 agent 的最终回答)
├── plan (1 tool) #3
├── tool: bash (ls) #4
└── response (generation——最终回答,结束回合)设计要点:
- 每次 LLM API 调用一个 generation。 Claude Code 把一次 Anthropic 响应拆成多行 JSONL(每个 content block 一行);hook 按
message.id合并回来。generation 按模型做了什么命名——发起工具调用叫plan (n tools) #N,最终文字回答叫response,纯思考叫think #N——而不是按用哪个模型(模型名是model属性)。 - 每个 agent 容器一个步骤计数器。
#N是 generation 和 tool 共用的单一时序编号;每个子 agent 容器从#1重计。tool 的agent_plan_stepmetadata 指向发起它的 generation 的agent_step_index。 - 子 agent 子树。 模型通过
Agent(旧名Task)工具委派时,hook 定位<session>/subagents/agent-<id>.jsonl子 transcript,生成完整三层子树。委派工具 span 包裹容器是有意设计:tool span 时长 − 容器时长 = 委派的真实开销(子进程启动、结果回收与统计),通常几秒。 - 真实时间戳。 span 起止取自 JSONL 的
timestamp字段而非 hook 执行时刻;相邻同时间戳的兄弟 span 间隔 1ms,保证图视图线性。 - harness 注入的 user 行不是回合。 skill 展开、auto-continue 等
isMeta行不会把 trace 劈成两条——它们续接进行中的回合,并作为下一次 generation 的输入。 - 进行中的回合会被推迟。 如果 Stop hook 在工具循环中途触发(最后一条 assistant 行不是最终
text),该回合会被推迟到下次触发再判定——绝不发出半截 trace。 - 确定性 ID。 trace/span ID 由会话 ID 和行 UUID 派生,意外重发是 upsert 而不是重复。
- 打平的
agent_*metadata。 所有集成字段以agent_前缀放在 metadata 顶层(agent_step_index、agent_plan_step、agent_duration_ms…),与其他 Litefuse agent 集成同一套 key,一个看板查询通吃。
Trace 什么时候出现?
hook 在 Stop hook 触发时运行——即每个回合结束时。整个回合(包括子 agent 子树)在那一刻整批上传;回合进行中服务端看不到任何内容。这是与 Pi 这类事件驱动集成(每个 observation 结束即发、中途可见)的有意差异——transcript 解析型 hook 只有被 Claude Code 调起时才有机会运行。
快速开始
前置条件
- Python ≥ 3.8——任何
python3都行,包括 macOS 系统自带的。hook 零第三方依赖:不需要 SDK、虚拟环境或 pip install。 - 一个 https://litefuse.cloud 项目及其 public + secret key。
下载 hook 脚本
mkdir -p ~/.claude/hooks
curl -fsSL https://litefuse.ai/integrations/claude-code/litefuse_hook.py \
-o ~/.claude/hooks/litefuse_hook.py
chmod +x ~/.claude/hooks/litefuse_hook.py同一 URL 可直接浏览源码,部署前欢迎先读一遍。
配置 ~/.claude/settings.json
加入 Stop hook 和 Litefuse 凭据:
{
"env": {
"LITEFUSE_PUBLIC_KEY": "pk-lf-xxx",
"LITEFUSE_SECRET_KEY": "sk-lf-xxx",
"LITEFUSE_BASE_URL": "https://litefuse.cloud"
},
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "python3 \"$HOME\"/.claude/hooks/litefuse_hook.py"
}
]
}
]
}
}不要注册 SubagentStop hook——子 agent 子树由父会话的 Stop hook 生成。
按项目使用时,把同样的 env 块放进 <project>/.claude/settings.local.json。
验证
在 Claude Code 里发一条消息,然后看 hook 日志:
tail -f ~/.claude/state/litefuse_hook.log
# 预期: "processed 1 turn(s), N span(s) in X.XXs (session=...)"打开 Litefuse 项目——每条用户消息对应一条上述结构的 trace。
从 v1 升级
旧版 hook 依赖 ~/.claude/hooks/.venv 里的 Langfuse Python SDK。v2 全都不需要:
- 用新脚本覆盖旧脚本(有自定义改动的先备份)。
settings.json里把 Stop hook 命令改成纯python3 "$HOME"/.claude/hooks/litefuse_hook.py,并删除SubagentStop条目(如有)。- 可选:env key 改名为
LITEFUSE_*(LANGFUSE_*仍作为 fallback 生效),删掉TRACE_TO_LANGFUSE(不再读取;有 key 即开启)。 - 可选:
rm -rf ~/.claude/hooks/.venv——已经没人用它了。
回合编号和字节偏移状态原样沿用(~/.claude/state/litefuse_state.json)。注意 v2 改了 observation 命名(plan (n tools) #N 取代 Decision to call tool: …,小写 tool: bash … #N 取代 Tool call: Bash (#N)),metadata 打平为 agent_*——记得更新保存过的看板过滤条件。
环境变量
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 | 否 | trace 写入的 Litefuse environment。默认 production;做实验用 development,免得污染生产看板。 |
LITEFUSE_USER_ID | 否 | 覆盖 trace user_id。回退到系统用户名、再到主机名。 |
LITEFUSE_EXTRA_TARGETS | 否 | 额外目标的 JSON 数组([{"publicKey", "secretKey", "baseUrl", "environment"}]),同一份 trace 双写多个实例(如自托管 + cloud)。 |
LITEFUSE_TRACEPARENT | 否 | W3C traceparent(00-<traceId>-<spanId>-01)。当 Claude Code 被其他 agent 的采集器作为子 agent 拉起时设置,每个回合会以 subagent 容器加入父 trace,而不是自建 trace。 |
CLAUDE_CODE_LITEFUSE_DISABLED | 否 | 设为 "true" 临时关闭 hook,无需卸载。 |
CLAUDE_CODE_LITEFUSE_DEBUG | 否 | 设为 "true" 输出详细日志。 |
CLAUDE_CODE_LITEFUSE_MAX_CHARS | 否 | span 输入/输出的截断阈值(字符数)。默认 1000000。 |
Metadata 参考
所有集成字段是带 agent_ 前缀的顶层 metadata key(与其他 Litefuse agent 集成统一)。源数据中不存在的字段完全不出现,不用 null 占位。
Trace 根:agent_turn_number、agent_session_id、agent_cwd、agent_model、agent_provider、agent_api_calls、agent_tool_calls、agent_steps、agent_message_count、agent_duration_ms、agent_git_branch、agent_claude_code_version、agent_permission_mode、agent_entrypoint、agent_transcript_path、agent_user_uuid、agent_prompt_id;prompt 含图片时有 agent_image_blocks / agent_image_media_types;截断时有 agent_prompt_truncated / agent_final_text_truncated(+ _orig_len)。
Generation:agent_turn_number、agent_step_index、agent_provider、agent_stop_reason、agent_message_id、agent_request_id、agent_api_duration_ms、agent_tool_call_count、agent_thinking_chars、agent_service_tier、截断标记。
Tool:agent_turn_number、agent_step_index、agent_plan_step(关联键:tool.agent_plan_step == generation.agent_step_index)、agent_tool_name(原始大小写,如 Bash)、agent_tool_call_id、agent_duration_ms、agent_is_error、agent_details(按工具类型的扁平 toolUseResult 摘要:Bash 的退出信息、WebFetch 的 HTTP 状态、Grep/Glob 的条目数、委派的 agent id/type)、截断标记。
Subagent 容器:agent_subagent: true、agent_subagent_id、agent_subagent_type、agent_subagent_description,以及该次运行的 agent_api_calls / agent_tool_calls / agent_steps / agent_duration_ms。
工作原理
每次 Stop hook 触发时脚本:
- 从上次偏移量起增量读会话 transcript(状态存于
~/.claude/state/litefuse_state.json,按sha256(session_id::transcript_path)分键)。 - 把行组装成回合。真实用户消息开启回合;
tool_result行、system行、harness 注入的isMeta行都归入进行中的回合。 - 最后一个回合若仍在进行中(最后一条 assistant 行不是
text)则推迟——偏移量回退,下次触发重新判定。会话级emitted_user_uuids集合防止回退后重复发送。 - 按
message.id把 assistant 行合并成 API 调用、编号步骤、匹配工具结果、递归展开子 agent transcript。 - 以 OTLP/HTTP JSON 发往
<base_url>/api/public/otel/v1/traces(按端点体积上限分批、Basic 认证、10 秒超时)。trace header 随每个 span 携带。
hook 是 fail-open 的:任何意外错误只写 ~/.claude/state/litefuse_hook.log 并以 0 退出,绝不阻塞或拖慢 Claude Code。
排障
Litefuse 里没有 trace。 tail ~/.claude/state/litefuse_hook.log。日志为空说明 hook 没跑——检查 settings.json 的 command 路径。有 send failed: 说明 key 或网络问题:核对 LITEFUSE_PUBLIC_KEY / LITEFUSE_SECRET_KEY / LITEFUSE_BASE_URL。
最新回合缺失。 看日志里有没有 deferred 1 (in-progress)——hook 触发时该回合还没产生最终回答,下次 Stop 触发会补发。
某条 trace 以 tool span 收尾、根是 WARNING。 该回合确实没有产生最终文字回答(在工具循环中被杀或中断)。WARNING 的 status message 写明了原因,这不是采集错误。
subagent 容器比上层 tool span 短。 符合预期——差值就是委派本身的真实开销(子进程启动 + 结果回收)。容器时长与 harness 自己记录的 totalDurationMs 误差在毫秒级。
成本显示 0。 Litefuse 按模型名计价;确认 Litefuse 项目的 Settings → Models 里有匹配该模型(如 claude-opus-4-8)的价格条目。
手动测试 hook(用 development environment,不碰生产数据):
echo '{"session_id":"manual-test","transcript_path":"'$HOME'/.claude/projects/<proj>/<session>.jsonl"}' | \
LITEFUSE_PUBLIC_KEY="pk-lf-..." \
LITEFUSE_SECRET_KEY="sk-lf-..." \
LITEFUSE_BASE_URL="https://litefuse.cloud" \
LITEFUSE_TRACING_ENVIRONMENT="development" \
CLAUDE_CODE_LITEFUSE_DEBUG=true \
python3 ~/.claude/hooks/litefuse_hook.py
tail ~/.claude/state/litefuse_hook.log