- 人工智能
- 大模型
- AI 应用
- 后端
- AI Agent
- RAG
- 流程编排
- 微服务
【免费下载链接】fit-framework
FIT: 企业级AI开发框架,提供多语言函数引擎(FIT)、流式编排引擎(WaterFlow)及Java生态的LangChain替代方案(FEL)。原生/Spring双模运行,支持插件热插拔与智能聚散部署,无缝统一大模型与业务系统。
导读
本文聚焦 FIT 框架中 FEL(Framework for Engineering LLM)的**工具调用(Tool Calling)**能力。大模型受限于训练知识截止时间,无法获取实时数据、执行精确计算或访问外部系统,而 FEL 通过两套互补机制解决这一短板:一是基于Pattern委托单元的代理模式,让开发者以最小代码把任意业务逻辑(如天气查询、计算、数据库访问)接入 AI 流程;二是基于 FIT 插件体系的工具插件化方案,通过@ToolMethod注解、编译期tools.json清单生成和运行期插件热插拔,实现工具的自动发现、大模型自主选择与统一执行。读完本文,你将掌握 FEL 中两种工具接入方式的工作原理、核心源码结构以及可运行的完整示例。
为什么大模型需要工具调用
大模型在语言生成与理解上能力极强,但在以下场景中必须借助外部工具才能给出可靠答案:
- 获取最新信息:模型训练数据存在知识截止时间,无法提供实时数据;通过调用浏览器或 API,可查询最新事件与动态数据。
- 精确计算:模型虽可做简单数学推理,却不擅长精确或复杂的数值运算,调用计算工具更可靠。
- 数据库/知识库查询:通过工具连接外部数据库、知识库或搜索引擎,查询结构化数据或更准确的答案。
- 多步逻辑推理:部分任务涉及多步骤推理、算法应用或代码执行,可调用专门程序完成。
FEL-Flow 提供两种接入方式(见 fel-flow 模块 README 中的核心特点说明):
- 委托机制
Pattern:在 AI 流程中插入一个委托节点,把输入数据交给外部函数处理后回流到流程中; - 预定义工具调用的流程编排:将工具封装为 FIT 插件,由大模型在运行时自主选择工具,通过流程编排轻松调用多个工具完成复杂任务(如多智能体互通)。
委托机制:用 Pattern 把任意逻辑接入 AI 流程
委托机制本质上是一种代理模式:大模型(或流程本身)根据任务内容与输入信息,自动识别需要调用的工具并将任务分配出去。其工作流程分三步:
- 输入解析和识别任务:模型分析用户输入上下文,判断是否需要外部工具协助(如检测到时间敏感信息、复杂计算等)。
- 工具选择和调用:模型通过预定义策略或规则选择合适的工具。常见工具包括 API 工具(浏览器、数据库查询接口、天气预报 API)、计算工具(计算器或数据处理工具)以及嵌入模型(文本检索或搜索工具)。
- 执行任务和结果返回:工具完成任务并返回结果,模型将结果整合进原始任务并反馈给用户。
Pattern 与 SimplePattern:委托单元的源码剖析
Pattern是 FEL 定义的委托单元接口,<I>表示输入数据类型,<O>表示输出数据类型。其定义位于 Pattern.java:
public interface Pattern<I, O> { O invoke(I input); }SimplePattern是该接口的简单实现,位于 SimplePattern.java。它通过运行时传入的Function<I, O>(即文档中的func)实现动态行为委托——不直接定义具体逻辑,而是接受外部传入的函数逻辑,因此非常灵活:
public class SimplePattern<I, O> implements Pattern<I, O> { private final Function<I, O> func; public SimplePattern(Function<I, O> func) { this.func = Validation.notNull(func, "The action function cannot be null."); } @Override public O invoke(I input) { Validation.notNull(input, "The input data cannot be null."); return this.func.apply(input); } }从源码可见两个关键约束:构造函数与invoke方法均通过Validation.notNull做了空值校验,分别保证委托函数和输入数据不为空。这是 FEL 对Pattern输入输出契约的强制要求。
delegate 操作:把 Pattern 编排进 AI 流程
SimplePattern要真正参与 AI 应用,需要通过 FEL-Flow 的delegate操作挂载到流程上。AiStart.java 中的实现说明了委托节点的语义:
/** * 将数据委托给 {@link Pattern}{@code <}{@link O}{@code , }{@link R}{@code >} * 处理,然后自身放弃处理数据。处理后的数据会发送回该节点,作为该节点的处理结果。 */ public <R> AiState<R, D, O, RF, F> delegate(Pattern<O, R> pattern) { Validation.notNull(pattern, "Pattern operator cannot be null."); return this.delegate(new SimpleFlowPattern<>(pattern)); }即delegate节点接收上游数据,交给Pattern处理后把结果发送回流程继续流转。除了接收Pattern,delegate还有多个重载:委托给Operators.ProcessMap(在独立线程池执行,适合 IO 密集型任务)、委托给子流程AiProcessFlow,甚至可以从子流程的指定节点开始处理(delegate(AiProcessFlow, nodeId))。这为"预定义工具调用流程"提供了基础。
对应的单元测试见 PatternTest.java,其中shouldOkWhenDelegateSimplePattern验证了"prompt → delegate(pattern) → close → converse → offer"的完整链路:
SimplePattern<Prompt, String> pattern = new SimplePattern<>(prompt -> { String inputContextValue = AiFlowSession.get() .map(target -> ObjectUtils.<String>cast(target.getState(key))) .orElse(StringUtils.EMPTY); return prompt.text() + inputContextValue; }); ConverseLatch<String> offer = AiFlows.<Tip>create() .prompt(Prompts.human("{{0}}")) .delegate(pattern) .close() .converse(session) .offer(Tip.fromArray("human msg.")); String result = offer.await(); assertThat(result).isEqualTo("human msg." + value);测试还展示了Pattern可以通过AiFlowSession访问流程会话上下文(session.getState(key)),说明委托单元并非孤立的黑盒,而是可以与Conversation绑定数据交互。
委托示例:天气查询
以下代码创建一个简单的委托Pattern:用户输入位置信息,将调用委托给WeatherTool获取天气,输出该位置的天气信息:
WeatherTool weatherTool = new WeatherTool(); SimplePattern<Prompt, String> pattern = new SimplePattern<>(prompt -> { String userInput = prompt.text(); String location = getLocation(userInput); if (location != null && !location.isEmpty()) { return weatherTool.getWeather(location); } else { return "Sorry, I couldn't identify the location."; } }); // 模型会话流程定义 AiFlows.<Tip>create() .prompt(Prompts.human("{{0}}")) // 以用户输入作为prompt .delegate(pattern) // 使用定义好的pattern进行委托调用 .close() .converse(session) // 开始对话 .offer(Tip.fromArray("Can you tell me the weather in Paris?")) // 模拟输入 .await(); // 等待结果输出流程节点依次为:prompt(将用户输入包装为提示词)→delegate(pattern)(调用天气工具)→close(终止)→converse(建立会话)→offer(注入输入)→await(阻塞等待结果)。
工具插件:基于 FIT 插件化的工具发现与执行
当工具数量增多、需要被大模型自主调度时,委托机制就显得不够体系化。FEL-Flow 通过FIT 插件将工具封装为插件,利用 FIT 插件化的优势支持工具的热插拔,并监听所有插件的加载和卸载。整个链路分为"插件扫描"与"插件选择和执行"两个阶段。
阶段一:插件扫描(编译期生成 tools.json)
FEL-Flow 提供一个 Maven 插件(tool-maven-plugin,源码位于 tool-maven-plugin 模块),在编译期间扫描所有 Tool class 文件。其工作方式为:
- 开发者在工具方法上使用注解
@ToolMethod描述工具,包括命名空间、工具名称、描述、方法参数等信息(注解定义见 ToolMethod.java)。 - 编译期由
GroupPluginCompiler(见 GroupPluginCompiler.java)驱动ByteBuddyGroupParser解析 class 文件中@ToolMethod注解,生成ToolEntity列表,最终输出工具清单文件tools.json(默认文件名常量见 ToolSchema.java 中的TOOL_MANIFEST = "tools.json")。
@ToolMethod注解的核心属性如下:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
namespace | String | "Common" | 工具的命名空间,工具名在命名空间内需唯一 |
name | String | 无(必填) | 工具方法名 |
description | String | 空字符串 | 工具方法描述,供大模型理解工具用途 |
extraParams | String[] | {} | 工具的额外参数名 |
returnConverter | String | 空字符串 | 输出转换器工具方法 |
extensions | Attribute[] | {} | 工具方法的扩展属性(键值对) |
其中Attribute注解(见 Attribute.java)由key()和value()组成。而方法参数的语义描述(参数名、描述、是否必填、默认值、样例值)由 FIT 框架的@Property注解提供(见 Property.java)。
tools.json的反序列化结构由 ToolJsonEntity.java 定义:包含version、definitionGroups(定义组)和toolGroups(工具组);每个工具由 ToolEntity.java 表示,携带namespace、schema(含名称、描述、参数、顺序、返回值、参数扩展等)、runnables、extensions和tags。
阶段二:插件选择和执行(运行期)
应用启动后:
- 解析清单入仓库:
DefaultToolDiscoverer(见 DefaultToolDiscoverer.java)实现了 FIT 的PluginStartedObserver与PluginStoppingObserver,在插件加载/卸载时扫描插件资源中的tools.json,将ToolEntity转成ToolInfoEntity注册进ToolRepository(加载时调用addTool,卸载时调用deleteTool)。它同时受tool.max-num配置约束,限制单插件可注册的工具数量。 - 工具仓库:默认实现
SimpleToolRepository(见 SimpleToolRepository.java)以ConcurrentHashMap缓存工具,用ToolInfo.identify(namespace, name)生成的namespace:name唯一标识(见 ToolInfo.java),并在工具增删时通知ToolChangedObserver观察者。 - 模型选择工具:应用把用户请求和可用工具列表(
toolRepository.listTool(namespace))通过ChatOption.tools(...)传给大模型(tools字段定义见 ChatOption.java),模型返回要调用的工具。 - 执行工具:
DefaultToolExecutor(见 DefaultToolExecutor.java)根据返回的namespace:name从仓库取出工具,通过ToolFactoryRepository匹配runnables类型对应的工厂创建Tool实例,再调用executeWithJson/executeWithJsonObject执行并返回结果。工具工厂按类型注册(DefaultToolFactoryRepository见 DefaultToolFactoryRepository.java),例如FitToolFactory负责把@Genericable定义的 FIT 函数封装为工具(见 FitToolFactory.java)。
完整可运行示例:天气助手 Agent
仓库中的 06-agent 示例 完整演示了"插件化工具 + Agent 自主调度"的用法。示例定义一个天气助手,将两个 FIT 函数get_current_temperature和get_rain_probability作为助手可调用的工具。
步骤 1:引入依赖。除fit-starter、fel-core、fel-flow、fel-model-openai-plugin外,还需引入工具相关的插件与服务依赖:
<!-- 工具自动发现服务 --> <dependency> <groupId>org.fitframework</groupId> <artifactId>fel-tool-discoverer</artifactId> <version>${fel.version}</version> </dependency> <!-- 工具执行服务 --> <dependency> <groupId>org.fitframework</groupId> <artifactId>fel-tool-executor</artifactId> <version>${fel.version}</version> </dependency> <!-- Fit 工具工厂 --> <dependency> <groupId>org.fitframework</groupId> <artifactId>fel-tool-factory-fit</artifactId> <version>${fel.version}</version> </dependency> <!-- 工具工厂存储 --> <dependency> <groupId>org.fitframework</groupId> <artifactId>fel-tool-factory-repository</artifactId> <version>${fel.version}</version> </dependency> <!-- 工具存储 --> <dependency> <groupId>org.fitframework</groupId> <artifactId>fel-tool-repository-simple</artifactId> <version>${fel.version}</version> </dependency>同时在<plugins>中加入工具编译插件tool-maven-plugin,绑定build-toolgoal 于 compile 阶段(对应 BuildGroupPluginMojo.java 中的@Mojo(name = "build-tool", defaultPhase = LifecyclePhase.COMPILE))。
步骤 2:配置模型参数(application.yml):
fel: openai: api-base: '${api-base}' api-key: '${your-api-key}' example: model: '${model-name}'步骤 3:定义工具。接口使用@Genericable声明可服务化的方法:
public interface WeatherService { @Genericable("modelengine.example.weather.temperature") String getCurrentTemperature(String location, String unit); @Genericable("modelengine.example.weather.rain") String getRainProbability(String location); }实现类通过@ToolMethod提供工具元数据,通过@Property描述参数:
@Component public class WeatherServiceImpl implements WeatherService { @Override @Fitable("default") @ToolMethod(namespace = "example", name = "get_current_temperature", description = "获取指定城市的当前温度") public String getCurrentTemperature(@Property(description = "城市名称", required = true) String location, @Property(description = "使用的温度单位,可选:Celsius,Fahrenheit", defaultValue = "Celsius") String unit) { return "26"; } @Override @Fitable("default") @ToolMethod(namespace = "example", name = "get_rain_probability", description = "获取指定城市下雨的概率") public String getRainProbability(@Property(description = "城市名称", required = true) String location) { return "0.06"; } }步骤 4:使用默认 Agent 调度。AgentExampleController.java 构建了DefaultAgent并把它作为Pattern通过delegate挂进 AI 流程:
DefaultAgent agent = new DefaultAgent(new ChatFlowModel(chatModel, this.chatOption), "example", toolExecuteService); this.agentFlow = AiFlows.<String>create() .map(query -> Tip.fromArray(query)) .prompt(Prompts.human("{{0}}")) .delegate(agent) .close();请求处理时,把toolRepository.listTool("example")得到的工具列表绑定进会话,模型即可在对话中自主选择工具:
@GetMapping("/chat") public ChatMessage chat(@RequestParam("query") String query) { List<ToolInfo> toolInfos = asParent(toolRepository.listTool("example")); return this.agentFlow.converse() .bind(ChatOption.custom(this.chatOption).tools(toolInfos).build()) .offer(query) .await(); }DefaultAgent的内部实现(见 DefaultAgent.java)与基类 AbstractAgent.java 揭示了 Agent 的循环调度逻辑:模型输出若包含工具调用(ChatMessage::isToolCall),则把toolCalls交给doToolCall执行(DefaultAgent通过ToolExecuteService按命名空间执行工具并封装为ToolMessage),再把工具结果合并进记忆后跳回检查点CHECK_POINT重新调用模型,直到模型输出不含工具调用为止。这正是"大模型选择工具 → 应用执行工具 → 结果回填 → 再次调用模型"闭环的源码级体现。
验证:启动应用后访问http://localhost:8080/ai/example/chat?query=北京下雨的概率是多少,得到:
{ "content": "北京下雨的概率是6%。", "toolCalls": [] }可以看到大模型根据工具返回的原始概率数据("0.06")进行了自然语言回答。
总结
FEL 的工具调用能力覆盖了两类典型诉求:
- 轻量、确定性的工具接入:使用
SimplePattern把任意Function封装为委托单元,通过delegate操作嵌入 AI 流程,适合逻辑固定、无需模型决策的调用场景; - 动态、模型驱动的工具编排:通过
@ToolMethod注解 +tool-maven-plugin编译期扫描生成tools.json,运行期由DefaultToolDiscoverer随插件热插拔注册/注销工具,ToolRepository统一管理,DefaultAgent与ToolExecuteService完成"模型选工具—执行—回填"的循环调度,适合需要大模型自主决策、多工具协作乃至多智能体互通的复杂场景。
两种机制共享同一套Pattern抽象与 FEL-Flow 流程编排底座,开发者可根据业务对"确定性"与"智能性"的需求灵活选用。
- 人工智能
- 大模型
- AI 应用
- 后端
- AI Agent
- RAG
- 流程编排
- 微服务
【免费下载链接】fit-framework
FIT: 企业级AI开发框架,提供多语言函数引擎(FIT)、流式编排引擎(WaterFlow)及Java生态的LangChain替代方案(FEL)。原生/Spring双模运行,支持插件热插拔与智能聚散部署,无缝统一大模型与业务系统。
相关推荐
FIT 框架 FEL 指南:检索增强生成(RAG)模块全解析与 AiFlows 实战编排
FIT 框架 FEL 指南:检索增强生成(RAG)模块全解析与 AiFlows 实战编排 本篇技术指南以 FIT 框架下 FEL(Function Engine
人工智能大模型AI 应用后端AI AgentRAG流程编排微服务HookLib²内存优化技巧:4KB页面容纳39个钩子的实现
HookLib²内存优化技巧:4KB页面容纳39个钩子的实现 HookLib作为一款纯C语言编写的函数拦截库,同时支持用户态和内核态操作,其卓越的内存优化能力让
ET框架UI事件系统实战指南:从委托机制到高效交互的深度解析
ET框架UI事件系统实战指南:从委托机制到高效交互的深度解析 ET框架作为一款优秀的Unity3D客户端和C 服务器框架,其事件系统设计巧妙且功能强大,特别适合
游戏开发后端微服务云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考