关于 braintrust-tracing
braintrust-tracing 是一套用于在 Braintrust 中追踪 Claude Code 会话的指南和 Hook 配置。它记录会话、轮次、工具及 LLM Span,并介绍对独立子 Agent 轨迹进行事后关联的方法。
主要功能
- 01记录会话和轮次 Span
- 02捕获工具与 LLM Span
- 03保存单会话追踪状态
- 04提供轨迹分析命令
- 05说明子 Agent 轨迹的事后关联
适用场景
- 01检查 Claude Code 会话活动
- 02通过 Hook 日志排查 Span 缺失
- 03基于父会话和子 Agent 轨迹构建数据集
MCP 相关
该项目介绍将 Claude Code 会话轨迹发送到 Braintrust 的 Hook;它不是 MCP 服务。README 未说明 MCP 集成。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ parcadei/ continuous-claude-v3 --skill braintrust-tracing安装后在 Claude Code 中输入/braintrust-tracing确认已加载。
详细介绍
Claude Code 的 Braintrust 追踪
在 Braintrust 中追踪 Claude Code 会话的综合指南,包括子 Agent 关联。
架构概览
PARENT SESSION
+---------------------+
| SessionStart |
| (creates root) |
+----------+----------+
|
+----------v----------+
| UserPromptSubmit |
| (creates Turn) |
+----------+----------+
|
+--------------------+--------------------+
| | |
+---------v--------+ +--------v--------+ +--------v--------+
| PostToolUse | | PostToolUse | | PreToolUse |
| (Read span) | | (Edit span) | | (Task - inject) |
+------------------+ +-----------------+ +--------+--------+
|
+----------v----------+
| SUB-AGENT |
| SessionStart |
| (NEW root_span_id)|
+----------+----------+
|
+----------v----------+
| SubagentStop |
| (has session_id) |
+---------------------+
Hook 事件流程
| Hook | 触发条件 | 创建内容 | 关键字段 |
|---|---|---|---|
| SessionStart | 会话开始 | 根 Span | session_id、root_span_id |
| UserPromptSubmit | 用户发送提示词 | 回合 Span | prompt、turn_number |
| PreToolUse | 工具运行前 | (修改 Task 提示词) | tool_input.prompt |
| PostToolUse | 工具运行后 | 工具 Span | tool_name、input、output |
| Stop | 回合完成 | LLM Span | model、tokens、tool_calls |
| SubagentStop | 子 Agent 完成 | (无 Span) | 子 Agent 的 session_id |
| SessionEnd | 会话结束 | (完成根 Span) | turn_count、tool_count |
追踪层级
Session (task span) - root_span_id = session_id
|
+-- Turn 1 (task span)
| |
| +-- claude-sonnet (llm span) - model call with tool_use
| +-- Read (tool span)
| +-- Edit (tool span)
| +-- claude-sonnet (llm span) - response after tools
|
+-- Turn 2 (task span)
| |
| +-- claude-sonnet (llm span)
| +-- Task (tool span) -----> [Sub-agent session - SEPARATE trace]
| +-- claude-sonnet (llm span)
|
+-- Turn 3 ...
子 Agent 追踪:可行与不可行之处
不可行之处
SessionStart 不会收到 Task 提示词。
我们尝试通过 PreToolUse 将追踪上下文注入 Task 提示词:
# PreToolUse hook injects:
[BRAINTRUST_TRACE_CONTEXT]
{"root_span_id": "abc", "parent_span_id": "xyz", "project_id": "123"}
[/BRAINTRUST_TRACE_CONTEXT]
但 SessionStart 只会收到会话元数据,不会收到修改后的提示词。注入的上下文会丢失。
可行之处
父会话中的 Task Span 包含所有信息:
agentId- 子 Agent 运行的标识符totalTokens、totalToolUseCount- 指标content- Agent 的完整响应/摘要tool_input.prompt- 原始任务提示词tool_input.subagent_type- Agent 类型(例如,“oracle”)
SubagentStop hook 会收到子 Agent 的 session_id:
- 该值等于子 Agent 孤立追踪的
root_span_id - 可用于关联父 Task Span 与子追踪
关联模式
**当前状态:**子 Agent 会创建孤立追踪(新的 root_span_id)。
关联方法:
- 查询父会话的 Task Span,获取 Agent 元数据
- 将
agentId或时间与孤立追踪进行匹配 - 子 Agent 的
session_id= 其追踪的root_span_id
未来方案(尚未实现):
SubagentStop fires -> writes session_id to temp file
PostToolUse (Task) -> reads temp file -> adds child_session_id to Task span metadata
这样即可关联:Task.agentId + Task.child_session_id -> 孤立追踪的 root_span_id
状态管理
每个会话的状态文件
~/.claude/state/braintrust_sessions/
{session_id}.json # Per-session state
每个会话文件包含:
{
"root_span_id": "abc-123",
"project_id": "proj-456",
"turn_count": 5,
"tool_count": 23,
"current_turn_span_id": "turn-789",
"current_turn_start": 1703456789,
"started": "2025-12-24T10:00:00.000Z",
"is_subagent": false
}
全局状态
~/.claude/state/braintrust_global.json # Cached project_id
~/.claude/state/braintrust_hook.log # Debug log
调试命令
检查追踪是否处于活动状态
# View hook logs in real-time
tail -f ~/.claude/state/braintrust_hook.log
# Check if session has state
cat ~/.claude/state/braintrust_sessions/*.json | jq -s '.'
# Verify environment
echo "TRACE_TO_BRAINTRUST=$TRACE_TO_BRAINTRUST"
echo "BRAINTRUST_API_KEY=${BRAINTRUST_API_KEY:+set}"
直接查询 Braintrust
# List recent sessions
uv run python -m runtime.harness scripts/braintrust_analyze.py --sessions 5
# Analyze last session
uv run python -m runtime.harness scripts/braintrust_analyze.py --last-session
# Replay specific session
uv run python -m runtime.harness scripts/braintrust_analyze.py --replay <session-id>
# Find sub-agent traces (orphaned roots)
uv run python -m runtime.harness scripts/braintrust_analyze.py --agent-stats
调试 Hook 执行
# Enable verbose logging
export BRAINTRUST_CC_DEBUG=true
# Test hooks manually
echo '{"session_id":"test-123","type":"resume"}' | \
bash "$CLAUDE_PROJECT_DIR/.claude/plugins/braintrust-tracing/hooks/session_start.sh"
# Test PreToolUse (Task injection)
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"
敥障排查清单
-
没有出现追踪记录:
- 检查
.claude/settings.local.json中的TRACE_TO_BRAINTRUST=true - 验证 API 密钥:
echo $BRAINTRUST_API_KEY - 检查日志:
tail -20 ~/.claude/state/braintrust_hook.log
- 检查
-
子 Agent 未关联:
- 这是预期行为——子 Agent 会创建孤立追踪
- 使用
--agent-stats查找 Agent 活动 - 通过时间或父 Task Span 中的
agentId进行关联
-
Span 缺失:
- 检查会话状态中的
current_turn_span_id - 确保 Stop hook 已运行(回合最终处理)
- 在日志中查找“Failed to create”错误
- 检查会话状态中的
-
状态损坏:
- 移除会话状态:
rm ~/.claude/state/braintrust_sessions/*.json - 清除全局缓存:
rm ~/.claude/state/braintrust_global.json
- 移除会话状态:
关键文件
| 文件 | 用途 |
|---|---|
.claude/plugins/braintrust-tracing/hooks/common.sh | 共享实用工具、API 和状态管理 |
.claude/plugins/braintrust-tracing/hooks/session_start.sh | 创建根 Span,处理子 Agent 上下文 |
.claude/plugins/braintrust-tracing/hooks/user_prompt_submit.sh | 为每条用户消息创建 Turn Span |
.claude/plugins/braintrust-tracing/hooks/pre_tool_use.sh | 将追踪上下文注入 Task 提示词 |
.claude/plugins/braintrust-tracing/hooks/post_tool_use.sh | 创建工具 Span,捕获 Agent/技能元数据 |
.claude/plugins/braintrust-tracing/hooks/stop_hook.sh | 创建 LLM Span,结束 Turn |
.claude/plugins/braintrust-tracing/hooks/session_end.sh | 结束会话,触发学习内容提取 |
scripts/braintrust_analyze.py | 查询和分析追踪的会话 |
~/.claude/state/braintrust_sessions/ | 每个会话的状态文件 |
~/.claude/state/braintrust_hook.log | 调试日志 |
环境变量
| 变量 | 必需 | 默认值 | 说明 |
|---|---|---|---|
TRACE_TO_BRAINTRUST | 是 | - | 设置为 "true" 以启用 |
BRAINTRUST_API_KEY | 是 | - | Braintrust 的 API 密钥 |
BRAINTRUST_CC_PROJECT | 否 | claude-code | 项目名称 |
BRAINTRUST_CC_DEBUG | 否 | false | 详细日志记录 |
BRAINTRUST_API_URL | 否 | https://api.braintrust.dev | API 端点 |
会话学习成果
我们对子 Agent 追踪的发现(2025 年 12 月)
尝试方案: 通过 PreToolUse 将追踪上下文注入 Task 提示词。
结果: 失败——SessionStart 只接收会话元数据,不接收提示词。
发现: Task Span 已包含丰富的子 Agent 数据:
metadata.agent_type- 来自subagent_type的 Agent 类型metadata.skill_name- 来自 Skill 工具的技能tool_input- 发送给 Agent 的完整提示词tool_output- Agent 的响应
当前关联路径:
- 父会话 Task Span 包含
agentId和时间信息 - 子 Agent 创建孤立追踪,其
root_span_id = session_id - SubagentStop 提供子 Agent 的
session_id - 手动关联:匹配时间信息或使用
session_id链接
后续工作: 在 SubagentStop 之后,通过 PostToolUse 将 child_session_id 写入 Task Span 元数据。
我们对关联子 Agent 的发现
问题
- 通过 Task 工具生成的子 Agent 会创建孤立的 Braintrust 追踪
- 父会话有包含
agentId的 Task Span,子 Agent 则有单独的session_id - 两者之间没有内置链接
不起作用的方式
1. 通过 PreToolUse 注入提示词
SessionStart Hook 只接收会话元数据(session_id、type、cwd),不会接收提示词。注入的追踪上下文永远不会被看到。
Hook 收到的内容:
{
"session_id": "...",
"type": "start|resume|compact|clear",
"cwd": "...",
"env": {...}
}
不存在提示词字段,因此不可能在 SessionStart 时注入上下文。
2. SubagentStop → PostToolUse 文件交接
存在竞态条件。这些是独立的异步 Hook,没有时序保证:
- 子 Agent 会话结束时触发 SubagentStop
- Task 工具完成时触发 PostToolUse(Task)
- 两者之间没有顺序保证
- 写入关联文件会造成竞态
3. PreToolUse 关联文件
SessionStart 无法访问 task_span_id,因为它没有关于哪个 Task 生成了该会话的上下文。PreToolUse 会修改提示词,但不会创建 SessionStart 能可靠访问的状态文件。
有效的做法
事后匹配以构建数据集:
父会话的 Task span 包含:
agentId- 子 Agent 运行的标识符totalTokens、totalToolUseCount- 汇总指标content- 完整的 Agent 响应/摘要tool_input.prompt- 原始任务提示词tool_input.subagent_type- Agent 类型(例如,“oracle”)- 开始/结束时间戳
子 Agent 会话包含:
session_id(等于孤立 trace 的root_span_id)- 开始/结束时间戳
- 所有内部 span 和工具调用
关联策略:
- 导出父会话 trace(查询父级
root_span_id) - 导出子 Agent trace(查询在父会话时间范围内创建的所有会话)
- 根据以下信息进行匹配:
- 时间:Task span 结束时间 ≈ 子 Agent 会话结束时间
- 元数据:Task 提示词中的
subagent_type - ID:SubagentStop hook 提供
session_id(可将其捕获并记录)
架构方面的认识
SessionStart 输入刻意保持精简——其中不包含提示词或工具上下文:
interface SessionStartInput {
session_id: string;
type: "start" | "resume" | "compact" | "clear";
cwd: string;
env: { [key: string]: string };
// NO: prompt, tool_context, task_span_id, parent_span_id
}
这种设计边界使 hook 执行时无法进行实时关联。
建议
如需构建带有子 Agent 关联信息的 Agent 运行数据集:
- 会话内记录: 在日志或状态中捕获 SubagentStop
session_id - 会话结束后导出: 通过 Braintrust API 查询父级和子 Agent trace
- 离线关联: 在脚本中根据时间和元数据匹配 trace
- 不要尝试实时关联: hook 不具备必要的上下文
脚本示例模式:
# 1. Export parent session
braintrust_analyze.py --replay <parent-session-id> > parent_traces.json
# 2. Query for orphaned sub-agent traces (those created during parent's time window)
braintrust_analyze.py --agent-stats > all_agent_traces.json
# 3. Correlate in Python:
# - Parent Task spans -> agentId, timestamps, subagent_type
# - Orphaned traces -> root_span_id, timestamps
# - Match by timing and type
这种方法可靠、可测试,而且不需要 hook 维护隐式状态。