☰
openJiuwen agent-core-java 的 TracerDecorator 源码解析:为 model、tool 与 workflow 无缝注入 trace 的动态代理装饰器
2026/10/15 6:54:58 网站建设 项目流程
  • 人工智能
  • AI Agent
  • Agent 框架
  • 工具调用
  • Agent 工作流
  • RAG
  • MCP Clients
  • 强化学习

【免费下载链接】agent-core-java

openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。

项目地址:https://gitcode.com/openJiuwen/agent-core-java
点击查看免费下载

导读

TracerDecorator是 openJiuwen agent-core-java(openJiuwen Core 的 Java 版 SDK)中 session 链路追踪(tracer)体系的关键装饰器工具类。它位于 core/src/main/java/com/openjiuwen/core/session/tracer/TracerDecorator.java,为model(大模型调用)、tool(工具/插件调用)和 workflow(工作流执行)创建带 trace 事件的动态代理,并提供同步调用包装器trace(),在调用开始、结束和异常时自动触发对应 trace 事件。读完本文,你将掌握:三个装饰方法的使用时机与语义、动态代理的生效条件与降级规则、trace()通用包装器的调用方式,以及这些能力在ModelManager/ToolManager/WorkflowManager中的实际挂载位置。

本文档主体对应 TracerDecorator.md,相关文档还包括 Tracer.md。


一、类概览:TracerDecorator的定位与设计

public final class TracerDecorator

TracerDecorator是一个final 工具类,构造函数为私有(private TracerDecorator()),所有能力以static方法对外暴露。源码注释明确其镜像了 Python 版openjiuwen/core/session/tracer/decorator.py的设计(见 TracerDecorator.java),因此行为语义与 Python 端保持一致。

其核心职责可以概括为一句话:为 model、tool 和 workflow 创建带 trace 的动态代理,并提供一个通用的同步调用包装器。

private static final String TRACE_HANDLER = "tracer_agent";

内部使用常量TRACE_HANDLER = "tracer_agent"作为事件触发的 handler 名。该名称与 Tracer.java 中定义的TRACE_AGENT常量、TracerHandlerName.java 中的TRACE_AGENT枚举值完全一致,确保装饰器触发的事件能被 tracer 正确路由到TraceAgentHandler。

方法总览

签名说明
public static<T> T decorateModelWithTrace(T model, Object agentSession)若对象可代理且会话暴露tracer()/span(),则为 model 的invoke/stream调用添加 trace。
public static<T> T decorateToolWithTrace(T tool, Object agentSession)为 tool 的invoke调用添加 trace;若对象没有接口或会话不支持 trace,则原样返回。
public static<T> T decorateWorkflowWithTrace(T workflow, Object agentSession)为 workflow 的invoke/stream调用添加 trace,并尝试从card提取元数据。
public static Object trace(Object session, InvokeType invokeType, Map<String, Object> instanceInfo, BiFunction<Object[], Map<String, Object>, Object> callable, Object[] args, Map<String, Object> kwargs)用同步方式包装一次调用,在开始、结束和异常时分别触发对应 trace 事件。

二、decorateModelWithTrace:为 LLM 调用注入 trace

方法与签名

public static <T> T decorateModelWithTrace(T model, Object agentSession)

该方法为传入的 model 对象创建带 trace 的动态代理,目标是拦截 model 的invoke和stream两个入口。源码实现见 TracerDecorator.java:

public static <T> T decorateModelWithTrace(T model, Object agentSession) { Object session = getInnerSession(agentSession); if (!shouldDecorate(model, session)) { return model; } Map<String, Object> instanceInfo = new LinkedHashMap<>(); instanceInfo.put("class_name", resolveModelName(model)); instanceInfo.put("type", "llm"); return (T) createTracingProxy(model, session, InvokeType.LLM, instanceInfo); }

关键行为点

  1. 先解析“内部会话”:getInnerSession()会优先判断传入的agentSession是否自身就暴露tracer()与span()两个零参方法;若不是,则尝试读取_inner字段或调用getInner()方法获取内层会话(TracerDecorator.java)。这意味着AgentSessionApi 包装器与直接传入的 AgentSession 均被支持。
  2. 条件判断shouldDecorate:只有 target 非空、session 非空、session 能取到tracer()返回值、且存在零参span()方法时,才会真正创建代理,否则原样返回原对象(TracerDecorator.java)。
  3. 实例信息(instanceInfo):写入class_name(模型名)与type = "llm"。模型名通过resolveModelName()解析:依次尝试getConfig().getModelConfig().getModelName()链式调用,失败则退化为读取字段,最后兜底为类的SimpleName(TracerDecorator.java)。
  4. 调用类型:使用InvokeType.LLM。在 InvokeType.java 中LLM("llm"),最终触发的事件名形如on_llm_start、on_llm_request、on_llm_end、on_llm_error。

特有的tracer_record_data回调注入

decorateModelWithTrace是三个装饰器中唯一会向调用参数注入额外回调的方法。在TracingInvocationHandler.invoke()中,当invokeType == LLM时,会对参数数组做withTracerRecordCallback()处理(TracerDecorator.java):

  • 若最后一个参数是Map(即 kwargs),会把tracer_record_data键替换为内部回调;
  • 该回调被调用时,会先触发on_<invokeType>_request事件(携带 span 与回调传入的 payload),再透传给用户原本设置的tracer_record_data回调(invokeExistingCallback)。

这一机制让大模型调用方(如 ModelManager 获取的模型资源)在真实发起 HTTP 请求前,可以把llm_params等请求细节上报到 trace 链路,同时不破坏用户自定义的 tracer_record 回调。


三、decorateToolWithTrace:为工具/插件调用注入 trace

方法与签名

public static <T> T decorateToolWithTrace(T tool, Object agentSession)

该方法为 tool 对象的invoke调用添加 trace,源码见 TracerDecorator.java:

public static <T> T decorateToolWithTrace(T tool, Object agentSession) { Object session = getInnerSession(agentSession); if (!shouldDecorate(tool, session)) { return tool; } Map<String, Object> instanceInfo = new LinkedHashMap<>(); instanceInfo.put("class_name", resolveCardName(tool)); instanceInfo.put("type", "tool"); return (T) createTracingProxy(tool, session, InvokeType.PLUGIN, instanceInfo); }

关键行为点

  1. 类型为PLUGIN:对应 InvokeType.java 中的PLUGIN("plugin"),触发的事件为on_plugin_start、on_plugin_end、on_plugin_error。这与type = "tool"的实例信息配合,在TraceAgentHandler中对应onPluginStart/onPluginEnd/onPluginError分支(见 Tracer.java)。
  2. 名称解析:resolveCardName()依次尝试getCard().getName(),失败则读取card/name字段,最后兜底为类名(TracerDecorator.java)。工具 Card 名称会写入 trace 的name与class_name。
  3. 降级规则:若 tool 没有实现任何接口(target.getClass().getInterfaces()为空),createTracingProxy()直接返回原对象;若会话不支持 trace,同样原样返回(TracerDecorator.java)。

会话形态的双重支持

原文档特别注明:decorateToolWithTrace()已验证同时支持AgentSessionApi 包装器和直接传入 AgentSession。这一点在单元测试 TracerDecoratorTest.java 中有直接印证:

@Test void decorateToolWithTraceSupportsWrappedInnerSession() { FakeTracer tracer = new FakeTracer(); ToolApi decorated = TracerDecorator.decorateToolWithTrace(new ToolImpl(), new WrappedSession(tracer)); assertTrue(Proxy.isProxyClass(decorated.getClass())); assertEquals("ok:ping", decorated.invoke("ping")); assertEquals(2, tracer.events.size()); assertEquals("on_plugin_start", tracer.events.get(0).eventName()); ... }

测试中的WrappedSession只有私有_inner字段、不直接暴露tracer(),但装饰器通过getInnerSession()的_inner字段读取逻辑仍能正确找到内层会话并完成装饰;且实例信息中的class_name被断言为test_tool(来自ToolCard.getName()),验证了resolveCardName()的取卡逻辑。


四、decorateWorkflowWithTrace:为工作流注入 trace 并提取元数据

方法与签名

public static <T> T decorateWorkflowWithTrace(T workflow, Object agentSession)

该方法为 workflow 的invoke/stream调用添加 trace,并尝试从card提取元数据,源码见 TracerDecorator.java:

public static <T> T decorateWorkflowWithTrace(T workflow, Object agentSession) { Object session = getInnerSession(agentSession); if (!shouldDecorate(workflow, session)) { return workflow; } Map<String, Object> instanceInfo = new LinkedHashMap<>(); instanceInfo.put("class_name", resolveCardName(workflow)); instanceInfo.put("type", "workflow"); Map<String, Object> metadata = resolveWorkflowMetadata(workflow); if (!metadata.isEmpty()) { instanceInfo.put("metadata", metadata); } return (T) createTracingProxy(workflow, session, InvokeType.WORKFLOW, instanceInfo); }

元数据提取:resolveWorkflowMetadata

这是 workflow 装饰区别于 model/tool 的核心能力。resolveWorkflowMetadata()(TracerDecorator.java)从 workflow 的card中尝试读取以下四个属性:

属性读取方式说明
idcard.getId()/ 字段id工作流 ID
namecard.getName()/ 字段name工作流名称
descriptioncard.getDescription()/ 字段description工作流描述
versioncard.getVersion()/ 字段version工作流版本号

只有非空属性才会被写入metadata(putIfPresent过滤);metadata 非空时才附加到instance_info.metadata,随on_workflow_start等事件一并上报。测试 TracerDecoratorTest.java 断言了metadata.id == "test_weather_agent"与class_name == "weather",正是这一提取逻辑的验证。

stream 结果包装

对于stream调用,装饰器会返回一个包装后的 Iterator(wrapStreamResult,TracerDecorator.java):

  • 内部累积所有next()产出的元素;
  • 当hasNext()返回false(迭代结束)时,只触发一次on_<type>_end事件,并把累积的全部输出作为outputs上报;
  • 若结果为Iterable,则先转为Iterator再走同一逻辑;若既不是 Iterator 也不是 Iterable,则立即触发 end 事件。

这保证了流式场景下 end 事件不会重复触发,且最终上报的是完整输出列表。workflow 的调用类型为InvokeType.WORKFLOW,对应事件on_workflow_start/on_workflow_end/on_workflow_error。


五、通用同步包装器:trace()

方法与签名

public static Object trace(Object session, InvokeType invokeType, Map<String, Object> instanceInfo, BiFunction<Object[], Map<String, Object>, Object> callable, Object[] args, Map<String, Object> kwargs)

trace()以同步方式包装一次调用,在开始、结束和异常时分别触发对应 trace 事件。其核心语义与TracingInvocationHandler.invoke()的处理流程一致,是同一个事件模型对外暴露的通用编程入口:

阶段触发的 trace 事件携带的关键载荷
开始on_<invokeType>_startspan、inputs(取第一个入参)、instance_info
正常结束on_<invokeType>_endspan、outputs
异常on_<invokeType>_errorspan、error(原始 Throwable)

invokeType可以是 InvokeType.java 中定义的任一枚举值:PROMPT、LLM、PLUGIN、WORKFLOW、CHAIN、RETRIEVER、EVALUATOR,各类型会拼接出on_prompt_start、on_chain_end等对应事件名。这也解释了Tracer.handleAgentEvent()中为何要覆盖从on_chain_*到on_evaluator_*的一整组分支(见 Tracer.java)。

事件触发统一走trigger()方法(TracerDecorator.java):按trigger、syncTrigger、sync_trigger三个方法名依次查找签名匹配(3 个参数:handler 名、事件名、kwargs)的方法,命中即调用并返回。这保证了与 Python 端trigger/syncTrigger/sync_trigger命名风格的兼容。


六、动态代理的内部机制:TracingInvocationHandler与事件编排

代理创建与降级条件

createTracingProxy()(TracerDecorator.java)的核心逻辑:

Class<?>[] interfaces = target.getClass().getInterfaces(); if (interfaces.length == 0) { return target; // 无接口则无法动态代理,原样返回 } return Proxy.newProxyInstance( target.getClass().getClassLoader(), interfaces, new TracingInvocationHandler(target, session, invokeType, instanceInfo) );

动态代理只适用于实现了接口的对象;没有接口时createTracingProxy()会直接返回原对象——这是原文档明确标注、也与 JDK 动态代理机制一致的限制。调用方需据此保证被装饰的 model/tool/workflow 均实现了对应接口。

调用拦截流程

TracingInvocationHandler.invoke()(TracerDecorator.java)是事件编排的核心:

  1. 只拦截invoke和stream:其他方法直接透传给原对象(method.invoke(target, args)),不影响业务行为。
  2. span 创建:从 session 取tracer(),调用tracer.getTracerAgentSpanManager().createAgentSpan(parentSpan)创建当前调用的TraceAgentSpan,并以当前会话span()作为父 span,从而在 trace 中维护正确的父子调用链(TracerDecorator.java)。
  3. 开始事件:触发on_<invokeType>_start,载荷包含span、inputs(第一个入参)与instance_info(含class_name、type,workflow 还含metadata)(TracerDecorator.java)。
  4. 执行原调用:LLM 类型先注入tracer_record_data回调,再执行。
  5. 结束事件:stream走wrapStreamResult的延迟结束逻辑;普通调用立即触发on_<invokeType>_end。
  6. 异常事件:捕获InvocationTargetException并解包出真实 cause,触发on_<invokeType>_error,随后重新抛出原始异常——trace 是旁路观测,绝不影响业务异常的传播。

事件的下游消费

装饰器触发的事件最终由Tracer统一调度(Tracer.java):trigger("tracer_agent", eventName, kwargs)进入handleAgentEvent(),按事件名分发到TraceAgentHandler的onLlmStart/onLlmEnd/onPluginStart/onWorkflowEnd等回调;TraceAgentHandler再把 span 数据(startTime、endTime、elapsedTime、inputs、outputs、error、metaData等)写入 TraceAgentSpan.java,最终通过StreamWriterManager的 trace writer 以{"type": "tracer_agent", "payload": span}形式流出(Tracer.java)。这样,装饰器、Tracer、Handler、Span 构成了从“调用拦截”到“trace 数据落盘”的完整链路。


七、在资源管理器中的实际挂载位置

TracerDecorator并不是孤立工具,它被 runner 的资源管理器(ResourceManager)在获取资源的出口统一挂载,凡是经过这些出口取得的 model / tool / workflow,都会自动带上 trace 能力:

资源类型挂载位置调用代码
modelModelManager.javaTracerDecorator.decorateModelWithTrace(model, session)
toolToolManager.javaTracerDecorator.decorateToolWithTrace(tool, session)
workflowWorkflowManager.javaTracerDecorator.decorateWorkflowWithTrace(workflow, session)

例如ToolManager.getTool(String toolId, Object session)(ToolManager.java)与getMcpTools(String serverId, Object session)都会把当前会话传入装饰器,使得 MCP 工具等一切通过 manager 获取的 tool 在调用时自动产生on_plugin_start/end/errortrace。这种“资源出口统一装饰”的设计,让业务代码无需关心 trace 细节,只需在调用资源时传入会话即可获得完整的调用链路观测能力。


八、测试验证与使用建议

测试覆盖

TracerDecoratorTest(core/src/test/java/com/openjiuwen/core/session/tracer/TracerDecoratorTest.java)共覆盖三个核心场景:

  1. decorateToolWithTraceSupportsWrappedInnerSession:验证包装会话(仅含_inner字段)下工具装饰生效、事件顺序为on_plugin_start→on_plugin_end、class_name取自工具 Card;
  2. decorateWorkflowWithTraceCapturesMetadataAndStreamOutputs:验证 workflow 的metadata提取(id/name/version/description)与 stream 输出累积、end 事件只触发一次;
  3. decorateModelWithTraceInjectsTracerRecordCallback:验证tracer_record_data回调注入,on_llm_start→on_llm_request→on_llm_end三段事件顺序,以及class_name从getConfig().getModelConfig().getModelName()链解析。

测试采用轻量 FakeTracer / FakeSpanManager 模拟真实Tracer与SpanManager,无需外部依赖即可运行,便于阅读事件载荷结构。

使用建议

  1. 确保被装饰对象实现接口:动态代理依赖 JDKProxy,无接口对象会被直接原样返回(trace 不生效但不报错)。若需要为无接口对象加 trace,应走trace()通用包装器手动编排。
  2. 传入能暴露 tracer 的会话:支持 AgentSessionApi 包装器与原生 AgentSession 两种形态,包装器内部需能解析到_inner/getInner()。传入不支持的会话时,装饰方法会静默降级返回原对象。
  3. 区分同步与流式:普通invoke在方法返回时立即触发 end 事件;stream则延迟到迭代结束才触发,且输出为完整累积列表,消费方应按此语义理解 trace 数据。
  4. trace 不影响业务:事件触发是旁路观测,业务异常会被原样重新抛出;trace writer 写入失败也被标记为 best effort,不会改变业务行为(Tracer.java)。

总结

TracerDecorator以"动态代理 + 统一事件编排"的方式,把 trace 观测能力以最低侵入度的形式注入到 model、tool、workflow 三类核心调用的生命周期中:开始、请求、结束、异常四个阶段分别触发语义化的事件,span 父子关系与实例元数据(模型名、工具 Card 名称、工作流 Card 的 id/name/description/version)随事件一起上报,最终由Tracer→TraceAgentHandler→StreamWriterManager落盘为结构化 trace 数据。结合 ModelManager、ToolManager、WorkflowManager 的资源出口统一挂载,开发者可以在不修改业务代码的前提下,获得完整的 agent 调用链路可观测性,为会话调试、性能分析与链路追踪提供基础支撑。

如需深入阅读,可继续查看同目录下的 Tracer.md、InvokeType.md 与 TraceAgentSpan.md,以及源码目录 core/src/main/java/com/openjiuwen/core/session/tracer/ 下的完整实现。

  • 人工智能
  • AI Agent
  • Agent 框架
  • 工具调用
  • Agent 工作流
  • RAG
  • MCP Clients
  • 强化学习

【免费下载链接】agent-core-java

openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。

项目地址:https://gitcode.com/openJiuwen/agent-core-java
点击查看免费下载

相关推荐

上一篇:Agno Learning Machine 完全指南:构建不断进化的自学习 Agent
下一篇:Awakened Poe Trade 新手快速上手指南:从安装到精通的价格查询神器

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询