
作者:ooder 团队 · 2026-08-31 · 关联专栏:Agent 可观测 / 分布式 Agent / 调用链
Agent 可观测|ooderAgent|Trace / SpanToken |归因A2A |分布式SSE 事件流
当 Agent 从"单机玩具"变成"组织级基础设施",团队的关注点就只剩下三件事: 耗时花在模型推理还是工具执行上;Token 消耗集中在哪些会话、哪些模型上; 失败与中断发生在哪一步,过程能不能回溯。
DSH(DeepSeek Harness)的解法,是给一个单机 ReAct 编码 Agent挂一个观测插件, 把执行过程还原成 entry / agent / step / chat / tool 五层调用链。 这个思路很干净,但它隐含了一个前提:执行模型是确定的"一轮任务 + 若干 step",数据链路是单机的。
ooderAgent 不一样。它是一个 Scene=Agent 的分布式 Agent 框架:
因此,DSH 的观测插件逻辑不能照搬。本文沿着原文"导语 → 运行原理 → 观测平台 → 落地实践 → 总结"的叙事结构, 把原图逐张重画,按 ooderAgent 的真实设计重新架构出一套可观测平台: 一次任务一条 Trace、五层调用树一树到底、A2A 跨节点横向拼接、Token 缓存感知归因。
DSH 的执行单元是"turn / step":一个 turn 对应一次任务,turn 内模型反复推理、调工具、看结果。 执行结构在运行前无法确定,这是所有 Agent 观测的第一性困难。
ooderAgent 的 agent-sdk-core 里没有 ReAct 循环类。它的运行主体是 Agent 接口下的五类 Agent:
Agent 类型 | 职责 |
|---|---|
SceneAgent | Scene=Agent的核心,持 SceneId / DomainId / Capability 注册表 |
WorkerAgent | 工人模型,execute(capId, params)返回 CompletableFuture |
McpAgent/RouteAgent/EndAgent | MCP 接入 / 路由转发 / 终端 |
每个 Agent 有完整状态机(CREATED → INITIALIZING → INITIALIZED → STARTING → RUNNING → … → ERROR), 由 AgentFactory 统一创建,AgentSession 承载会话(sessionId / capabilities / state / context)。
执行的最小可观测单元不是"step",而是 CommandPacket:
public class CommandPacket {
private String packetId;
private CommandDirection direction; // NORTHBOUND / SOUTHBOUND
private String parentCommandId; // 命令树父节点
private List<String> childCommandIds; // 命令树子节点 → 天然形成调用链
private int retryCount;
private String llmIntent; // LLM 意图
private String reasoningChain; // 推理链(文本),可拆出推理步数
private ContextLevel contextLevel; // GLOBAL/DOMAIN/SCENE/SESSION/EXECUTION
private TokenUsage tokenUsage; // 内嵌 token 用量
public int getReasoningStepCount() { return reasoningChain.split("step").length - 1; }
public void recordTokenUsage(int promptTokens, int completionTokens, String model) {...}
}parentCommandId / childCommandIds 就是命令树即调用树。跨 Agent 时由 A2ACommand 接力, 它带着 traceId + List<String> spanIds 跨节点透传——这是 ooderAgent 相对 DSH 最本质的差异: 链路在"协议层"就预留了分布式拼装位。
scenario 包把一次复杂任务切成 SG-UNDERSTAND → SG-DESIGN → SG-GENERATE → SG-QUALITY → SG-INTEGRATE 五组, 每组由 ScenarioStep 组成(带 FailureStrategy: FATAL/RECOVERABLE/SKIPABLE、requiredCapabilities、executionTimeMs 结果)。 这是 ooderAgent 独有的"任务中间层"——比 turn 粗、比 step 细,是天然的一级 Span 切分点。
真正接近 ReAct 的循环在 llm-sdk:LlmService.chatWithTools + ToolCallingApiImpl, 由 UnifiedLlmService 统一编排,LlmResponse 携带 latency / tokenUsage / finishReason / reasoningContent。 每个真实模型调用都能拿到端到端耗时与 token,这就是 chat 层 Span 的数据来源。
AgentSdkConfig(aiserver)把本节点注册进 CLUSTER_NODE,按角色声明能力(buildCapabilities): ROUTE_AGENT / MCP_AGENT / BPM_AGENT / VFS_STORE_AGENT / VFS_NAMENODE_AGENT / ORG_AGENT / KNOWLEDGE_AGENT / SCENE_AGENT / STUDIO_AGENT / TEST_AGENT / MESSAGE_AGENT。 节点间用 DiscoveryProtocol(UDP 广播)+ GossipProtocol(UDP P2P)+ 心跳/失联清理自组织。 观测对象不是"一个 Agent",而是一整个动态 Agent 舰队。
ooderAgent 把事件做成一等公民,且严格分三层:
事件 | 语义 | |
|---|---|---|
API 层 | Event/EventBus | 对外可订阅(publish / subscribe / publishSync / publishAndWait) |
Core 层 | CoreEvent/EventBean | 不可变、只观察,内置EventStatistics(published / processed / error) |
Engine 层 | EngineEvent | 可中断、可审计(CallerInfo+AuditLevel),携带状态 |
下行流式侧,ooder-pro 的 StudioChatSseController → SseEventPushService → SseEmitter 已经推一路完整事件流: connected / token / thinking / step / flow_start / flow_step / flow_tool_call / flow_tool_output / tool_call / tool_result / skill_orchestrate / agent_message / harness_plan / guard_escalation / complete / error …
这意味着观测平台不需要像 DSH 那样"在模型流式管道上包中间件"——事件本来就是现成的数据管道。

图 1 ooderAgent 的分层结构、一次任务的执行时序,以及对外两条数据产出(对应原文图 1,按 ooderAgent 重画)
维度 | ooderAgent 自带 | 规模化缺口 | 所需数据 |
|---|---|---|---|
会话轨迹 | SSE 实时轨迹(token/thinking/step/flow_*) | 跨会话聚合、历史回放 | 链路与时间区间 |
Token | 每次 LLM 调用 tokenUsage + 流程级_total_tokens_used | 会话/模型/缓存命中率多维归因 | 分层用量归因 |
失败 | EngineEvent 可审计 + error 事件 | 模型/工具/循环/回滚的分类定位 | 状态与错误码可追溯 |
链路 | 分散的 TraceSpan/ExecutionTrace/ObservationTrace/CommandTrace | 一树到底的父子关系与耗时占比 | 统一状态树 → Span 发射 |
直接订阅三层事件(API 层 EventBus + Engine 层 EngineEvent)与 SSE 事件流,不插桩、不改业务代码——因为事件本来就是一等公民。
订阅即采集
已有 TraceSpan / ExecutionTrace / ObservationTrace / CommandTrace 是分散埋点;平台在其上收敛为状态树,再统一发射为语义一致的 Span——收敛而不是重造。
收敛即能力
批量直传(对齐 ContextAuditLog 的 50 条 / 60s 批量落盘模式),不经过常驻采集进程;存量埋点与新增 Span 双写兼容。
双写兼容
原文的五层是 entry / agent / step / chat / tool,对应一台机器上的 DSH。 ooderAgent 的任务结构是"会话 → 场景编排 → 命令树 → 模型调用 → 能力调用",且多一层"跨 Agent"。 因此重新架构为 五层纵向 + 一维横向:

图 2 ooderAgent 的层级 Span 模型与各层承载信息(对应原文图 3,重新架构为五层 + A2A 横向)
ooderAgent 的 TokenUsage 比"输入 / 输出"多一档——DeepSeek 前缀缓存:
private Integer cacheHitTokens; // prompt_cache_hit_tokens,按 ~10% 价格计费
private Integer cacheMissTokens; // prompt_cache_miss_tokens,正常输入价
public double getEffectiveInputTokens() {
return (cacheHitTokens * 0.1) + cacheMissTokens; // 有效输入成本
}TokenConsumption(scope / prompt / completion / model / operationId / timestamp)与 TokenQuotaService(checkQuota / consumeQuota / getUsageStats)提供配额与用量统计; 流程侧 SkillFlowEngine.recordTokenUsage 把 token 沉淀到 _total_tokens_used / _llm_prompt_tokens / _llm_completion_tokens。 观测平台在此之上做 session / scene / model 多维聚合,并可直接回答"缓存命中率是否下滑、成本涨在哪"。
每个 Span 独立状态,并允许自定义错误类型,用于区分: 模型侧失败(FinishReason.ERROR / CONTENT_FILTER)、工具侧失败(ToolCall 异常 / 超时)、循环中断(FC-Loop 轮数 / 悬挂)、流程回滚(ScenarioStep.RECOVERABLE / FATAL)。 CommandTrace.retryCount 与 EngineEvent 审计级别让"重试了几次、在哪一步、能不能回放"一查便知。

图 3 ooderAgent 可观测整体技术架构与数据路径(对应原文图 4,基于现有事件/埋点重画)

图 4 一次任务的完整处理逻辑:事件流 → 状态树 → Span 发射 → 批量上报(对应原文图 5)
session / scene / command / llm / capability 逐层展开,A2A 跨节点子树挂在同一条 Trace 上,各层耗时占比一目了然。
一树到底
端到端与单轮耗时、模型推理与工具执行各自占用、首 Token 延迟与 P95 分位数。
慢在哪
输入 / 输出 / 缓存命中分别统计,可下钻到单次模型调用,也可按会话、模型、场景组聚合排行。
贵在哪
模型名称、结束原因与重试次数;工具名称、耗时、错误情况。
调用级
每层 Span 独立状态 + 错误类型(模型 / 工具 / 循环中断 / 流程回滚)。
错在哪
Traces / Spans / Sessions 三种视角,按 Trace ID、Session ID、状态检索,对耗时、失败率、Token 突增配置告警。
可回放

图 5 可观测面板 · Token 消耗部分(对应原文图 6,按 ooderAgent 缓存感知语义绘制)

图 6 调用链面板 · 一次完整的 ooderAgent 任务执行过程(对应原文图 7)
设置 | 默认值 | 说明 |
|---|---|---|
ood.obs.enabled | true | 禁用采集但不卸载 |
ood.obs.endpoint | — | 上报接入点 |
ood.obs.captureContent | true | 是否捕获 prompts / responses / tool 内容 |
ood.obs.contentMaxChars | 128000 | 单个内容属性最大字符数 |
ood.obs.batchMaxSize | 32 | 每批最大 Span 数 |
ood.obs.flushIntervalMs | 5000 | 定时刷新间隔 |
ood.obs.retryTimes | 3 | 上报重试次数 |
ood.obs.auditPath | logs/audit/audit-yyyy-MM-dd.jsonl | 审计落盘(对齐 ContextAuditLog) |
访问凭证属敏感信息,建议经环境变量或密钥管理注入,不要提交真实密钥到代码仓库。
ContextAuditLog 以 JSONL 长期留存的审计流,可按 contextId / category 查询并 getTraceReport 恢复历史。
可回溯
MonitoringApi(recordMetric / queryMetrics / createAlert / checkHealth / startProfiling),EventBean 的 EventStatistics 提供事件健康度。
可告警
TokenQuotaService 的 checkQuota / reserveQuota / confirmReservation,让可观测与成本治理闭环。
成本闭环
web / 集群节点(aiserver)/ 流程引擎(scene-engine)写入同一套数据模型,可在同一控制台内横向对比。
统一口径
DSH 的观测插件解决的是单机、单会话、实时的调试问题;ooderAgent 的观测平台要解决的是 分布式、跨会话、长期留存的治理问题。本文没有照搬 entry/agent/step/chat/tool, 而是按 ooderAgent 的真实结构重建为 session / scene / command / llm / capability 五层 + a2a 横向拼接, 并利用它"事件即一等公民"的既有管道——订阅不插桩、收敛不重造、缓存感知归因—— 让"慢在哪、贵在哪、错在哪"都能一树到底。
所有架构结论基于 ooderAgent 仓库源码实读;示例中的 Trace / 面板数据为示意,非真实线上数据。 相关标签:Agent 可观测 · ooderAgent · Trace · Token · A2A · SSE · 分布式。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。