关于 agent-observability-replay-trace
用于将 Datadog Agent Observability 跟踪在本地代码上重放,并与原始输出对比的技能。它引导开发者获取跟踪、准备本地运行、隔离重放,并循环修改和审查结果。
主要功能
- 01获取跟踪并定位基准输出
- 02使用本地代码运行跟踪
- 03对比原始与重放输出
- 04将重放隔离在 -local 应用名下
- 05支持 MCP 或 pup 获取跟踪
适用场景
- 01复现不满意的生产跟踪结果
- 02检查代码更改是否改善输出
- 03通过本地重放和差异比较迭代调整
MCP 相关
有 datadog-llmo MCP 时,技能优先用它访问跟踪,否则回退到 pup。它获取原始跟踪,并检查重放生成的跟踪。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ datadog-labs/ agent-skills --skill agent-observability-replay-trace安装后在 Claude Code 中输入/agent-observability-replay-trace确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/details.mdlocal-setup.mdscripts/replay_runner_template.py详细介绍
针对本地代码重放一条追踪记录
针对单条生产环境追踪记录的快速迭代循环:选取一条开发者不满意其输出的追踪记录, 可以选择修改代码,使用本地代码重新运行,并简洁地展示新旧输出的差异——重复此过程,直到满意为止。不对项目布局作任何假设。
从开发者的编码 Agent 调用:/agent-observability-replay-trace <trace-id> [<changes to test>]。
如果不做修改,只执行重放和差异对比(复现/回归检查),然后询问是否进入循环。
此文件是工作流的主干——有意保持简洁。详细内容见 references/details.md(追踪后端及 pup 标志、运行器约定、导出模式、轮询、追踪链接范围修复)和
references/local-setup.md(如何让仅部署的应用可在本地运行)。在使用 pup 或生成运行器之前,请先阅读 details.md。
编写代码——尽量少写注释,最好不写。 你生成或编辑的所有内容(注释、运行器的 ENTRYPOINTS 条目、本地测试框架、迭代修改)都应与周围代码保持一致,且不包含不必要的注释——不要叙述代码显而易见的功能;只有在解释真正不明显的原因时才添加注释。
意图标记: 每次调用 datadog-llmo MCP 工具时,都在 telemetry.intent 前加上 skill:agent-observability-replay-trace[<inv_id>] — (每次运行生成一个简短 ID,并在每次调用中重复使用),后接调用工具的原因。仅在首次 MCP 工具调用时使用 skill:agent-observability-replay-trace:start[<inv_id>] — (注意 :start 后缀)。首次调用示例:skill:agent-observability-replay-trace:start[3a9f1c2b] — fetch the original trace's baseline output。pup-CLI 调用不带 telemetry.intent,因此这仅适用于 MCP 路径。
交互模式——使用选择器作为关卡,绝不直接结束
这是一个持续进行的循环。在每个决策点,都要使用 AskUserQuestion 选择器(类似计划模式的菜单)呈现选项,而不是提出一个普通问题后结束本轮。有两个关卡:(a) 提出代码修改方案后、重放之前;(b) 每次差异对比之后。选择器中的自由文本选项允许用户直接输入详细说明(要改进的内容)——直接据此执行,不要追问。每次重放后都要重新呈现选择器,直到用户选择“到此为止”。
范围——先检查
- 使用
ddtrace/LLM Obs 跟踪(ml_app+ 可发现的入口点)。优先支持 Python; 其他语言也可使用,但你需要按照其 SDK/构建工具的约定编写运行器。 - 入口点输入可序列化为 JSON,并且存在一个可调用的根 Span 接缝(见步骤 3.5——判断标准不是“能否运行”;仅部署的应用通常仍会暴露一个普通的可调用项)。
- 可访问追踪记录的后端——
datadog-llmoMCP(可用时使用)或pupCLI(回退方案;两者都没有时也更易于安装)(步骤 0)。 - 凭据:
DD_API_KEY+DD_SITE+ 提供方密钥。不是DD_APP_KEY——这是普通追踪记录,不是 Experiment(后者见agent-observability-replay-experiment)。 - 副作用不可逆:重放会重新运行真实代码(产生模型费用并执行真实写入),而且无法删除 LLM Obs 追踪记录——范围设置错误的重放(
ml_app错误)会永久污染生产应用的仪表板/评估集。因此,隔离使用<ml_app>-local(步骤 4/6/7)至关重要,而不是为了整洁。 首次重放之前必须警告用户。
工作流
0. 确保有可访问追踪数据的后端
按以下顺序选择:(1)如果有 mcp__datadog-llmo-mcp__* 工具,则使用 MCP — 默认选项(读取能力稍丰富:结构化树 + content_info);(2)否则,如果已安装 pup 且 pup auth 指向该应用的组织,则使用 pup;(3)否则,用户两者都没有 → 引导用户安装 pup(它比 MCP 更容易设置,因此此处推荐 pup):
brew tap datadog-labs/pack && brew install datadog-labs/pack/pup
pup auth login
(MCP 替代方案:claude mcp add --scope user --transport http "datadog-llmo-mcp" "https://mcp.datadoghq.com/v1/mcp?toolsets=llmobs";请参阅 https://docs.datadoghq.com/bits_ai/mcp_server/setup/。)没有后端时不要继续。
后端与操作的对应关系,以及 pup 的确切标志和注意事项,请参阅 details.md — 使用 pup 前先阅读该部分。 使用 pup 时必须注意两点:(1)结果会返回在 data.spans[] 或顶层 spans[] 中(因版本或 --no-agent 而异)— 解析实际存在的那个,否则对于已摄取的追踪数据会得到零条命中(这是静默的假阴性,见步骤 7);(2)检查令牌是否过期(pup auth status),不能只确认存在认证信息 — 如果令牌在循环过程中途过期,看起来就像“找不到追踪数据”。
1. 解析命令
<trace-id> + 可选的自由文本修改说明(ID 后的所有内容);没有修改说明 → 仅差异模式。根据项目(LLMObs.enable(ml_app=…) / DD_LLMOBS_ML_APP)或追踪数据确定 ml_app;若有歧义,请确认。
2. 获取追踪数据 + 定位基线
通过后端获取追踪数据;记下 total_duration_ms(它会影响步骤 7)、trace_url,以及(如果存在)metadata.replay_input/replay_entrypoint。**定位基线字段 — 它不一定是根节点的输出:**开发者不满意的值可能是工具调用的输入,也可能是嵌套多个层级的中间输出;而应用可能会在 span 记录该值之前对其进行后处理。选择代码变更实际能够影响的字段,否则差异就会被噪声淹没。
2.5. 检查扇出
如果根 span 扇出为多个重复的同级子树(对 N 个并行子运行执行批处理/映射),待测变更通常只会体现在单个分支中 — 重放整个根节点会造成约 N× 的开销和耗时,却不会带来额外信号。可以提出只重放一个有代表性的分支;记录跳过了什么。有意选择:挑选到达终端工具或产生副作用的工具的最廉价分支(大多数分支都是不能证明任何问题的空操作),并从子 span 的输入(而非根节点的输入)还原其输入。只有当变更本质上会影响多个分支时,才重放整个根节点。
3. 确定入口点 + 输入
- **入口点:**如果存在,则使用
metadata.replay_entrypoint;否则根据根 span(名称/类型)和代码推断,并向用户确认。 - 输入:如果存在,则使用
metadata.replay_input;否则推导一个建议输入(优先参考代码签名 — 渲染后的提示词会丢失信息),并让用户确认/编辑。
3.5. 确保有本地运行路径(找到最内层的可调用接缝)
询问**“这个根 span 最内层的可调用接缝是什么?我能直接用 JSON 调用它吗?”** — 不要问“应用能运行吗?”。如果已经可以直接调用 → 跳过,继续。如果它藏在处理程序/服务下(只能通过部署环境运行、没有本地 __main__、与在线基础设施耦合)→ 按照 references/local-setup.md 操作(检测 → 提议 → 批准 → 构建)。常见的中间情况 — 已部署服务的核心逻辑本身已经是普通的可调用对象(端口与适配器架构)— 只需提取/调用该接缝;进行完整的本地设置步骤过于繁琐。
4. 确保两个持久化产物(一次性设置)
- a) 在入口点添加注释:在应用的实际入口点添加注释,让所有未来的追踪(包括生产环境中的追踪)都能自我描述。请在 span 开始时,而不是成功/延迟完成路径中写入标记——失败的运行也必须带有
replay_input(这些正是你最想重放的运行):
不要添加LLMObs.annotate(span=span, metadata={"replay_entrypoint": "<stable id>", "replay_input": <extractor>})replay_output——原始追踪就是基线。(非 Python 的注释 API 各不相同,例如 Go 的span.Annotate(llmobs.WithAnnotatedMetadata(...));请参阅details.md。) - 隔离预检(编写运行器之前):检查入口点的调用路径,搜索每 span/每次调用的 ml_app 覆盖项(Go 的
llmobs.WithMLApp;Python 装饰器上的ml_app=或LLMObs.annotate中的ml_app=)。这些覆盖项的优先级高于初始化级别的-local,因此应用的 span 仍可能进入生产环境——追踪器级配置不能证明已隔离。如果存在此类覆盖项,应用的 ml_app 必须从环境变量解析,这样-local才会生效。 - b) 运行器——满足
details.md中的语言无关运行器约定(加载环境变量 → 推导<ml_app>-local→ 针对 JSON 分派到一个入口点 → 在每个退出路径上都执行 flush,包括错误路径 → 仅当 ml_app 以-local结尾时才允许启动 → 打印-localml_app)。Python:复制scripts/replay_runner_template.py并填充ENTRYPOINTS。其他语言:按该约定编写——不要假设 Python API 也适用(Go API 及导出模式注意事项见details.md);如果该语言不支持进程内 dotenv,请添加一个运行包装脚本(产物 c),用于加载项目环境变量、取消设置环境中已有的 provider 变量,并导出-local覆盖项。推断并确认运行命令;遵循宿主仓库的构建文件约定(Bazel/Gazelle →cmd/<name>/,运行 Gazelle,并在重放前构建)。
5.(如果有人请求更改)编辑,然后设置门槛
完成代码更改,向开发者展示更改的 diff,然后显示一个 AskUserQuestion 选择器:
立即重放 / 先调整更改 / 取消。仅当选择“立即重放”时才执行重放。
6. 重放
首次重放前:发出警告(重新运行会产生真实影响——模型费用和实际写入),并清理环境变量。编码 Agent 自身的环境变量(由 Claude Code 设置的 ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL,以及其他 provider 密钥)可能导致应用的 SDK 绕过已配置的模型网关——这种保真度差异无法从 diff 中看出。默认取消设置环境中已有的 provider 变量,并告知你已这样做(不要只询问);同时在应用中搜索其自身针对环境变量的密钥检查逻辑。还要验证凭据所属的组织与追踪所属组织一致——如果不一致,重放会被发送到你无法查询的位置(看起来像是数据摄取延迟)。
确认后,记录 t0 并运行——加载项目的环境变量文件,绝不要内联密钥(命令中可以使用标记标签;内联 DD_API_KEY=<value> 会被权限分类器拦截,并泄漏到历史记录/转录中——请使用包装脚本/环境变量文件):
DD_TAGS=replay_run_id:<unique-id> <run cmd or wrapper> --entrypoint <id> --input-file <path>
运行器会在 <ml_app>-local 下生成数据(具幂等性,因此重放绝不会污染生产环境),并打印该名称——请在该名称下轮询新追踪。
7. 等待新 trace
- Runner 子进程超时 =
max(120s, ~3 × total_duration_ms)。 - **摄取轮询:**返回后,每隔约 5s 轮询后端,最长约 2 min,查找
<ml_app>-local下的replay_run_id标签(pup:--query "replay_run_id:<id>",纯key:value)。在报告“未找到”之前,务必先不加标签筛选重新查询(只用<ml_app>-local+ 时间范围):如果查询返回了 spans,说明筛选/解析/范围有误,不是摄取问题。错误地报告“没有 trace”看起来像正常情况,还会导致无谓的重跑。 - 每次命中都要验证隔离情况——标签匹配并不能证明隔离成功。
--query/标签匹配可能返回实际ml_app属于另一个应用的 span(--ml-app筛选条件会被忽略)。读取每个返回 span 的ml_app,并在报告干净的重放之前断言其以-local结尾——否则你报告的是“在-local下干净重放”,而 trace 实际上在生产环境中(且无法撤销)。这种错误的信心比漏报更糟。超时不要直接判定失败;可以提议继续等待。
8. Diff(附两条 trace 的链接)
简要总结新输出与旧输出的差异——只记录有意义的差异。注意线上环境变化;由于任何非确定性 Agent 的运行结果都会有所不同,默认进行两次重放(diff-only 模式也一样,不仅限于面向模型的编辑),并使用重放之间的比较——如果两次本地运行彼此之间的差异,与它们和生产环境之间的差异大致相同,那么差异来自抽样方差,而非你的更改。如果重放禁用了有副作用的集成(dry-run),该集成的子树将缺失——比较 span 数量前,从两侧都排除该子树,否则结构性 diff 就毫无意义。diff 开头先列出两条 trace 链接:
- 旧版:
trace_url原样照搬——但在扇出场景下(你重放的是一个分支),链接到分支 span,而不是整个根 trace 的 url。 - 新版(重放):必须带有
ml_app=<ml_app>-local,否则打开后会是空白页面——而且trace_url是一个 org-switch 包装链接(…/switch_to_user/<id>?next=<encoded /llm/traces …>&flow=org_switch),所以要将ml_app=<ml_app>-local注入解码后的next查询参数中,再重新编码;不要追加到外层 URL(具体方法见details.md)。无法在此处通过浏览器验证——确认链接打开后内容非空。
9. 门槛——继续迭代,或在测试框架损坏时停止
**测试框架故障门槛(diff 之前):**如果重放发现测试框架有误——trace 落在错误的 ml_app 下、完成第 7 步的合理性检查后仍无 trace、缺少 flush,或 auth/org 路由错误——**不要基于错误数据继续做 diff。**停止并提供一个选择器来修复测试框架(重新限定 ml_app 范围 / 添加 flush / 修复环境),然后重新重放。
否则,在 diff 之后,显示一个 AskUserQuestion 选择器:看起来不错——到此为止(完成;将编辑留在工作树中)/ 进行更多更改(内联自由文本 → 返回第 5 步)。每次重放后都要重新显示;只有在用户选择“到此为止”后才结束。
参考资料
references/details.md— trace 后端 + pup 的准确标志、runner 合约(+ Go、导出模式)、轮询 + 误报未找到的合理性检查、trace 链接范围修复、限制。使用 pup / runner 前请阅读。references/local-setup.md— 如何让仅部署的应用可在本地运行(步骤 3.5)。发现此问题时请阅读。scripts/replay_runner_template.py— 复制并填写的 Python runner。