为什么我们需要 Function Call? (背景与痛点)
RAG 的局限性:
-
只能"查"不能"做": 传统 RAG 的工作流是
用户提问 -> 向量检索 -> 生成答案。它只能回答"知识库里有什么"(静态知识)。 -
无法处理实时/业务数据: 面对"我还剩几天年假?"、"订单 #12345 到哪了?"或"帮我提交报销"这类需求,RAG 无能为力,因为这些数据不在向量数据库中,而是在业务系统的数据库或 API 里。
传统解决方案的弊端:
-
在 Prompt 里写死规则或兜底回复。
-
用硬编码的规则匹配意图(如正则匹配),然后调用接口。
-
后果: 规则写不完,维护成本极高。每增加一个新功能(如查考勤),都要修改代码、加规则、重新部署。
Function Call 的核心概念
定义:
Function Call 是一种让大模型从"只会说话"进化到"能干活"的机制。
- 核心思想: 将"判断何时调用工具"的逻辑交给模型,将"具体执行工具"的逻辑留给代码。
工作流程(6步闭环):
-
定义工具: 开发者定义工具列表(名称、描述、参数 Schema)。
-
用户提问: 将用户问题和工具列表一起发给模型。
-
模型决策: 模型判断需要调用哪个工具,输出一个 JSON 格式(
tool_calls),包含函数名和参数。 -
执行代码: 本地程序解析 JSON,执行对应的函数(查库、调接口)。
-
返回结果: 将函数执行的结果(如
{"remaining": 5})返回给模型。 -
生成回答: 模型结合工具返回的数据,生成最终的自然语言回答。
技术实现:OpenAI 协议详解
目前主流模型(包括 Qwen)都遵循类似的协议标准。
A. 工具定义 (Tools Definition)
我们需要告诉模型有哪些"武器"可用。通常是一个 JSON 数组,包含函数名、描述和参数定义(JSON Schema)。
示例:定义一个查询年假的工具
getUserAnnualLeave,需要参数userId。
B. 交互协议 (Request & Response)
-
第一轮请求 (User -> Model):
- 发送用户问题 +
tools参数 +tool_choice="auto"(让模型自己决定)。
- 发送用户问题 +
-
第一轮响应 (Model -> User):
-
模型不直接回答,而是返回
finish_reason: "tool_calls"。 -
消息体中包含
tool_calls数组,里面有id(调用ID)、name(函数名)和arguments(参数 JSON)。
-
-
第二轮请求 (User -> Model):
-
关键点: 需要把完整的对话历史发回去。
-
新增一条
role: "tool"的消息,包含tool_call_id(对应第一轮的ID)和content(函数执行的实际结果)。
-
-
第二轮响应 (Model -> User):
- 模型看到工具返回的结果(例如:剩余5天),生成最终回复:"您还剩 5 天年假。"
Function Call 在 RAG 系统中的应用
意图识别:查知识库 vs 调工具
核心思想
将"知识库检索"与"业务工具调用"统一抽象为工具,利用大模型的意图识别能力作为智能路由器,根据用户问题的性质动态选择执行路径,从而实现从"被动问答"到"主动服务"的架构升级。
演进对比
-
传统 RAG 模式(单一技能):无论用户提问何种类型,系统强制执行知识库检索。这种方式缺乏灵活性,无法处理实时数据或特定业务逻辑,容易导致模型在面对非文档类问题时产生幻觉。
-
工具化架构模式(智能调度):将"搜索知识库"视为一个工具,将"查询业务系统"视为另一个工具。大模型不再直接生成答案,而是先分析用户意图,判断该问题属于"静态知识查询"还是"动态业务操作",进而自动路由至相应的工具。
Function Call 的局限性与痛点
虽然 Function Call 很强,但目前落地还有坑:
-
维护成本: 工具定义(Schema)多了以后,管理很麻烦。
-
集成复杂度: 跨语言、跨系统的调用链路调试困难。
-
安全性: 权限控制(谁能调什么接口)需要自己在代码层实现,模型本身不管这个。
-
可观测性: 链路变长了(用户->模型->代码->模型->用户),排查问题不如传统代码直观。