news 2026/4/25 9:54:06

LLM到AgentRAG——AI知识点概述 第六章:Function Call函数调用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LLM到AgentRAG——AI知识点概述 第六章:Function Call函数调用

Function Call——函数调用

以我们的RAG系统为例,整个RAG流程大概是这样的:用户提出问题→(问题拆分)→检索分块→生成答案→(比标注来源)

在基础场景中已经很完善了,能够给出符盖对应知识点的回答,但是用户的问题有时候并不只是查询文档并生成回答这么简单。

EX:用户“帮我查询12345中订单”,“帮我点杯奶茶”(千问点单、小美等)。RAG或许能够通过预训练知识/知识库文档查询到对应的知识/相关指南,但是无法真正查询到用户相关的真实订单信息、无法真实执行下单操作。——需要查询/点单相关API。

通过Function Call 我们能够拓宽RAG系统边界、让Agent执行能力落地。

查询知识库到其他功能:传统应用能力边界

即使是RAG这种应用,所有的数据来源都是写死的预训练知识/知识库分块的再组装。

企业场景下/特定实际应用场景下,用户需求实际上不止查询文档:

  • 查询实时业务数据

  • 查询业务/服务状态

  • 执行操作:申报/发送信息等

传统方案&局限性

最LOW的方案:Prompt当中写死兜底回复:

最烂的方案,用户体验是最差的,同时也是最死板的。

通过规则进行用户意图匹配,调用接口:

例如用户询问的问题出现“年假”关键字我们就查询用户的年假

if (userQuestion.contains("年假")) { int days = hrSystem.getAnnualLeave(userId); return "您还剩 " + days + " 天年假"; } if (userQuestion.contains("订单") && userQuestion.contains("物流")) { String status = logisticsSystem.getOrderStatus(orderId); return "订单物流状态:" + status; }

方案问题:规则是写不完的,维护成本很高。这和我们为什么需要LLM的原因是一样的——每种不同的表达背后的意图是一样的,难以靠规则进行匹配。

每次新增一个工具,我们就要修改代码,增加规则(当然你可以用责任链模式进行解耦,但是规则匹配问题依旧存在)

需要更加灵活的方案:模型判断使用什么工具,什么时候需要使用,传入什么参数。

Function Call

本质:模型输出调用意图

模型并不具备直接调用程序当中方法函数的功能,而是输出我们规定好的格式的文本(例如JSON),告诉程序“当前我认为需要调用什么函数,对应的参数有什么”。真正执行函数/对应服务的还是我们的代码。

流程

  1. 定义好工具函数,写好对应的参数描述(参数名称、类型、描述)、函数含义、描述

  2. 将所有工具列表、用户问题拼接好之后发送给模型

  3. 模型判断需要调用工具,输出JSON(统一称为tool_calls),包含函数名、参数

  4. 解析JSON,组装为函数参数对象,调用对应的函数,得到结果

  5. 执行结果、记忆等返回给大模型

  6. 大模型生成最终答案

EX:

第一轮:发送工具列表、用户问题

第一轮响应:模型输出调用意图:

模型分析用户问题,发现需要调用getUserAnnualLeave函数,并给出对应参数

{ "tool_calls": [ { "id": "call_abc123", "type": "function", "function": { "name": "getUserAnnualLeave", "arguments": "{\"userId\": \"12345\"}" } } ] }

代码执行函数:

解析tool_calls,执行函数,拿到结果:

{ "remainingDays": 5, "totalDays": 10, "usedDays": 5 }

第二轮:返回结果、记忆等给模型

请求包含:

  • 第一轮的用户问题

  • 第一轮的模型响应(带有tool_calls)

  • 函数返回执行结果

    您还剩 5 天年假(总共 10 天,已使用 5 天)。

    通过Function Call:

    将工具执行需求判断交给大模型,函数具体执行留给项目代码

    LLM工具调用决策:

OpenAI Function Call协议详解

工具格式

将工具列表以JSON数组格式发给模型

每个工具定义格式:

{ "type": "function", "function": { "name": "getUserAnnualLeave", "description": "查询用户的年假余额,包括总天数、已使用天数、剩余天数", "parameters": { "type": "object", "properties": { "userId": { "type": "string", "description": "用户 ID" } }, "required": ["userId"] } } }

字段说明:

  1. 类型type:固定为function

  2. function.name:函数名称,后续function calls当中返回这个名字

  3. function.description:函数功能性描述,模型判断是否调用工具的依据

  4. function.parameters:函数当中参数定义

function.parameters的JSON Schema格式

parameters使用JSON Schema格式定义参数,常用字段:

  • type:参数类型,常用“object”,标识参数是一个对象

  • properties:对象属性,每个属性有对应的type(类型)和description(描述)

  • required:必填参数列表

EX:

{ "type": "object", "properties": { "userId": { "type": "string", "description": "用户 ID" }, "year": { "type": "integer", "description": "查询的年份,默认为当前年份" } }, "required": ["userId"] }

含义:这个函数当中有两个参数userId和year,userId是必填的(类型为字符串),year是可选的(类型为整数)

请求格式:工具列表发给模型

完整请求JSON实例:

{ "model": "Qwen/Qwen2.5-7B-Instruct", "messages": [ { "role": "user", "content": "我还剩几天年假" } ], "tools": [ { "type": "function", "function": { "name": "getUserAnnualLeave", "description": "查询用户的年假余额", "parameters": { "type": "object", "properties": { "userId": { "type": "string", "description": "用户 ID" } }, "required": ["userId"] } } } ], "tool_choice": "auto" }

字段说明:

  • model:模型名称

  • messages:对话历史,格式和普通Chat API一样

  • tools:工具列表

  • tool_choice:控制模型是否调用工具,可选值:

    • auto:模型自己判断是否需要调用工具(默认值,大部分场景够用,让模型自己判断)

    • none:不调用工具,只生成文本回答

    • required:必须调用,不能只生成文本回答

    • {"type": "function", "function": {"name": "getUserAnnualLeave"}}:指定调用某个工具

响应格式:模型输出tool_calls

模型判断需要使用工具,响应格式如下:

{ "choices": [ { "message": { "role": "assistant", "content": null, "tool_calls": [ { "id": "call_abc123", "type": "function", "function": { "name": "getUserAnnualLeave", "arguments": "{\"userId\": \"12345\"}" } } ] }, "finish_reason": "tool_calls" } ] }

关键字段:

  • message.tool_calls:模型调用数组,可能包含多个工具调用(可能一次调用多个工具)

  • tool_calls[0].id:调用ID,第二轮请求时需要带上对应id,用于确定这次返回结果属于哪次调用,填补之前特定请求的空白(虽然LLM的api本身并不存储我们的上下文记忆,但是这是协议的强制要求,并且实际上我们通过id用于区分多个历史记录)

  • tool_calls[0].function.name:需要调用的函数名

  • tool_calls[0].functionl.arguments:函数参数名,JSON字符串(请注意),需要自己进行解析

  • finish_reason:值为tool_calls,因为模型输出的是工具调用而不是正常结束(对应reason为stop)

注意:message.content为null,因为模型没有生成文本回答,而是输出工具调用。

再次请求:函数执行结果返回给模型

执行完函数之后需要将整个函数执行结果以及完整对话历史返回给api用于生成最终答案:

{ "model": "Qwen/Qwen2.5-7B-Instruct", "messages": [//第一次调用内容 { "role": "user", "content": "我还剩几天年假" }, { "role": "assistant", "content": null, "tool_calls": [ { "id": "call_abc123",//可以看到这里和下面的tool_call_id是对应的 "type": "function", "function": { "name": "getUserAnnualLeave", "arguments": "{\"userId\": \"12345\"}" } } ] }, { //函数对应返回结果 "role": "tool", "tool_call_id": "call_abc123", "content": "{\"remainingDays\": 5, \"totalDays\": 10, \"usedDays\": 5}" } ] }

关键:

  1. 第一条消息:用户原始问题

  2. 第二条消息:第一轮模型响应(带tool_calls),role是assistant

  3. 第三条消息:函数执行结果、role是tool,tool_call_id需要和第二条消息当中的对应,content是函数对应的返回值

模型基于上面信息返回答案:

{ "choices": [ { "message": { "role": "assistant", "content": "您还剩 5 天年假(总共 10 天,已使用 5 天)。" }, "finish_reason": "stop" } ] }

注:这次的final_reason是stop,标识模型生成最终答案。

存在问题

Function Call解决了让模型调用工具的问题,但是带来了新的问题:

  • 工具定义维护成本高

  • 跨语言、跨系统集成复杂

  • 权限和安全控制需要自己实现

  • 可观测性不足(开发者无法清晰看到、理解或者追踪系统内部发生了什么、中间的工具调用过程,需要完备的日志记录等手段)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/25 9:54:05

04月25日AI每日参考:谷歌豪掷400亿押注Anthropic,DeepSeek V4横空出世

今日概览 今天AI圈有两件大事值得重点关注。谷歌宣布向Anthropic投资最高400亿美元,同时提供5吉瓦算力,将Anthropic估值推至3500亿美元,是AI领域迄今最大单笔战略投资之一。与此同时,中国AI公司DeepSeek发布V4模型预览版&#xf…

作者头像 李华
网站建设 2026/4/25 9:49:28

AI协议网关Agent Vibes:免费连接Cursor与Claude客户端的智能路由方案

1. 项目概述:一个连接AI客户端与免费后端的协议翻译网关如果你和我一样,日常开发离不开像Cursor IDE和Claude Code CLI这样的AI编程助手,但又对订阅多个付费API的成本感到头疼,那么Agent Vibes这个项目可能会让你眼前一亮。简单来…

作者头像 李华
网站建设 2026/4/25 9:47:49

如何用Spek轻松完成音频频谱分析:免费工具的终极指南

如何用Spek轻松完成音频频谱分析:免费工具的终极指南 【免费下载链接】spek Acoustic spectrum analyser 项目地址: https://gitcode.com/gh_mirrors/sp/spek Spek是一款功能强大的免费音频频谱分析工具,能够将复杂的音频信号转化为直观的视觉频谱…

作者头像 李华