【agent应用开发】| 深入理解Function Call

目录

一、Function Call是什么?

二、Function Call详解

1、参数

2、如何使用

3、混合调用

三、Fcuntion Call局限

四、总结


回顾一下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的本质以及如何使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值