跳到主要内容

Tools 总览

这一章讲的不是“模型可以调函数”这么一句话,而是 AI4J 如何把可执行能力组织成一个正式子系统。

从源码看,Tools 至少包含 4 层:

  • 工具声明
  • 请求级白名单暴露
  • provider 侧 schema 投影
  • 本地执行与结果回流

如果只讲第一层注解,就会把真正的执行链讲薄。

1. Tools 在基座里的真实定位

Tools 属于 Core SDK,不是 AgentCoding Agent 才有的附属能力。

原因很直接:

  • ChatCompletion 请求可以挂工具
  • ResponseRequest 请求也可以挂工具
  • 工具 schema 组装和本地执行桥都在 ai4j/ 模块里

也就是说,即使你完全不用上层 runtime,只要想让模型调用 JVM 内能力,这一章就已经和你相关。

2. 这套子系统到底解决什么问题

它回答的是 4 个问题:

  1. 某个能力如何被声明成模型可调用对象
  2. 本次请求到底暴露哪些工具
  3. 这些工具如何被统一转成 provider 可识别 schema
  4. 模型返回 tool call 之后,调用如何落回本地执行链

所以 Tools 不是“注解语法”,而是从声明到执行的完整桥。

3. 当前 AI4J 里有哪些工具来源

ToolUtil 的实现看,最后进入模型视野的“工具面”并不只有一类。

3.1 内建 coding tools

BuiltInTools / BuiltInToolExecutor 提供,当前共 8 个(见 BuiltInTools.allCodingToolNames()):

  • bash —— 执行 shell 命令,并管理后台进程(start/status/logs/write/stop/list
  • read_file —— 读取工作区或已批准只读 skill 根下的文本文件
  • write_file —— 创建、覆盖或追加文本文件
  • apply_patch —— 对工作区文件应用结构化补丁
  • glob —— 按 glob 模式(如 **/*.java)快速匹配文件路径
  • grep —— 按正则搜索文件内容(ripgrep 风格输出)
  • edit —— 在文件内做精确字符串替换
  • update_agents_md —— 读写项目 AGENTS.md 记忆文件

这些工具已经有固定 schema 和固定执行器,不经过普通业务函数反射调用。其中 bash/read_file/glob/grep 构成只读集合(BuiltInTools.readOnlyCodingToolNames()),便于宿主按“只读 / 可写”分级暴露。

3.2 注解式 Function 工具

由:

  • @FunctionCall
  • @FunctionRequest
  • @FunctionParameter

声明,再由 ToolUtil 扫描、缓存和调用。

3.3 本地 MCP 工具投影

本地 @McpService / @McpTool 也可以被 ToolUtil.scanMcpTools() 扫描,并转换成普通 Tool.Function 视图。

注意这里讲的是“最终投影成 tool schema”,不是说 MCP 概念上属于 Tools 子章节。MCP 的生命周期、transport、网关治理仍然属于独立协议层。

3.4 Gateway 管理的远程 MCP 工具

McpGateway 已初始化时,ToolUtil 还能把 gateway 里的远程服务工具并入当前请求的工具集。

这就是为什么模型看到的“一个工具列表”,在实现层其实可能来自多条不同的能力来源。

3.5 第三方扩展插件(SPI)

上述四类都是基座内置来源。若要让第三方 jar 也注入工具(或命令、Skill、Prompt、Guardrail),走的是扩展 SPI——ServiceLoader 发现 + ExtensionRegistrydiscover/enable/exposeTool 三段式门禁,默认不自动把插件工具暴露给模型。这条注入路径与本章的注解/白名单机制互补,详见 插件包

4. 一条最重要的主线

如果只记一条链,可以先记这个:

tool declaration
-> request-scoped whitelist
-> provider tool schema
-> model emits tool call
-> ToolUtil routes invocation
-> result returns as string payload

这条链里最重要的分界点有两个:

  • provider 之前:是工具暴露问题
  • provider 之后:是工具执行问题

很多文档把这两件事写成一件事,读起来就会很混。

5. ToolUtil 是整个子系统的中心

如果你只看一个文件,先看:

  • ai4j/src/main/java/io/github/lnyocly/ai4j/tool/ToolUtil.java

它至少负责 5 件事:

  1. 扫描注解工具
  2. 扫描本地 MCP 工具
  3. 根据 functions / mcpServices 生成本次工具白名单
  4. 统一执行入口 invoke(...)
  5. 在 built-in、Function、本地 MCP、远程 MCP 之间做调用路由

因此 ToolUtil 不是一个“小工具类”,而是当前整个工具子系统的调度中心。

6. ToolsFunction Calling 的关系

Function Calling 是这章的主线,但不是全部。

更准确地说:

  • Function Calling 讲的是模型与可执行能力之间的桥
  • Tools 讲的是 AI4J 如何把不同来源的能力统一组织到这条桥上

所以 Function Calling 是这章的核心机制,Tools 是更大的能力面。

7. ToolsSkill 的边界

这点必须保持非常清楚。

  • Tool 负责执行
  • Skill 负责说明

模型读了一个 Skill,不代表它获得了执行权限;模型看到了一个 Tool,也不代表它理解了最佳工作方法。

这两者经常配合使用,但职责不同。

8. ToolsMCP 的边界

这点也不能写混。

  • Tools 关注最终模型可调用的执行面
  • MCP 关注外部能力如何通过协议接入宿主

到了请求发送前,MCP 工具确实会被转换成 Tool.Function 风格 schema;但那只是投影结果,不是归属关系。

9. 当前子系统的真实限制

从源码看,有几个限制应该提前说清楚。

9.1 反射扫描是 classpath 级的

ToolUtil 基于 Reflections 扫描 @FunctionCall@McpService。这适合中小规模工具集,但不是无限扩展的注册中心。

9.2 本地工具返回值最终走字符串

无论是 built-in、Function 还是 gateway 调用,ToolUtil.invoke(...) 最终返回的都是 String。上层 runtime 看到的是文本化结果,而不是强类型 Java 对象。

9.3 执行治理不在这一层闭环

Core SDK 负责:

  • 工具组织
  • 工具暴露
  • 工具桥接

它不直接负责:

  • 人机审批
  • 多步重试策略
  • 长任务 checkpoint
  • 宿主级权限策略

这些必须由更上层 runtime 接管。

10. 这章最适合哪些读者

只想暴露本地 JVM 函数

重点看:

想理解模型返回 tool call 后怎么执行

重点看:

关心暴露边界和宿主安全

重点看:

11. 推荐阅读顺序

  1. 函数调用
  2. 注解式工具
  3. 工具执行模型
  4. 工具白名单与安全
  5. Skill、Tool 与 MCP 对比

12. 这页最该记住的结论

AI4J 的 Tools 不是几个注解,而是一个把多种能力来源统一成模型可调用执行面的基座子系统。

它真正解决的是:

  • 什么能力可被声明
  • 本次请求暴露什么
  • 调用怎么回到宿主

而不是上层 runtime 的所有治理问题。