关于 agent-observability-trace-rca
agent-observability-trace-rca 是用于诊断生产环境 LLM 应用故障的 Agent 技能,通过 Datadog LLM traces 分析问题。它根据评估结果、运行时错误或结构异常选择分析路径,并可使用 MCP 工具或 pup 命令行。
主要功能
- 01根据可用信号选择分析模式
- 02搜索并检查 traces 和 spans
- 03沿 span 层级追踪错误
- 04检查评估分数和推理内容
- 05查看 Agent 执行时间线
- 06使用 MCP 工具或 pup 命令行
适用场景
- 01查找 LLM 评估失败的原因
- 02排查生产 traces 中的运行时错误
- 03分析延迟或 Agent 循环异常
- 04比较不同评估器或时间范围内的故障
MCP 相关
技能在 Datadog LLM Observability MCP 工具可用时使用这些工具,否则将工作流转换为对应的 pup 命令行调用。它搜索 spans,并获取评估、trace 和内容详情。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ datadog-labs/ agent-skills --skill agent-observability-trace-rca安装后在 Claude Code 中输入/agent-observability-trace-rca确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/__init__.pyevaluator.pyexample_config.jsonexecutor.pyevals/data/llm_obs_trace_rca_v1.jsonevals/prompts/judge_rubric.txt详细介绍
后端
检测 — 在每次调用开始时,采取任何操作之前,确定要使用的后端:
- 如果用户在调用的任何位置传入了
--backend pup→ 立即使用 pup 模式,无论是否存在 MCP 工具。跳过步骤 2–4。 - 检查当前可用工具列表中是否存在 MCP 工具。判断依据是可用工具中是否出现
mcp__datadog-llmo-mcp__list_llmobs_evals。 - 如果存在 MCP 工具 → 全程使用 MCP 模式。按照本技能工作流部分中的名称原样调用 MCP 工具。
- 如果不存在 MCP 工具 → 检查
pup是否可执行:通过 Bash 运行pup --version。包含"version"的 JSON 响应表示 pup 可用。 - 如果 pup 有响应 → 全程使用 pup 模式。使用本文底部的工具参考附录,将每次 MCP 工具调用转换为对应的 pup 命令。
- 如果两者都不可用 → 停止并告知用户:
“Datadog MCP 服务和 pup CLI 均不可用。请连接 MCP 服务(
claude mcp add --scope user --transport http datadog-llmo-mcp 'https://mcp.datadoghq.com/v1/mcp?toolsets=llmobs')或安装 pup。”
调用参数中的任何位置都可以使用 --backend pup,并且在将其余参数传递给技能逻辑之前会将其移除。
pup 调用规则:
- 通过 Bash 调用:
pup llm-obs <subcommand> [flags] - pup 始终输出 JSON。直接解析即可——无需像 MCP 结果那样解包内容块;MCP 结果可能会包装为
[{"type": "text", "text": "<json>"}]。 - 如果 pup 返回身份验证错误,请告知用户运行
pup auth login,然后停止。 - 并行执行:在同一条消息中发起多个 Bash 工具调用,每次调用执行一个 pup 命令。
- 时间标志:pup 接受裸时长字符串(
1h、7d、30m)和 RFC3339 时间戳。不要使用带now-前缀的字符串——从技能的--timeframe参数转换时,请去掉此前缀:now-7d→7d、now-24h→24h、now-30d→30d。 pup llm-obs spans search中的--summary会将负载字段精简为必要的元数据。请在不需要内容的批量搜索阶段使用它。
调用 ID: 每次调用一开始、任何 MCP 工具调用之前,生成一个 8 位十六进制调用 ID(例如 3a9f1c2b)。在整个调用过程中保持该 ID 不变。
意图标记: 每次调用 MCP 工具时,都在 telemetry.intent 前加上 skill:agent-observability-trace-rca[<inv_id>] — ,后接调用原因说明。仅第一次调用 MCP 工具时,使用 skill:agent-observability-trace-rca:start[<inv_id>] — (注意 :start 后缀)。首次调用示例:skill:agent-observability-trace-rca:start[3a9f1c2b] — Phase 0:发现 task-cruncher 的已配置评估器,以推断分析模式
Agent Observability Trace RCA — 基于生产环境 LLM Trace 的根因分析
通过搜索生产环境 Trace,并沿着 Span 树从症状追溯至根因,诊断 LLM 应用为何出现故障。该技能会根据可用信号自动选择最佳分析模式:
| 模式 | 信号 | 自动选择条件 |
|---|---|---|
| 评估信号 | LLM 评判器的判定和推理(通过/失败率、评分) | 应用已配置评估器 |
| 错误信号 | 运行时错误(@status:error、错误类型、堆栈跟踪) | 未配置评估器,或用户明确询问错误/崩溃 |
| 通用 | 结构异常(延迟、Agent 循环、检索未命中) | 明确覆盖为 mode=generic,或第 1 阶段未发现强信号 |
首次面向用户的输出会直接宣布所选模式,不会询问用户,并附上一行覆盖提示。
方法
解析 → 搜索 → 观察 → 开放式编码 → 轴心式编码 → 根因导航 → 建议
用法
What's wrong with <ml_app> over the last <timeframe>
Why is <ml_app> failing today
Analyze eval failures for <eval_name> on <ml_app>
Look at the errors on <ml_app> over the last <timeframe>
Root-cause low scores on <eval_name>
输入
| 输入 | 必需 | 默认值 | 描述 |
|---|---|---|---|
ml_app | 是(或 eval_name) | — | 要分析的应用。 |
eval_name | 否 | — | 一个或多个重点关注的评估器。此项会启用 Eval Signal 模式。传入列表可进行多项评估分析。 |
timeframe | 否 | now-24h | 回溯的时间范围。 |
mode | 否 | 推断 | 显式指定模式:eval、errors、generic。完全跳过推断。 |
failure_filter | 否 | — | 缩小范围:"errors"(转到 Error Signal 路径)、"high latency"(获取数据后按耗时排序)、"low scores on <eval>"(提升为 eval_name)、工具名称或 span 名称(@name:<x> 查询)。 |
如果未提供 ml_app 和 eval_name,请询问用户。
可用工具
评估发现与概览
| 工具 | 用途 |
|---|---|
list_llmobs_evals | 发现 ml_app 的所有已配置评估器。用于 Phase 0 模式推断。 |
get_llmobs_eval_aggregate_stats | 获取某个评估器在一段时间范围内的通过/失败率或分数分布。 |
get_llmobs_evaluator | 完整的评估器配置:提示词模板、评估标准、span 筛选条件、采样和提供方。请使用此工具,而非已弃用的 get_llmobs_eval_config。 |
Trace 与 span 探索
| 工具 | 用途 |
|---|---|
search_llmobs_spans | 按标签、span 类型、状态和查询语法查找 span。使用游标翻页。Phase 1 的入口。 |
get_llmobs_span_details | 元数据、评估结果(分数、标签、推理)、status、错误字段、耗时,以及显示可用字段和大小的 content_info 映射。 |
get_llmobs_span_content | 获取 span 字段的实际内容。支持通过 path 参数使用 JSONPath 进行定向提取。 |
get_llmobs_trace | 以 span 树形式显示完整 trace 层级及各类型 span 的数量。 |
find_llmobs_error_spans | 获取 trace 中的所有错误 span,包括错误类型、消息、堆栈和传播上下文。 |
expand_llmobs_spans | 加载折叠的 trace 节点的子节点。 |
get_llmobs_agent_loop | 按时间顺序显示 Agent 执行时间线(LLM 调用、工具调用、决策)。可能返回空结果——参见 Phase 4b 回退方案。 |
get_llmobs_span_content 的常见用法
| 字段 | 路径 | 获取内容 |
|---|---|---|
messages | $.messages[0] | 系统提示词(第一条消息,通常为 system 角色) |
messages | $.messages[-1] | 助手的最后一条回复 |
messages | (无路径) | 包含工具调用的完整对话 |
input / output | — | Span 的输入/输出 |
documents | — | 检索到的文档(RAG 应用) |
metadata | — | 自定义元数据(提示词版本、功能标记、用户分组) |
如何使用 search_llmobs_spans
始终在 query 字符串中包含 @ml_app:"<ml_app>"——结构化的 ml_app 参数不可靠,可能返回来自其他应用的 span。 不要只依赖结构化参数。
实用的查询片段——用空格组合(AND):
| 目标 | 查询 |
|---|---|
| 仅错误 | @status:error |
| Span 上存在评估结果 | @evaluations.custom.<eval_name>:*(仅检查是否存在——通过/失败状态应从 get_llmobs_span_details 读取,而非从查询中判断) |
| 指定名称的工具 | @name:<tool_name> |
专用参数(span_kind、root_spans_only、ml_app)可与 query 一起使用,但 query 的优先级高于 tags。
并行化规则
get_llmobs_span_details:按trace_id对span_ids分组,并将每个 trace 的span_ids分成最多包含 20 个 ID 的批次。针对一页结果的所有批次,均在同一条消息中发出。get_llmobs_span_content:每次调用相互独立——始终在同一条消息中发出所有调用。get_llmobs_trace/find_llmobs_error_spans/get_llmobs_agent_loop:在同一条消息中并行处理不同的 trace。- 流水线并行:第 1 页结果一到,立即开始调用
get_llmobs_span_details——无需等待收集完所有页面。
分析工作流
输出规范:第 0–5 阶段属于内部分析。在这些阶段,面向用户的输出仅限于第 1 阶段的信号摘要,以及第 2 和第 3 阶段的强制检查点。不要叙述推理过程、总结中间发现,也不要以散文形式输出第 4 阶段的深入分析结果。所有详细发现只能写入第 6 阶段报告。
第 0 阶段:解析输入并推断模式
首先:检查分类上下文。 扫描对话,查找 # Session Classification Summary 标题。若找到 → 进入下方的 步骤 0S,跳过第 0 阶段的其余所有步骤以及整个第 1 阶段。
步骤 0S — 从分类输出中提取失败类别
标准交接格式是 # Session Classification Summary 部分中的 Per-Unit Details 表格。每个单元提取一行:
| 字段 | 来源 |
|---|---|
trace_id | ID 列中的链接 URL:从链接 href 中解析 trace_id= 或 session_id= 查询参数 |
verdict | Verdict 列 |
failure_mode | Failure Mode 列(通过的行填写 none) |
detail | Reason 列——用作第 2 阶段的推理输入(作用与评估判定推理或错误消息相同) |
app_type | 来自 # Session Classification Summary 标题行(例如 Root span kind: agent)——若缺失,则默认为 LLM |
失败类别 = verdict 为 no 或 partial 的所有行。
- 少于 5 条 → 注明置信度较低,仍继续处理。
- 为空 → 报告“No failures found in the classification output”并停止。
继续处理前,先展示以下概览:
## Classification Overview (from agent-observability-session-classify)
**ml_app**: <from summary header> | **Classified**: N | **Failures (no+partial)**: F | **Pass rate**: X%
| Failure Mode | Count |
|---|---|
...
Proceeding to Phase 2 using F failure traces. Mode inference bypassed — classification verdict is the signal.
然后跳过第 1 阶段,直接进入第 2 阶段。沿用以下内容:
- 第 2 阶段推理输入:
(trace_id, span_id, detail)元组——结构与评估推理或错误消息相同 - 第 4 阶段导航:使用每个 trace 块中的
app_type选择 span 导航策略 - 第 2–7 阶段:完全照常运行——无论来源如何,失败类别结构都相同
标准解析(无分类上下文):
- 如果既未提供
ml_app,也未提供eval_name→ 询问用户。如果提供了eval_name,但未提供ml_app→ 也询问用户提供ml_app(评估名称并非全局唯一;没有它,span 搜索会返回所有使用该评估名称的应用中的结果)。 - 如果未提供
timeframe→ 默认使用now-24h。 - 解析
failure_filter(在推断模式之前):"errors"→ 强制使用错误信号模式"low scores on <eval>"→ 视为eval_name=<eval>,然后继续推断"high latency"→ 在第 1 阶段注明(获取数据后按持续时间排序);继续推断- 工具/span 名称 → 在第 1 阶段注明查询片段
@name:<x>;继续推断
- 解析模式(若已明确提供
mode,则跳过):- 提供了
eval_name→ 评估信号 - 用户明确提及错误/异常/崩溃 → 错误信号
- 否则 → 调用
list_llmobs_evals_by_ml_app(ml_app):- 返回了评估 → 评估信号
- 未返回评估 → 错误信号(在第 1 阶段说明这是自动选择的模式)
- 提供了
- 当
eval_name有多个值时,在第 1 阶段注明:按每个评估并行搜索,并按(trace_id, span_id)合并去重。
第 1 阶段:查找问题 span
有三条特定模式的路径。每条路径最终都以信号摘要结束,其中需标明模式,并包含一行模式覆盖提示。
模式切换处理:在任一检查点,如果用户说“切换到[错误|评估|通用]模式”,则使用新模式重新进入第 1 阶段。第 0 阶段的输入无需重新解析。
自动切换:如果所选模式未找到数据(配置的评估数量为 0,或时间范围内的错误 span 数量为 0),则说明将切换到通用模式并继续——不要停止并询问。
评估信号路径
步骤 1a:评估概览(并行)
对于每项评估,在一个并行批次中同时调用:
get_llmobs_eval_aggregate_stats(eval_name, from, to)get_llmobs_evaluator(eval_name)
解读汇总统计:
total_count == 0→ 备注“无数据”。跳过此项评估(如果这是唯一一项,则切换到 Generic)。- 布尔型
pass_rate == 1.0→ 备注“100% 通过”。跳过此项评估,除非它是唯一一项。 - 有失败项的布尔型评估 → 备注数量和 pass_rate。继续。
- 带有评估标准的评分型评估 → 备注分布以及通过/失败数量。继续。
- 不带评估标准的评分型评估 → 推断失败项:最低四分位数,或双峰分布时低于中位数。在报告中标注为“推断的失败项”。
- 带有评估标准的类别型评估 → 备注 top_values 以及通过/失败情况。继续。
- 不带评估标准的类别型评估 → 根据上下文推断(例如,“error”“incomplete”“off_topic”可能表示失败)。如果确实无法判断,请询问用户。
解读评估配置:
- 返回配置(自定义)→ 保存
prompt_template、assessment_criteria、parsing_type、output_schema。 - 配置为 nil(OOTB)→ 备注提示词不可查看。
校准交叉检查:当两项评估名称前缀相同但类型不同(例如,foo-boolean 和 foo-score)时,比较它们在重叠时间段内的通过率。差异超过 20% 时,属于评估器校准差异——在报告中标出。
步骤 1b:收集失败跨度
对于每项评估:
search_llmobs_spans(query="@evaluations.custom.<eval_name>:*", from, limit=50)。如果评估值有多个,则并行地为每项评估单独执行一次搜索——合并结果集,并按(trace_id, span_id)去重。- 翻页,直到获得 ≥15–20 个失败项,或没有更多页面。总跨度数上限为 200。
- 按每批
trace_id调用get_llmobs_span_details(遵循并行化规则)。 - 对每行提取:assessment、value、reasoning、span_id、trace_id、span_kind、content_info。
- 使用步骤 1a 中的阈值,分别归入通过/失败类别。
JSON 类型评估回退方案:如果 @evaluations.custom.<eval_name>:* 返回 0 个跨度,但 get_llmobs_eval_aggregate_stats 确认 total_count > 0,则该评估属于 JSON 类型,且此字段上的评分未建立索引。回退方案:按该评估的目标跨度名称或跨度类型搜索(检查 get_llmobs_evaluator 中的跨度筛选条件),然后通过 get_llmobs_span_content(field="output") 检查输出负载中的 JSON 判定字段。
步骤 1c:信号摘要(评估信号)
## Signal Summary: `{ml_app}` · Eval Signal
(Inferred from {N} configured eval(s). Say `switch to error mode` or `switch to generic mode` to change.)
**Timeframe**: {from} → {to}
| Eval | Type | Total | Pass Rate | Status |
|------|------|------:|:---------:|--------|
| eval_1 | boolean | 4,891 | 37.3% | ⚠ Investigating |
| eval_2 | score | 1,200 | — (inferred threshold) | ⚠ Investigating |
| eval_3 | boolean | 500 | 99.2% | ✓ Healthy |
**Collected**: {pass_count} passing, {fail_count} failing.
对于单项评估,将其合并为单行标题,而不是使用表格。
错误信号路径
步骤 1a:采样错误跨度
search_llmobs_spans(query="@ml_app:\"<ml_app>\" @status:error", from=timeframe, limit=50)。翻页,直到获得 ≥30 个错误跨度,或没有更多页面。
步骤 1a.5:软错误扫描
MCP 工具跨度有时会报告 @status:ok,但其输出负载中包含 "isError": true——这些错误不会出现在 @status:error 查询结果中,且数量可能超过硬错误。
调用 search_llmobs_spans(query="@ml_app:\"<ml_app>\" @status:ok", span_kind="tool", from=timeframe, limit=20)。从结果中抽取 5–10 个样本,并行调用 get_llmobs_span_content(field="output")。如果任何负载中包含 "isError": true,则在错误频率表中新增一行MCP 软错误,并附上说明:(status:ok,但负载中 isError:true——无法通过 @status:error 查询)。
步骤 1b:按错误类型分组
按 error_type 标签对跨度分组 → 频率表。如果某些跨度缺少 error_type 标签,则补充使用 get_llmobs_span_details 中的 error.type 字段(在步骤 1d 中获取)。
步骤 1c:获取堆栈跟踪(并行)
针对计数最高的 3–4 种错误类型,每种选取 2–3 个具有代表性的 trace ID。在所有选中的 trace 上并行调用 find_llmobs_error_spans(trace_id)。提取:
- 错误消息和堆栈跟踪
- 源跨度类型和名称
- 错误是从子级传播到父级(级联),还是彼此独立
Step 1d:获取 span 详情
对每种错误类型的代表性 span 调用 get_llmobs_span_details(遵循并行化规则)。提取 content_info、span_kind 和持续时间。
Step 1e:信号摘要(错误信号)
## Signal Summary: `{ml_app}` · Error Signal
(No evals configured — analyzing runtime errors. Say `switch to eval mode` or `switch to generic mode` to change.)
**Timeframe**: {from} → {to} | **Total error spans sampled**: {N}
| Error Type | Spans | Cascade? | Origin Span Kind |
|------------|------:|:--------:|-----------------|
| TimeoutError | 42 | Yes | tool |
| APIError 429 | 18 | No | tool |
| ValueError | 7 | No | llm |
| MCP soft errors (isError:true) | 23 | No | tool |
通用路径
Step 1a:评估健康检查(配置了评估时)
如果 Phase 0 中 list_llmobs_evals 返回了评估,则并行调用每项已启用评估的 get_llmobs_eval_aggregate_stats。将任何 total_count: 0 的已启用评估标记为评估配置损坏——在信号摘要异常表中加入一行,严重性为 High。
Step 1b:广泛搜索 span
search_llmobs_spans(query="@ml_app:\"<ml_app>\"", root_spans_only=true, from=timeframe, limit=50)。如果存在 failure_filter,则应用筛选条件缩小范围(工具/span 名称 → @name:<x> 查询;"high latency" → 在 Step 1c 之后按 duration 对结果集排序)。翻页,直到达到 ≥30 个 span。
Step 1c:获取 span 详情
按 trace_id 批次调用 get_llmobs_span_details。
Step 1d:按结构异常排序
使用启发式规则对 span 进行分组:
duration排名前 10% 的 span(延迟异常值)- 工具/LLM 迭代次数超过 N 次的 Agent span(长时间运行的循环)
- 返回 0 个文档的检索 span(RAG 未命中)
- 缺少预期步骤子项的工作流 span(与子项布局中位数进行比较)
- 令牌效率:检查各 LLM span 中
non_cached_input_tokens ≈ input_tokens是否成立。如果应用具有稳定的系统提示词(>1k 个令牌)且缓存命中率为 0%,则标记为 High 严重性——在系统提示词上启用cache_control: ephemeral可将输入令牌成本降低 60–90%
Step 1e:信号摘要(通用)
## Signal Summary: `{ml_app}` · Generic
(Analyzing structural anomalies. Say `switch to eval mode` or `switch to error mode` to change.)
**Timeframe**: {from} → {to} | **Sampled**: {N} root spans
| Anomaly Type | Count |
|---|:---:|
| Latency outliers (>p90) | 12 |
| Long agent loops (>8 iterations) | 5 |
| RAG retrieval misses | 3 |
| Zero prompt cache utilization | All LLM spans |
| Broken eval configurations | 2 |
阶段 1.5:确定应用配置及根本原因所在位置
检查已收集跨度中的 content_info 和 span_kind。这将决定阶段 4 的策略。
应用配置(来自 content_info):
| 信号 | 应用配置 | 阶段 4 策略 |
|---|---|---|
content_info 有 messages | 大模型/聊天应用 | 通过 messages[0] 提取系统提示词,检查对话流程 |
content_info 有 documents | RAG 应用 | 除了检查大模型输出,还要检查检索质量 |
跟踪记录包含 agent span kind | Agent 应用 | 优先尝试 get_llmobs_agent_loop;如果返回空结果,则使用子跨度重建(见阶段 4b) |
messages.count > 10 | 长对话 | 检查上下文是否溢出 |
content_info 有 metadata | 包含自定义元数据 | 检查是否可按元数据值(提示词版本、功能标志)聚类 |
LLM Experiments 跟踪记录:如果根跨度的
span_kind: experiment,并且包含结构化字段input、output和expected_output,则你查看的是 Datadog LLM Experiments 跟踪记录。每个跨度代表一次数据集记录运行。通过get_llmobs_span_content从根跨度的input/output/expected_output字段读取质量信号,而不是从大模型子跨度的消息中读取;这些消息可能包含存根或占位内容。附加到实验跨度的评估由 Experiments 框架在运行时计算,可能不会注册为 Datadog 在线评估器(get_llmobs_evaluator会对它们返回 404)。
根本原因可能所在的位置——按症状跨度类型:
| 症状跨度类型 | 症状表现 | 但根本原因通常在…… |
|---|---|---|
llm | 大模型响应不佳(评估标记异常、输出错误) | 父级 Agent(指令不佳)、同级检索(上下文不佳)、同级工具(数据不佳) |
agent | 编排不佳 | 子级跨度(工具调用错误、路由不佳)、完整的 Agent 循环 |
tool | 工具结果不佳 | 父级大模型(传入错误参数)、工具实现 |
workflow | 整体输出不佳 | 子级子跨度(最先偏离的是哪一步?) |
retrieval | 检索不佳 | 查询构造(父级)、索引/嵌入配置(跟踪记录之外) |
关键见解:评估判定、错误消息、延迟异常值等信号会单独标记一个跨度。这是症状报告,而非诊断。根本原因通常位于另一个跨度中:发出错误指令的父级、提供了错误上下文的同级,或做出错误决策的子级。阶段 4 会遍历这棵树以找到根本原因。
阶段 2:开放式编码 — 初步故障分类
目标:阅读每行证据,并提出初步、具体的故障类别。将所有有问题的行放在一起分析 — 类别应描述应用行为,而不是指出是哪种信号标记了它们。
按模式提供每行的“推理输入”:
- 评估信号:评审判断 + 来自
get_llmobs_span_details的推理 - 错误信号:来自
find_llmobs_error_spans的错误消息 + 堆栈跟踪摘录 - 通用:对标记该行的结构异常进行一句话描述
快捷方式:
- 有问题的行少于 15 条:将阶段 2 和阶段 3 合并为一轮处理。仍需生成检查点。
- 超过 80% 的行具有相同的推理/错误/症状:直接进入阶段 4,采用占主导地位的模式。仍需输出检查点。
- 有问题的行超过 50 条:抽取约 50 条样本,构建分类体系,然后再抽查 10–15 条。
-
使用阶段 1 中每行的信号 — 不要重新获取。只有当推理不充分(通用、为空,或仅包含缺少应用上下文的堆栈跟踪)时,才对相应 span 调用
get_llmobs_span_content(field="input"/"output")。 -
如果已加载评估配置(评估信号),请及早区分:
- 应用故障:输出确实违反了评估标准
- 评估故障:输出看起来合理,但评估标准过于严格或含糊
-
每种模式都必须具体:“Agent 为计算价格调用了 search,而不是 calculator” — 而不是“工具问题。”
必须完成的检查点
**Open coding**: {N} problematic rows → {K} initial categories: {Category1} ({count}), {Category2} ({count}), ...
阶段 3:主轴编码 — 优化故障分类体系
目标:最终确定 3–8 个类别,并按影响排序。
- 合并:出现次数少于 3 次的类别 → 并入父类别或作为噪声删除。
- 拆分:占故障超过 30% 的类别 → 拆分为更具体的子类别。如有需要,获取更多 span 内容。
- 验证:每个类别选取 2–3 个代表性示例,确认标签符合实际情况。
- 排序:
priority = count × severity(严重程度:高 / 中 / 低)。
必须完成的检查点
**Axial coding**: {merges/splits/drops}. Final categories:
1. {Category} ({count}, {pct}%) — {severity}
2. ...
阶段 4:根因分析 — 从症状追溯到根因
目标:信号标记了一个 span,那只是症状。沿 trace 树查找实际根因 — 根因通常位于另一个 span 中。
对于排名最高的 3 个类别,各选取 2–3 条代表性 trace:
步骤 4a:trace 结构 + 错误(并行)
对于每条代表性 trace,在一条消息中调用:
get_llmobs_trace(trace_id)— 查看 span 层级;定位症状 span 及其父级/同级/子级find_llmobs_error_spans(trace_id)— 检查 trace 中是否存在运行时错误
运行时问题与行为问题:如果症状 span 上或附近存在错误,根因可能是运行时故障,而非行为问题。请先检查这一点。
分布式 trace 回退方案:如果 get_llmobs_trace 返回“cannot find parent”或空 span 列表(在基于 Ray 或多进程执行中很常见),请使用阶段 1 收集的 span_id,通过 get_llmobs_span_details 手动重建 trace,并按 start_ms 排序。
步骤 4b:定位根本原因(并行)
使用症状所在 span 的类型(来自阶段 1.5)。在一条消息中发出所有调用。
如果症状位于 llm span 上(最常见):
- 对症状 span调用
get_llmobs_span_content(field="messages", path="$.messages[0]")— 系统提示词 - 对症状 span调用
get_llmobs_span_content(field="messages")— 收到的完整上下文 - 对同级检索 span(如果有)调用
get_llmobs_span_content(field="documents") - 对同级工具 span(如果有)调用
get_llmobs_span_content(field="input") - 对父级 Agent/工作流 span调用
get_llmobs_span_content(field="messages", path="$.messages[0]")
如果症状位于 agent span 上:
- 调用
get_llmobs_agent_loop(trace_id, span_id)— 完整的决策时间线 (优先尝试;如果返回 0 次迭代,则使用下方的回退方案) - 对子 span调用
get_llmobs_span_details— 按start_ms排序以重建执行时间线 - 对看起来有问题的子 span调用
get_llmobs_span_content(field="input"/"output")
Agent 循环回退方案(当 get_llmobs_agent_loop 返回 0 次迭代时):根据 get_llmobs_span_details 的结果,按 start_ms 排序重建时间线。按 span_kind 分组,识别 LLM → 工具 → LLM 序列。此回退方案经常需要使用 — 许多应用的 get_llmobs_agent_loop 都会返回空结果。
如果症状位于 tool span 上:
- 对症状 span调用
get_llmobs_span_content(field="input")— 调用时使用了哪些参数? - 对父级 LLM span调用
get_llmobs_span_content(field="messages")— LLM 是否正确构造了调用?
如果症状位于 workflow span 上:
- 对所有子 span调用
get_llmobs_span_details— 找出最先偏离预期的步骤 - 对偏离预期的子 span调用
get_llmobs_span_content(field="input"/"output")
还要始终获取:
- 对症状 span 调用
get_llmobs_span_content(field="metadata")— 聚类信号(提示词版本、功能标志)
步骤 4c:诊断 — 从症状到根本原因
对每个类别,追踪因果链:
- 症状 — 信号标记了什么(评估推理、错误消息、异常备注)。信号只孤立地查看了一个 span,因此其推理可能较浅。
- 跟踪上下文 — 周边 span 揭示了什么(父级指令、同级数据、子级决策)。
- 根本原因 — 故障起源的具体 span 和决策点。通常不是症状 span 本身。
对于疑似评估问题(评估信号,若已加载配置):将评估标准与证据进行比较。提示词是否有歧义?标准是否过于严格?
根本原因类别:
| 类别 | 描述 |
|---|---|
| 系统提示词缺陷 | 指令不清楚、缺失或相互矛盾 — 位于症状 span 或其父级中 |
| 工具缺失 | 所需工具不存在,或参数过于粗略 |
| 工具误用 | 调用了错误的工具或使用了错误的参数 — 通常可在 Agent 循环或父级 LLM 中看到 |
| 路由/交接错误 | 选择了错误的子 Agent(多 Agent 系统) |
| 检索失败 | RAG 返回了不相关或缺失的上下文 — 检查同级检索 span |
| 上下文溢出 | 由于上下文长度,关键信息丢失 |
| 上游数据问题 | 同级或父级 span 提供了错误数据,并一路传递到症状 span |
| 运行时错误 | 工具/API 故障、超时、异常 — 来自 find_llmobs_error_spans |
| 评估器校准不当 | 评估标准产生误报/漏报(仅限评估信号模式) |
阶段 5:生成建议
目标:基于跟踪证据,提出具体、可执行的建议。提供实际文本/代码修改,并引用跟踪中的修改前/修改后内容——而非泛泛而谈。
建议类型:系统提示词编辑(引用实际提示词,提供修改前/修改后内容)、工具缺口/误用(引用 Agent 循环步骤)、路由/交接修复、检索修复(展示已检索内容与所需内容)、评估器提示词编辑(指出评估变更需要重新验证;仅适用于评估信号)、其他。
在可访问代码库的 Claude Code 中运行时:在代码库中搜索系统提示词、工具定义或路由逻辑。提出具体的差异。修改文件前务必征求同意。
阶段 6:编写 RCA 报告
按照下方的输出格式编写完整报告。这是主要交付内容——直接在聊天中输出。
阶段 7:分析后的操作
不要自动执行任何操作。 呈现报告后,询问用户接下来想做什么:
- 将报告保存到
agent-observability-rca-{ml_app}-{date}.md - 应用修复(如果代码库可用)
- 深入调查剩余类别
- 导出到 Datadog notebook——在 pup 模式下,使用
pup notebooks create创建 notebook,再使用pup notebooks edit NOTEBOOK_ID --file /tmp/nb_cells.json追加章节(参见工具参考) - 使用扩大的时间范围重新运行(例如,当前窗口为
now-24h时使用now-7d)
如果用户选择选项 4,请遵循 notebook 创建回退流程:
-
调用
mcp__datadog-mcp-core__create_datadog_notebook,并提供:name:Agent Observability RCA: {ml_app} ({mode}) — YYYY-MM-DDtype:reporttime_span:1wcells:每个章节对应一个单元格(参见下方的 Notebook 单元格结构)
-
如果 MCP 调用失败,请先检查错误,再决定是否放弃:
- 身份验证/权限错误(401、403) → 停止并告知用户。
- 字段验证错误(错误消息指出了具体字段)→ 修正该字段,然后重试一次 MCP 调用。
- 任何其他错误(绑定、序列化、意外响应)→ 回退到 pup:
- 将 notebook 负载作为完整 API 信封写入
/tmp/nb_rca_{ml_app}.json:{"data": {"attributes": {"name": "...", "time": {...}, "cells": [...]}, "type": "notebooks"}} - 运行
pup notebooks create --file /tmp/nb_rca_{ml_app}.json - 如果 pup 也不可用,则在聊天中以 Markdown 格式呈现完整 notebook 内容,供用户使用。
- 将 notebook 负载作为完整 API 信封写入
-
使用任一方式成功创建后,将 URL 单独放在一行输出:
RCA report exported to notebook: <url>
醒目地输出 URL——如果同一会话中接下来运行 /eval-bootstrap,它会检测到此 URL,并提出将评估器套件追加到同一 notebook。
Notebook 单元格结构
| 单元格 | 内容 |
|---|---|
| 1 — 概览 | 结构化标题(参见下方的概览单元格格式——必须严格遵循) |
| 2 — 信号摘要 | 特定模式的健康状况表 |
| 3 — 失败分类 | 分类表 |
| 4…N — 失败模式 | 每种失败模式一个单元格 |
| N+1 — 行动计划 + 限制 | 行动计划表 + 项目符号列表 |
Notebook 格式规则(适用于每个单元格):
- 不要使用三个反引号的代码块——提示词/评分标准使用引用块(
>),较短的值使用行内代码(`) - 以表格呈现证据,不要使用项目符号列表
- 以表格呈现工具输入——参数 | 传入的错误值 | 正确做法
- 以表格呈现行动计划——优先级 | 行动 | 置信度 | 影响
输出格式
概览单元格(Notebook 单元格 1/报告标题)
概览单元格必须严格遵循此结构。不得使用散文段落。不得使用行内编号的发现。应用描述最多一句。
## `{ml_app}` · {Eval Signal | Error Signal | Generic} · {timeframe}
**Date**: {YYYY-MM-DD} | **Profile**: {short app profile} | **Model**: `{model(s)}`
{One sentence: what does this app do?}
| Metric | Value |
|--------|-------|
{mode-appropriate rows — see below}
### Findings
- **{Finding 1}** (~{pct}%): one-line root cause description
- **{Finding 2}** (~{pct}%): one-line root cause description
- **{Finding 3}** (if present): one-line root cause description
### Recommendations
- **{Recommendation 1}**: specific next step tied to Finding 1
- **{Recommendation 2}**: specific next step tied to Finding 2
*Sample: {N} spans analyzed. Confidence: High | Medium | Low — {one-line reason if Medium or Low}.*
与模式相符的指标行:
评估信号:
| Eval | `{eval_name}` ({type}) |
| Spans evaluated | {total_count} |
| Pass rate | {pass_rate}% ({pass_count} pass / {fail_count} fail) |
| Top failure mode | {name} (~{pct}%) |
| Evals configured | {N} |
错误信号:
| Error spans | {N} confirmed |
| Top error type | `{type}` ({pct}%) |
| Affected operation | `{span_name}` |
| Cascade pattern | Isolated / Cascading |
| Evals configured | {N} (none = no quality signal) |
通用:
| Spans sampled | {N} root spans |
| Top anomaly | {type}: {count} spans |
| Error spans | {N} (0 = structurally healthy) |
| Evals configured | {N} (none = no quality signal) |
Signal 汇总表
从 Step 0S(分类上下文)进入时,将 Signal 汇总表替换为:
## Classification Signal Summary
**Source**: agent-observability-session-classify | **ml_app**: {app} | **Signal**: content-only | content+evals
| Metric | Value |
|--------|-------|
| Traces classified | N |
| Failures in corpus (no+partial) | F |
| Pass rate | X% |
| Failure modes | list |
*Root cause analysis is based on per-trace classification verdicts, not automated eval judge reasoning.*
否则,按模式选择合适的变体:
Eval Signal — 每个 Eval 一行:
| Eval | 类型 | 总数 | 通过率 | 状态 |
|---|---|---|---|---|
| eval_1 | boolean | 4,891 | 37.3% | ⚠ 正在调查 |
Error Signal — 每种错误类型一行:
| 错误类型 | Spans | 是否级联? | 来源 Span 类型 |
|---|---|---|---|
| TimeoutError | 42 | 是 | tool |
Generic — 每种异常类型一行:
| 异常类型 | 数量 |
|---|---|
| 延迟异常值(>p90) | 12 |
失败分类
| # | 失败模式 | Traces | % | 严重程度 | 根本原因 |
|---|---|---|---|---|---|
| 1 | ... | ... | ...% | 高 | 工具误用 |
失败模式章节(针对排名前 3–5 的模式各一节)
## Failure Mode N: [Name]
**Count**: {n} spans, {t} traces | **Severity**: High/Medium/Low | **Root Cause**: [Category]
[3–5 sentences: what goes wrong, when, what triggers it, causal chain.]
**Evidence**
{Use the mode-appropriate column set:}
Eval Signal — Trace | Judge verdict | What the trace revealed:
| Trace | Judge verdict | What the trace revealed |
|---|---|---|
| [69de86a7...](https://app.datadoghq.com/llm/traces?query=trace_id:{full_id}) | fail | Parent agent has no date format instruction |
Error Signal — Trace | Behavior | Version:
| Trace | Behavior | Version |
|---|---|---|
| [69de86a7...](https://app.datadoghq.com/llm/traces?query=trace_id:{full_id}) | 7 parallel calls, all 400 | v107624932 |
Generic — Trace | Anomaly | Signal:
| Trace | Anomaly | Signal |
|---|---|---|
| [69de86a7...](https://app.datadoghq.com/llm/traces?query=trace_id:{full_id}) | 94s, 12 tool calls | Latency outlier |
{For tool misuse — add a tool inputs table:}
**Tool inputs (100% of sampled calls)**
| Argument | Value passed (wrong) | Correct approach |
|---|---|---|
| `query` | `"monitor_id:123 group_status:alert"` | `"monitor_id:123"` (name/tag only) |
{For Eval Signal — add judge reasoning as a blockquote:}
> "{quoted judge reasoning}"
**Root cause**: [WHY this happens — specific span, parameter, or prompt.]
**Fix**:
BEFORE: [actual text from trace]
AFTER: [proposed replacement]
**Impact**: Eliminates ~{n} spans / {timeframe}.
优先行动计划
| 优先级 | 行动 | 置信度 | 影响 |
|---|---|---|---|
| 1 | 修复 monitor_groups_search schema — 添加 group_states 参数 | 高 | 消除约 21 个 spans/7d |
当模式为 Generic 且未配置任何 eval 时,始终将以下内容作为行动计划的最后一行追加:
| N | 至少配置一个评估器 | 高 | 为后续 RCA 启用 Eval Signal 模式 — 应用目前没有持续的质量信号 |
限制与后续事项
以项目符号列出需要更多数据或后续采取行动的事项。
操作规则
- 以证据为依据:每项判断都要引用 span ID,并附上可点击的 trace 链接:
[Trace {first_8}...](https://app.datadoghq.com/llm/traces?query=trace_id:{full_32_char_id})。 - 关注根本原因,而非症状:“系统提示词未指定日期格式”,而不是“模型给出了错误答案”。
- 展示计算过程:“47 次失败(34%)”,而不是“很多失败”。
- 如实说明不确定性:示例少于 5 个时,结论仅供参考,并标注出来。
- 匿名化 PII:不得包含电子邮件地址或姓名。用户/组织 ID 可以保留。
- 保障 MCP 结果解析安全:在编写任何遍历 MCP 工具结果的脚本之前,先检查原始结构——查看顶层键,以及 payload 是否嵌套在 content block 中(例如
[{'type': 'text', 'text': '<json>'}])。如有需要,提取内部 payload 并使用json.loads()解析。切勿假定 MCP 结果是裸 dict 或 list。
工具参考
本附录仅适用于 pup 模式。在 MCP 模式下,直接使用工作流章节中的工具名称。
Spans 和 traces
| MCP 工具 | pup 命令 |
|---|---|
search_llmobs_spans(query, ml_app, from, to, limit, cursor, root_spans_only, span_kind, summary) | pup llm-obs spans search --query "@ml_app:A [other_filters]" [--from F] [--to T] [--limit N] [--cursor C] [--root-spans-only] [--span-kind K] [--summary] — 始终使用 --query "@ml_app:A" 按 ml_app 筛选;--ml-app A 标志不可靠,并会静默返回来自其他应用的 spans。 |
get_llmobs_span_details(trace_id, span_ids, from, to) | pup llm-obs spans get-details --trace-id T --span-ids S1,S2,... |
get_llmobs_span_content(trace_id, span_id, field, path) | pup llm-obs spans get-content --trace-id T --span-id S --field F [--path P] |
get_llmobs_trace(trace_id, include_tree) | pup llm-obs spans get-trace --trace-id T [--include-tree] |
get_llmobs_agent_loop(trace_id, span_id) | pup llm-obs spans get-agent-loop --trace-id T [--span-id S] |
find_llmobs_error_spans(trace_id) | pup llm-obs spans find-errors --trace-id T |
expand_llmobs_spans(trace_id, span_ids, max_depth, filter_kind) | pup llm-obs spans expand --trace-id T --span-ids S1,S2,... [--max-depth N] [--filter-kind K] |
评估器
| MCP 工具 | pup 命令 |
|---|---|
list_llmobs_evals() | pup llm-obs evals list(在客户端按 ml_app 筛选) |
list_llmobs_evals_by_ml_app(ml_app) | pup llm-obs evals list-by-ml-app --ml-app A |
get_llmobs_evaluator(eval_name) | pup llm-obs evals get-evaluator EVAL_NAME |
get_llmobs_eval_aggregate_stats(eval_name, ml_app, from, to) | pup llm-obs evals get-aggregate-stats EVAL_NAME [--ml-app A] [--from F] [--to T] |
笔记本
| MCP 工具 | pup 命令 |
|---|---|
create_datadog_notebook(name, cells, ...) | pup notebooks create --title "TITLE" --file /tmp/nb_cells.json——使用 pup notebooks create --help 确认准确的标志 |
edit_datadog_notebook(id, cells, append_only=true) | pup notebooks edit NOTEBOOK_ID --file /tmp/nb_cells.json(获取当前笔记本,追加提供的单元格,然后写回) |
单元格文件是一个由单元格对象组成的 JSON 数组:
[{"attributes": {"definition": {"type": "markdown", "text": "## Section\n\nContent."}}, "type": "notebook_cells"}]