摘要:工具调用指的是function Calling/Tool Calling。工具调用是指大语言模型(LLM)在处理用户请求时,能够主动调用外部工具(如 API、数据库查询、计算服务等)以扩展自身能力的机制。这一机制允许模型突破自身知识限制,通过与外部系统交互获取

工具调用指的是function Calling/Tool Calling。工具调用是指大语言模型(LLM)在处理用户请求时,能够主动调用外部工具(如 API、数据库查询、计算服务等)以扩展自身能力的机制。这一机制允许模型突破自身知识限制,通过与外部系统交互获取实时数据或执行复杂操作,从而更精准地回答问题或完成任务。
工具调用的核心在于模型与外部工具的解耦:模型仅负责生成工具调用请求(包括工具名称和参数),而实际执行由应用程序完成。例如,当用户询问 “上海明天的天气如何?” 时,模型会生成一个调用天气查询工具的请求,应用程序执行该工具获取数据后,再将结果返回给模型进行最终回答。
这样一来,大模型不仅仅可以回复它已知的内容,还可以调用工具完成各种它不知道的事情,比如:网页搜索、对外部 API的调用、访问外部数据、或执行特定的代码等。
为什么AI这么强大了,还需要我们手动去写工具的执行程序,而不让AI自己去完成工具可以做的事呢?
主要是因为AI做为预训练模型,只能很好的去回复它已知的数据,对于未知的数据它就会不做答复甚至会胡言乱语。AI的数据来源一般都是来自互联网公开的数据,并且数据的时效性也比较滞后(比如OpenAI的GPT-4o模型,知识截止日期到2023年),这就导致用户问它2025年的数据时,它就不知道了。
其次,对于企业私有数据,AI是完全不知道的,如果直接开问,AI大概率是无法正确回答的。比如我们直接问DeepSeek:我本月拜访了多少店铺?它完全不知道该如何处理了。如果我们有一个工具专门来统计销售的拜访店铺数,AI就能很容易就能给用户一个非常准确的答复。
角色定位核心作用示例是否主动发起对话是否调用外部工具System对话框架设定者初始化对话规则、上下文或AI行为(如身份、语言、格式要求)System: "你是一个中文翻译助手"否(仅首轮)否User需求提出者输入问题或指令,驱动对话进程(支持文本、图片等多模态输入)User: "将'Hello'翻译成中文"是否Assistant响应执行者根据System设定和User输入生成回答,可自主调用工具完成复杂任务Assistant: "翻译结果是'你好'"否(被动响应)是Tool外部能力扩展模块执行模型无法直接处理的任务(如计算、实时数据查询),通过API返回结果供整合Tool调用计算器: 3²+5=14否自身即为工具说明:
交互逻辑System→User→Assistant→Tool→Assistant→User 是典型流程(如用户提问→助手调用工具→返回结果)。System设定仅在对话开始时生效,后续对话由User和Assistant主导。工具调用场景Assistant根据需求自主决定是否调用Tool(如计算、搜索、绘图等),无需User显式要求。Tool返回结果后,Assistant需将结果整合为自然语言回复(如“计算结果为14”)。角色权限User和System角色由程序控制,Assistant和Tool由模型自主管理。其实,工具调用的工作原理比较简单,假如用户说:“北京今天天气如何?”,就需要经历下列流程:
用户提出问题:"北京今天天气如何?"程序将问题传递给大模型大模型分析问题,判断需要使用工具(请求天气API)来获取信息大模型输出工具名称和参数(天气获取工具,地址是北京)程序接收工具调用请求,执行调用天气API操作,获取天气信息程序将获取的天气结果传回给大模型大模型根据结果生成相应的话术程序将大模型响应的结果返回给用户
调用大模型的接口流程是这样的:
[{"type": "function","function": {"name": "get_weather","description": "Get weather of an location, the user shoud supply a location first","parameters": {"type": "object","properties": {"location": {"type": "string","description": "The city and state, e.g. San Francisco, CA",}},"required": ["location"]},}},]/*** 获取制定地区的天气*/@Slf4jpublic class WeatherTool {@Tool(name = "getWeather", description = "获取天气信息")public String getWeather(@ToolParam(description = "位置信息", required = true) String location) {log.info("location: {}", location);return "今天30度";}}/*** 发送邮件*/@Slf4jpublic class SendMailTool {@Tool(name = "sendMail", description = "给指定人发送邮件")public String getWeather(@ToolParam(description = "邮件接收人", required = true) String receiver,@ToolParam(description = "邮件内容", required = true) String content) {log.info("sendMail receiver={}, content={}", receiver, content);return "已将邮件成功发送给:" + receiver;}}/*** 获取当前时间*/@Slf4jpublic class TimeTool {@Tool(name = "getTime", description = "获取当前时间")public String getTime {String format = DateUtil.format(new Date, "yyyy-MM-dd HH:mm:ss");log.info("当前时间时: {}", format);return format;}}/*** 获取销售拜访数据*/@Slf4jpublic class SaleVisitInfo {@Tool(name = "getVisitInfo", description = "获取销售拜访数据")public String getVisitInfo(@ToolParam(description = "拜访开始时间,格式yyyy-MM-dd HH:mm:ss", required = false) String startTime,@ToolParam(description = "拜访结束时间,格式yyyy-MM-dd HH:mm:ss", required = false) String endTime) {log.info("startTime: {}, endTime: {}", startTime, endTime);Map context = toolContext.getContext;log.info("context: {}", context);return "100个店铺";}}void testNormal throws IOException {String reqBody = """{"messages": [{"content": "You are a helpful assistant","role": "system"},{"content": "今天北京天气怎么样,并将天气信息通过邮件发送给张三","role": "user"}],"model": "deepseek-chat","tools": [{"type": "function","function": {"name": "getWeather","description": "Get weather of an location, the user shoud supply a location first","parameters": {"type": "object","properties": {"location": {"type": "string","description": "The city and state, e.g. San Francisco, CA"}},"required": ["location"]}}},{"type": "function","function": {"name": "sendMail","description": "给指定人发送邮件","parameters": {"type": "object","properties": {"receiver": {"type": "string","description": "邮件接收人"},"content": {"type": "string","description": "邮件内容"}},"required": ["receiver", "content"]}}}]}""";doChat(reqBody);}private void doChat(String reqBody) throws IOException {Response response = sendMsg(reqBody);String res = response.body.string;log.info("response = {}", res);JSONObject jsonObject = JSONUtil.parseObj(res);JSONArray choices = jsonObject.getJSONArray("choices");JSONObject choice = (JSONObject) choices.get(0);JSONObject message = choice.getJSONObject("message");if (!"tool_calls".equals(choice.getStr("finish_reason"))) {log.info("最终请求体:{}", reqBody);log.info("最终响应:{}", message.getStr("content"));return;}// 执行方法调用doFunction(reqBody, message);}private void doFunction(String reqBody, JSONObject message) throws IOException {JSONObject bodyObject = JSONUtil.parseObj(reqBody);/*将大模型响应信息拼接到请求body中*/bodyObject.getJSONArray("messages").add(message);for (Object toolCallObject : message.getJSONArray("tool_calls")) {// 工具信息JSONObject toolCall = (JSONObject) toolCallObject;JSONObject function = toolCall.getJSONObject("function");String name = function.getStr("name");String toolCallId = toolCall.getStr("id");JSONObject arguments = JSONUtil.parseObj(function.getStr("arguments"));String funRes = null;if ("getWeather".equals(name)) {log.info("获取天气");// 调用天气方法String location = arguments.getStr("location");WeatherTool weatherTool = new WeatherTool;funRes= weatherTool.getWeather(location, null);} else if ("sendMail".equals(name)) {log.info("发送邮件");// 调用发送邮件方法String receiver = arguments.getStr("receiver");String content = arguments.getStr("content");SendMailTool sendMailTool = new SendMailTool;funRes = sendMailTool.sendMail(receiver, content, null);}if (funRes != null) {/*将方法返回值拼接到message中*/bodyObject.getJSONArray("messages").add(Map.of("role", "tool", "content", funRes, "name", name, "tool_call_id", toolCallId));}doChat(bodyObject.toString);}}private Response sendMsg(String reqBody) throws IOException {OkHttpClient client = new OkHttpClient.newBuilder.build;MediaType mediaType = MediaType.parse("application/json");RequestBody body = requestBody.create(mediaType, reqBody);Request request = new Request.Builder.url("https://api.deepseek.com/chat/completions").method("POST", body).addHeader("Content-Type", "application/json").addHeader("Accept", "application/json").addHeader("Authorization", "Bearer " + deepSeekApiKey).build;return client.newCall(request).execute;}// 测试天气@Testvoid test throws IOException {String prompt = "今天北京的天气怎么样";ChatResponse response = chatClient.prompt(prompt).tools(new WeatherTool) // 此处需要先将可能使用到的工具都要注册进来.call.chatResponse;System.out.println(response.getResult.getOutput.getText);}// 响应:今天北京的天气是30度我们程序中可以用ThreadLocal来保存请求的上下文信息,同样在工具调用的时候,也可以通过工具的上下文来记录一些必要信息。通过该方式传递的上下文信息不回拼接到大模型的请求参数中,也就不回消耗额外的token。
工具的上下文信息是通过toolContext字段来保存的,用法如下:
定义工具的时候,需要在参数中加上ToolContext参数/*** 获取制定地区的天气*/@Slf4jpublic class WeatherTool {/****/@Tool(name = "getWeather", description = "获取天气信息")public String getWeather(@ToolParam(description = "位置信息", required = true) String location,@ToolParam(required = false) ToolContext toolContext) {log.info("location: {}", location);return "今天30度";}}在请求大模型的时候,通过给toolContext字段设置相应的值,在定义的工具中就可以获取到该值了。// 测试天气@Testvoid test throws IOException {String prompt = "今天北京的天气怎么样";ChatResponse response = chatClient.prompt(prompt).tools(new WeatherTool) // 此处需要先将可能使用到的工具都要注册进来.toolContext(Map.of("saleId", "zhangsan")).call.chatResponse;System.out.println(response.getResult.getOutput.getText);}// 响应:今天北京的天气是30度
通过示例可见,工具的调用功能显著增强了大模型的实用性,为用户提供实时且有效的数据支持,从而在提升用户体验的同时,也使得产品更加智能化和高效化。
来源:记忆旅途
免责声明:本站系转载,并不代表本网赞同其观点和对其真实性负责。如涉及作品内容、版权和其它问题,请在30日内与本站联系,我们将在第一时间删除内容!