ByteNoteByteNote
从 0 造一套 Agent 可观测系统
字

字节笔记本

2026年10月24日 · 约 11 分钟读完

从 0 造一套 Agent 可观测系统

API中转
¥120

模型配置:模型选择与上下文窗口

本文是《从 0 写 Agent》系列第 25 章。这个系列基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版),用 Go 从零实现一个生产级 Agent 运行时;本章讲「Tracing:从 0 造一套可观测系统」。

Agent 出错时,你需要知道是哪一步炸的,是模型调用超时?工具执行失败?还是 handoff 出了问题?

没有 tracing,你只能看到"Run 失败了",不知道在哪一步。agents-go 自实现了一套 trace/span/processor/exporter 四层架构,不依赖 OpenTelemetry,对标 Python SDK。这一章拆这套可观测系统。


引子:Agent 调试的噩梦

Agent 一次 Run 可能涉及,5 轮循环、10 个工具调用、2 次 handoff、若干次模型调用。如果失败了,日志里只有"error: tool failed",哪个工具?第几轮?在什么上下文里?

没有 trace,调试是噩梦。 你只能一遍遍加 log,重新跑,试图捕捉错误。有了 trace,你能看到完整的执行树,每一步耗时、每一步的状态、错误在哪。

text
trace: 用户咨询退款
├── span: agent(分诊) 2.1s
│   ├── span: generation(模型调用) 1.5s
│   ├── span: function(get_order) 0.4s
│   └── span: handoff(→退款) 0.1s
├── span: agent(退款) 5.3s
│   ├── span: generation(模型调用) 1.2s
│   ├── span: function(refund) ❌ 0.5s 失败  ← 这里炸了
│   └── span: generation(重试) 1.8s

一眼看出,退款的 refund 工具失败了。 这就是 tracing 的价值。


成本管理:token 用量与预算

为什么不用 OpenTelemetry

agents-go 自实现 trace,不用 OpenTelemetry。为什么?

  1. 对标 Python SDK 一致性,trace ID 格式(trace_ + 32hex)、span ID 格式(span_ + 24hex)和 Python SDK 完全一致,方便跨语言对照
  2. 依赖极简,不引入 OTel 庞大的依赖
  3. 5 种 span 类型专用,agent/generation/function/handoff/guardrail,直接对应 Agent 运行时阶段

代价,不能直接接 OTel 生态(Jaeger、Zipkin 等)。但可以写 adapter 把 agents-go 的 trace 转成 OTel 格式。这是个取舍,简洁一致 vs 生态兼容。


四层架构

agents-go 的 tracing 是经典的四层:

text
1. 数据模型(Trace + Span)    , 表示"发生了什么"
2. Tracer                       , 创建和管理 trace/span
3. Processor(BatchProcessor)  , 异步收集、缓冲、批处理
4. Exporter                     , 把 trace/span 发到目的地(控制台、HTTP、OTel)

第一层:Trace 和 Span

go
// tracing/tracing.go:14
type Trace struct {
    TraceID      string         // trace_ + 32hex
    WorkflowName string         // 工作流名(agent 名)
    GroupID      string
    Metadata     map[string]any
}

// tracing/tracing.go:39
type Span struct {
    TraceID   string            // 属于哪个 trace
    SpanID    string            // span_ + 24hex
    ParentID  string            // 父 span(形成树)
    Name      string            // span 名
    Type      string            // agent/generation/function/handoff/guardrail
    StartedAt time.Time
    EndedAt   time.Time
    Error     *SpanError        // 错误信息
    Data      map[string]any    // 结构化数据
}

Trace 是一次完整 Run 的根,Span 是其中的每一步。 Span 的 ParentID 形成树状结构,工具调用 span 的父是 agent span,agent span 的父是 trace。

五种 span 类型

go
// tracing/tracing.go:29
const (
    SpanTypeAgent      = "agent"       // Agent 执行
    SpanTypeGeneration = "generation"  // 模型调用
    SpanTypeFunction   = "function"    // 工具调用
    SpanTypeHandoff    = "handoff"     // Agent 交接
    SpanTypeGuardrail  = "guardrail"   // 护栏检查
)

直接对应 Agent 运行时的关键阶段。 consumer 按 Span.Type 分发(不用解析 Name 字符串)。

第二层:Tracer

Tracer 创建 span,管理父子关系。关键设计,nil Tracer 是 no-op(tracer.go:37 等处处 nil 检查),调用方不用判空:

go
// 即使 tracer 是 nil,这些方法也能调(no-op)
span := tracer.StartAgentSpan(agent.Name, parentID)
defer span.Finish()

这是 Go 的 nil-safe 惯用法,零值可用。 不配 tracer 时,所有 trace 调用都是 no-op,零开销。

第三层:Processor

Processor(tracing.go:56)接收 trace/span 生命周期事件。最常用的是 BatchProcessor(第 26 章详讲),后台异步批处理。

第四层:Exporter

Exporter(tracing.go:83)把 trace/span 发到目的地。agents-go 内置 5 种(第 27 章详讲)。


ID 生成与 Python SDK 一致

go
// tracing/tracing.go:98
func NewTraceID() string { return "trace_" + randHex(16) }  // trace_ + 32hex
func NewSpanID() string  { return "span_" + randHex(12) }   // span_ + 24hex

格式刻意匹配 Python SDK。为什么?因为 agents-go 是 Python SDK 的移植版,如果你同时用 Python 和 Go 版,trace ID 格式一致,方便对照。

randHex 用 crypto/rand(tracing.go:90),注释指出 Go 1.24 起 rand.Read 永不失败(OS 随机源不可用会直接 abort,不返回 error)。所以代码里 _, _ = rand.Read(buf) 忽略 error 是安全的。


小结

读完这一章,你应该理解 tracing 的四层架构:

  1. Trace + Span,数据模型,Span 的 ParentID 形成执行树
  2. 五种 span 类型,agent/generation/function/handoff/guardrail,对应运行时阶段
  3. Tracer,nil-safe(不配就是 no-op),typed span 构造器自动设 Type
  4. Processor + Exporter,异步收集和导出

Tracing 让 Agent 从"黑盒"变成"玻璃盒",每一步都看得见。 下一章讲 BatchProcessor 怎么异步处理这些 trace。


下一篇

第 26 章,BatchProcessor:异步流水线工程。trace 不能阻塞 Agent 循环,所以要异步。BatchProcessor 用后台 goroutine 缓冲批导出,还要处理背压、孤儿 span、优雅关闭。


本文是《从 0 写 Agent》系列第 25 章,案例代码基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版)。全系列目录见总览。

相关文章

分享: