LLM到Agent&RAG——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解决了让模型调用工具的问题,但是带来了新的问题:

  • 工具定义维护成本高

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

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

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

相关推荐
Rcnhtin2 小时前
RocketMQ
java·linux·rocketmq
imbackneverdie2 小时前
科研绘图素材从哪找?
人工智能·科研·绘图工具·科研绘图·研究生·ai工具·科研工具
JH30732 小时前
RedLock-红锁
java·redis
十铭忘2 小时前
infoGCN++——尝试训练
人工智能
传说故事2 小时前
【论文阅读】DreamDojo: A Generalist Robot World Model from Large-Scale Human Videos
论文阅读·人工智能·具身智能
李昊哲小课2 小时前
LLM Wiki - 本地知识库管理系统
人工智能·本地知识库·llm wiki
水如烟2 小时前
孤能子视角:跨域联接之示例,观察符投射
人工智能
思绪无限2 小时前
YOLOv5至YOLOv12升级:金属锈蚀检测系统的设计与实现(完整代码+界面+数据集项目)
人工智能·python·深度学习·目标检测·计算机视觉·yolov12