Agent Profiler:看清会话时间去向的 Claude Code 性能分析器
看清 Claude Code 会话的时间花在哪:模型等待、工具和子代理,状态栏实时显示,并可导出 OTLP 追踪。
权限
- 写入文件可以在你的电脑上创建或覆盖文件($.fs.write)。
- 网络访问可以发起 HTTP 请求或与 MCP 服务器通信($.http.fetch / $.mcp.*)。
- 拦截工具调用能看到 Claude 发起的每一次工具调用(tool.call / tool.check),并可以阻止或修改它。
- 修改界面在 Claude Code 界面中绘制内容:横条、面板、状态文字、弹出通知(ui.render / $.ui.*)。
- 添加命令/工具注册新的斜杠命令或工具,供你或 Claude 调用($.command.register / $.tool.register)。
- 实测版本
- v2.1.303
- 最近实测
- 需要
- Claude Code ≥ 2.1.287
- 显示位置
- 状态栏、面板、弹出通知
在 Claude Code 中安装:
/plugin install agent-profiler --marketplace YeonwooSung/my-claude-code-mods这个插件能做什么?
Agent Profiler 是 YeonwooSung 开发的 Claude Code 性能分析器,属于 my-claude-code-mods 合集。它只回答一个问题:会话感觉很慢时,时间到底花在了哪里?它把每一轮的时间拆成等待模型、运行工具、阻塞的子代理和引擎开销,并在三个地方展示结果。
- 状态栏:Claude 工作时实时更新,例如
model 100% · tools 0% · ▶ model claude-haiku-5-5 12.5s。 /profile:整个会话的文字报告;/profile turn只看最近一轮。内容包括时间类别、首个 token 延迟、token 数、慢工具、重复读取的文件、子代理,以及简短的结论列表。/profile-pane:实时面板,显示进行中的请求和工具。/profile-export会把会话写成 OTLP JSON。
插件对自己的说法很谨慎,从不报告“推理时间”。每个数字都带一个等级:measured(由 Claude Code 提供,例如工具耗时)、observed(插件在事件边界自己计时)、reported(API 报告的 token 用量)或 inferred(推算出的余量)。作者的 README 要求 Claude Code 2.1.287 或更高版本。
演示
录屏中,我们在小型演示项目里启动加载了该插件的 Claude Code,并让 Claude 读取 README.md,用一句话描述这个项目。这一轮运行期间,页脚上方的状态栏实时计时:先是 model 92% · tools 0%,随后是 model 100%,以及正在进行的请求 ▶ model claude-haiku-5-5 和已用秒数。这一轮结束后,录屏运行 /profile。报告显示 29.0 秒的模型响应等待、5 毫秒的工具时间、首个 token 延迟、82.3k 输入 token、唯一一次 Read 调用,以及“等待模型占 agent 时间的 100%”这条结论。
安装
使用安装区块中的任意一种方式;它们都直接从作者自己的插件市场安装。该市场里有许多插件,请选择 agent-profiler。状态栏和命令会在下一次会话开始时出现。
$ install agent-profiler
需要 Claude Code ≥ 2.1.287
从作者自己的 marketplace 安装: YeonwooSung/my-claude-code-mods
1.在 Claude Code 中一行安装
/plugin install agent-profiler --marketplace YeonwooSung/my-claude-code-mods粘贴到正在运行的会话中(v2.1.275+)。Claude Code 会先询问是否添加该插件市场。
2.从终端安装
claude plugin marketplace add YeonwooSung/my-claude-code-mods claude plugin install agent-profiler@my-claude-code-mods然后在所有已打开的会话中运行 /reload-plugins。
3.免安装试用
git clone https://github.com/YeonwooSung/my-claude-code-mods claude --plugin-dir ./my-claude-code-mods/agent-profiler仅在当前会话中加载该插件,不会写入你的设置。
工作原理
模块 hooks/register.tsx 在内存中保存一个 Profiler 对象,并在各个事件边界上喂给它数据:turn.start 和 turn.complete,每次模型调用通过 turn.step,每个工具通过 tool.call,以及来自 classic.PostToolUse 的 Claude Code 自己的工具耗时。tool.call 钩子只做观察:它用 await next(e) 等待工具完成,并原样返回结果,来自 hooks/register.tsx:
on('tool.call', async ($, e, next) => {
anonymous += 1
const id = e.tool_use_id ?? `anonymous-${anonymous}`
const tool = String(e.tool)
safe(() => profiler.toolStart(Date.now(), { id, tool, agentId: e.agentId, input: summarizeInput(tool, e as unknown as Record<string, unknown>) }))
paint($)
try {
const answer = await next(e)
safe(() => profiler.toolEnd(Date.now(), id, toolOutcome(answer)))
return answer
} // ...
}).catch(($, e, next) => next(e))所有记录步骤都经过 safe(),所以分析器自身的错误会被丢弃,而不会破坏会话。状态栏每秒最多重绘两次。每一轮结束后,插件把新完成的 span 写入 <outputDir>/<sessionId>/<loadId>-<seq>.otlp.json,会话结束时再写入剩余部分和会话根 span。span 名称遵循 GenAI 约定(chat <model>、execute_tool <tool>、invoke_agent <type>)。span 和 trace 的 id 是确定性的,所以同一会话发送两次会得到相同的 span。内存中最多保留 20,000 个请求和工具调用,超出后最旧的已完成轮次会被丢弃。
自定义
三个选项位于 /plugin 设置菜单或 pluginConfigs 中:
| 选项 | 默认值 | 含义 |
|---|---|---|
otlpEndpoint |
空 | OTLP/HTTP 收集器的基础 URL,例如 http://localhost:4318。为空表示只写文件。 |
outputDir |
~/.claude/agent-profiler |
追踪文件的位置,每个会话一个文件夹。 |
exportCommandText |
false |
在导出中包含轮次提示词、Bash 命令行和搜索模式,并对明显的密钥做脱敏。 |
要在 Jaeger 中查看追踪,运行仓库里的 examples/jaeger/docker-compose.yml,把 otlpEndpoint 设为 http://localhost:4318,然后打开 http://localhost:16686。Grafana Tempo 使用同样的 OTLP/HTTP 端点。
权限与安全
在运行之前,我们读过了插件可以执行的文件:.claude-plugin/plugin.json、hooks/hooks.json,以及完整的 hooks/register.tsx 和 Jaeger compose 文件。hooks/core/ 中的十一个模块里,我们完整读了 export.ts、otlp.ts、redact.ts 和 profiler.ts 中的输入摘要函数,并在所有模块中搜索了网络、进程、文件、eval、import() 和环境变量调用(唯一的命中都在 register.tsx 中)。测试文件只列出了文件名,没有阅读。没有 package.json、安装脚本、二进制文件、打包或压缩过的代码,也没有 eval 或动态 import()。hooks/ 源码中没有零宽字符或隐藏字符;非 ASCII 字符只是可见的状态栏和报告符号,例如 █、░、· 和 ⏱。插件不会向提示词或工具结果中注入文本。
值得注意的发现:
- 默认会写文件。 它在每一轮之后和会话结束时调用
$.fs.write,写入~/.claude/agent-profiler/<sessionId>/,除非你设置了outputDir。这是插件自己的文件夹,不是settings.json或 shell 文件,但写入时不会询问。文件包含耗时、token 数、模型名称、工具名称、Read/Edit/Write的文件路径,以及每条 Bash 命令的类别(例如 test 或 build)。文件路径总会被导出。提示词、Bash 命令和搜索模式只有在exportCommandText开启时才会导出。 - 只有你选择后才会联网。 只有
otlpEndpoint不为空时才会调用$.http.fetch,而且只是把同样的追踪 JSON POST 到<endpoint>/v1/traces。主机由你自己填写。如果指向远程收集器,文件路径以及(开启exportCommandText时)经过脱敏的命令会离开你的机器。README 提醒脱敏只是尽力而为。 - 读取一个环境变量,
HOME,用来展开outputDir中的~。 - 拦截工具调用但不改变它们。
tool.call和tool.check只被观察并原样放行,两者都有.catch保护。没有任何调用被拒绝或改写。claude plugin validate还把classic.*钩子列为没有.catch的拦截型钩子;它们其实是返回next(e)的观察者。按 validate 的说明,在组织管理 Claude Code 的环境里,classic.*钩子不会运行,所以那里的报告可能不完整。 - 不运行进程、没有守护进程、不调用模型。 它不运行命令,也没有后台服务,只有会话内的计时器。
- 可选的外部服务。 只有想浏览追踪时才需要 Jaeger 或 Tempo。compose 文件会拉取
jaegertracing/all-in-one:latest,插件自己从不运行它。 - 许可证。 Apache-2.0(仓库根目录的
LICENSE)。
claude plugin validate 没有报告错误或警告,只有上面的提示信息。作者的 11 个文件共 67 个测试,用 claude plugin test 在 Claude Code 2.1.303 上全部通过。
- 写入文件
- 可以在你的电脑上创建或覆盖文件($.fs.write)。
- 网络访问
- 可以发起 HTTP 请求或与 MCP 服务器通信($.http.fetch / $.mcp.*)。
- 拦截工具调用
- 能看到 Claude 发起的每一次工具调用(tool.call / tool.check),并可以阻止或修改它。
- 修改界面
- 在 Claude Code 界面中绘制内容:横条、面板、状态文字、弹出通知(ui.render / $.ui.*)。
- 添加命令/工具
- 注册新的斜杠命令或工具,供你或 Claude 调用($.command.register / $.tool.register)。
兼容性与故障排除
已在 macOS 上使用 Claude Code 2.1.303 检查:validate、作者的测试和演示录屏。README 说明它自己的端到端测试在 2.1.280 上运行,事件结构遵循 2.1.292 的类型声明。面板和状态栏可以在终端和桌面版 Code 标签页中绘制,但 VS Code 聊天面板和 claude -p 中不行;/profile 文字在各处都可用。
- 没有状态栏或命令。 确认
claude --version为 2.1.287 或更高版本,并在已打开的会话中安装后运行/reload-plugins。 - 没有追踪文件。 在得知会话 id 之前不会写入任何内容。写入失败会以通知形式显示一次;请检查
outputDir是否可写。 - 热重载后数字会重置。 内存中的状态会重新开始,但已经写入的批次仍保留在磁盘上,位于旧的加载 id 之下。
- 通过 Bash 修改的文件。 重复读取检测看不到它们。
- 模型服务器内部的时间(排队、预填充、解码)任何客户端工具都无法拆分;首个 token 延迟把它们合在一起。
常见问题
- Agent Profiler 会把我的数据发到别处吗?
- 默认不会。它只把追踪文件写到你本机的文件夹。只有设置了 otlpEndpoint 选项,它才会发出网络请求,并把每批追踪 POST 到该地址加 /v1/traces。不会发送给作者。
- 追踪文件写在哪里?
- 除非设置了 outputDir,否则写在 ~/.claude/agent-profiler/<sessionId>/。每一轮结束后写一次,会话结束时再写一次。文件包含耗时、token 数、工具名称和文件路径。
- 我的提示词和 Bash 命令会被导出吗?
- 除非开启 exportCommandText,否则不会。关闭时,提示词、Bash 命令行和搜索模式不会进入文件和 POST,只保留 test、build 之类的命令类别。开启后,它们会在尽力脱敏之后导出。
- 它能测出模型的计算时间吗?
- 不能,README 也明确这样说。它测量的是客户端等待了多久,例如首个 token 的延迟,其中网络、排队和预填充混在一起。每个数字都标有 observed、measured、reported 或 inferred。
- 我需要 Jaeger 或其他服务吗?
- 不需要。/profile 和状态栏可以独立工作。只有想在界面里浏览导出的 OTLP 追踪时,才需要 Jaeger 或 Grafana Tempo。仓库附带一个小型 Jaeger docker-compose 文件。
# 相关插件
精选Agent Radar
在提示词上方为每个运行中的子代理显示一行实时状态:用时、工具数和当前动作,另有 /radar 面板。
- 输入框上方
- 面板
- 弹出通知
- 拦截工具调用
- 读取你的提示词
- 修改界面
- 添加命令/工具
已在 v2.1.291 实测
精选Cache Clock
在 Claude Code 状态栏下方显示提示缓存:热缓存的剩余时间、命中率,以及缓存变冷后需重新缓存的 token 数。
- 状态栏
- 读取文件
- 写入文件
- 执行命令
- 添加命令/工具
已在 v2.1.296 实测
精选工具调用计数器
统计 Claude 每轮和每个会话的工具调用次数,在加载指示器旁显示计数,并新增 /tally 查看明细。
- 加载动画
- 对话记录
- 状态栏
- 拦截工具调用
- 修改界面
- 添加命令/工具
已在 v2.1.310 实测