1. 从“treg”这个标题说起:一个被低估的CLI工具链入口
第一次看到“treg”这个词,很多人会以为是某个拼写错误,或者某个小众库的缩写。但如果你最近在折腾 AI Agent 开发、CLI 工具链、MCP 协议这些东西,大概率已经在某个 GitHub issue、Discord 频道或者技术群里见过它。treg 本质上是一个围绕 Agent 执行链路做“注册、追踪、回放”的轻量级 CLI 工具,它的定位不是替代 Codex CLI、Claude CLI 这类交互式终端,而是补上它们缺失的那一环——把一次 Agent 执行过程变成可复现、可审计、可对比的结构化记录。
我最初接触 treg 是因为一个很具体的痛点:用 Codex CLI 跑一个多步骤任务,中间某一步工具调用失败了,终端里刷过去几百行日志,想回头定位到底是哪个 MCP Server 返回了异常、当时传给模型的上下文是什么、重试了几次,几乎不可能。Agent 执行不像传统程序,它有随机性、有外部工具依赖、有上下文窗口的裁剪,出了问题光靠翻 scrollback 根本不够。treg 解决的正是这个问题:它在 Agent 和底层 CLI 之间插一层薄薄的记录层,把每次执行的输入、工具调用、返回、耗时、token 消耗落成结构化文件,之后你可以用treg replay回放、用treg diff对比两次执行的差异。
这篇文章适合三类人看:一是正在用 Codex CLI、Claude CLI 或者自研 Agent 框架做开发,被调试和复现问题折磨的工程师;二是想理解 MCP 协议在实际工具链里怎么落地、Agent 执行链路到底长什么样的进阶学习者;三是需要给团队搭建 Agent 可观测性方案的技术负责人。我会从设计思路、核心机制、实操步骤、踩坑经验四个层面把 treg 讲透,所有命令和配置都是我实际跑过的,你可以直接抄。
2. treg 的整体设计与思路拆解
2.1 为什么要在 CLI 和 Agent 之间加一层记录
要理解 treg 的价值,先得理解现在 Agent 执行链路的典型结构。以 Codex CLI 为例,一次任务大致是这样流转的:用户在终端输入自然语言指令,CLI 把指令和当前工作目录的上下文打包成 prompt 发给模型,模型返回一个或多个工具调用请求(比如读文件、执行 shell、调用 MCP Server),CLI 执行这些调用,把结果再喂回模型,循环直到模型认为任务完成。这个循环里,每一次模型往返都是一个潜在的故障点,而终端默认只展示最终结果和部分中间输出。
问题在于,Agent 的失败往往不是“崩了”,而是“悄悄跑偏了”。比如模型第三次调用某个 MCP 工具时传错了参数,工具返回了一个空结果,模型没意识到异常,继续往下走,最后产出一个看起来合理但实际错误的结论。这种问题在终端里几乎看不出来,因为每一步都“成功”了。treg 的思路很直接:把每一次模型往返和工具调用都当成一条事件记录下来,形成一条完整的时间线。这条时间线不是给人看的日志,而是结构化的 JSON Lines,可以被程序读取、过滤、对比。
这个设计选择背后有一个关键判断:Agent 调试的核心不是“看日志”,而是“复现现场”。传统日志是线性的、易失的,而 treg 记录的是可重放的事件流。你可以把一次失败的执行导出,在另一台机器上重放,甚至把两次执行的同一阶段拿出来做 diff,看是哪一步的输入变了导致结果不同。这是纯终端 scrollback 做不到的。
2.2 与 OpenRouter、MCP 的关系定位
热词里出现了 OpenRouter、MCP、agent、CLI 这些词,说明 treg 的使用场景和它们高度重叠。我实际用下来的理解是:treg 不绑定任何特定的模型提供商或 Agent 框架。你可以用它记录通过 OpenRouter 调用的模型请求,也可以记录本地直连的模型;可以记录 Codex CLI 的执行,也可以记录你自己写的 Agent 循环。它的接入方式是在你的 Agent 执行入口处调用 treg 的 SDK 或者用treg wrap包裹一个 CLI 命令。
和 MCP 的关系更微妙一些。MCP 协议定义的是模型和工具之间的通信标准,它管的是“怎么调用工具”,但不管“调用过程怎么记录”。treg 恰好补上这一块:当你的 Agent 通过 MCP 调用一个 Server 时,treg 可以记录下请求的 method、params、返回的 content、以及这次调用的耗时。如果你在调试一个自定义的 MCP Server,这些记录能直接告诉你模型传了什么参数进来、你的 Server 返回了什么、模型拿到之后做了什么决策。这比在 Server 里打 print 要系统得多。
提示:treg 的记录层是旁路式的,它不改变 Agent 的执行逻辑,只是观察和落盘。这意味着你可以在生产环境低风险地开启它,出问题时再分析记录,而不是一开始就侵入式地改造代码。
2.3 方案选型:为什么是 CLI 而不是 GUI 或 SaaS
市面上做 Agent 可观测性的方案不少,有 SaaS 平台,也有 IDE 插件。treg 选择 CLI 形态,我认为有几个务实的原因。第一,Agent 开发者的工作流本来就在终端里,Codex CLI、Claude CLI、各种 shell 脚本,CLI 工具能无缝嵌入现有流程,不需要切换窗口。第二,记录文件落在本地,数据不出机器,对于处理敏感代码库的场景更可控。第三,CLI 天然适合脚本化和 CI 集成,你可以在自动化测试里跑treg replay来验证 Agent 行为是否回归。
这个选型也有代价:CLI 的可视化能力弱,看复杂时间线不如 GUI 直观。treg 的应对方式是提供treg view命令,在终端里用 TUI 展示事件流,同时支持导出成 HTML 报告。我个人的习惯是日常调试用treg view快速扫,深度分析时导出 JSON 用 jq 或者写脚本处理。这种“CLI 为主、导出为辅”的组合,在灵活性和效率之间取得了不错的平衡。
3. 核心细节解析与实操要点
3.1 安装与环境准备
treg 的安装方式取决于你的运行环境。目前主流的是通过包管理器安装,macOS 上可以用 Homebrew,Linux 上可以用官方提供的安装脚本,Windows 建议在 WSL2 里跑,因为部分 Agent CLI 本身对 Windows 原生支持就不完整。我实测下来,最稳的方式是用 Node.js 生态的包管理器全局安装,因为 treg 的很多集成点(比如 MCP 记录)依赖 Node 运行时。
# 通过 npm 全局安装(推荐,版本更新最快) npm install -g treg-cli # 验证安装 treg --version # 初始化配置目录 treg inittreg init会在你的用户目录下创建~/.treg/配置目录,里面包含默认的配置文件config.toml和记录存储目录records/。配置文件里最关键的几个参数是记录存储路径、单次记录的最大事件数、以及是否默认开启脱敏。脱敏这个功能值得单独说:Agent 执行过程中可能会把 API Key、数据库连接串、用户隐私数据带进上下文,treg 默认会对常见敏感模式做正则替换,但你不能完全依赖它,涉及真实敏感数据时建议在隔离环境里跑。
注意:如果你用的是公司内网环境,npm 安装可能走私有 registry,需要提前确认 treg-cli 这个包在你们的镜像源里可用。我遇到过私有源同步延迟导致装到旧版本的情况,用
npm view treg-cli versions确认一下最新版本号再装。
3.2 记录格式与事件模型
treg 的记录文件是 JSON Lines 格式,每行一个事件。这个选择很务实:JSON Lines 既能流式追加写入,又能被 jq、Python、Go 等各种工具直接处理,不需要额外的解析库。一个典型的事件长这样:
{ "event_id": "evt_01HX...", "trace_id": "trc_01HX...", "timestamp": "2025-01-15T10:23:45.123Z", "type": "tool_call", "source": "mcp", "payload": { "server": "filesystem", "method": "read_file", "params": {"path": "/src/main.py"}, "result": {"content": "...", "truncated": false}, "duration_ms": 45 }, "parent_event_id": "evt_01HX..." }事件类型主要有几种:model_request(发给模型的请求)、model_response(模型返回)、tool_call(工具调用)、tool_result(工具返回)、error(异常)。每个事件都有trace_id和parent_event_id,这样就能还原出完整的调用树。我特别喜欢parent_event_id这个设计,它让你能精确知道某个工具调用是被哪次模型响应触发的,排查“模型为什么调这个工具”时特别有用。
记录文件按 trace 分目录存储,路径是~/.treg/records/<date>/<trace_id>.jsonl。这个按日期分目录的设计方便清理旧记录,你可以写个 cron 任务定期删掉 N 天前的记录。我一般保留最近 7 天,因为 Agent 调试通常集中在开发阶段,太久远的记录参考价值不大。
3.3 与 Codex CLI 的集成方式
把 treg 和 Codex CLI 结合使用,有两种方式。第一种是treg wrap,直接包裹 Codex CLI 的启动命令:
treg wrap -- codex-cli run "重构 src/utils.py 里的日期处理函数"这种方式最省事,treg 会拦截 Codex CLI 的标准输入输出和子进程调用,自动记录模型往返和工具调用。但它的局限是只能记录到 CLI 层面的交互,如果 Codex CLI 内部有更细粒度的 MCP 调用,wrap 方式可能抓不全。第二种方式是在 Codex CLI 的配置里显式指定 treg 作为记录后端,这需要 Codex CLI 支持自定义 hook 或者插件机制。我实测下来,wrap 方式对大多数调试场景够用,只有在深度分析 MCP 交互时才需要第二种方式。
# 查看某次执行的记录 treg list --since 1h # 回放指定 trace treg replay trc_01HX... # 对比两次执行 treg diff trc_01HX... trc_01HY...treg diff是我用得最多的命令。比如我改了一版 prompt,想确认改动是否影响了 Agent 的工具调用序列,直接 diff 两次执行的 trace,就能看到哪些工具调用多了、哪些少了、参数有什么变化。这比人肉对比终端输出高效太多。
3.4 MCP Server 调用的记录要点
MCP 是当前 Agent 工具调用的主流协议,treg 对 MCP 的支持是它区别于普通日志工具的关键。当你的 Agent 通过 MCP 调用一个 Server 时,treg 记录的不只是“调用了什么”,还包括完整的请求和响应 payload。这对调试自定义 MCP Server 特别有价值:你可以看到模型实际传进来的参数结构,而不是靠猜。
配置 MCP 记录需要在 treg 的 config.toml 里指定 MCP Server 的启动命令:
[[mcp_servers]] name = "filesystem" command = "npx" args = ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"] record_payload = true max_payload_size = 1048576record_payload = true会记录完整的请求和响应内容,max_payload_size限制单条 payload 的最大字节数,防止大文件读取把记录撑爆。我建议把这个值设在 1MB 左右,超过的部分会被截断并标记truncated: true。如果你在调试一个返回大 JSON 的 MCP Server,可以临时调大这个值,但记得调试完改回去,否则记录文件会膨胀得很快。
提示:MCP Server 的启动命令如果依赖环境变量(比如 API Key),treg 默认会继承当前 shell 的环境变量。但如果你在 CI 里跑,需要显式在配置里声明 env 字段,否则 Server 可能启动失败,而 treg 只会记录一个启动错误事件,不会告诉你具体缺了哪个变量。
4. 实操过程与核心环节实现
4.1 从零搭建一个可记录的 Agent 执行环境
我拿一个真实场景来演示:用 Codex CLI 加一个自定义 MCP Server,完成“读取项目里的配置文件,根据配置生成一份报告”的任务,全程用 treg 记录。这个场景覆盖了模型调用、MCP 工具调用、文件读写三类事件,比较有代表性。
第一步,准备 MCP Server。我用一个简单的 Python 脚本模拟,它提供一个read_config工具,读取指定路径的 YAML 文件并返回解析后的内容。实际项目中你可能是连接数据库、调用内部 API 的 Server,但记录原理是一样的。
# mcp_server.py import yaml from mcp.server import Server from mcp.types import Tool, TextContent app = Server("config-reader") @app.list_tools() async def list_tools(): return [Tool( name="read_config", description="读取 YAML 配置文件", inputSchema={ "type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"] } )] @app.call_tool() async def call_tool(name, arguments): if name == "read_config": with open(arguments["path"]) as f: data = yaml.safe_load(f) return [TextContent(type="text", text=str(data))]第二步,在 treg 配置里注册这个 Server,然后启动 Codex CLI 并让它使用这个 MCP Server。Codex CLI 的 MCP 配置通常在它的配置文件里,你需要把 Server 的启动命令加进去。treg 这边只需要在 config.toml 里声明同一个 Server,它就能在记录时关联上。
第三步,执行任务并观察记录。跑完之后用treg list找到这次 trace,再用treg view看事件流。你会看到类似这样的序列:model_request(用户指令)→model_response(模型决定调用 read_config)→tool_call(read_config,path=config.yaml)→tool_result(返回配置内容)→model_response(模型生成报告)→ 结束。每一步的耗时、payload 大小都记录在案。
4.2 参数计算与记录体积控制
记录体积是个容易被忽视的问题。一次复杂的 Agent 执行可能产生几百个事件,如果每个事件的 payload 都完整记录,单个 trace 文件可能达到几十 MB。我做过一个粗略的估算:一次包含 50 次模型往返、每次往返上下文 8K token 的执行,如果完整记录请求和响应,文本量大约是 50 × 8K × 2 = 800K token,按每 token 约 4 字节算,就是 3.2MB 左右。加上工具调用的 payload,轻松超过 5MB。
treg 提供了几个控制手段。max_payload_size限制单条 payload,max_events_per_trace限制单次 trace 的事件总数,超过后新事件会被丢弃并记录一个truncated标记。我的经验值是:日常调试设max_payload_size = 262144(256KB)、max_events_per_trace = 500,足够覆盖绝大多数场景;深度调试时临时调大,但记得手动清理记录目录。
[recording] max_payload_size = 262144 max_events_per_trace = 500 retention_days = 7 compress_after_days = 1compress_after_days = 1会让 treg 在记录生成一天后自动 gzip 压缩,能省不少磁盘空间。我实测下来,JSON Lines 的压缩比通常在 8:1 到 12:1 之间,5MB 的记录压完不到 500KB。
4.3 用 replay 复现问题现场
replay 是 treg 最有价值的功能之一,但它的实现方式需要理解清楚。treg 的 replay 不是“重新执行一遍 Agent”,而是按记录的事件流重新播放,把当时模型返回的内容、工具返回的结果原样重放出来。这意味着 replay 是确定性的,不会因为模型随机性产生不同结果。这对于复现“当时到底发生了什么”非常关键。
# 基础回放,按时间顺序输出事件 treg replay trc_01HX... # 只回放工具调用事件 treg replay trc_01HX... --filter type=tool_call # 回放到指定事件为止 treg replay trc_01HX... --until evt_01HX...我常用的一个技巧是--until配合--filter:当我知道问题出在第 N 次工具调用时,先 replay 到那一步,仔细看当时的输入输出,再决定是改 prompt、改工具实现还是改 Agent 逻辑。这比从头跑一遍快得多,也不消耗模型 token。
注意:replay 依赖记录文件的完整性。如果某次执行因为进程被 kill 导致记录文件不完整(最后一行 JSON 可能被截断),replay 会在截断处报错。treg 对这种情况的处理是跳过最后一行不完整的记录,并在输出里警告。如果你发现 replay 结果和记忆中的执行不一致,先检查记录文件最后几行是否完整。
4.4 在 CI 里做 Agent 行为回归测试
把 treg 集成到 CI 里,可以做一件很有价值的事:Agent 行为回归测试。传统单元测试验证的是确定性代码,而 Agent 的输出有随机性,很难用断言直接判断对错。但 Agent 的工具调用序列相对稳定,可以作为回归的锚点。
我的做法是:在 CI 里跑一个固定的 Agent 任务,用 treg 记录,然后把这次 trace 的工具调用序列和基线序列做 diff。如果序列发生了非预期的变化(比如多了一次不该有的文件删除调用),就判定回归失败。这个方案不能覆盖所有问题,但能抓住“Agent 行为漂移”这类最危险的回归。
# 在 CI 脚本里 treg wrap --output /tmp/ci-trace.jsonl -- codex-cli run "$TASK" treg diff --baseline ./baselines/expected.jsonl /tmp/ci-trace.jsonl --fail-on-diff--fail-on-diff会让 diff 发现差异时返回非零退出码,CI 就能据此判定失败。基线文件需要人工审核后提交到仓库,每次 Agent 逻辑有意变更时更新基线。这个流程跑顺之后,我对 Agent 改动的信心提升了很多,至少不会出现“改了一个 prompt,结果 Agent 开始乱删文件”这种事故。
5. 常见问题与排查技巧实录
5.1 记录为空或只有部分事件
这是最常见的入门问题。原因通常有三类:一是 treg 没有正确拦截到 Agent 的执行入口,比如你用treg wrap包裹的命令本身是个 shell 脚本,脚本里又 fork 了子进程,treg 可能只记录到脚本层面;二是 Agent 使用了独立的网络请求库,绕过了 treg 的拦截点;三是记录目录权限问题,treg 写不进去但没报错。
排查顺序建议这样:先treg doctor检查环境,它会验证记录目录可写、依赖版本匹配、拦截点是否生效。然后手动跑一个最小任务,用treg list --since 5m确认有没有生成 trace。如果 trace 存在但事件很少,检查 Agent 是否用了 treg 不认识的 HTTP 客户端。我遇到过用 Go 写的 Agent 直接用net/http发请求,treg 的 Node 层拦截不到,这种情况需要在 Agent 代码里显式调用 treg 的 SDK。
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 完全没有 trace | 拦截未生效 | treg doctor检查 |
| trace 存在但事件少 | 部分请求绕过拦截 | 检查 Agent 网络库 |
| 记录文件为空 | 权限或磁盘满 | 检查目录权限和磁盘空间 |
| 事件时间戳乱序 | 多进程并发写入 | 确认 trace_id 是否混用 |
5.2 MCP Server 启动失败导致记录中断
MCP Server 启动失败是另一个高频问题。treg 记录到的是一个error事件,但错误信息往往只有“Server exited with code 1”,具体原因需要看 Server 自己的 stderr。treg 会把 Server 的 stderr 也记录到事件里,但默认只在record_payload = true时记录。如果你在调试 MCP Server,建议临时开启这个选项。
我踩过的一个坑是:MCP Server 依赖的环境变量在 treg 启动的子进程里没有继承。比如 Server 需要DATABASE_URL,我在 shell 里 export 了,但 treg 的配置里没声明 env,子进程就启动失败。解决办法是在 config.toml 的[[mcp_servers]]里显式加env = ["DATABASE_URL"],treg 会从当前环境读取并传给子进程。
提示:MCP Server 的启动超时默认是 10 秒,如果你的 Server 启动时需要加载大模型或者连接慢速数据库,可能超时。可以在配置里调大
startup_timeout_ms,但别设太大,否则 Agent 会卡住等 Server。
5.3 replay 结果与预期不符
replay 是确定性的,所以如果结果和预期不符,通常是记录本身有问题,而不是 replay 逻辑有问题。常见情况有两种:一是记录文件被手动修改过,JSON 格式损坏;二是记录时 payload 被截断(超过max_payload_size),replay 时只能看到截断后的内容。
排查方法是用treg validate <trace_file>检查记录文件的完整性,它会逐行解析 JSON 并报告格式错误。如果是截断问题,检查事件里的truncated字段,如果为 true,说明当时记录就不完整,需要调大max_payload_size重新跑一次。我建议在调试关键问题时,临时把max_payload_size设成 10MB,确保记录完整,问题定位后再改回默认值。
5.4 记录文件膨胀过快
如果你频繁跑 Agent 任务,记录目录可能几天就涨到几个 GB。除了前面说的retention_days和compress_after_days,还有一个技巧是按 trace 类型选择性记录。treg 支持在配置里定义过滤规则,比如只记录包含工具调用错误的 trace,或者只记录特定 MCP Server 的调用。
[[filters]] name = "errors-only" condition = "has_error == true" action = "keep" [[filters]] name = "drop-healthcheck" condition = "tool_name == 'health_check'" action = "drop"这个过滤机制在配置里是声明式的,treg 在写入记录前会先过一遍过滤器。我一般会加一条规则,把心跳类、健康检查类的工具调用直接丢掉,这些调用量大但调试价值低。实测下来,这条规则能减少 30% 到 40% 的记录量。
5.5 与 OpenRouter 等模型网关配合时的注意事项
如果你的 Agent 通过 OpenRouter 这类模型网关调用模型,treg 记录的是网关层面的请求和响应。这里有个细节:网关可能会对请求做改写(比如注入系统提示、调整参数),treg 记录的是改写后的版本还是改写前的,取决于你在哪一层接入。我建议在网关的客户端侧接入 treg,这样记录的是你实际发出的请求,便于对比网关行为。
另外,通过网关调用时,响应里通常包含网关自己的元数据(如实际路由到的模型、计费信息),treg 会把这些一并记录。这些信息在排查“为什么同一个请求两次结果差异很大”时很有用,因为可能是网关路由到了不同的后端模型。我遇到过这种情况:同一个 prompt,一次路由到 A 模型,一次路由到 B 模型,输出风格完全不同,看 treg 记录里的网关元数据才定位到原因。
6. 我在实际使用中积累的几个经验
treg 这类工具的价值,很大程度上取决于你怎么用它。我用了几个月,有几个体会比较深。第一,记录要趁早开。很多人是出了问题才想起来加记录,但 Agent 的问题往往需要对比“正常时”和“异常时”的记录才能定位,如果正常时没记录,排查效率会大打折扣。我现在是默认开着 treg,只在确认不影响性能的场景下才关。
第二,trace_id 要透传到业务日志里。treg 的 trace_id 是它自己生成的,但如果你能在 Agent 的业务日志里也打上这个 id,两边就能关联起来。比如 Agent 在处理某个用户请求时打了一条业务日志,你可以用 trace_id 找到对应的 treg 记录,看到当时模型和工具的完整交互。这个关联在排查线上问题时特别有用。
第三,不要过度依赖自动记录,关键节点手动打点。treg 的自动拦截能覆盖大部分场景,但有些关键决策点(比如 Agent 在多个方案里选了哪个、为什么选)是模型内部的,自动记录抓不到。我习惯在 Agent 代码的关键分支里手动调用 treg 的annotate接口,打一个自定义事件,把决策依据记下来。这些手动打点的事件在 replay 时会和自动事件混在一起,形成更完整的叙事。
第四,定期清理和归档。记录文件是调试资产,但不是永久资产。我现在的策略是:最近 3 天的记录保留完整,3 到 7 天的压缩保留,7 天以上的只保留包含错误的 trace,其余删除。这个策略在磁盘占用和可追溯性之间取得了平衡。如果你在团队里用,建议把归档策略写进文档,避免每个人都按自己的习惯清理,导致需要时找不到记录。
最后分享一个我最近才用上的技巧:treg 的记录文件可以直接喂给一些分析工具做可视化。比如把 JSON Lines 转成 CSV,用 pandas 做耗时分布分析,找出哪些工具调用是性能瓶颈。我用这个方法发现某个 MCP Server 的search工具平均耗时 2.3 秒,占了整个任务耗时的 60%,后来优化了它的索引策略,整体任务时间降了一半。这种基于记录的量化分析,是纯靠感觉调试做不到的。