观测约 12 分钟

可观测与 Trajectory

一次运行怎么变成一棵 span 树:观测本身就是一道 Rail;同一份数据再归档成 Trajectory,给评估和自进化用。

依据 agent-core 0.1.20 · develop@fbe6a0d9(2026-11-13)核实

观测要回答的问题

智能体上线以后,客户说"交期答错了",你得能回答:这次调用模型调了几次、每次看到了什么、调了哪些工具、参数是什么、哪一步花了最久、哪一步报了错。

openJiuwen 用 OpenTelemetry(OTel) 记录这些信息,属性命名遵循 OTel 的生成式 AI 语义约定(GenAI semantic conventions),所以能直接接进常见的链路追踪后端。

一套观测栈,两层接入

openjiuwen/extensions/observability 是唯一的采集框架。单智能体 / DeepAgent 和团队(Team)各有一层很薄的接入,共享下面的一切:全局 TracerProvider、属性词表、回调处理、处理器和导出器。

源码观测栈的设计边界agent-core/openjiuwen/extensions/observability/AGENTS.md@fbe6a0d9

初始化只要一个配置:

from openjiuwen.extensions.observability.config import ObservabilityConfig
from openjiuwen.extensions.observability.setup import init_observability

init_observability(ObservabilityConfig(
    service_name="promise-agent",
    exporter="otlp_grpc",              # 还支持 otlp_http、langfuse、console、file
    endpoint="http://localhost:4317",
    sample_rate=1.0,                   # 采样比例
    redact_prompts=True,               # 提示词脱敏
    redact_completions=False,
))
说明

示意代码,字段名和可选值取自 ObservabilityConfig;本站没有运行它。

源码ObservabilityConfigagent-core/openjiuwen/extensions/observability/config.py:13@fbe6a0d9

另有一个进程级开关(环境变量 OPENJIUWEN_OTEL_GLOBAL_INSTRUMENT_ENABLE),打开后会给 httpx、requests、aiohttp 的出站请求带上 W3C traceparent,并从 FastAPI 的入站请求里取出它。这样智能体调用你的库存服务时,两边的 trace 能串成一条。

观测本身就是一道 Rail

DeepAgent 的 span 由 AgentObservabilityRail 产生:它在 Rail 关卡讲过的那些事件上开 span、关 span。一次调用大致形成这样一棵树:

agent.<名字>.invoke                       ← before_invoke
 └─ agent.<名字>.task_iteration.1          ← before_task_iteration
     └─ agent.<名字>.react_iteration.1     ← 内层每轮
         ├─ chat <模型名>                    ← 模型调用
         └─ execute_tool check_inventory    ← 工具调用
源码AgentObservabilityRailagent-core/openjiuwen/harness/observability/rail.py:357@fbe6a0d9

这说明了一件事:治理和观测用的是同一套生命周期事件。 你自己的 Rail 想记录业务字段(比如询单号),也是在同样的位置往 span 上加属性。

Trajectory:把一次运行归档

观测数据发往后端是给人看的;要让程序拿来做评估和自进化,还需要一个稳定的归档格式,这就是 Trajectory:

  • 它就是标准的 OTLP JSON,没有另起一套格式
  • 创建时深拷贝,之后不能被修改;必须带 trajectory_id
  • 由 TrajectorySpanProcessor 在进程内订阅 span,按会话和成员(或团队)汇总
  • 归档存储(TrajectoryStore)提供内存和追加写 JSONL 等实现
源码Trajectoryagent-core/openjiuwen/agent_evolving/trajectory/model.py:132@fbe6a0d9

TrajectoryRail 负责"每次 invoke 归档一份":调用过程中累积 span,在最后一个钩子里同步写一次,默认最多保留 200 个 span。

源码TrajectoryRailagent-core/openjiuwen/harness/rails/evolution/trajectory_rail.py:23@fbe6a0d9

小结

  • 观测基于 OTel 和 GenAI 语义约定,init_observability 一次配置,支持多种导出方式和脱敏。
  • span 由一道 Rail 在生命周期事件上产生,自己的业务属性也挂在同样的位置。
  • 跨服务打开全局 HTTP 探针,trace 才能串起来。
  • Trajectory 是同一批 span 的不可变归档,是下一关"自进化"的输入。

源码出处

链接指向固定提交,仓库后续更新不影响这里的行号。

过关测验

  1. 1. DeepAgent 的 span 是怎么产生的?

  2. 2. 想在不改代码的情况下,让 span 不记录提示词原文,该改什么?

  3. 3. Trajectory 和普通的 trace 数据是什么关系?

通关条件

  • ○ 测验全部答对