用 Trace 和 Span 串起一次任务
通过显式遥测上下文记录父子操作与业务失败,区分追踪结构、时间测量和自动插桩。
用 Trace 和 Span 串起一次任务
按时间排序的日志能告诉我们先发生什么,却未必能说明两条并行操作分别属于哪个任务。Trace 将一次任务中的操作关联起来,Span 表达其中一个操作及其子操作,适合沿调用关系追查问题。
这篇先使用内存记录器学习父子关系和状态,不部署后端。它与前面的 Agent 示例是独立练习:没有宣称自动捕获了 Agent 内部所有操作。
本系列的独立示例位于仓库 columns/coding-agent/examples/debugging-observability/,固定使用 pi-agent-core、pi-ai、pi-schema、pi-telemetry 1.5.0。先在该目录执行 npm install,下文命令也从该目录运行。示例不需要模型密钥,不读取个人配置。
上下文显式传给子操作
src/trace.ts 使用实际发布包的 InMemoryTelemetryContext。名称使用 lesson 前缀,属于教学约定,不冒充官方遥测语义。
import { InMemoryTelemetryContext } from "@earendil-works/pi-telemetry"
export async function captureTrace(failTool = false) {
const telemetry = new InMemoryTelemetryContext()
await telemetry.startSpan({ name: "lesson.task" }, async (task) => {
await task.startSpan({ name: "lesson.model-request" }, async (request) => {
request.setAttributes({ "lesson.mock": true })
})
await task.startSpan({ name: "lesson.tool" }, async (tool) => {
if (failTool) {
// 业务用返回值表示失败时,显式标记;遥测不替业务决定结果。
tool.setStatus({ status: "error", error: { name: "FixtureError", message: "教学错误" } })
task.setStatus({ status: "error", error: { name: "TaskError", message: "工具未成功" } })
}
tool.addEvent("lesson.inspection.finished", { "lesson.success": !failTool })
})
})
return telemetry.getSpans()
}
if (process.argv[1]?.endsWith("/trace.ts")) {
console.log(JSON.stringify(await captureTrace(), null, 2))
}npm run trace应得到三个 span:任务是根,模型请求与工具是它的两个子操作。它们顺序执行,但没有互相嵌套,因此不能把工具错误归为模型请求的子错误。并行时也应由同一个父上下文创建两个子 span,而非依赖“最近创建的 span”。
回调结束控制 span 生命周期
该 API 没有让业务手动调用 end;startSpan 等待回调返回或 Promise settle。正常返回默认成功,抛错或拒绝按失败处理。业务以普通返回表示失败时,需要显式 setStatus,否则“代码没抛错”会被误读为“业务成功”。
captureTrace(true) 会标记工具与任务失败。父操作是否失败由业务判断,不能认为子 span 一报错父级就一定自动失败;有些任务允许备用方案恢复成功。
遥测适配器应保持业务返回值和拒绝值身份,不重复执行回调,并压制记录器后端故障。业务并不因为遥测停用就停止运行。测试对真实内存实现和 NOOP 实现都检查了返回与错误身份;自定义 exporter 仍要做官方 conformance 检查。
内存记录能证明什么
本地快照和本系列使用的内存原始 span 没有时间戳;不要拿 ID 或 endSequence 相减当作耗时。它的存储也没有容量上限,示例每次创建新实例,只适用于短任务或测试。长驻进程应换有界采集与导出适配器。
避免两个容易误用的开关
coding-agent 的 PI_TELEMETRY 控制安装遥测,不是运行追踪开关。向请求传入 telemetryContext 也不保证某个 Provider 自动创建 span。需要把实际可观察的请求和工具边界包进追踪,或核对目标版本已提供的插桩。
本地遥测 README 提到的部分 Agent Schema 导出未在相应源码中找到,因此这里不导入这些名字。后续接 OpenTelemetry 时,由适配器负责后端上下文、ID、采样和导出,应用仍显式传递当前父上下文。
OpenTelemetry 的 Trace 概念可作为跨后端理解依据。先建立正确操作关系,再考虑可视化;有一张漂亮的追踪图并不能弥补缺少工具结果和任务完成定义。
依据与版本
本地官方快照为 8efd314fa,相关包标记 1.3.0;配套代码另按已发布的 1.5.0 类型和运行行为核验。主分支文档持续变化,不能混作同一版本。