- 人工智能
- AI Agent
- Agent 框架
- 工具调用
- Agent 工作流
- RAG
- MCP Clients
- 强化学习
【免费下载链接】agent-core-java
openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。
导读
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 TracerDecoratorTracerDecorator是一个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); }关键行为点
- 先解析“内部会话”:
getInnerSession()会优先判断传入的agentSession是否自身就暴露tracer()与span()两个零参方法;若不是,则尝试读取_inner字段或调用getInner()方法获取内层会话(TracerDecorator.java)。这意味着AgentSessionApi 包装器与直接传入的 AgentSession 均被支持。 - 条件判断
shouldDecorate:只有 target 非空、session 非空、session 能取到tracer()返回值、且存在零参span()方法时,才会真正创建代理,否则原样返回原对象(TracerDecorator.java)。 - 实例信息(instanceInfo):写入
class_name(模型名)与type = "llm"。模型名通过resolveModelName()解析:依次尝试getConfig().getModelConfig().getModelName()链式调用,失败则退化为读取字段,最后兜底为类的SimpleName(TracerDecorator.java)。 - 调用类型:使用
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); }关键行为点
- 类型为
PLUGIN:对应 InvokeType.java 中的PLUGIN("plugin"),触发的事件为on_plugin_start、on_plugin_end、on_plugin_error。这与type = "tool"的实例信息配合,在TraceAgentHandler中对应onPluginStart/onPluginEnd/onPluginError分支(见 Tracer.java)。 - 名称解析:
resolveCardName()依次尝试getCard().getName(),失败则读取card/name字段,最后兜底为类名(TracerDecorator.java)。工具 Card 名称会写入 trace 的name与class_name。 - 降级规则:若 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中尝试读取以下四个属性:
| 属性 | 读取方式 | 说明 |
|---|---|---|
id | card.getId()/ 字段id | 工作流 ID |
name | card.getName()/ 字段name | 工作流名称 |
description | card.getDescription()/ 字段description | 工作流描述 |
version | card.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>_start | span、inputs(取第一个入参)、instance_info |
| 正常结束 | on_<invokeType>_end | span、outputs |
| 异常 | on_<invokeType>_error | span、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)是事件编排的核心:
- 只拦截
invoke和stream:其他方法直接透传给原对象(method.invoke(target, args)),不影响业务行为。 - span 创建:从 session 取
tracer(),调用tracer.getTracerAgentSpanManager().createAgentSpan(parentSpan)创建当前调用的TraceAgentSpan,并以当前会话span()作为父 span,从而在 trace 中维护正确的父子调用链(TracerDecorator.java)。 - 开始事件:触发
on_<invokeType>_start,载荷包含span、inputs(第一个入参)与instance_info(含class_name、type,workflow 还含metadata)(TracerDecorator.java)。 - 执行原调用:LLM 类型先注入
tracer_record_data回调,再执行。 - 结束事件:
stream走wrapStreamResult的延迟结束逻辑;普通调用立即触发on_<invokeType>_end。 - 异常事件:捕获
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 能力:
| 资源类型 | 挂载位置 | 调用代码 |
|---|---|---|
| model | ModelManager.java | TracerDecorator.decorateModelWithTrace(model, session) |
| tool | ToolManager.java | TracerDecorator.decorateToolWithTrace(tool, session) |
| workflow | WorkflowManager.java | TracerDecorator.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)共覆盖三个核心场景:
decorateToolWithTraceSupportsWrappedInnerSession:验证包装会话(仅含_inner字段)下工具装饰生效、事件顺序为on_plugin_start→on_plugin_end、class_name取自工具 Card;decorateWorkflowWithTraceCapturesMetadataAndStreamOutputs:验证 workflow 的metadata提取(id/name/version/description)与 stream 输出累积、end 事件只触发一次;decorateModelWithTraceInjectsTracerRecordCallback:验证tracer_record_data回调注入,on_llm_start→on_llm_request→on_llm_end三段事件顺序,以及class_name从getConfig().getModelConfig().getModelName()链解析。
测试采用轻量 FakeTracer / FakeSpanManager 模拟真实Tracer与SpanManager,无需外部依赖即可运行,便于阅读事件载荷结构。
使用建议
- 确保被装饰对象实现接口:动态代理依赖 JDK
Proxy,无接口对象会被直接原样返回(trace 不生效但不报错)。若需要为无接口对象加 trace,应走trace()通用包装器手动编排。 - 传入能暴露 tracer 的会话:支持 AgentSessionApi 包装器与原生 AgentSession 两种形态,包装器内部需能解析到
_inner/getInner()。传入不支持的会话时,装饰方法会静默降级返回原对象。 - 区分同步与流式:普通
invoke在方法返回时立即触发 end 事件;stream则延迟到迭代结束才触发,且输出为完整累积列表,消费方应按此语义理解 trace 数据。 - 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能力。
相关推荐
GetQzonehistory:QQ空间数据导出工具,历史说说、图片与评论全部归档到本地 Excel
GetQzonehistory:QQ空间数据导出工具,历史说说、图片与评论全部归档到本地 Excel 老QQ空间一条条翻回去,像徒手淘井:时间线越往上加载越慢,
人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习openJiuwen agent-core-java 控制器依赖注入:TaskExecutorDependencies 使用与源码解析
openJiuwen agent core java 控制器依赖注入:TaskExecutorDependencies 使用与源码解析 导读 TaskExecu
人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习openJiuwen agent-core 工具系统实战:从 ToolCard、@tool 装饰器到 RESTful API 与 MCP 的统一 Tool 抽象
openJiuwen agent core 工具系统实战:从 ToolCard、@tool 装饰器到 RESTful API 与 MCP 的统一 Tool 抽象
人工智能AI AgentAgent 框架大模型工具调用RAG提示工程强化学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考