有了前 2 節的基礎,接下來要上點強度了,Function Calling - 函數調用。
那麼什麼是函數調用呢?官方的解釋是:
You can register custom Java functions with the OpenAiChatClient and have the OpenAI model intelligently choose to output a JSON object containing arguments to call one or many of the registered functions. This allows you to connect the LLM capabilities with external tools and APIs. The OpenAI models are trained to detect when a function should be called and to respond with JSON that adheres to the function signature.
您可以使用 OpenAiChatClient 註冊自定義 Java 函數,讓 OpenAI 模型智能地選擇輸出包含參數的 JSON 對象,以調用一個或多個已註冊函數。這樣,您就可以將 LLM 功能與外部工具和 API 相連接。OpenAI 模型經過訓練,能夠檢測函數何時應被調用,並用符合函數簽名的 JSON 做出響應。
眾所周知,OpenAI 的 ChatGPT 是不能聯網的,但是有了 Function Calling 我們就可以提供給模型最新的數據。
想像一下,給 ChatGPT 加上 ‘天氣’ 插件,告訴 GPT 北京上海最新的天氣,讓 GPT 總結告訴你應該穿什麼衣服。或者加上一個 ‘熱搜榜’ 插件,讓 AI 總結最新的熱點。
創建函數#
函數的創建需要實現 Function 接口
public class MockWeatherService implements Function<MockWeatherService.Request, MockWeatherService.Response> {
public enum Unit { C, F }
public record Request(String location, Unit unit) {}
public record Response(double temp, Unit unit) {}
public Response apply(Request request) {
System.err.println("MockWeatherService.apply");
return new Response(30.0, Unit.C);
}
}
這裡返回的是固定值,30C° 實際可以調用接口返回真實的數據。
函數註冊#
有了函數之後需要進行註冊,註冊有 2 種方式。
創建模型時註冊#
你可以在創建模型時把函數註冊進去,例如
var openAiApi = new OpenAiApi("https://xxx", "sk-xxx");
OpenAiChatClient chatClient = new OpenAiChatClient(openAiApi, OpenAiChatOptions.builder()
.withModel("gpt-3.5-turbo-1106")
.withTemperature(0.4F)
.withFunctionCallbacks(List.of(
FunctionCallbackWrapper.builder(new MockWeatherService())
.withName("weather").withDescription("Get the weather in location").build()))
.build());
其中 withFunctionCallbacks()
表示註冊函數, FunctionCallbackWrapper.builder
用於構建函數,withName("weather").withDescription("Get the weather in location")
表示函數的名稱和介紹,儘量使用英文,介紹應符合函數的操作以便於讓 AI 判斷何時使用某個函數
動態註冊#
還有一種動態註冊是更推薦的,可以根據提問選擇對應的函數
var promptOptions = OpenAiChatOptions.builder().withFunctionCallbacks(List.of(
FunctionCallbackWrapper.builder(new MockWeatherService()).withName("weather").withDescription("Get the weather in location").build(),
FunctionCallbackWrapper.builder(new WbHotService()).withName("wbHot").withDescription("Get the hot list of Weibo").build(),
FunctionCallbackWrapper.builder(new TodayNews()).withName("todayNews").withDescription("60s watch world news").build())).build();
ChatResponse response = chatClient.call(new Prompt(message, promptOptions));
return response.getResult().getOutput().getContent();
這裡面註冊了 3 個函數
- weather 就是上面創建的天氣函數
- wbHot 是調用接口獲取的微博熱搜榜
- todayNews 也是調用接口獲取的 60s 世界新聞
根據不同的提示,AI 會選擇調用最符合的函數
以下是微博熱榜的函數示例
public class WbHotService implements Function<WbHotService.Request, WbHotService.Response> {
public record Request(String wb) {}
public record Response(WbHot wbHot) {}
/**
* Applies this function to the given argument.
*
* @param request the function argument
* @return the function result
*/
@Override
public Response apply(Request request) {
System.err.println("微博熱榜哦,表哥我進來了哦(*/ω\*)(*/ω\*)");
// 使用hutool請求接口
String result = HttpUtil.get("https://api.vvhan.com/api/hotlist/wbHot");
WbHot bean = JSONUtil.toBean(result, WbHot.class);
return new Response(bean);
}
}
事實上,也不需要將接口返回的 json 轉為對象,直接返回給 AI 它會自動處理的。
不過目前 Function Calling 也有問題,即函數返回的結果總會被 AI 處理。
例如,我有一個圖片函數,我對 AI 說 “來點色圖”,然後調用了這個函數,函數很簡單啊,返回了一個圖片的 URL,這就完全沒有必要讓 AI 處理啊。萬一 AI 看完來了句 “淦,老兄你的 XP 真的很怪” 呢🤣 不過之前也有人提過 ISSUES
推薦#
關於 Function Calling 的具體應用落地,可以看下面這篇文章
Spring AI 應用 - 智能記者
完整的代碼在這個gist
Spring AI Chat 簡單示例
韓小韓 API 接口站 - 免費 API 數據接口調用服務平台
韓小韓 API 接口站 -