可观测与 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、属性词表、回调处理、处理器和导出器。
初始化只要一个配置:
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;本站没有运行它。
另有一个进程级开关(环境变量 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 等实现
TrajectoryRail 负责"每次 invoke 归档一份":调用过程中累积 span,在最后一个钩子里同步写一次,默认最多保留 200 个 span。
小结
- 观测基于 OTel 和 GenAI 语义约定,
init_observability一次配置,支持多种导出方式和脱敏。 - span 由一道 Rail 在生命周期事件上产生,自己的业务属性也挂在同样的位置。
- 跨服务打开全局 HTTP 探针,trace 才能串起来。
Trajectory是同一批 span 的不可变归档,是下一关"自进化"的输入。
源码出处
- agent-core/openjiuwen/extensions/observability/AGENTS.md观测栈的设计边界
- agent-core/openjiuwen/extensions/observability/config.py:13
class ObservabilityConfig导出方式、采样、脱敏 - agent-core/openjiuwen/extensions/observability/setup.py:21
def init_observability初始化入口 - agent-core/openjiuwen/harness/observability/rail.py:357
class AgentObservabilityRail用 Rail 生成 span - agent-core/openjiuwen/extensions/observability/instrumentation.py:72
def ensure_global_http_instrumentation跨服务传递 trace 上下文 - agent-core/openjiuwen/agent_evolving/trajectory/model.py:132
class Trajectory规范轨迹对象 - agent-core/openjiuwen/harness/rails/evolution/trajectory_rail.py:23
class TrajectoryRail每次调用归档一份轨迹 - agent-core/openjiuwen/agent_evolving/trajectory/AGENTS.md轨迹的捕获生命周期
链接指向固定提交,仓库后续更新不影响这里的行号。
过关测验
1. DeepAgent 的 span 是怎么产生的?
2. 想在不改代码的情况下,让 span 不记录提示词原文,该改什么?
3. Trajectory 和普通的 trace 数据是什么关系?