本文介绍如何通过集成扣子罗盘 SDK,将 Claude Code 的开发会话与工具调用轨迹上报至扣子罗盘,实现开发过程的可观测性、分析与评测。
说明
扣子罗盘 SDK 会自动处理数据构型与上报细节,你只需关注核心业务逻辑。
集成方案通过 Claude Code 内置的 hooks 系统实现。该系统允许在会话生命周期的特定节点(如每次响应结束后的 Stop 阶段)执行自定义命令。
通过一个简单的 Python Hook 脚本,你可以自动捕获 Claude Code 的本地会话记录 (.jsonl),并将其构造成符合 OpenTelemetry 规范的 Span,实时上报至扣子罗盘。Python Hook 脚本的主要优势如下:
在开始之前,请确保你的开发环境满足以下要求:
注意
扣子罗盘 Python SDK 需要 Python 3.8 或更高版本。
pip install cozeloop
为了安全地管理访问凭证,请在你的项目的 .claude/settings.local.json 文件中设置环境变量。
在你的项目根目录下,创建或编辑 .claude/settings.local.json:
{
"env": {
"COZELOOP_WORKSPACE_ID": "你的扣子罗盘工作空间 ID",
"COZELOOP_API_TOKEN": "你的个人访问令牌(PAT)或服务访问令牌(SAT)"
}
}
|
变量名 |
说明 |
|---|---|
|
COZELOOP_WORKSPACE_ID |
扣子罗盘工作空间 ID。获取方式请参考 获取扣子罗盘空间 ID。 |
|
COZELOOP_API_TOKEN |
个人访问令牌(PAT)或服务访问令牌(SAT)。获取方式请参考 SDK 鉴权。 |
说明
Hook 脚本的核心逻辑包括:
~/.claude/projects/ 目录中定位最近修改的 .jsonl 文件。~/.claude/cozeloop_state/) 记录已处理的行号,避免重复上报。session_id。cozeloop.start_span() 和 span.finish() 等方法,将用户输入、模型响应和工具调用分别作为独立的 Span 上报。client.close(),确保所有缓存的 Trace 数据都被成功发送。hooks 文件夹。mkdir -p ~/.claude/hooks
cozeloop_hook.py。修改 Claude Code 的全局配置文件 ~/.claude/settings.json,注册 Stop 阶段的 Hook。
注意
确保 command 中使用的 python 路径正确。如果使用了虚拟环境,建议指定绝对路径。
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "python ~/.claude/hooks/cozeloop_hook.py"
}
]
}
]
}
}
完成配置后,正常使用 Claude Code 进行几轮会话。然后:
cozeloop_hook.py 脚本会将 Claude Code 的会话映射为扣子罗盘中的 Trace 和 Span。
Claude Code 的每个会话轮次都会被上报为一个独立的 Trace。在这个 Trace 内部,包含一个根 Span (claude_code_turn_*),以及多个代表不同操作的子 Span。
一个典型的 Trace 结构如下:
claude_code_turn_{i} (root span, type: main)
├── user_input (type: query)
│ └── input: "用户输入的完整内容"
├── assistant_response (type: model)
│ ├── input: "用户输入的完整内容"
│ └── output: "模型生成的完整响应,包括思考过程和文本"
├── tool_{tool_name} (type: tool)
│ ├── input: {"command": "ls -l"}
│ └── output: "total 0..."
└── ... (其他 tool spans)
claude_code_turn_{i}:作为根 Span,代表一个完整的“用户-模型”交互轮次。user_input:记录用户的提问或指令。assistant_response:记录模型的完整回答,包括其思考过程和最终的文本输出。tool_*:每个工具调用都对应一个独立的 Span,例如 tool_Bash、tool_write_file 等。input 字段记录了调用工具时传入的参数,output 字段记录了工具执行后的返回结果。为了方便在 CozeLoop 平台上进行筛选和聚合,每个 Span 都会附带一组结构化的标签(Tag)。
|
标签 |
说明 |
附加于 |
|---|---|---|
|
|
整个对话会话的唯一标识符。格式如 |
Root Span |
|
|
当前对话轮次在整个会话中的索引号,从 0 开始。 |
Root Span |
|
|
数据来源标识,固定为 |
Root Span |
|
|
消息的角色,如 |
|
|
|
被调用的工具名称,例如 |
Tool Spans |
|
|
Claude Code 为每次工具调用生成的唯一 ID。 |
Tool Spans |
一个连续的 Claude Code 会话中的所有轮次都将共享同一个会话 ID(session_id)。这使得你可以在 CozeLoop 中轻松地筛选出特定会话的全部交互历史。session_id 由 Hook 脚本在首次处理会话时生成,格式为:claude-code-YYYYMMDD-HHMMSS-PID。
只需在项目的 .claude/settings.local.json 中设置 TRACE_TO_COZELOOP:
{
"env": {
"TRACE_TO_COZELOOP": "false"
}
}
如果你发现数据没有正常上报,可以开启调试模式来排查问题。在 .claude/settings.local.json 中添加 CC_COZELOOP_DEBUG 环境变量:
{
"env": {
"COZELOOP_WORKSPACE_ID": "your-workspace-id",
"COZELOOP_API_TOKEN": "your-api-token",
"CC_COZELOOP_DEBUG": "true"
}
}
启用后,cozeloop_hook.py 脚本的详细执行日志将输出到 Claude Code 的终端中,包括:
以下是与 Claude Code Trace 上报相关的所有文件的位置和用途说明。
|
文件路径 |
说明 |
|---|---|
|
|
Claude Code 的全局配置文件,用于注册全局 |
|
|
项目级配置文件,用于存储敏感的环境变量,如 CozeLoop 的凭证。此文件应被加入 |
|
|
核心的 Hook 脚本,负责数据解析和上报。 |
|
|
Claude Code 自动生成的会话日志文件,是数据源。 |
|
|
Hook 脚本自动创建的状态文件目录,用于存放每个会话文件的处理进度。 |
你可以直接修改 cozeloop_hook.py 脚本以满足更高级的追踪需求。
在创建 Span 后,可以使用 .set_tags() 方法添加任何你需要的自定义标签,以便在 CozeLoop 平台上进行更精细的分析。
例如,在 send_to_cozeloop 函数中,为 root_span 添加项目名称标签:
# 在 cozeloop_hook.py 的 send_to_cozeloop 函数中
with client.start_span(name=f"claude_code_turn_{i}", span_type="main") as root_span:
root_span.set_tags({
"session_id": session_id,
"turn_index": i,
"source": "claude_code",
"project_name": os.environ.get("CLAUDE_PROJECT_NAME", "unknown"), # 添加项目名
"custom_eval_metric": "some_value" # 添加自定义评测指标
})
如果你不希望追踪某些工具的调用(例如,频繁但价值不高的 read_file),可以在创建 Tool Span 前添加过滤逻辑。
# 在 cozeloop_hook.py 的 send_to_cozeloop 函数中
ignored_tools = ["read_file", "list_directory"]
for tool_call in tool_calls:
tool_name = tool_call.get("name", "unknown_tool")
if tool_name in ignored_tools:
debug_log(f"Skipping ignored tool: {tool_name}")
continue # 跳过上报
# ... 创建并上报 tool_span 的逻辑 ...
cozeloop: command not found可能原因:执行 Hook 的 Python 环境中未安装 cozeloop 库。
解决方案:
~/.claude/settings.json 文件,检查 command 字段中指定的 Python 解释器路径是否正确。pip install cozeloop 来安装所需依赖。请按照以下顺序进行排查:
.claude/settings.local.json 中的 COZELOOP_WORKSPACE_ID 和 COZELOOP_API_TOKEN 是否已正确配置,并确保所用令牌拥有 Trace 上报权限。~/.claude/settings.json 中的 hooks 部分,确保其指向了正确的脚本路径,且 JSON 格式无误。可能原因:这是由 Hook 脚本的实现机制导致的。默认的 find_latest_conversation_file 函数通过文件的“最后修改时间”来定位会话文件。在多个实例并行写入时,这种方式无法保证找到正确的对应文件,从而导致数据归属错误。
解决方案:当前的 Hook 实现是一个轻量级方案,最适用于单实例运行的场景。对于多实例并行使用的场景,这是一个已知的局限。你需要自行实现更复杂的 Hook 逻辑(例如,通过进程 ID 或启动参数来唯一标识和定位会话文件)来确保数据的准确性。