资讯详情

本地CLI调试工具Paper:AI Agent执行轨迹与Token成本分析实战

📅 2026/10/15 13:00:09 | 华诺云谱 👁 阅读
本地CLI调试工具Paper:AI Agent执行轨迹与Token成本分析实战
1. 为什么我花时间做一个本地 CLI 调试工具如果你最近在折腾 AI Agent不管是 Claude Code、Codex 这类偏编码的智能体还是自己用 LangChain、OpenAI SDK 搭的多步推理工作流你一定经历过这种痛点Agent 跑完一轮任务结果不对你想知道它中间到底做了哪些决策、调了哪些工具、哪一步开始跑偏——结果只能翻日志、打日志、加 print一轮下来几个小时没了。我自己的项目里Agent 一多日志一杂问题就变成了一场灾难。云端 LLM 服务商自带的调试面板不是不好用而是它只管模型侧的内容管不到你本地代码里工具调用的上下文、变量状态、token 消耗和重试策略。出于这个背景我把手头一直在用的内部调试脚本梳理了一遍做成了一个本地 CLI 工具名字就叫 Paper。它解决的问题很直接让 Agent 的执行过程像翻文件一样可以回看、可以搜索、可以对比而不是靠肉眼在一堆混合日志里大海捞针。这篇文章我会把 Paper 的设计思路、核心功能、真实使用场景和踩过的坑一次性讲清楚。适合正在做 Agent 开发、或者打算入坑 LLM 应用调试的工程师参考。2. 整体设计为什么选 CLI 而不是 Web 面板2.1 本地优先的三个理由做调试工具市面上九成方案是 Web Dashboard数据上报、端口监听、图表可视化一套组合拳。但我不太认同这条路。至少对于 Agent 调试这个场景CLI 有它不可替代的优势。第一环境零依赖。Web 面板要起服务、要开端口、要浏览器访问这在本地开发环境里没什么但一旦跑到 Docker 容器、CI 流水线、服务器上的定时任务里你根本没有机会打开浏览器。CLI 工具只要一个二进制文件和一条命令终端里直接跑任何环境都适用。第二管道友好。CLI 天然支持管道和重定向这意味着你可以在脚本里把调试输出直接喂给 grep、jq、awk 继续处理数据加工链路可以完全自动化。Web 面板的接口再丰富也不如终端管道的灵活性来得直接。第三心智负担低。调试这件事本身就是在一个终端里同时操作代码和观察行为。切到浏览器再切回来每切换一次都是上下文的损失。CLI 把运行-观察-修改三个动作收敛在一个窗口里这个体验差异长期下来非常明显。2.2 Paper 的架构分层Paper 整体分三层。最底层是数据采集层它通过环境变量注入和回调接口来拦截 Agent 运行过程中的各类事件包括 LLM 请求、工具调用、中间推理、最终输出。中间层是存储层数据不落外部数据库直接写到本地目录下的结构化文件里按会话 ID 分文件夹每个事件一条记录。最上层是展示层提供paper view、paper search、paper stats这类命令把存储的数据以不同视角呈现给你。这个架构最大的好处是采集和展示完全解耦。也就是说你可以先在后台跑一批 Agent 任务采集数据数据落地之后再慢慢分析。不需要实时盯着屏幕这比较接近事后取证的调试哲学——但事后不是指 Agent 跑完了才看而是指分析动作和执行动作可以不在同一个时刻发生。2.3 和现有开源方案的差异化市面上现有的 Agent 调试方案要么是 LangSmith 这样的托管服务要么是各类 E2B、AgentOps 之类的观测平台。它们的问题在于其一数据默认上传云端对某些敏感项目来说这本身就是不可接受的其二平台的抽象层级通常绑定特定框架你用自己的代码做 Agent 编排时接入成本很高其三平台展示的是它们认为重要的信息而不是你真正关心的信息。Paper 做的就是反过来不绑定任何框架不强制上传任何数据所有字段你可以自定义扩展。它像一个本地记录仪你告诉它要记录什么它就忠实记什么。从需求匹配的角度我认为本地 CLI 才是调试这个动作的最佳载体——观测平台适合做大盘监控但真正一行一行看执行轨迹的还是命令行来得顺手。3. 核心功能拆解从执行轨迹到成本分析3.1 执行轨迹回放tracing 的核心实现Paper 最核心的功能是执行轨迹回放。每个 Agent 会话的所有事件都会按照发生顺序记录下来形成一个完整的执行树。这个执行树不是只有名称的列表它包含每一个节点的输入、输出、耗时、token 消耗以及和父节点的调用关系。实现上我用了类似于 OpenTelemetry Span 的数据模型。每个事件是一个 TraceEntry包含session_id、parent_id、event_type、timestamp、payload这几个核心字段。执行树就是通过parent_id关联出来的。查看命令做了缩进树形展示缩进层级就是调用深度。这事看着简单实际上有一个细节非常关键还原状态。调试 Agent 时只看到调用了一个工具是不够的你还得知道调用前后变量值变成了什么。所以 Paper 在拦截事件的时候会额外捕获执行上下文的关键变量快照记录到 payload 里。这也是我踩过最深的一个坑——早期版本不记录上下文回放的时候经常出现对调了这个工具但为什么不传这几个参数的困惑加了快照之后很多问题一眼就能定位。3.2 检索能力为什么一般的 grep 不够Agent 调试中有一个高频场景任务跑完结果不对你想知道某个工具在哪些会话里被调用过某段报错信息是哪个环节产生的。如果只是 grep 日志你会被海量噪声淹没因为日志里 Agent 的中间输出、框架自身的打印、第三方 SDK 的日志全都混在一起。Paper 的paper search做了一个结构化查询的事你可以指定事件类型、工具名称、时间范围、以及 payload 中的任意字段来做组合过滤。比如paper search --session abc123 --tool web_search --has-error意思就是只查这个会话中所有 web_search 工具调用、且结果里包含 error 的记录。这在排查工具链故障时非常高效把看日志找线索变成了按字段查记录。底层存储我选的是 JSON Lines 格式每条事件一行。搜索时默认基于行列定位超大会话会建一个轻量索引来加速。为什么不用 SQLite因为 JSONL 的可读性最好出了问题可以直接用编辑器打开看而且 diff 友好——你可以在调试前后导出两份 JSONL直接 diff 出差异事件。这对于复现为什么上次跑通这次跑挂特别有用。3.3 Token 与成本分析Agent 这么贵得盯着点Agent 的 token 消耗和普通单次 LLM 调用不同它呈阶梯式累加。一个典型的多步 Agent 任务可能先调用一次规划模型再执行两次工具调用每次工具返回之后还要再请求一次模型来决策下一步。每滚一层上下文就可能把前面的结果全部带上消耗会指数级膨胀。很多人在 Agent 上线之后才发现成本暴增就是因为没有在开发阶段监控 token 变化。Paper 专门做了一个paper stats命令它按会话归组、按事件类型分桶展示 token 使用情况。核心指标包括总prompt_tokens、completion_tokens、以及按工具维度拆解的二次消耗统计。最有用的是一个token 增长曲线——看每个事件之后的累计 token 数你能清晰地看到是哪一步调用的上下文把预算烧掉的。我自己的实测数据某个抓取网页后做总结的 Agent 任务前两步 token 消耗都很正常到了第三步把网页全文塞进去瞬间从 2K 跳到 15K。这类行为在开发调试期发现和上线后发现成本差着两个数量级。3.4 交互式会话检查深入现场除了事后分析Paper 还支持交互式模式paper attach。这个命令把一个正在运行的 Agent 进程挂到 Paper 上实时打印它的事件流并且可以按CtrlO临时打开一个只读事件浏览器上下键翻页查看已经发生的事件确认当前 Agent 走到了哪一步。这个功能模仿了kubectl attach和docker attach的思路。调试分布式系统时attach 是一个成熟的手段Agent 是某种意义上的异步系统——它的执行上下文是逐步累积的你没法预知哪一步出错实时观察比事后翻查更符合直觉。4. 实操过程从接入到定位问题的完整流程4.1 一分钟接入环境变量注入接入方式我用的是环境变量注入不需要改业务代码。Paper 会在安装时生成一段加载脚本你只要在启动 Agent 前 source 它之后该进程内所有符合协议的事件都会被自动采集。# 按 Paper 生成的配置预先加载采集钩子 source ~/.paper/env.sh # 正常运行你的 Agent 程序 python run_agent.py --task 汇总本周项目进展env.sh里实际做的事是设置几组环境变量包括事件输出目录、采样率、脱敏规则。如果你的 Agent 是用 LangChain 或 OpenAI SDK 写的Paper 通过 Monkey-Patch SDK 底层客户端来捕获事件如果是自定义编排提供了一组显式采集 API在你的关键路径上手动埋点。对大多数框架型 Agent第一条路零侵入。但是要注意Monkey-Patch 方式只对当前进程有效如果你有多个子进程需要确保环境变量沿进程树继承。这是我建议优先用source而不是export的原因——source 你在哪个终端执行就管哪个终端行为可预期。4.2 跑一次任务并查看轨迹接入之后跑一次 Agent 任务。任务跑完或跑挂之后用paper view来看完整轨迹# 列出所有会话 paper list # 查看最近一个会话的执行轨迹 paper view --last # 查看指定会话展开所有节点 paper view --session 8f3a2e --depth 10Session: 8f3a2e Task: 汇总本周项目进展 耗时 42.8s LLM调用 7次 工具调用 5次 ├─[1] planner.plan (LLM) input项目进展数据收集 output分三步执行计划 tokens1240 │ ├─[2] tool.read_files (TOOL) path./reports/*.md output找到4个报告文件 │ ├─[3] tool.web_search (TOOL) query团队周报模版 output搜索完成 │ ├─[4] planner.summarize (LLM) input报告内容搜索内容 output生成周报摘要 tokens8320 │ └─[5] tool.write_file (TOOL) path./summary.md output写入完成这是我用 Paper 记录的真实会话轨迹。你能直观看到执行链路哪一步耗时最长、哪一步调用 LLM 吞了大额 token一目了然。这个例子中[4]的 8320 tokens 就是前面说的累积问题它把四份报告的全文都带进了上下文token 数远超第一阶段。4.3 实战排查一个典型的 Agent 任务失败案例我拿一个真实的排查过程演示。某次任务Agent 需要总结多个网页内容但最终生成的摘要缺少第一个网页的信息。第一次排查我用paper search快速定位了所有fetch_url相关事件paper search --session 8f3a2e --tool fetch_url[2] fetch_url urlhttps://site-a.com/article/1 status200 length24KB [3] fetch_url urlhttps://site-b.com/article/2 status500 errorTimeout [5] fetch_url urlhttps://site-c.com/article/3 status200 length18KB看到了吗问题不在总结环节而是在第三步抓取时发生了超时。Agent 的策略是继续往下执行而不是重试最终总结阶段就没有拿到第二个页面的内容整个任务带伤跑完。这类问题如果你只盯着最终输出会误判是总结模型的问题白白浪费好几次调试迭代有了结构化轨迹问题源头一次就锁定了。修复方案也很直接在 Agent 的工具调用逻辑里加了一个 fetch 重试策略并对超时事件打上标记供 Paper 后续统计失败率。这个案例充分说明了调试工具最怕的不是信息不够而是信息太杂。4.4 自定义事件扩展记录你自己的业务指标如果你的 Agent 不是框架型而是自己写的编排代码那用 Paper 的采集 API 做精细埋点更合适。它提供一个装饰器你只需要在关键函数上一挂即可# agent_task.py from paper import capture capture(event_typetask_step, fields[status, elapsed]) def execute_step(step_name: str, context: dict): # 你的业务逻辑 ... return {status: ok, elapsed: 0.8}这个装饰器会在函数调用时自动记录函数名、参数快照和返回值全部按事件写入当前会话。此后execute_step的每次调用都会出现在paper view的轨迹树里。配合paper search --event-type task_step你甚至可以做一个简单的Agent 单步健康度看板——统计每类 step 的成功率、平均耗时及时发现哪个环节开始频繁失败。这个能力的重要性在于通用工具只能记录 LLM 和 SDK 层面的行为但真正需要排障的往往是你的业务逻辑。框架外的自定义事件是让调试工具从能看到好用的关键一步。5. 常见问题与排查技巧实录5.1 采集不到事件先检查环境变量继承接入了 Paper 但运行后paper list显示空的会话这是最常见的入门问题。九成原因是子进程环境变量没继承——你可能把source ~/.paper/env.sh写在了脚本里脚本内部的子进程能采集但脚本执行完环境变量就消失了。另一个常见原因是 SDK 版本兼容性新版 OpenAI SDK 改了底层方法名Monkey-Patch 挂载时静默失败。Paper 内部对这类情况有个自检命令paper doctor它会检查当前环境下 SDK 版本、采集钩子是否生效、环境变量是否正确并把检查结果直接输出。如果doctor报告钩子未生效优先确认 Python 版本和 SDK 版本是否在支持列表内。5.2 轨迹记录不完整异步回调丢事件如果你用异步 SDK比如AsyncOpenAI在异步回调中写文件时没有做同步处理进程可能在事件写入缓存但还没刷盘时就退出了导致最后几个事件丢失。解决方法是监听退出信号并主动 flush或者给写入层加一个定时落盘机制。Paper 的采集层做了解法默认每 50 条事件落盘一次进程正常退出时再补一次 flush。但如果你用kill -9强杀进程最后一批总是会丢。所以调试期间建议把多步 Agent 任务做完再退出或者至少保留重跑能力不要让丢尾部事件干扰你的判断。5.3 输出太多看不过来采样率调节长任务轨迹动辄上百个节点全展开根本看不完。这种情况下除了用--depth限制展开深度Paper 还提供采样率配置# 只显示调用了工具的节点和出错节点忽略纯 LLM 中间过程 paper view --session 8f3a2e --focus-tool --only-error采样率设置的逻辑是按事件类型过滤展示而不是按比例丢弃记录。这个区别很重要——丢弃记录是数据损失过滤展示只是视角调整。记录永远全量展示永远可选这是设计调试工具的一个底线原则。5.4 token 统计对不上账单Paper 统计的 token 和你的账单数字有出入这通常不是 bug而是上下文缓存被忽略导致的。主流 LLM 服务商对 prompt 缓存部分有专门的折扣计价和独立的 token 计费显示如果 Paper 按朴素prompt_tokens统计会把缓存命中的 token 算作全价。解决方式是在配置里显式开启缓存字段采集从响应的usage结构中读出prompt_tokens_details.cached_tokens单独做一列展示。5.5 常见问题速查表现象可能原因排查手段采集不到任何事件环境变量未生效、SDK 版本不兼容paper doctor自检轨迹尾部缺失进程被强杀、异步未 flush正常退出流程、定时落盘事件时间戳错乱多线程并发写入未做串行化按事件序号而非时间排序搜索找不到内容payload 字段结构变化先paper dump导出再 grep重复记录同一调用同时启用 Monkey-Patch 和手动埋点关闭其中一种采集方式以上每一条我都实际遇到过最隐蔽的是最后一种某次我既保留了框架自动采集又在自己的工具函数上加了capture结果同一个fetch_url被记录两次统计失败率时数字翻倍。后来统一为只保留一种采集路径才恢复正常。建议你接入时就把采集方式定死混合使用会让数据一致性难以保障。6. 一些经验沉淀与扩展方向6.1 调试思维的转变用了很长一段时间的 Paper 之后我最大的体会是调试 AI Agent 和调试传统程序本质上是不同的思维模式。传统程序的行为是确定性的你定位到代码行问题就解决了一大半。而 Agent 的行为是概率性的同样的输入每次输出都可能不同。所以调试 Agent的核心不是找哪一行代码错了而是找出执行轨迹中哪个分支选择和你的预期出现了偏差。因此工具的抓手也不同。传统调试器提供的是断点和变量监视Agent 调试工具提供的应该是完整的决策路径和每一步的输入输出快照。Paper 的整个设计原则都围绕决策路径可回溯来展开没有走传统调试器的老路。6.2 这个工具后续可以怎么扩展Paper 目前已经能覆盖单机、单进程的调试场景但横向扩展的空间还很大。我个人下一个打算做的是把多个 Agent 会话聚合对比——同一个任务跑多次通过 diff 轨迹来定位偶发失败背后的模式。目前遇到的真实案例一个任务 10 次里会有 1 次结果异常单看某一次轨迹找不出原因但如果能把 10 次轨迹并排对比定位哪个节点的行为在失败批次中出现了分歧会比逐次排查高效得多。另外把 Paper 的事件格式标准化兼容 OpenTelemetry 的 Trace 语义就可以直接接入 Grafana 或 Jaeger把本地调试数据和服务端监控打通。一旦建立这个桥开发期和运维期的数据就能形成闭环Agent 的行为观测就不止步于 debug而能沉淀为长期的健康度基线。最后分享一个小技巧如果你现在正被 Agent 的诡异行为困扰先别急着改提示词。把这一次的运行轨迹完整留下来跑第二次再留一次然后 diff 两次轨迹。多数看似随机的失败在轨迹对比下都会露出规律。好的调试工具不应该只是帮你找到这次的问题更应该帮你建立一套可复用的排障流程。Paper 目前算是我自己比较满意的沉淀后续还会继续在这个方向打磨。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑