- 人工智能
- AI Agent
- Agent 框架
- 工具调用
- Agent 工作流
- RAG
- MCP Clients
- 强化学习
【免费下载链接】agent-core-java
openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。
本文基于
openJiuwen/agent-core-java开源仓库中 com.openjiuwen.core.foundation.tool.Tool 的 API 文档,并结合仓库源码(Tool.java、LocalFunction.java、McpTool.java、RestfulApi.java 等)进行纵深展开。全文围绕该抽象基类的设计意图、字段与构造约束、统一调用入口、子类扩展机制及配套回调事件展开,帮助读者理解 openJiuwen Core Java 版工具体系的调用形态,并掌握自定义工具、接入 MCP/HTTP 工具与流式输出的完整实现路径。
Tool是 openJiuwen agent-core-java 工具体系的抽象基类。它本身不承载任何具体业务逻辑,而是通过持有一张ToolCard元数据卡片,为所有工具(本地函数、MCP 工具、RESTful API 工具等)定义统一的「同步执行」与「流式执行」两个入口,并围绕这两个入口挂接完整的回调事件生命周期。理解Tool,就掌握了 openJiuwen 中所有工具被 LLM 调用时的统一形态:入参解析 → 调用开始 → 结果产出/异常上报 → 输出变换。
一、Tool 在工具体系中的定位
从源码结构看,Tool位于core/src/main/java/com/openjiuwen/core/foundation/tool/Tool.java,是全工具族系的抽象基类,仓库中的具体工具实现均直接或间接继承自它:
| 实现类 | 源码路径 | 用途 |
|---|---|---|
LocalFunction | function/LocalFunction.java | 包装本地 Java 函数/方法,将可调用对象封装为工具 |
McpTool | mcp/McpTool.java | 包装 MCP(Model Context Protocol)客户端,透传远端工具调用 |
RestfulApi | service_api/RestfulApi.java | 将 RESTful HTTP 接口封装为工具,执行请求并解析响应 |
这些子类共享同一套「卡片 + 调用入口」抽象:构造时注入一张描述工具元数据的ToolCard,对外通过invoke/stream两个统一入口被上层调用。源码注释明确指出该类镜像了 Python 侧openjiuwen/core/foundation/tool/base.py中的Tool,因此本仓库的 Java 实现与 openJiuwen Python 版保持一致的调用契约。
设计要点:Tool自身不实现具体业务逻辑,只负责约束所有工具共有的调用形态。具体行为全部由子类通过两个受保护的模板方法(invokeInternal/streamInternal)提供,父类负责输入预处理、回调触发与输出变换的公共编排。
二、唯一字段 card 与构造约束
Tool只有一个实例字段:card(类型ToolCard),即工具卡片,保存工具的名称、描述、输入参数 Schema 等元数据。字段说明如下:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
card | ToolCard | - | 工具卡片。构造时要求非空且id非空字符串。 |
源码 Tool.java 中的受保护构造器Tool(ToolCard card)在初始化时执行两条硬性校验:
public Tool(ToolCard card) { if (card == null) { throw toolCardInvalid(null, "card is None"); } if (card.getId() == null || card.getId().isEmpty()) { throw toolCardInvalid(card, "card id is None or empty"); } this.card = card; }card为null,直接抛出异常(错误码TOOL_CARD_INVALID,由 ErrorHelper 构造);card.getId()为空或 null,同样拒绝构造。
因此「每个工具必须携带一张有合法 id 的卡片」是工具体系不可绕过的约束。值得注意:该构造器为受保护(protected),仅供子类初始化时调用,不作为公开构造方法暴露,这与文档「源码中的Tool(ToolCard card)为受保护构造器,供子类初始化时使用」的说明一致。card声明为final,一旦构造完成不可替换,保证工具生命周期内元数据稳定。
三、统一调用入口:invoke 与 stream
Tool为所有工具定义了两种执行模式:同步执行invoke与流式执行stream,各自提供带/不带kwargs两个重载版本。公开方法签名如下:
| 签名 | 说明 |
|---|---|
public ToolCard getCard() | 返回当前工具持有的卡片对象。 |
public abstract Object invoke(Map<String, Object> inputs, Map<String, Object> kwargs) throws Exception | 执行完整工具调用,具体行为由子类实现。 |
public Object invoke(Map<String, Object> inputs) throws Exception | 以空kwargs调用重载版本。 |
public abstract Iterator<Object> stream(Map<String, Object> inputs, Map<String, Object> kwargs) throws Exception | 以流式方式执行工具,返回增量结果迭代器。 |
public Iterator<Object> stream(Map<String, Object> inputs) throws Exception | 以空kwargs调用流式重载版本。 |
3.1 参数语义
两个入口的第一个参数inputs是结构化输入,需要符合工具卡片ToolCard.inputParams声明的 JSON Schema;第二个参数kwargs是附加执行参数,用于向执行过程传递额外控制信息。以 LocalFunction.java 的实现为例,kwargs中以下两个键会被识别并影响入参解析:
skip_none_value(布尔):为true时跳过值为None/null的输入字段;skip_inputs_validate(布尔):为true时跳过输入 Schema 校验。
单参数重载版本内部直接以Map.of()构造空kwargs调用双参数版本(见 Tool.java),因此无论调用方传不传kwargs,行为路径完全一致。
3.2 invoke 的内部编排
从源码 Tool.java 看,invoke并非简单转发到子类,而是父类编排的一整条事件链:
- 入参准备(
prepareInput):复制inputs/kwargs,若注册了回调框架(DecoratorFramework),先触发tool_invoke_input变换事件,允许回调修改入参; - 调用开始:触发
tool_call_started事件(携带tool_name、tool_id、inputs等生命周期参数); - 执行与结果/异常上报:调用子类实现的
invokeInternal;成功触发tool_call_finished,失败捕获异常并触发tool_call_error后重新抛出; - 输出变换:触发
tool_invoke_output事件,回调可对结果做二次加工(transformOutput)。
3.3 stream 的流式生命周期
stream的编排见 Tool.java:父类先触发tool_stream_input与tool_call_started,然后调用子类的streamInternal获得底层迭代器;若返回null则替换为空迭代器(Collections.emptyIterator()),最后包一层内部的CallbackIterator返回。该迭代器在遍历过程中按需触发:
- 每取出一块增量结果:触发
tool_result_received与tool_stream_output; - 迭代耗尽:触发
tool_call_finished; - 遍历抛异常:触发
tool_call_error(同一迭代器只上报一次,通过finished标志保证幂等)。
所有事件名常量集中在 ToolCallEvents.java 中,完整清单包括:tool_call_started、tool_call_finished、tool_call_error、tool_result_received、tool_parse_started、tool_parse_finished、tool_invoke_input、tool_invoke_output、tool_stream_input、tool_stream_output、tool_auth。这意味着Tool 层的可观测能力是全体系统一的:无论底层是本地函数、MCP 远端还是 HTTP 接口,回调消费者拿到的生命周期事件形态一致。
四、子类如何实现具体工具:invokeInternal / streamInternal
Tool将「父类编排 + 子类实现」分离。公开的invoke/stream是final级编排入口(非 abstract),而真正的业务逻辑由两个受保护模板方法承载:
protected Object invokeInternal(Map<String, Object> inputs, Map<String, Object> kwargs) throws Exception; protected Iterator<Object> streamInternal(Map<String, Object> inputs, Map<String, Object> kwargs) throws Exception;若子类未覆盖某一方法,基类默认抛出明确错误:
- 未实现
invokeInternal→TOOL_INVOKE_NOT_SUPPORTED(工具不支持同步调用); - 未实现
streamInternal→TOOL_STREAM_NOT_SUPPORTED(工具不支持流式调用)。
这正是McpTool的做法:MCP 远端调用本身不具备原生增量流式契约,因此 McpTool.java 的streamInternal直接抛出TOOL_STREAM_NOT_SUPPORTED,而invokeInternal则完成参数按 Schema 清洗、调用 MCP 客户端的callTool/call_tool方法、并支持operation_timeout/operationTimeout/timeout键动态覆盖超时(默认McpBase.NO_TIMEOUT)。
LocalFunction则两个方法都实现(LocalFunction.java),其流式策略很有代表性:
- 返回结果是
Iterator:直接透传; - 返回结果是
Iterable(如List):取其iterator(); - 返回普通对象:包装成单元素迭代器(
singletonList(result).iterator())。
同时invokeInternal中若发现函数返回的是Iterator,会抛出func can not be generator错误——即同步调用不允许返回生成器语义的迭代器,必须走stream入口,从源码与测试双重印证了这一约束。
五、ToolCard:工具元数据卡片
ToolCard(ToolCard.java)继承自BaseCard,除基础的身份信息(id、name、description)外,还维护工具声明的输入 Schema 与执行特性:
| 成员 | 默认值 | 含义 |
|---|---|---|
inputParams | 空LinkedHashMap | 输入参数的 JSON Schema(type、properties、required等) |
properties | 空LinkedHashMap | 附加属性(如 MCP 场景的传输细节) |
isParallelSafe | true | 是否可并行安全调用 |
isStateless | false | 是否无状态 |
isIdempotent | false | 是否幂等 |
ToolCard提供两种构建方式:
- 多参构造器:
ToolCard(id, name, description[, inputParams[, properties]]); - Builder 模式:
ToolCard.builder().id(...).name(...).description(...).inputParams(...).parallelSafe(...).stateless(...).idempotent(...).build(),其中id缺省时会自动生成,name/description缺省为空字符串。
卡片的核心产出方法是toolInfo(),返回 ToolInfo.java 描述符——遵循OpenAI function calling 格式(type="function"、name、description、parameters),可直接用于 LLM 的 function/tool 参数拼接。equals/hashCode覆盖了完整声明面(身份 + 参数/属性 + 三个特性标志),用于工具重复注册时的等价性判定(ability-declaration reuse check)。
六、工具的三种典型创建方式
围绕Tool基类,仓库提供了三种把「普通代码」变成「工具」的路径:
6.1 手动构造 LocalFunction
最直接的方式:先构建ToolCard,再new LocalFunction(card, func),其中func是Function<Map<String, Object>, Object>。LocalFunction还支持ContextFunction变体((inputs, kwargs) -> Object),可在函数体内访问session等执行期上下文;且函数返回CompletionStage(如CompletableFuture)时会被自动等待并解包(见 LocalFunction.java)。
6.2 ToolDecorator 工厂
ToolDecorator.java 是 Python 版@tool(...)装饰器的 Java 等价物,提供tool(Function)、tool(name, Function)、toolFromMethod(target, method)等静态工厂方法,以及携带完整选项的Options记录(name、description、inputParams、card、autoExtract、sourceMethod):
- 未显式指定
inputParams且autoExtract=true时,通过 CallableSchemaExtractor.java 基于反射自动生成参数 Schema; - 方法参数为
Optional类型时会自动包裹空值(argumentValue); - 显式传入
card时,若name/description/inputParams与卡片不一致,会生成一张覆盖后的新卡片而非修改原卡片。
6.3 @ToolDefinition 注解扫描
annotation/ToolDefinition.java 提供name、description、autoExtract三个注解属性;配合 AnnotatedToolFactory.java 的scan(target),可一键扫描目标对象上所有@ToolDefinition标注的方法并批量转换为LocalFunction,适用于工具注册表批量装配场景。
七、异步执行与虚拟线程:Java 版的设计取舍
文档明确说明:Java 版本没有内建异步接口,Tool的invoke/stream均为同步阻塞形态(throws Exception)。如果需要异步执行,由调用方自行结合虚拟线程或CompletableFuture处理。这一点与 Python 版Tool的协程语义不同——Java 侧刻意不把异步写进基类契约,原因可以从两个层面印证:
- 函数层支持异步返回值:
LocalFunction允许包装的函数返回CompletionStage,父类在调用线程上统一await等待完成(awaitIfNeeded),因此「异步函数 + 同步调用入口」天然兼容; - 调用层由使用者编排异步:需要真正并行发起多个工具调用时,可在外层用虚拟线程(本项目文档 Java版本与虚拟线程兼容 有专项说明)包裹每个
invoke,或将调用提交给CompletableFuture组合,工具自身无需感知线程模型。
另外,LocalFunction.invokeFunction在调用前后会保存/恢复SessionContextHolder中的当前 Session(见 LocalFunction.java),其注释明确指出这是为「并行工具调用不互相污染调用线程绑定的 Session」而设计——从源码结构看,这是对虚拟线程/并行场景的显式防护。
八、测试验证:四个相关测试覆盖的行为
文档列出四个相关测试,均可在仓库core/src/test下找到,覆盖了Tool体系的核心行为契约:
| 测试类 | 路径 | 验证重点 |
|---|---|---|
LocalFunctionTest | core/src/test/java/com/openjiuwen/core/foundation/tool/function/LocalFunctionTest.java | 构造时拒绝null函数;输入按 Schema 填充默认值(如mode缺省补"text");tool_parse_started/tool_parse_finished回调携带tool_name/tool_id/raw_inputs/schema/formatted_inputs;CompletableFuture返回值被自动等待;同步invoke拒绝迭代器返回(func can not be generator);stream正确产出Iterator元素并触发解析回调 |
McpToolTest | core/src/test/java/com/openjiuwen/core/foundation/tool/mcp/McpToolTest.java | MCP 客户端调用透传、参数清洗与超时控制 |
RestfulApiTest | core/src/test/java/com/openjiuwen/core/foundation/tool/service_api/RestfulApiTest.java | RESTful 工具的 URL 校验、HTTP 执行与响应解析 |
ToolCardTest | core/src/test/java/com/openjiuwen/core/foundation/tool/ToolCardTest.java | Builder 构建卡片属性、id自动生成、默认inputParams为空 Map、默认特性标志(isParallelSafe=true、isStateless=false、isIdempotent=false)、toolInfo()输出 |
其中LocalFunctionTest的invokeFormatsInputsWithSchemaDefaults用例直观展示了 Schema 默认值填充:卡片声明mode字段默认"text"且path必填,仅传path调用即可得到"readme.md:text"的结果——这正是「工具卡片声明驱动入参解析」的具体体现。
九、小结:Tool 基类的设计价值
从整体设计看,openJiuwen agent-core-java 的Tool抽象基类承担了三层职责:
- 统一契约:以
ToolCard承载元数据、以invoke/stream承载调用入口,所有工具(本地、MCP、HTTP)对上层与 LLM 暴露一致的形态; - 集中编排:输入变换、生命周期回调、输出变换全部收敛在父类,子类只需实现
invokeInternal/streamInternal两个模板方法,且通过默认抛错让「不支持的能力」显式化; - 可观测基础:
tool_call_started/finished/error、tool_parse_started/finished、tool_stream_output等标准事件构成统一的工具调用追踪与装饰器挂接点。
对于希望在本仓库中自定义工具的开发者,标准路径是:构建一张ToolCard(或直接用ToolDecorator/@ToolDefinition自动生成)→ 继承Tool实现invokeInternal(必要时实现streamInternal)→ 通过setCallbackFramework注册回调框架即可获得全套调用事件。若仅需快速接入,优先使用LocalFunction包装函数,或通过McpTool/RestfulApi对接远端能力。
- 人工智能
- AI Agent
- Agent 框架
- 工具调用
- Agent 工作流
- RAG
- MCP Clients
- 强化学习
【免费下载链接】agent-core-java
openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。
相关推荐
openJiuwen agent-core 工具系统实战:从 ToolCard、@tool 装饰器到 RESTful API 与 MCP 的统一 Tool 抽象
openJiuwen agent core 工具系统实战:从 ToolCard、@tool 装饰器到 RESTful API 与 MCP 的统一 Tool 抽象
人工智能AI AgentAgent 框架大模型工具调用RAG提示工程强化学习openJiuwen agent-core 检索器统一抽象接口:Retriever 基类深度解析与实战指南
openJiuwen agent core 检索器统一抽象接口:Retriever 基类深度解析与实战指南 导读 本文围绕 openJiuwen agent c
人工智能AI AgentAgent 框架大模型工具调用RAG提示工程强化学习openJiuwen agent-core 沙箱启动器(SandboxLauncher)基类解析:生命周期抽象、配置模型与内置实现
openJiuwen agent core 沙箱启动器(SandboxLauncher)基类解析:生命周期抽象、配置模型与内置实现 导读 本文围绕 openji
人工智能AI AgentAgent 框架大模型工具调用RAG提示工程强化学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考