mcpField
A
agent-observability-trace-rca

agent-observability-trace-rca

github.com/datadog-labs测试与质量180561 次安装6 天前更新
代码仓库

关于 agent-observability-trace-rca

agent-observability-trace-rca 是用于诊断生产环境 LLM 应用故障的 Agent 技能,通过 Datadog LLM traces 分析问题。它根据评估结果、运行时错误或结构异常选择分析路径,并可使用 MCP 工具或 pup 命令行。

主要功能

  1. 01根据可用信号选择分析模式
  2. 02搜索并检查 traces 和 spans
  3. 03沿 span 层级追踪错误
  4. 04检查评估分数和推理内容
  5. 05查看 Agent 执行时间线
  6. 06使用 MCP 工具或 pup 命令行

适用场景

  1. 01查找 LLM 评估失败的原因
  2. 02排查生产 traces 中的运行时错误
  3. 03分析延迟或 Agent 循环异常
  4. 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__.py
evaluator.py
example_config.json
executor.py
evals/data/
llm_obs_trace_rca_v1.json
evals/prompts/
judge_rubric.txt

详细介绍

后端

检测 — 在每次调用开始时,采取任何操作之前,确定要使用的后端:

  1. 如果用户在调用的任何位置传入了 --backend pup → 立即使用 pup 模式,无论是否存在 MCP 工具。跳过步骤 2–4。
  2. 检查当前可用工具列表中是否存在 MCP 工具。判断依据是可用工具中是否出现 mcp__datadog-llmo-mcp__list_llmobs_evals。
  3. 如果存在 MCP 工具 → 全程使用 MCP 模式。按照本技能工作流部分中的名称原样调用 MCP 工具。
  4. 如果不存在 MCP 工具 → 检查 pup 是否可执行:通过 Bash 运行 pup --version。包含 "version" 的 JSON 响应表示 pup 可用。
  5. 如果 pup 有响应 → 全程使用 pup 模式。使用本文底部的工具参考附录,将每次 MCP 工具调用转换为对应的 pup 命令。
  6. 如果两者都不可用 → 停止并告知用户:

    “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。

并行化规则
  1. get_llmobs_span_details:按 trace_id 对 span_ids 分组,并将每个 trace 的 span_ids 分成最多包含 20 个 ID 的批次。针对一页结果的所有批次,均在同一条消息中发出。
  2. get_llmobs_span_content:每次调用相互独立——始终在同一条消息中发出所有调用。
  3. get_llmobs_trace / find_llmobs_error_spans / get_llmobs_agent_loop:在同一条消息中并行处理不同的 trace。
  4. 流水线并行:第 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_idID 列中的链接 URL:从链接 href 中解析 trace_id= 或 session_id= 查询参数
verdictVerdict 列
failure_modeFailure Mode 列(通过的行填写 none)
detailReason 列——用作第 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 阶段:完全照常运行——无论来源如何,失败类别结构都相同

标准解析(无分类上下文):

  1. 如果既未提供 ml_app,也未提供 eval_name → 询问用户。如果提供了 eval_name,但未提供 ml_app → 也询问用户提供 ml_app(评估名称并非全局唯一;没有它,span 搜索会返回所有使用该评估名称的应用中的结果)。
  2. 如果未提供 timeframe → 默认使用 now-24h。
  3. 解析 failure_filter(在推断模式之前):
    • "errors" → 强制使用错误信号模式
    • "low scores on <eval>" → 视为 eval_name=<eval>,然后继续推断
    • "high latency" → 在第 1 阶段注明(获取数据后按持续时间排序);继续推断
    • 工具/span 名称 → 在第 1 阶段注明查询片段 @name:<x>;继续推断
  4. 解析模式(若已明确提供 mode,则跳过):
    • 提供了 eval_name → 评估信号
    • 用户明确提及错误/异常/崩溃 → 错误信号
    • 否则 → 调用 list_llmobs_evals_by_ml_app(ml_app):
      • 返回了评估 → 评估信号
      • 未返回评估 → 错误信号(在第 1 阶段说明这是自动选择的模式)
  5. 当 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:收集失败跨度

对于每项评估:

  1. search_llmobs_spans(query="@evaluations.custom.<eval_name>:*", from, limit=50)。如果评估值有多个,则并行地为每项评估单独执行一次搜索——合并结果集,并按 (trace_id, span_id) 去重。
  2. 翻页,直到获得 ≥15–20 个失败项,或没有更多页面。总跨度数上限为 200。
  3. 按每批 trace_id 调用 get_llmobs_span_details(遵循并行化规则)。
  4. 对每行提取:assessment、value、reasoning、span_id、trace_id、span_kind、content_info。
  5. 使用步骤 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 有 documentsRAG 应用除了检查大模型输出,还要检查检索质量
跟踪记录包含 agent span kindAgent 应用优先尝试 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. 使用阶段 1 中每行的信号 — 不要重新获取。只有当推理不充分(通用、为空,或仅包含缺少应用上下文的堆栈跟踪)时,才对相应 span 调用 get_llmobs_span_content(field="input"/"output")。

  2. 如果已加载评估配置(评估信号),请及早区分:

    • 应用故障:输出确实违反了评估标准
    • 评估故障:输出看起来合理,但评估标准过于严格或含糊
  3. 每种模式都必须具体:“Agent 为计算价格调用了 search,而不是 calculator” — 而不是“工具问题。”

必须完成的检查点
**Open coding**: {N} problematic rows → {K} initial categories: {Category1} ({count}), {Category2} ({count}), ...

阶段 3:主轴编码 — 优化故障分类体系

目标:最终确定 3–8 个类别,并按影响排序。

  1. 合并:出现次数少于 3 次的类别 → 并入父类别或作为噪声删除。
  2. 拆分:占故障超过 30% 的类别 → 拆分为更具体的子类别。如有需要,获取更多 span 内容。
  3. 验证:每个类别选取 2–3 个代表性示例,确认标签符合实际情况。
  4. 排序: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:诊断 — 从症状到根本原因

对每个类别,追踪因果链:

  1. 症状 — 信号标记了什么(评估推理、错误消息、异常备注)。信号只孤立地查看了一个 span,因此其推理可能较浅。
  2. 跟踪上下文 — 周边 span 揭示了什么(父级指令、同级数据、子级决策)。
  3. 根本原因 — 故障起源的具体 span 和决策点。通常不是症状 span 本身。

对于疑似评估问题(评估信号,若已加载配置):将评估标准与证据进行比较。提示词是否有歧义?标准是否过于严格?

根本原因类别:

类别描述
系统提示词缺陷指令不清楚、缺失或相互矛盾 — 位于症状 span 或其父级中
工具缺失所需工具不存在,或参数过于粗略
工具误用调用了错误的工具或使用了错误的参数 — 通常可在 Agent 循环或父级 LLM 中看到
路由/交接错误选择了错误的子 Agent(多 Agent 系统)
检索失败RAG 返回了不相关或缺失的上下文 — 检查同级检索 span
上下文溢出由于上下文长度,关键信息丢失
上游数据问题同级或父级 span 提供了错误数据,并一路传递到症状 span
运行时错误工具/API 故障、超时、异常 — 来自 find_llmobs_error_spans
评估器校准不当评估标准产生误报/漏报(仅限评估信号模式)
阶段 5:生成建议

目标:基于跟踪证据,提出具体、可执行的建议。提供实际文本/代码修改,并引用跟踪中的修改前/修改后内容——而非泛泛而谈。

建议类型:系统提示词编辑(引用实际提示词,提供修改前/修改后内容)、工具缺口/误用(引用 Agent 循环步骤)、路由/交接修复、检索修复(展示已检索内容与所需内容)、评估器提示词编辑(指出评估变更需要重新验证;仅适用于评估信号)、其他。

在可访问代码库的 Claude Code 中运行时:在代码库中搜索系统提示词、工具定义或路由逻辑。提出具体的差异。修改文件前务必征求同意。


阶段 6:编写 RCA 报告

按照下方的输出格式编写完整报告。这是主要交付内容——直接在聊天中输出。


阶段 7:分析后的操作

不要自动执行任何操作。 呈现报告后,询问用户接下来想做什么:

  1. 将报告保存到 agent-observability-rca-{ml_app}-{date}.md
  2. 应用修复(如果代码库可用)
  3. 深入调查剩余类别
  4. 导出到 Datadog notebook——在 pup 模式下,使用 pup notebooks create 创建 notebook,再使用 pup notebooks edit NOTEBOOK_ID --file /tmp/nb_cells.json 追加章节(参见工具参考)
  5. 使用扩大的时间范围重新运行(例如,当前窗口为 now-24h 时使用 now-7d)

如果用户选择选项 4,请遵循 notebook 创建回退流程:

  1. 调用 mcp__datadog-mcp-core__create_datadog_notebook,并提供:

    • name:Agent Observability RCA: {ml_app} ({mode}) — YYYY-MM-DD
    • type:report
    • time_span:1w
    • cells:每个章节对应一个单元格(参见下方的 Notebook 单元格结构)
  2. 如果 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 内容,供用户使用。
  3. 使用任一方式成功创建后,将 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_1boolean4,89137.3%⚠ 正在调查

Error Signal — 每种错误类型一行:

错误类型Spans是否级联?来源 Span 类型
TimeoutError42是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"}]

评论

支持 Markdown