跳到主内容
创建时间:最后更新:作者:白川

用 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 前缀,属于教学约定,不冒充官方遥测语义。

ts
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))
}
bash
npm run trace

应得到三个 span:任务是根,模型请求与工具是它的两个子操作。它们顺序执行,但没有互相嵌套,因此不能把工具错误归为模型请求的子错误。并行时也应由同一个父上下文创建两个子 span,而非依赖“最近创建的 span”。

回调结束控制 span 生命周期

该 API 没有让业务手动调用 end;startSpan 等待回调返回或 Promise settle。正常返回默认成功,抛错或拒绝按失败处理。业务以普通返回表示失败时,需要显式 setStatus,否则“代码没抛错”会被误读为“业务成功”。

captureTrace(true) 会标记工具与任务失败。父操作是否失败由业务判断,不能认为子 span 一报错父级就一定自动失败;有些任务允许备用方案恢复成功。

遥测适配器应保持业务返回值和拒绝值身份,不重复执行回调,并压制记录器后端故障。业务并不因为遥测停用就停止运行。测试对真实内存实现和 NOOP 实现都检查了返回与错误身份;自定义 exporter 仍要做官方 conformance 检查。

内存记录能证明什么

数据可以使用不应据此推断
parentId、顺序
父子关系与执行组织
端到端真实耗时
status、事件
某个观察范围的结果
全部业务已经正确
自定义属性
模型或工具等低基数分类
可以存任意提示词和凭据
detached snapshot
离线断言
可恢复业务状态或继续执行

本地快照和本系列使用的内存原始 span 没有时间戳;不要拿 ID 或 endSequence 相减当作耗时。它的存储也没有容量上限,示例每次创建新实例,只适用于短任务或测试。长驻进程应换有界采集与导出适配器。

避免两个容易误用的开关

coding-agent 的 PI_TELEMETRY 控制安装遥测,不是运行追踪开关。向请求传入 telemetryContext 也不保证某个 Provider 自动创建 span。需要把实际可观察的请求和工具边界包进追踪,或核对目标版本已提供的插桩。

本地遥测 README 提到的部分 Agent Schema 导出未在相应源码中找到,因此这里不导入这些名字。后续接 OpenTelemetry 时,由适配器负责后端上下文、ID、采样和导出,应用仍显式传递当前父上下文。

OpenTelemetry 的 Trace 概念可作为跨后端理解依据。先建立正确操作关系,再考虑可视化;有一张漂亮的追踪图并不能弥补缺少工具结果和任务完成定义。

依据与版本

本地官方快照为 8efd314fa,相关包标记 1.3.0;配套代码另按已发布的 1.5.0 类型和运行行为核验。主分支文档持续变化,不能混作同一版本。