mcpField
L
llm-obs-trace-rca

llm-obs-trace-rca

github.com/datadog-labs开发180236 次安装6 天前更新
代码仓库

关于 llm-obs-trace-rca

llm-obs-trace-rca 是用于诊断生产环境 LLM 应用故障的技能。它搜索 Datadog 跟踪数据并沿 Span 树追查根因,根据评估或错误信号选择分析模式。

主要功能

  1. 01自动选择分析模式
  2. 02搜索并检查生产 Span
  3. 03探索跟踪树和 Agent 循环
  4. 04分析根因并提出建议

适用场景

  1. 01调查评估分数下降
  2. 02追查运行时错误来源
  3. 03发现延迟或检索异常

MCP 相关

在 MCP 模式下,它使用 Datadog LLM Observability 工具查找和检查评估、Span、跟踪及 Agent 循环。MCP 工具不可用时,可将调用转换为 pup 命令行操作。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/datadog-labs/agent-skills --skill llm-obs-trace-rca

安装后在 Claude Code 中输入/llm-obs-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/api/unstable/mcp-server/mcp?toolsets=llmobs')或安装 pup。”

--backend pup 可以出现在调用参数的任意位置;传递其余参数给技能逻辑之前,会先将其移除。

pup 调用规则:

  • 通过 Bash 调用:pup llm-obs <subcommand> [flags]
  • pup 始终输出 JSON。直接解析即可——无需解包内容块(与 MCP 结果不同,MCP 结果可能会将 JSON 包装在 [{"type": "text", "text": "<json>"}] 中)。
  • 如果 pup 返回身份验证错误,请告知用户运行 pup auth login,然后停止。
  • 并行执行:在一条消息中发出多个 Bash 工具调用(每次调用运行一条 pup 命令)。
  • 时间参数标志:pup 接受不带前缀的时长字符串(1h、7d、30m)和 RFC3339 时间戳。请勿使用带 now- 前缀的字符串——将技能的 --timeframe 参数转换为 pup 命令时,请去掉此前缀: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:llm-obs-trace-rca[<inv_id>] — ,后接调用原因说明。仅首次 MCP 工具调用使用 skill:llm-obs-trace-rca:start[<inv_id>] — (注意 :start 后缀)。首次调用示例:skill:llm-obs-trace-rca:start[3a9f1c2b] — Phase 0: discover configured evals for task-cruncher to infer analysis mode

LLM Obs 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 llm-obs-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. 将报告保存到 llm-obs-rca-{ml_app}-{date}.md
  2. 应用修复(如果可访问代码库)
  3. 深入调查其余类别
  4. 导出到 Datadog 笔记本 — 在 pup 模式下,使用 pup notebooks create 创建笔记本,并使用 pup notebooks edit NOTEBOOK_ID --file /tmp/nb_cells.json 追加章节(参见工具参考)
  5. 在扩展的时间范围内重新运行(例如,当前时间窗口为 now-24h 时,使用 now-7d)

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

  1. 调用 mcp__datadog-mcp-core__create_datadog_notebook,并传入:

    • name:LLM Obs RCA: {ml_app} ({mode}) — YYYY-MM-DD
    • type:report
    • time_span:1w
    • cells:每个章节对应一个单元格(见下方的笔记本单元格结构)
  2. 如果 MCP 调用失败,请先检查错误,再决定是否放弃:

    • 身份验证/权限错误(401、403) → 停止并告知用户。
    • 字段验证错误(错误消息指出了具体字段)→ 修正该字段,并重试一次 MCP 调用。
    • 任何其他错误(绑定、序列化、意外响应)→ 回退到 pup:
      • 将笔记本负载写入 /tmp/nb_rca_{ml_app}.json,格式为完整的 API 封装:{"data": {"attributes": {"name": "...", "time": {...}, "cells": [...]}, "type": "notebooks"}}
      • 运行 pup notebooks create --file /tmp/nb_rca_{ml_app}.json
      • 如果 pup 也不可用,则在聊天中以 Markdown 格式呈现完整笔记本内容,供用户使用。
  3. 通过任一方式成功创建后,将 URL 单独输出一行: RCA report exported to notebook: <url>

醒目地输出 URL — 如果同一会话中接下来运行 /eval-bootstrap,它会检测到此 URL,并提示将评估器套件追加到同一个笔记本。

笔记本单元格结构
单元格内容
1 — 概览结构化标题(见下方的概览单元格格式 — 必须严格遵循)
2 — 信号摘要符合模式的健康状况表
3 — 失败分类分类表
4…N — 失败模式每种失败模式对应一个单元格
N+1 — 行动计划 + 限制行动计划表 + 项目符号列表

笔记本格式规则(适用于每个单元格):

  • 不要使用三个反引号的代码块 — 提示词/评分标准使用引用块(>),较短的值使用行内代码(`)
  • 证据使用表格,而非项目符号列表
  • 工具输入使用表格 — 参数 | 传入的错误值 | 正确做法
  • 行动计划使用表格 — 优先级 | 行动 | 置信度 | 影响

输出格式


概览单元格(笔记本单元格 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**: llm-obs-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