大模型应用
需要为 Claude Code 会话与子代理建立…
当你要用 Braintrust 追踪 Claude Code 的 Session/Turn/LLM/Tool 及子代理运行,并排查 trace 缺失、状态损坏和子代理孤立 trace 时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
需要为 Claude Code 会话与子特定
解决思路
- 按 hook 事件流建立 trace 层级:SessionStart 创建 root span 并记录 session_id、root_span_id;UserPromptSubmit 创建 Turn span 并记录 prompt、turn_number;PreToolUse 在 Task 运行前修改 tool_input.prompt;PostToolUse 在工具运行后创建 tool span 并记录 tool_name、input、output;Stop 在回合完成时创建 LLM spans 并记录 model、tokens、tool_calls;SubagentStop 记录子代理 session_id 但不创建 span;SessionEnd 汇总 turn_count、tool_count 并 finalize root。
- 保持 trace 层级:Session 使用 task span 且 root_span_id = session_id;其下每个 Turn 是 task span;Turn 下放 claude-sonnet 等 LLM spans 和 Read/Edit 等 tool spans;Task tool span 指向一个独立的 sub-agent session trace。
- 不要只依赖 PreToolUse 向 Task prompt 注入 [BRAINTRUST_TRACE_CONTEXT] JSON(例如 {"root_span_id":"abc","parent_span_id":"xyz","project_id":"123"})来关联子代理;SessionStart 只接收 session metadata,不接收被修改后的 prompt,注入上下文会丢失。
- 从父会话 Task spans 读取子代理信息:agentId、totalTokens、totalToolUseCount、content、tool_input.prompt、tool_input.subagent_type。
- 用 SubagentStop hook 接收子代理的 session_id,并将其视为该子代理孤立 trace 的 root_span_id,用于关联父 Task span 与子 trace。
- 当前关联子代理时:先查询父会话的 Task spans 获取 agent metadata;再用 agentId 或时间匹配孤立 traces;记住子代理 session_id = 其 trace 的 root_span_id。
- 未来若要自动关联,可让 SubagentStop 触发时将 session_id 写入临时文件,并让 PostToolUse (Task) 读取该文件,把 child_session_id 加到 Task span metadata,从而链接 Task.agentId + Task.child_session_id -> orphaned trace root_span_id;但原文标注此方案尚未实现。
- 维护每会话状态文件 ~/.claude/state/braintrust_sessions/{session_id}.json,字段包括 root_span_id、project_id、turn_count、tool_count、current_turn_span_id、current_turn_start、started、is_subagent。
- 维护全局状态和日志:~/.claude/state/braintrust_global.json 缓存 project_id;~/.claude/state/braintrust_hook.log 作为 debug 日志。
- 实时查看 hook 日志:tail -f ~/.claude/state/braintrust_hook.log。
- 检查所有会话状态:cat ~/.claude/state/braintrust_sessions/*.json | jq -s '.'。
- 验证环境变量:echo "TRACE_TO_BRAINTRUST=$TRACE_TO_BRAINTRUST" 和 echo "BRAINTRUST_API_KEY=${BRAINTRUST_API_KEY:+set}"。
- 查询 Braintrust:用 uv run python -m runtime.harness scripts/braintrust_analyze.py --sessions 5 列出最近会话;用 --last-session 分析最后会话;用 --replay <session-id> 回放指定会话;用 --agent-stats 查找子代理孤立根 trace。
- 启用 hook 详细日志:export BRAINTRUST_CC_DEBUG=true。
- 手动测试 hook:echo '{"session_id":"test-123","type":"resume"}' | bash "$CLAUDE_PROJECT_DIR/.claude/plugins/braintrust-tracing/hooks/session_start.sh";以及 echo '{"session_id":"test-123","tool_name":"Task","tool_input":{"prompt":"test"}}' | bash "$CLAUDE_PROJECT_DIR/.claude/plugins/braintrust-tracing/hooks/pre_tool_use.sh"。
- 排查无 traces:确认 .claude/settings.local.json 中 TRACE_TO_BRAINTRUST=true;执行 echo $BRAINTRUST_API_KEY 验证 API key;执行 tail -20 ~/.claude/state/braintrust_hook.log 查看日志。
- 排查子代理未链接:这是预期现象,子代理会创建孤立 traces;用 --agent-stats 查找 agent 活动,并通过父 Task span 中的 timing 或 agentId 关联。
- 排查缺失 spans:检查 session state 中的 current_turn_span_id;确保 Stop hook 运行以 finalize Turn;在 log 中查找 "Failed to create" 错误。
- 修复状态损坏:删除会话状态 rm ~/.claude/state/braintrust_sessions/*.json;清除全局缓存 rm ~/.claude/state/braintrust_global.json。
- 了解关键文件职责:common.sh 提供共享工具、API 和状态管理;session_start.sh 创建 root span 并处理子代理上下文;user_prompt_submit.sh 为每条用户消息创建 Turn spans;pre_tool_use.sh 向 Task prompts 注入 trace context;post_tool_use.sh 创建 tool spans 并捕获 agent/skill metadata;stop_hook.sh 创建 LLM spans 并 finalize Turns;session_end.sh finalize session 并触发 learning extraction;scripts/braintrust_analyze.py 查询和分析 traced sessions;~/.claude/state/braintrust_sessions/ 存放每会话状态;~/.claude/state/braintrust_hook.log 是 debug 日志。
- 配置环境变量:TRACE_TO_BRAINTRUST 必填,设为 "true" 启用;BRAINTRUST_API_KEY 必填;BRAINTRUST_CC_PROJECT 可选,默认 claude-code;BRAINTRUST_CC_DEBUG 可选,默认 false;BRAINTRUST_API_URL 可选,默认 https://api.braintrust.dev。
- 记录 2025-12 经验:尝试通过 PreToolUse 把 trace context 注入 Task prompts 失败,因为 SessionStart 只收到 session metadata 而不是 prompt。
- 利用已发现的父 Task span 子代理数据:metadata.agent_type、metadata.skill_name、tool_input、tool_output。
- 记录当前子代理关联路径:父 session Task span 有 agentId 和 timing;子代理创建 orphaned trace 且 root_span_id = session_id;SubagentStop 提供子代理 session_id;手动按 timing 匹配或使用 session_id 链接。
- 记录未来工作:从 PostToolUse 在 SubagentStop 后把 child_session_id 写入 Task span metadata。
- 理解子代理关联问题:通过 Task tool 启动的子代理会创建 orphaned Braintrust traces;父会话有带 agentId 的 Task spans,子代理有独立 session_id,二者没有内建链接。
- 避免无效方案 1:不要通过 PreToolUse 做 prompt injection 后在 SessionStart 关联;SessionStart hook 只收到 session_id、type、cwd、env,没有 prompt 字段,无法在 SessionStart 注入上下文。
- 避免无效方案 2:不要用 SubagentStop -> PostToolUse 文件交接做实时关联;这些 hooks 是独立异步且无时序保证,写 correlation file 会产生 race condition。
- 避免无效方案 3:不要依赖 PreToolUse correlation files;SessionStart 无法访问 task_span_id,且 PreToolUse 修改 prompts 时没有创建 SessionStart 能可靠找到的状态文件。
- 构建数据集时使用事后匹配:父会话 Task spans 含 agentId、totalTokens、totalToolUseCount、content、tool_input.prompt、tool_input.subagent_type、开始/结束时间戳;子代理 sessions 含 session_id(= orphaned trace root_span_id)、开始/结束时间戳、所有内部 spans 和 tool calls。
- 事后关联策略:1) 导出父 session traces(查询父 root_span_id);2) 导出子代理 traces(查询父时间窗口内创建的所有 sessions);3) 按 timing(Task span 结束 ≈ 子代理 session 结束)、metadata(Task prompt 中的 subagent_type)、IDs(SubagentStop 提供并可记录的 session_id)匹配。
- 注意架构边界:SessionStart input 故意最小,只有 session_id、type、cwd、env,没有 prompt、tool_context、task_span_id、parent_span_id;该设计边界导致 hook 阶段无法做实时关联。
- 为带子代理关联的 agent run 数据集推荐做法:1) 在会话中记录 SubagentStop 的 session_id 到 logs 或 state;2) 会后导出时查询 Braintrust API 获取父与子代理 traces;3) 离线脚本按 timing 和 metadata 匹配 traces;4) 不要尝试实时 linking,因为 hooks 没有必要上下文。
- 示例脚本模式:先导出父 session:braintrust_analyze.py --replay <parent-session-id> > parent_traces.json;再查询 orphaned 子代理 traces:braintrust_analyze.py --agent-stats > all_agent_traces.json;最后在 Python 中关联:父 Task spans -> agentId、timestamps、subagent_type;orphaned traces -> root_span_id、timestamps;按 timing 和 type 匹配。
- 采用该事后关联方法的原因:可靠、可测试,且不要求 hooks 维护隐式状态。
适用边界 · 注意事项
- 不适用于未安装 Claude Code 相关 hooks/插件、未配置 BRAINTRUST_API_KEY 或没有 Braintrust 服务/账号的环境。
- 不适用于必须实时把父子 trace 链接起来的场景;原文明确不要尝试实时 linking,应改为事后按 timing/metadata 关联。
- 不要尝试用 PreToolUse 注入上下文让 SessionStart 读取,或用 SubagentStop/PostToolUse 文件交接做关联,这些方案已证明失败或存在 race condition。