传统性能分析有一个被反复验证的前提:函数名是稳定的,调用栈是天然可聚合的。所以 CPU profiler 才能把成千上万次采样按函数折叠成一棵树,让你一眼看到瓶颈在哪。火焰图能工作,靠的就是这个前提。
把同样的思路搬到 Agent 上,前提立刻崩塌。Agent 轨迹里真正该被归因的责任实体是「诊断认证失败」「对比两个分支」这种任务意图,不是代码路径;而两次意图相同的执行,提示词措辞可能完全不同,工具调用序列也不一样,没有任何字段能把它们可靠地折叠到同一个节点下。结果就是:现有的 Agent 可观测性工具全都停在单次执行的调试与追踪上,跨运行、长期的成本与故障归因,没人能回答。
arXiv 2609.20301 的 AgentPProf 就是补这一层的。作者来自 UC Santa Cruz、Eunomia Labs、HKUST 与阿里巴巴,代码落在 eunomia-bpf/agentsight。
一、缺口到底在哪
论文把这个问题讲得很清楚:生产团队几周下来手里会堆着大量轨迹,需要回答三个问题——哪一类任务最烧 token 预算?失败集中在哪些工作流?哪些行为模式触发了不安全的系统副作用?
这三个问题都需要在大量轨迹上做聚合和排序,而现有工具的能力边界是这样的:应用层工具(LangSmith、Langfuse、Phoenix)是为 trace、提示词、token、评测和延迟设计的,前提是你拥有应用代码;网关/代理类工具(Helicone)适合把流量导到一个托管端点;OpenTelemetry 那套语义约定也不是为「任务意图」这个粒度设计的,因为提示词是自然语言,两个表达同一意图的提示词可能字面完全不同。
一句话概括:Agent 可观测性需要的不是调试,而是 profiling;但 profiling 的前提条件在 Agent 场景里恰好缺失。
二、语义操作栈:把调用栈换成操作栈
AgentPProf 的核心抽象叫语义操作栈(semantic operation stack)。它做了两件事:
第一,引入统一操作(uniform operations)来代表所有活动——模型调用、工具调用、文件读写、进程执行,在模型里都被规整成同一种东西。这样下游的聚合器不需要为每种活动类型写一套逻辑。
第二,用操作栈替换运行时调用栈。采样时刻的栈不再是「函数 A 调用了函数 B 调用了函数 C」,而是「当前处在哪个任务、哪个子任务的哪个阶段」。这把归因的层级从代码结构搬到了任务结构上。
这个抽象的好处是兼容性:论文实现的是一个离线 profiler,把本地轨迹编译成 pprof 兼容的 profile。也就是说,最终产物可以直接喂给现成的 pprof 工具与火焰图渲染器。图 1 在论文里做得很有说服力——左边是一个 Go HTTP 服务的 CPU time profile,右边是三次 Agent 执行,在同一个层级结构下分别按操作数和 token 数渲染,两边用的是同一套阅读规则:宽度代表聚合成本,父节点包含子节点,出现在不同时刻的相同栈折叠成一帧。区别只在于帧从哪来。
三、递归操作分段:怎么凭空造出标识符
第二个设计是递归操作分段(recursive operation segmentation),解决的是「没有稳定标识符」这个根本困难。
思路来自一个结构观察:一个任务在轨迹里占据一段连续的区间,并且能继续分解成若干连续的子任务。既然如此,就不需要给每一轮对话都打标签(那贵得离谱),只需要找到责任发生转移的那些边界点。算法负责找出切点、给切出来的区间命名,然后递归地切下去,最终得到一棵可以按任意可加度量重放的层级树。
这段设计的工程含义值得单独说:分段是一次性的。论文的实测是,用 Codex(GPT-5.6-sol-high)把全部 405 条 CodeTraceBench 轨迹分段,配最多四个 worker,耗时 37 分钟,平均每条轨迹消耗 29,754 个输入 token 和 573 个输出 token。之后每次换视图、换度量、做检查,都只是在已有的树上重放,成本可以忽略。
四、四个评测问题的结果
论文用八个公开基准和三个真实轨迹数据集做评测,关键数字如下。
RQ3,标识符准不准。 在全部 405 条 CodeTraceBench 轨迹上(20,866 个操作,2,948 个人工标注阶段),递归分段与人工阶段标注的一致性达到 0.764 B3 F1,对比之下统计基线是 0.663,直接用原始动作名只有 0.541。边界 F1 为 0.480,比最强的自动基线(多分辨率递归)高出 0.101(B3)和 0.214(边界)。作者还试了一个有状态的逐轮 Qwen2.5-3B 基线,只有 0.650 B3 F1,说明「逐轮小模型打标」这条路走不通。
RQ2,输出是否对应真问题。 在三个完整的故障定位基准上(AgentProcessBench、TraceElephant、HINTBench,MAP 查询数分别是 614、400、220),把 profile 与基准自带的诊断信号结合后,MAP 相比只用自带诊断分别提升 0.031、0.107、0.117,且全部统计显著。摘要里「最多提升 56%」说的就是其中最显著的那个相对增幅。
导航收益。 同等排序质量下,profile 能把需要查看的内容量减少 12 个百分点。在 TraceElephant 的 220 条查询上对比更直观:全轨迹阅读拿到 MAP .502,但每条查询要烧 12.6K token,而且受上下文窗口限制;profile 引导的阅读最多只选五个分组,只打开 53% 的源码,就拿到 .455,明显优于用原始动作名分组的 .465 配 65% 打开率。
成本收益。 一次由 profile 推导出的修复让 Agent token 下降 19%,任务质量没有退化;注解成本下降 20%。用「紧凑骨架加选定完整结果」替换「每轮完整内容」的压缩策略,在 32 个留出任务簇上把供应商 token 削减 20.4%,同时保持完整的操作覆盖,并在 B3 与边界 F1 上满足预设的质量阈值。至于构建开销,27,765 个操作的并集构建耗时 1.16 秒,峰值常驻内存 465 MiB。
五、工程意义与边界
这份工作的价值不在于某个具体算法,而在于它明确指出了 Agent 可观测性缺失的那一层,并且给出了一条足够无聊、因此可落地的实现路径:不发明新的可视化范式,直接复用 pprof,让已有工具链继续工作。对已经在用 Go 系 profiling 工具的团队来说,学习成本几乎为零。
同时也要看清它的边界。分段质量决定一切,而分段本身依赖一个外部强模型,一旦切错,上层所有聚合都会失真,成本也从一次性支出变成需要预算的项。评测基准集中在代码与运维类轨迹,任务边界相对清晰;换成开放式研究或多轮协商场景,边界本身是否可定义还是未知数。最后,它输出的是离线 profile,需要轨迹先落盘,覆盖不了「Agent 正在跑、需要实时止损」的场景。
把这三条合起来看,最合理的定位是:AgentPProf 是把事后归因做扎实的一层,它和实时拦截、在线熔断并不冲突,而是上下游关系。先把「钱花在哪、错出在哪」量化清楚,再谈优化,这个顺序是对的。