如何让 AI 代理访问运行时跟踪
Source: Dev.to
谁适合阅读?
本文面向使用 AI 编码助手(Claude、Cursor 等)进行本地真实应用调试的开发者,帮助他们获得比静态代码分析更快、更具体的答案。
AI 编码助手非常擅长阅读代码。只要指向一个代码仓库,它就能几乎瞬间解释控制流、追踪依赖、发现模式并提出修改建议。
但当你在本地调试时,一切却变慢了:
- “在测试中很快,点几下就卡住了。”
- “这个请求大约每 20 次运行就会挂一次。”
- “重启服务器后才正常。”
- “代码没有改动,却突然超时。”
这些并不是代码理解上的问题,而是 代码实际执行方式 的问题。
目前,助手大多只处理静态输入。它们知道代码写了什么,却看不到代码运行时的表现。因此,你不得不去做那段不那么光鲜的工作:复现问题、检查追踪或日志、找出慢点或失败点,然后再把这些信息翻译成文字给助手。
正是这一步的翻译耗费了大部分时间。
本文展示了如何通过 OpenTelemetry 追踪和我编写的一个小工具 otel‑mcp,在 本地调试 时省去这一步。
当前 AI 调试工作流(以及为何它慢)
如果你曾使用 AI 代理调试除语法错误之外的任何问题,这个循环可能会让你感到熟悉。
之前
You: The /users endpoint is slow locally.
Agent: I see two DB queries and an external call. Which part is slow?
You: (spends 10 minutes checking logs / traces / console output)
You: The external API averages ~600 ms.
Agent: Add caching. Here’s how.
代理的建议甚至可能是正确的,但请注意时间都花在哪里了:
- 没有编写代码
- 没有理解逻辑
但 大量时间被用于 寻找证据 和 解释证据。你充当了运行系统与模型之间的人类适配器。
问题不在于推理;问题在于 可见性。
阅读代码并不能告诉你它的行为
这里有一个完全正常的端点:
app.get('/api/users/:id', async (req, res) => {
const user = await db.query('SELECT * FROM users WHERE id = ?', [req.params.id]);
const posts = await db.query('SELECT * FROM posts WHERE user_id = ?', [user.id]);
const enriched = await enrichUserData(user, posts);
res.json(enriched);
});
仅凭源码,代理就可以说出一些合理的话:
- 有两个数据库查询。
enrichUserData可能会进行一些 I/O。await是顺序执行的。- 可能存在慢速依赖或缺失索引。
这些都是真的,但不足以进行调试。当你真正要解决本地问题时,你关心的是:
- 哪一步 现在 在 这台 机器上很慢?
- 它是一直慢还是偶尔慢?
- 是否涉及重试或超时?
- 工作是否在池或锁后排队?
- 是否有错误发生且被吞掉了?
你无法仅靠阅读代码得到如下答案:
“这个查询大约耗时 430 ms,因为它在没有索引的情况下返回了约 50 行。”
或
“HTTP 调用本身没问题,但重启后连接复用会卡住。”
这些答案只有在代码实际运行时才会出现。
跟踪:AI 实际可用的执行数据
分布式跟踪为你提供了请求执行的结构化记录。
跟踪(trace) 是由 跨度(span) 构成的树。每个跨度代表一次计时操作——数据库查询、HTTP 调用、缓存访问——并带有持续时间、状态以及可选的元数据等属性。
使用 OpenTelemetry 自动仪表化时,你通常会得到恰好对应本地延迟主要来源的跨度。
同一个端点可能会产生如下的跟踪:
Trace: GET /api/users/123 (total: 892 ms)
db.query users_by_id 6 ms
db.query posts_by_user_id 438 ms rows=52
enrichUserData 440 ms
http.client GET /user-metadata 411 ms status=200
cache.set 9 ms
这是一组具体的、计时的、层级化的数据。它告诉你执行了什么、执行顺序以及每一步耗时——正是手动调试时你所寻找的信息。
如果代理能够直接检查这些数据会怎样?
otel‑mcp:AI 代理的追踪
“MCP(模型上下文协议)是一种让 AI 代理在会话期间调用外部工具的方式。”
otel‑mcp 是一个小型 MCP 服务器,可在本地开发时向 AI 代理提供运行时追踪。它实现了三件简单的事:
- 运行一个 本地 OTLP HTTP 收集器(仅限 localhost)。
- 将 最近的追踪存储在内存中(临时的,面向开发)。
- 暴露一小套 查询工具,供代理通过 MCP 调用。
它 不是 APM——没有 UI、没有仪表盘、没有保留策略。它专为 使用 AI 代理进行本地调试 设计。
这个功能适用于哪些代理?
otel‑mcp 可与 任何兼容 MCP 的 AI 编码助手 配合使用,包括:
- Claude Code
- Cursor
- 任何其他支持 MCP 工具的工具
如果你的代理能够调用 MCP 工具,就可以通过 otel‑mcp 查询追踪。
当代理能够看到追踪时会有什么变化?
让我们重放相同的调试场景。
使用 otel‑mcp 后
You: The /users endpoint is slow locally.
Agent: (lists slow traces from the last few minutes)
Agent: I found 23 slow requests. The posts query averages ~430 ms and
returns ~50 rows without an index. The external API averages ~70 ms.
Adding an index on posts(user_id) should fix this.
不再需要“你能检查一下追踪并告诉我吗”的来回——代理可以直接检查执行数据并给出可操作的建议。
TL;DR
- 静态代码分析告诉你 代码做了什么,而不是它在运行时 如何 表现。
- OpenTelemetry 跟踪提供了缺失的运行时可视化。
- otel‑mcp 弥合了这一差距,将最新的跟踪暴露给任何 MCP‑compatible AI assistant,将缓慢的手动调试循环转变为快速的 AI‑驱动循环。
Source: …
代理拥有的工具
otel‑mcp 提供了一组刻意保持精简且可预测的工具:
get_summary()
哪些服务在产生追踪?数量多少?最近有错误吗?
list_traces(filters)
显示慢速追踪、错误追踪,或特定服务的追踪。
get_trace(trace_id)
获取单个请求的完整 span 树。
query_spans(where)
查找符合条件的 span,例如:
duration > 100 AND status = error
http.status_code >= 500
示例
Agent calls:
query_spans({ where: "duration > 100 AND http.status_code >= 500" })
Result:
12 spans from the last 5 minutes showing slow, failed API calls
clear_traces()
在测试运行之间重置状态。
这些工具反映了人们实际调试的方式:先宽泛搜索,再逐步缩小范围,最后检查细节。
您需要的准备
在开始设置之前,请确保您拥有:
- 一个已 OpenTelemetry 注入的应用程序(或愿意添加它)
- 一个兼容 MCP 的 AI 代理(Claude Code、Cursor 等)
- 一个支持 OpenTelemetry 的运行时环境(Node.js、Java、Go、Python,…)
您不需要生产环境的可观测性或托管的后端——全部在本地完成。
设置
如果您已经在使用 OpenTelemetry,设置通常非常简单——只需将导出器指向本地 collector 即可。
典型的 Node.js 配置
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
const sdk = new NodeSDK({
serviceName: 'my-app',
traceExporter: new OTLPTraceExporter({
url: 'http://localhost:4318/v1/traces',
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
自动仪器化通常能够捕获本地大部分重要信息:
- HTTP 处理器
- 数据库查询
- 出站 HTTP 调用
- 缓存
如果没有捕获到所需信息,在关键代码段手动添加一个 span 通常就足够了。
为什么它在本地调试时效果很好
- In‑memory storage – 快速、简单,足以满足本地迭代的需求。
- Constrained queries – 代理会提出有针对性的问题,而不是把所有内容都拉进上下文。
- Local‑only by default – 默认仅在本地运行,避免意外泄露生产数据的风险。
- Shared collector – 如果运行多个代理客户端,它们会共享同一批追踪信息。
目标不是构建完整的可观测性平台,而是消除调试过程中的摩擦。
常见问题
-
跟踪未显示?
检查您的应用是否导出到http://localhost:4318/v1/traces。 -
代理未检测到工具?
确保在 MCP 配置中包含otel‑mcp。 -
使用多个代理?
第一个会启动收集器;其他会自动连接。 -
跟踪中没有有趣的内容?
您可能需要在要调查的代码周围手动添加一个 span。
限制(以及它们在此为何可接受)
- 不是生产级 APM – 没有仪表板、警报或长期存储。
- 自动仪器化并不完美 – 某些库和异步边缘需要手动 span。
- 跟踪可能包含敏感数据 – 将收集器保持在本地主机上,并在需要时对属性进行脱敏。
- 存在开销 – 通常在本地可以接受;如果你的代码非常紧凑,请进行测量。
对于本地调试,这些权衡通常是可以接受的。
试一试
如果你在本地使用 AI 代理进行调试,并且发现自己不断需要解释日志或跟踪中看到的内容,那么值得一试。
👉 https://github.com/moondef/otel-mcp
- 继续使用 OpenTelemetry。
- 在开发期间将导出器指向
localhost。 - 让代理查询跟踪,而不是让你去总结它们。
一旦代理能够看到代码的实际运行情况,对话就会改变——调试速度也会明显加快。