当前位置:AIGC资讯 > AIGC > 正文

AIGC: 关于ChatGPT中Function Call的调用

Function Call 概念

关于 GPT 中API的function参数,提供了一些能力 这个函数调用是 Open AI 在2023年的6.13号发布的新能力 根据它的官方描述, 函数调用能力可以让模型输出一个请求调用函数的消息 其中包含所需调用函数的信息,以及调用函数时所需携带的参数的信息 这种方式是一种将GPT的能力和外部的工具,外部的API连接起来的新的方式

函数调用的机制

那么应该如何去使用函数调用? 首先我们需要去选择函数调用的新模型 用户在构造message参数时候,需要主动的告诉模型有哪些函数 GPT 知道我们有哪些函数之后,根据对于自然语言的理解,根据用户的输入 GPT会自行的判断何时需要调用这些函数,然后会根据目标函数它的描述生成符合要求的请求的参数 然后返回给我们,我们根据GPT的信息再去调用函数

函数的作用

第一种,进行自然语言交流时,通过调用外部工具回答问题 通过这种函数调用的能力,我们可以将GPT和第三方的工具去进行一个集成,形成类似于GPT插件的这种模式 第二种,如果我们有特殊的对于自然语言处理的逻辑 我们可以利用GPT, 将自然语言转换成调用API时使用的参数, 或转换成查询数据库时所使用的条件等等 第三种,我们可以利用这种能力从文本当中去提取一些结构化的数据,这是函数的一些基础的作用。

函数调用的使用

那么我们如何去使用函数调用呢?如何在它的聊天API的接口上去添加函数相关的这些参数呢? 为了实现函数调用的能力, 在API里面有新的请求的参数就是function, function call等等, 在官方的API文档上可以查询 在使用之前,我们先来了解一下函数调用的步骤 第一步, 当我们去调用函数的时候,首先我们需要在请求参数当中向API也就是向GPT去传递信息, 我们要告诉GPT, 我们有哪些可以调用的函数 第二步, 我们根据GPT的返回, 我们要去进行解析, 判断模型是不是需要调用函数, 如果不需要,我们则不处理 如果需要调用函数,我们这里要根据需要调用的函数,根据GPT输入的参数去进行调用 调用完成之后,得到函数调用的结果 最后, 我们再将函数调用的结果添加到GPT的消息列表来告诉GPT

代码实现

参考前文: https://blog.csdn.net/Tyro_java/article/details/134781021

1 )新增一些实现类,结构如下

src main java com.xxx.gpt.client entity ChatFunction.java FunctionCallResult.java … test java com.xxx.gpt.client.test FunctionCallTest.java …

ChatFunction.java

package com.xxx.gpt.client.entity;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ChatFunction {
    String name;
    String description;
    ChatParameter parameters;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    @JsonInclude(JsonInclude.Include.NON_NULL)
    public static class ChatParameter {
        String type;
        List<String> required;
        Object properties;
    }
}
ChatFunction 类中包含:名称,描述,参数等等字段

FunctionCallResult.java

package com.xxx.gpt.client.entity;

import lombok.Data;

@Data
public class FunctionCallResult {
    String name;
    String arguments;
}
FunctionCallResult 定义了名称,参数的字段

FunctionCallTest.java

package com.xxx.gpt.client.test;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xxx.gpt.client.ChatGPTClient;
import com.xxx.gpt.client.entity.*;
import com.xxx.gpt.client.util.Proxys;
import org.junit.Before;
import org.junit.Test;

import java.net.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class FunctionCallTest {
    private ChatGPTClient chatGPTClient;

    @Before
    public void before() {
        Proxy proxy = Proxys.http("127.0.0.1", 7890);
        chatGPTClient = ChatGPTClient.builder()
                .apiKey("sk-6kchn0DasdfqOJqkc3aI665ct") // 填入自己的 key
                .timeout(900)
                .proxy(proxy)
                .apiHost("https://api.openai.com/")
                .build()
                .init();
    }

    // 调用gpt的时候,带上函数信息,让GPT选择是否调用
    @Test
    public void chat() {
        List<ChatFunction> functions = new ArrayList<>();
        ChatFunction function = new ChatFunction();
        function.setName("getCurrentWeather"); // 设置函数信息
        function.setDescription("获取给定位置的当前天气");
        function.setParameters(ChatFunction.ChatParameter.builder()
                .type("object")
                .required(Arrays.asList("location"))
                .properties(JSON.parseObject("{\n" +
                        "          \"location\": {\n" +
                        "            \"type\": \"string\",\n" +
                        "            \"description\": \"The city and state, e.g. San Francisco, " +
                        "CA\"\n" +
                        "          },\n" +
                        "          \"unit\": {\n" +
                        "            \"type\": \"string\",\n" +
                        "            \"enum\": [\"celsius\", \"fahrenheit\"]\n" +
                        "          }\n" +
                        "        }"))
                .build());
        // 添加到列表中
        functions.add(function);
        // 构造 message
        Message message = Message.of("上海的天气怎么样?");
        // 构造调用 api 参数
        ChatCompletion chatCompletion = ChatCompletion.builder()
                .model(Model.GPT_3_5_TURBO_16K.getName())
                .messages(Arrays.asList(message))
                .functions(functions)
                .maxTokens(8000)
                .temperature(0.9)
                .build();
        // 调用
        ChatCompletionResponse response = chatGPTClient.chatCompletion(chatCompletion);
        ChatChoice choice = response.getChoices().get(0);
        Message res = choice.getMessage();
        System.out.println(res);
        // 基于 finish reason 判断,如果是 function_call 就需要调用函数
        if ("function_call".equals(choice.getFinishReason())) {
            FunctionCallResult functionCall = res.getFunctionCall();
            String functionCallName = functionCall.getName();
            // 如果需要调用的是 getCurrentWeather
            if ("getCurrentWeather".equals(functionCallName)) {
                String arguments = functionCall.getArguments();
                JSONObject jsonObject = JSON.parseObject(arguments);
                String location = jsonObject.getString("location");
                String unit = jsonObject.getString("unit");
                // 得到最终的结果
                String weather = getCurrentWeather(location, unit);
                res.setContent("");
                // 将结果 weather 告诉GPT
                callWithWeather(weather, res, functions);
            }
        }
    }

    // 将结果传送给GPT
    private void callWithWeather(String weather, Message res, List<ChatFunction> functions) {
        Message message = Message.of("上海的天气怎么样?");
        Message function1 = Message.ofFunction(weather);
        function1.setName("getCurrentWeather");
        ChatCompletion chatCompletion = ChatCompletion.builder()
                .model(Model.GPT_3_5_TURBO_16K.getName())
                .messages(Arrays.asList(message, res, function1))
                .functions(functions)
                .maxTokens(8000)
                .temperature(0.9)
                .build();
        ChatCompletionResponse response = chatGPTClient.chatCompletion(chatCompletion);
        ChatChoice choice = response.getChoices().get(0);
        Message res2 = choice.getMessage();
        //上海目前天气晴朗,气温为 22 摄氏度。
        System.out.println(res2.getContent());
    }

    // 首先我们添加一个函数,函数是获取天气的信息,这里需要传入 location
    // return 我们这里的返回值是根据location构造出来的一个JSON, 这里设置的固定的,就是模拟接口,或者对接天气网站接口都可
    public String getCurrentWeather(String location, String unit) {
        return "{ \"temperature\": 22, \"unit\": \"celsius\", \"description\": \"晴朗\" }";
    }
}
// 本地有一个函数,将函数信息告诉chatgpt,并告诉chatgpt什么情况需要调用这个函数。由chatgpt判断是否需要调用该函数,如果需要在交互中进行调用。类似于委托机制
第一步,需要告诉GPT我们有哪些函数?也就是这些函数是我们本地定义的 第二步,是在调用GPT的时候带上函数信息,然后让GPT选择是否调用函数 第三步,解析GPT的返回,如果GPT需要调用函数,我们在本地根据GPT返回的参数,调用函数获取结果,在获取结果之后,将结果告诉GPT 通过GPT的函数调用的一个简单的示例 看到通过 GPT函数调用的这种方式,可以将我们本地的一些函数 可以和外部的一些第三方的工具做一个更好的集成 整体的这个模式,就类似于委托的机制 GPT根据我们的自然语言,也就是我们的 Prompt 自行去判断是不是需要调用函数 如果需要调用的话,然后再告诉我们,由我们完成调用 完成调用之后,再将调用的结果返回给它 整个过程是我们和GPT的程序上的密切交互

更新时间 2024-01-04