当前位置: 首页 > news >正文

LangChain4J-(7)-Function Calling

一、Function Calling 是什么?

Function Calling(函数调用) 是大语言模型(LLM)与外部工具、系统或数据库进行交互的核心能力。简单来说,它让原本只能进行文本生成的 AI,能够像程序员一样 “调用函数”—— 发送指令给外部工具(如计算器、搜索引擎、API 接口等),获取工具返回的结果后,再结合自身逻辑生成最终回答。


它解决了传统大语言模型的两大核心局限:

  1. 知识滞后:模型训练数据有截止日期,无法获取实时信息(如最新天气、股票价格)。

  2. 能力边界有限:无法直接完成复杂计算、访问私有数据、控制硬件设备等任务。

二、Function Calling 能干什么?(核心应用场景)

Function Calling 的价值在于扩展 AI 的 “行动力”,从 “只能说” 升级为 “能做、能交互、能解决实际问题”。以下是典型应用场景:

1. 实时信息获取与更新

模型通过调用第三方 API 或搜索引擎,获取训练数据之外的实时 / 动态信息,解决 “知识过期” 问题。

  • 示例 1:查询天气
    用户问 “北京明天的天气适合户外跑步吗?”,模型会调用天气 API(如高德天气、OpenWeatherMap),获取北京明日气温、降水概率等数据,再判断是否适合跑步并生成回答。

  • 示例 2:获取实时财经数据
    用户问 “贵州茅台今天的收盘价是多少?”,模型调用股票行情 API(如东方财富、同花顺),返回最新价格并解读。

2. 复杂计算与逻辑处理

大语言模型本身不擅长高精度数学计算(如微积分、复杂统计),但可通过调用计算工具或代码执行函数完成。

  • 示例 1:数学 / 物理计算
    用户问 “一个质量为 2kg 的物体从 10m 高处自由下落,落地时的动能是多少?”,模型调用计算器函数,代入公式E=mgh(g 取 9.8N/kg),计算得 196J 后返回结果。

  • 示例 2:数据统计与分析
    用户上传一份 Excel 表格,要求 “计算表格中‘销售额’列的平均值和最大值”,模型调用读取 Excel 的函数 + 统计函数,处理数据后生成分析结论。

3. 访问私有 / 专业数据库

企业或个人可通过 Function Calling 让模型访问内部私有数据(如客户档案、订单系统、医疗病历),既保证数据安全,又能利用 AI 的理解能力解读数据。

  • 示例 1:企业客服场景
    客户问 “我的订单(编号 12345)什么时候发货?”,模型调用企业订单管理系统的 API,查询该订单的物流状态,回复 “您的订单已出库,预计明日送达”。

  • 示例 2:医疗辅助场景
    医生输入患者 ID,模型调用医院电子病历系统函数,获取患者病史、检查报告,再结合医学知识生成诊断建议(需严格合规)。

4. 控制外部工具与设备

通过调用硬件或软件的控制接口,AI 可直接 “操作” 物理世界或数字工具,实现 “指令执行”。

  • 示例 1:智能家居控制
    用户说 “把客厅灯调成暖光,亮度 50%”,模型调用智能家居 API(如米家、苹果 HomeKit),发送控制指令,灯执行操作后返回 “已为您调整客厅灯光”。

  • 示例 2:办公工具联动
    用户说 “用我邮箱里的‘Q3 数据’文件生成一份柱状图,发送给张三”,模型依次调用:① 邮件 API 读取文件 ② 图表工具 API 生成图片 ③ 邮件 API 发送给张三,最终反馈执行结果。

5. 多工具协同解决复杂问题

面对需要多步骤的任务,模型会自动规划 “调用顺序”,串联多个工具完成目标,相当于一个 “AI 指挥官”。

  • 示例:“帮我规划周末去青岛的行程,预算 1500 元 / 人,包含海鲜餐,避开热门景点”
    模型的执行逻辑:

    1. 调用机票 / 高铁 API:查询出发地到青岛的往返交通费用(过滤超预算选项);

    2. 调用酒店 API:根据交通站点和预算筛选住宿;

    3. 调用本地生活 API:搜索青岛非热门景点(如小麦岛公园、信号山公园)和评价高的海鲜小店;

    4. 调用计算器函数:汇总交通 + 住宿 + 餐饮费用,确保不超预算;

    5. 整合所有信息,生成结构化行程单。

三、Function Calling 的工作流程(核心逻辑)

它的运行遵循一套标准化的 “思考 - 调用 - 整合” 流程,无需人工干预:

  1. 用户提问解析:模型理解用户需求,判断 “是否需要调用工具”(比如 “2+2 等于几” 可直接回答,“明天天气” 需调用工具)。

  2. 函数选择与参数生成:若需调用工具,模型自动选择合适的函数(如 “get_weather”),并提取参数(如城市 “北京”、日期 “明日”)。

  3. 发送调用请求:将函数名和参数以结构化格式(如 JSON)发送给外部工具 / 系统。

  4. 接收工具返回结果:工具执行后,将数据(如 “气温 25℃,无降水”)返回给模型。

  5. 生成最终回答:模型结合自身语言能力,将工具返回的原始数据转化为自然、易懂的回答。

总结

Function Calling 是大语言模型从 “文本模型” 进化为 “实用工具” 的关键技术。它本身不直接产生数据或执行操作,而是通过 “连接外部能力”,让 AI 具备了实时性、准确性、行动力和个性化,广泛应用于智能助手、企业服务、智能家居、医疗辅助等领域,是实现 “通用人工智能(AGI)” 的重要基石。

四、撸代码

1、ChatModel and ToolSpecification

在 LangChain4j 中,ToolSpecification是用于定义工具规格的一个重要概念,它允许开发者明确地告知大语言模型(LLM)可以使用的工具及其相关信息。以下是关于ToolSpecification的详细介绍:

  • 作用:LLM 本身不能直接调用工具,而是在响应中表达调用特定工具的意图。ToolSpecification就是用来提供工具的详细信息,以便 LLM 能够根据这些信息决定是否调用该工具以及如何正确使用它。

  • 包含信息

    • name:工具的名称,是一个明确的功能标识,用于唯一标识该工具,例如"getWeather"

    • description:工具的描述,用来说明工具的功能及适用场景,比如"返回指定城市的天气预报",清晰的描述有助于 LLM 判断是否需要调用该工具。

    • parameters:工具的参数及其描述,通常使用JsonObjectSchema来定义。例如,可以通过addStringPropertyaddEnumProperty等方法来添加不同类型的参数,并对每个参数的语义进行定义,如addStringProperty("city", "应返回天气预报的城市")

Step1

构建一个大模型调用的功能接口FunctionAssistant接口

package com.xxx.demo.service;public interface FunctionAssistant {//客户指令:出差住宿发票开票,// 开票信息:    公司名称xxx// 税号序列:    xx// 开票金额:    xxx.00元String chat(String message);
}

Step2

构建一个LLMConfig--

package com.xxx.demo.config;import com.bbchat.demo.service.FunctionAssistant;
import dev.langchain4j.agent.tool.ToolSpecification;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.request.json.JsonObjectSchema;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.tool.ToolExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.Map;@Configuration
public class LLMConfig {@Beanpublic ChatModel chatModel(){return OpenAiChatModel.builder().apiKey(System.getenv("aliqwen-apikey")).modelName("qwen-plus").baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1").build();}/*** @Description  Low Level Tool API* https://docs.langchain4j.dev/tutorials/tools#low-level-tool-api* @Auther: zzyybs@126.com*/@Beanpublic FunctionAssistant functionAssistant(ChatModel chatModel){// 工具说明 ToolSpecificationToolSpecification toolSpecification = ToolSpecification.builder().name("开具发票助手").description("根据用户提交的开票信息,开具发票").parameters(JsonObjectSchema.builder().addStringProperty("companyName", "公司名称").addStringProperty("dutyNumber", "税号序列").addStringProperty("amount", "开票金额,保留两位有效数字").build()).build();// 业务逻辑 ToolExecutorToolExecutor toolExecutor = (toolExecutionRequest, memoryId) -> {System.out.println(toolExecutionRequest.id());System.out.println(toolExecutionRequest.name());String arguments1 = toolExecutionRequest.arguments();System.out.println("arguments1****》 " + arguments1);return "开具成功";};return AiServices.builder(FunctionAssistant.class).chatModel(chatModel).tools(Map.of(toolSpecification, toolExecutor)) // Tools (Function Calling).build();}
}

Step3

写一个Controller用来测试

package com.xxx.demo.controller;import cn.hutool.core.date.DateUtil;
import com.bbchat.demo.service.FunctionAssistant;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@Slf4j
public class ChatFunctionCallingController {@Resourceprivate FunctionAssistant functionAssistant;//  http://localhost:9011/chatfunction/test1@GetMapping(value = "/chatfunction/test1")public String test1(){String chat = functionAssistant.chat("开张发票,公司:BK公司 税号:1234567890 金额:99999.00");System.out.println(chat);return "success : "+ DateUtil.now() + "\t"+chat;}
}

Step4

查看下结果

2、AI Service and @Tool

在 LangChain4j 中,@Tool 注解是一个核心功能,用于将普通 Java 方法标记为可被大语言模型(LLM)调用的工具。通过这个注解,开发者可以轻松地将自定义功能暴露给 LLM,使模型能够根据需要调用这些工具来完成特定任务(如数据查询、计算、API 调用等)。

@Tool 注解的主要作用

  • 标记方法为可供 LLM 调用的工具

  • 提供工具的描述信息,帮助 LLM 理解工具的功能和用途

  • 自动生成工具的元数据(ToolSpecification),无需手动构建

1). 标记工具方法

使用 @Tool 注解标记方法,并提供描述信息:

import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.agent.tool.P; // 用于参数描述public class CalculatorTools {// 简单的加法工具@Tool("计算两个数字的和")public int add(@P("第一个数字") int a, @P("第二个数字") int b) {return a + b;}// 天气查询工具(示例)@Tool("获取指定城市的当前温度")public String getCurrentTemperature(@P("城市名称,例如:北京") String city,@P("温度单位,可选:摄氏度、华氏度,默认:摄氏度") String unit) {// 实际实现可能调用天气APIreturn city + "当前温度:25" + (unit.equals("华氏度") ? "°F" : "°C");}
}

2). 自动生成工具规格

通过 ToolSpecifications 工具类,可以从标记了 @Tool 的类或对象中自动提取工具规格:

import dev.langchain4j.agent.tool.ToolSpecifications;
import java.util.List;public class ToolExample {public static void main(String[] args) {// 从类中提取工具规格List<ToolSpecification> toolsFromClass = ToolSpecifications.toolSpecificationsFrom(CalculatorTools.class);// 从对象中提取工具规格CalculatorTools calculator = new CalculatorTools();List<ToolSpecification> toolsFromObject = ToolSpecifications.toolSpecificationsFrom(calculator);}
}

3). 在 Agent 中使用工具

将提取的工具规格交给 Agent,使 LLM 能够根据需要调用这些工具:

import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.agent.Agent;
import java.time.Duration;public class AgentWithToolsExample {public static void main(String[] args) {// 创建工具实例CalculatorTools calculator = new CalculatorTools();// 创建Agent,绑定工具Agent agent = Agent.builder().chatLanguageModel(OpenAiChatModel.withApiKey("your-api-key")).tools(calculator) // 直接传入工具对象.chatMemory(MessageWindowChatMemory.withMaxMessages(10)).build();// 向Agent提问,触发工具调用String response = agent.execute("3加5等于多少?");System.out.println(response); // 输出:3加5等于8}
}

4).关键注解说明

  • @Tool:标记方法为工具,value 属性用于描述工具功能(给 LLM 看的说明)

  • @P:用于描述方法参数(给 LLM 看的参数说明),帮助 LLM 正确传递参数

5).注意事项

  1. 工具方法的参数类型应尽量简单(如基本类型、String、枚举等),便于 LLM 处理

  2. 工具描述应清晰具体,明确说明功能和适用场景,这直接影响 LLM 是否会正确调用工具

  3. 工具方法可以有返回值(会被传递给 LLM),也可以是 void(仅执行操作)

  4. 如果工具方法可能抛出异常,建议捕获并返回友好的错误信息,避免 Agent 中断

通过 @Tool 注解,LangChain4j 实现了工具与 LLM 的无缝集成,大大简化了智能代理(Agent)的开发流程,使开发者可以专注于工具功能的实现而非复杂的集成逻辑。

Step1

使用和风天气接口做三方接口调用,新建一个WeatherService类。

package com.xxx.demo.service;import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;@Service
public class WeatherService {//和风天气开发服务 https://dev.qweather.com/// 替换成你自己的和风天气API密钥private static final String API_KEY = "#############################################";// 调用的url地址和指定的城市,本案例以北京为例private static final String BASE_URL = "https://devapi.qweather.com/v7/weather/now?location=%s&key=%s";public JsonNode getWeatherV2(String city) throws Exception{//1 传入调用地址url和apikeyString url = String.format(BASE_URL, city, API_KEY);//2 使用默认配置创建HttpClient实例var httpClient = HttpClients.createDefault();//3 创建请求工厂并将其设置给RestTemplate,开启微服务调用和风天气开发服务HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);//4 RestTemplatew微服务调用String response = new RestTemplate(factory).getForObject(url, String.class);//5 解析JSON响应获得第3方和风天气返回的天气预报信息JsonNode jsonNode = new ObjectMapper().readTree(response);//6 想知道具体信息和结果请查看https://dev.qweather.com/docs/api/weather/weather-now/#responsereturn jsonNode;}
}

Step2

重写一个InvoiceHandle

package com.xxx.demo.service;import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import lombok.extern.slf4j.Slf4j;@Slf4j
public class InvoiceHandler {@Tool("根据用户提交的开票信息进行开票")public String handle(@P("公司名称") String companyName,@P("税号") String dutyNumber,@P("金额保留两位有效数字") String amount) throws Exception{log.info("companyName =>>>> {} dutyNumber =>>>> {} amount =>>>> {}", companyName, dutyNumber, amount);//----------------------------------// 这块写自己的业务逻辑,调用redis/rabbitmq/kafka/mybatis/顺丰单据/医疗化验报告/支付接口等第3方//----------------------------------System.out.println(new WeatherService().getWeatherV2("101010100"));return "开票成功";}
}

Step3

改写下之前的controlle-

package com.xx.demo.controller;import cn.hutool.core.date.DateUtil;
import com.bbchat.demo.service.FunctionAssistant;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;@Controller
public class ChatFunctionCallingController {@Resourceprivate FunctionAssistant functionAssistant;//  http://localhost:9011/chatfunction/test2@GetMapping(value = "/chatfunction/test2")public String test2(){String chat = functionAssistant.chat("开张发票,公司:BK公司 税号:1234567890 金额:99999.00,顺便帮我查一下今天的天气");System.out.println(chat);return "success : "+ DateUtil.now() + "\t"+chat;}
}

Step4

顺便测试下工具调用错误的情况下的效果

http://www.dtcms.com/a/398374.html

相关文章:

  • C程序设计-01程序设计和C语言
  • 为何上不了建设银行网站网络营销工程师前景
  • 设计模式的几个准则
  • python+nodejs+springboot在线车辆租赁信息管理信息可视化系统
  • 计算机毕业设计 基于Python的音乐推荐系统 Python 大数据毕业设计 Hadoop毕业设计选题【附源码+文档报告+安装调试】
  • 《人机分工重塑开发:遗留系统重构的AI实践指南》
  • 从0死磕全栈第十天:nest.js集成prisma完成CRUD
  • 网站开发做什么科目网页设计与网站建设连接数据库
  • 如何看网站是html几代做的加拿大pc网站搭建
  • C#的MVVM架构中的几种数据绑定方式
  • Jmeter接口测试:jmeter组件元件介绍,利用取样器中http发送请求
  • Apache Tomcat 部署与配置
  • 网站建设详细合同范本西部数码网站管理助手破解版
  • 权限提升专项训练靶场:hacksudo: L.P.E.
  • 工作笔记----lwip的数据管理结构pbuf源码解析
  • 生产环境实战:Spring Cloud Sleuth与Zipkin分布式链路追踪实践
  • 学习React-15-useImperativeHandle
  • 响应式网站案列小学生做电子小报的网站
  • 【AskAI系列课程】:P4.将AI助手集成到Astro网站前端
  • 自注意力机制(Self-Attention)简介
  • App 代上架全流程解析 iOS 应用代上架服务、苹果应用发布步骤、ipa 文件上传与 App Store 审核经验
  • 学习日报 20250921|MQ (Kafka)面试深度复盘
  • 趣味学Solana(启航)
  • 期权末日论效应怎么来的?
  • iOS 混淆与反调试反 Hook 实战,运行时防护、注入检测与安全加固流程
  • 建设工程管理网站邹平建设网站
  • wordpress英文下主题怎么换苏州seo专家教优化网站结构
  • 《灼灼韶华》还原民国上海滩,虎鲸文娱虚拟拍摄让创作突破时空束缚
  • Redo Log 与 Crash Recovery:MySQL 事务持久化的核心技术
  • 金乡网站建设公司云南企业网站