目录
回顾一下RAG的工作流程:用户提问——>向量检索——>召回相关文档——>生成答案,在上述过程中,大模型都是基于知识库进行回答的,在知识库中没有的大模型无法回答,但是在实际企业应用中,我们经常需要一些实时数据,比如员工问”我的年假还有多少天“等等。因此,我们引入Function Calling,让模型判断是否要调用工具、调用什么工具以及什么时候调用工具。
一、Function Call是什么?
本质:模型输出调用意图
模型并不是真正调用函数的,真正执行函数的是自己写的代码,模型只是输出JSON,说明要调用的函数以及所需要的参数。
也就是说:比如你到外面点餐,你跟服务员说我想吃点解火的,服务员根据菜单名回答说那来个爆炒龙虾吧,由服务员跟厨房说#101要一份爆炒龙虾,由厨师负责出餐。
二、Function Call详解
1、参数
type:固定为function
fcuntion.name:调用的函数方法名
function.description:该函数的描述,模型主要根据描述来判断函数功能
function.parameters:函数所需的参数,为JSON Schema格式
2、如何使用
1)先定义工具
2)将工具列表以标准格式发给模型
3)模型判断是否要实时调用
4)要调用则执行函数并把执行结果返回给大模型
5)大模型组织语言回答用户
完整的使用示例:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import okhttp3.*;
import java.io.IOException;
public class FinancialAssistantFunctionCallDemo {
private static final String API_KEY = "YOUR_API_KEY";
private static final String API_URL = "https://api.siliconflow.cn/v1/chat/completions";
private static final String MODEL = "deepseek-ai/DeepSeek-V3";
private static final OkHttpClient client = new OkHttpClient();
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
public static void main(String[] args) throws IOException {
// 用户问题
String userQuestion = "帮我查一下苹果公司(AAPL)目前的股价是多少?";
System.out.println("用户问题:" + userQuestion);
System.out.println("\n" + "=".repeat(60) + "\n");
// 第一轮:发送工具列表和用户问题
JsonObject firstResponse = callModelWithTools(userQuestion);
System.out.println("第一轮响应:");
System.out.println(gson.toJson(firstResponse));
System.out.println("\n" + "=".repeat(60) + "\n");
// 解析 tool_calls
JsonArray toolCalls = firstResponse.getAsJsonArray("choices")
.get(0).getAsJsonObject()
.getAsJsonObject("message")
.getAsJsonArray("tool_calls");
// 这里替换了 CollUtil.isEmpty,使用标准库方法,避免缺少依赖
if (toolCalls == null || toolCalls.size() == 0) {
System.out.println("模型没有调用工具,直接返回答案");
return;
}
// 执行函数
JsonObject toolCall = toolCalls.get(0).getAsJsonObject();
String functionName = toolCall.getAsJsonObject("function").get("name").getAsString();
String arguments = toolCall.getAsJsonObject("function").get("arguments").getAsString();
String toolCallId = toolCall.get("id").getAsString();
System.out.println("模型要调用的函数:" + functionName);
System.out.println("函数参数:" + arguments);
System.out.println("\n" + "=".repeat(60) + "\n");
// 执行函数(这里用 mock 数据模拟调用金融API)
String functionResult = executeFunction(functionName, arguments);
System.out.println("函数执行结果:" + functionResult);
System.out.println("\n" + "=".repeat(60) + "\n");
// 第二轮:把结果返回给模型
JsonObject secondResponse = callModelWithFunctionResult(
userQuestion, toolCall, toolCallId, functionResult);
System.out.println("第二轮响应:");
System.out.println(gson.toJson(secondResponse));
System.out.println("\n" + "=".repeat(60) + "\n");
// 提取最终答案
String finalAnswer = secondResponse.getAsJsonArray("choices")
.get(0).getAsJsonObject()
.getAsJsonObject("message")
.get("content").getAsString();
System.out.println("最终答案:" + finalAnswer);
}
/**
* 第一轮调用:发送工具列表和用户问题
*/
private static JsonObject callModelWithTools(String userQuestion) throws IOException {
// 定义工具列表
JsonArray tools = new JsonArray();
// 工具 1:查询股票信息
JsonObject tool1 = new JsonObject();
tool1.addProperty("type", "function");
JsonObject function1 = new JsonObject();
function1.addProperty("name", "getStockInfo");
function1.addProperty("description", "查询指定股票的最新价格、涨跌幅和市值信息");
JsonObject parameters1 = new JsonObject();
parameters1.addProperty("type", "object");
JsonObject properties1 = new JsonObject();
JsonObject stockCode = new JsonObject();
stockCode.addProperty("type", "string");
stockCode.addProperty("description", "股票代码,例如:AAPL, TSLA, 00700");
properties1.add("stockCode", stockCode);
parameters1.add("properties", properties1);
JsonArray required1 = new JsonArray();
required1.add("stockCode");
parameters1.add("required", required1);
function1.add("parameters", parameters1);
tool1.add("function", function1);
tools.add(tool1);
// 工具 2:查询汇率
JsonObject tool2 = new JsonObject();
tool2.addProperty("type", "function");
JsonObject function2 = new JsonObject();
function2.addProperty("name", "getExchangeRate");
function2.addProperty("description", "查询两个指定货币之间的实时汇率");
JsonObject parameters2 = new JsonObject();
parameters2.addProperty("type", "object");
JsonObject properties2 = new JsonObject();
JsonObject baseCurrency = new JsonObject();
baseCurrency.addProperty("type", "string");
baseCurrency.addProperty("description", "基础货币代码,例如:USD, CNY, EUR");
JsonObject targetCurrency = new JsonObject();
targetCurrency.addProperty("type", "string");
targetCurrency.addProperty("description", "目标货币代码,例如:CNY, JPY");
properties2.add("baseCurrency", baseCurrency);
properties2.add("targetCurrency", targetCurrency);
parameters2.add("properties", properties2);
JsonArray required2 = new JsonArray();
required2.add("baseCurrency");
required2.add("targetCurrency");
parameters2.add("required", required2);
function2.add("parameters", parameters2);
tool2.add("function", function2);
tools.add(tool2);
// 构建请求体
JsonObject requestBody = new JsonObject();
requestBody.addProperty("model", MODEL);
JsonArray messages = new JsonArray();
// ★★★ 设定系统人设,让模型回答更专业 ★★★
JsonObject systemMessage = new JsonObject();
systemMessage.addProperty("role", "system");
systemMessage.addProperty("content", "你是一个专业的金融分析助手,请根据提供的实时数据为用户解答财务和市场问题。");
messages.add(systemMessage);
JsonObject userMessage = new JsonObject();
userMessage.addProperty("role", "user");
userMessage.addProperty("content", userQuestion);
messages.add(userMessage);
requestBody.add("messages", messages);
requestBody.add("tools", tools);
requestBody.addProperty("tool_choice", "auto");
// 发送请求
return sendRequest(requestBody);
}
/**
* 第二轮调用:把函数执行结果返回给模型
*/
private static JsonObject callModelWithFunctionResult(
String userQuestion, JsonObject toolCall, String toolCallId, String functionResult) throws IOException {
JsonObject requestBody = new JsonObject();
requestBody.addProperty("model", MODEL);
JsonArray messages = new JsonArray();
// 第一条消息:用户原始问题
JsonObject userMessage = new JsonObject();
userMessage.addProperty("role", "user");
userMessage.addProperty("content", userQuestion);
messages.add(userMessage);
// 第二条消息:第一轮的模型响应(必须包含原样的 tool_calls)
JsonObject assistantMessage = new JsonObject();
assistantMessage.addProperty("role", "assistant");
assistantMessage.add("content", JsonNull.INSTANCE);
JsonArray toolCalls = new JsonArray();
toolCalls.add(toolCall);
assistantMessage.add("tool_calls", toolCalls);
messages.add(assistantMessage);
// 第三条消息:我们本地执行函数后的结果
JsonObject toolMessage = new JsonObject();
toolMessage.addProperty("role", "tool");
toolMessage.addProperty("tool_call_id", toolCallId);
toolMessage.addProperty("content", functionResult);
messages.add(toolMessage);
requestBody.add("messages", messages);
// 发送请求
return sendRequest(requestBody);
}
/**
* 执行函数(这里用 mock 数据模拟调用外部行情服务)
*/
private static String executeFunction(String functionName, String arguments) {
JsonObject args = gson.fromJson(arguments, JsonObject.class);
if ("getStockInfo".equals(functionName)) {
// 模拟查询股票行情系统
String stockCode = args.get("stockCode").getAsString();
JsonObject result = new JsonObject();
result.addProperty("stockCode", stockCode);
result.addProperty("currentPrice", 173.50);
result.addProperty("currency", "USD");
result.addProperty("changePercent", "+1.2%");
result.addProperty("marketCap", "2.65 Trillion");
result.addProperty("updateTime", "2026-04-20 10:00:00 EST");
return gson.toJson(result);
} else if ("getExchangeRate".equals(functionName)) {
// 模拟查询外汇系统
String base = args.get("baseCurrency").getAsString();
String target = args.get("targetCurrency").getAsString();
JsonObject result = new JsonObject();
result.addProperty("baseCurrency", base);
result.addProperty("targetCurrency", target);
result.addProperty("rate", 7.2345);
result.addProperty("updateTime", "2026-04-20 11:30:00 CST");
return gson.toJson(result);
}
return "{\"error\": \"未知的函数调用\"}";
}
/**
* 发送 HTTP 请求
*/
private static JsonObject sendRequest(JsonObject requestBody) throws IOException {
RequestBody body = RequestBody.create(
gson.toJson(requestBody),
MediaType.parse("application/json"));
Request request = new Request.Builder()
.url(API_URL)
.addHeader("Authorization", "Bearer " + API_KEY)
.addHeader("Content-Type", "application/json")
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("请求失败:" + response);
}
String responseBody = response.body().string();
return gson.fromJson(responseBody, JsonObject.class);
}
}
}
3、混合调用
有些场景需要同时调用RAG知识库跟实时调用:
由模型同时输出多个tool_calls(如果支持并行调用),如果不行则先调用工具A,再调用工具B即可
三、Fcuntion Call局限
Fcuntion Call解决了工具调用的问题,但是又引入了新问题,可以参照上面的实例代码,每定义一个新工具,都需要在代码中定义JS
ON Schema,参数多的代码就更长了,代码冗余且不利于后期维护;并且,工具是用Java实现的,有的用Python,模型如何才能调用不同语言的工具,需要自己去写一个定义工具以及工具调用的框架,过程有点繁琐。
因此,如果有一个统一的协议,只要支持了这种协议的工具模型都可调用,就解决了上述问题,下一次讲一下MCP协议
四、总结
明白了Function Call的本质以及如何使用

2640

被折叠的 条评论
为什么被折叠?



