讓 AI 更懂你的「尚方寶劍」:Function Call 到底是什麼?

2023-08-18     少數派

原標題:讓 AI 更懂你的「尚方寶劍」:Function Call 到底是什麼?

作為只使用 GPT API 的開發者,我非常眼饞 ChatGPT 的插件能力,但限於當時 GPT-3.5 沒有開放相關接口,雖然可以使用 LangChain 等輔助工具實現類似能力,但終歸有些麻煩。不過隨著 OpenAI 大更新 ,我們終於可以在利用 API 來實現插件能力了!而實現插件能力的基礎就是這次新出的 Function Call。

Function Call 是個啥?

眾所周知,ChatGPT 3 月發布的版本正式支持了第三方插件,這一版本通過插件讓 GPT 可以聯網獲取新知識,並且可以與超過 5000 個應用進行交互。

通過建立插件體系,ChatGPT 不僅可以查詢最新的新聞,還可以幫助用戶查詢航班、酒店信息,規劃差旅並訪問各大電商數據,甚至可以比價和下單。這使得 ChatGPT 在更多領域發揮更重要的作用,國內外一大堆媒體都鋪天蓋地地宣傳這項功能,形容這就像蘋果的 「App Store」。

基於官方的文檔來看,Function Call 是 GPT API 中的一項新功能。它可以讓開發者在調用 GPT-4 和 GPT-3.5-turbo 模型時,描述函數並讓模型智能地輸出一個包含調用這些函數所需參數的 JSON 對象。這種功能可以更可靠地將 GPT 的能力與外部工具和 API 進行連接,從而實現以下應用:

  • 創建聊天機器人:開發者可以通過調用外部工具,如 ChatGPT 插件,回答問題,或者將查詢「北京的天氣如何?」轉換為調用 getCurrentWeather(location: string) 的函數。
  • 將自然語言轉換為 API 調用或資料庫查詢:例如,將查詢「這個月我的前十個客戶是誰?」轉換為調用 get_customers_by_revenue(start_date, end_date, limit) 的內部 API 調用,或者將查詢「上個月 Acme 公司下了多少訂單?」轉換為使用 sql_query(query)的 SQL 查詢。
  • 從文本中提取結構化數據:開發者可以定義一個名為 extract_people_data(people) 的函數,以提取在維基百科文章中提到的所有人物。

Function Call 的交互技術解讀

官方文檔為了讓大家好理解,舉了上面這些例子,我想從我自己感受到的來聊聊這個 Function Call 到底意味著什麼。那作為一名懂技術的設計師,自然從我的專業角度 人機互動領域來聊。

從人機互動上來說, Function Call 本質上只做了一件事,那就是實現了「準確識別用戶的語義,將其轉為結構化的指令」

而這,非常了不起。

我們如今的交互介面,存在非常多形態的組件,文本輸入框、切換器、選擇器、操縱滑杆… 這是為什麼?原因是我們需要約束用戶的輸入,才能輔助機器理解我們輸入意圖。

比如填寫一個表單用於收集個人信息。對於機器來說,它無法理解「我叫小明,今年18歲,家在杭州市西湖邊」這樣非結構化的輸入。只有將其拆分為「姓名」「年齡」這樣的欄位,機器才能識別相應的信息結構,並將這樣結構化後的信息接入後續的流程。所以如今的表單這種組件,本質上是人機互動領域一個技術妥協的產物。

再回到 Function Call 本身來說,它實現的最大的價值,就是讓機器輕易地理解了用戶模糊化的輸入,將其轉換為機器可以理解的技術指令。這對於人機互動的範式來說,完全是質的改變。

上圖演示了一個 Function Call 的典型案例 —— 天氣查詢插件。但是大家可以注意到的是,我們在提問上並沒有直接問明天的天氣如何,而是問「我明天應該穿什麼?」此時 GPT 完全理解了我的問題,而且知道穿衣建議是需要了解相關天氣情況的。因此它問了我在哪個城市,並且在獲得該信息後,調用了天氣查詢插件。並基於天氣查詢返回的結果,告訴了我穿衣建議。

這樣的交互形態,在過去是完全不敢想像的。但在 GPT 與 Function Call 的加持下,開發者可以輕鬆實現用戶模糊輸入的意圖識別,並轉換為結構化的系統指令,與現有系統做集成。

天氣插件的實現原理:Function Call 調用揭秘

讓我們來拆解一下上述的交互流程,仔細看看這個插件的交互是如何實現的。這樣的一次交互,包含了5 個對話:

用戶提出了一個問題:「我明天應該穿什麼?」

AI 提問「請告訴你所在的城市」;

用戶回答完城市;

AI 開始調用天氣查詢插件,獲取最近一周的天氣數據;

為了更方便理解本質,我會將上述 5 條消息,轉換為偽代碼的語法,來方便大家理解:

#1

-role:user

content:我明天應該穿什麼?

#2

-role:assistant

content:請告訴你所在的城市

#3

-role:user

content:杭州

#4

-role:assistant

content:"{"name":"realtimeWeather","arguments":"{\n "city": "杭州"\n}"}

#5

-role:function

name:realtimeWeather

content:[{

"city":"杭州市",

"adcode":"330100",

"province":"浙江",

"reporttime":"2023-08-10 00:02:43",

"casts":[

{

"date":"2023-08-10",

"week":"4",

"dayweather":"雷陣雨",

"nightweather":"多雲",

"daytemp":"35",

"nighttemp":"25",

"daywind":"北",

"nightwind":"北",

"daypower":"≤3",

"nightpower":"≤3",

"daytemp_float":"35.0",

"nighttemp_float":"25.0"

},

]

}]

#6

-role:assistant

content:根據杭州的天氣預報...

上述偽代碼中, #1 ~ #3 和 1~3 步沒有任何區別。而 #4 ~ #5 在用戶側的感知就是第 4 步。#6 對應的則是第5步。接下來詳細講講這 6 步到底發生了什麼。

理解意圖

首先是 #1 ,用戶提問「我明天應該穿什麼?」,這背後,系統發送給 AI 的請求的信息是下面這樣的:

{

"messages": [

{

"role": "user",

"content": "我明天應該穿什麼?"

}

],

"functions": [

{

"name": "realtimeWeather",

"deion": "獲取當前天氣情況",

"parameters": {

"type": "object"

"properties": {

"city": {

"deion": "城市名稱",

"type": "string"

}

},

"required": [

"city"

],

}

}

]

}

除了 messages 以外,我們還向 GPT-3.5 傳遞了一個 functions 的列表,在這個列表中,我們使用 JSON Schema 描述了 GPT 可以調用的方法 ,即 realtimeWeather

我們在這個方法的描述中介紹了這個方法的作用。同時這個方法支持傳入 city 這一個參數,參數名為城市,且 city 這個參數是必填的。

收集必要信息

講完了 #1,接下來看 #2,AI 的消息是:「請告訴你所在的城市」。注意到了沒,其實從這一步開始,GPT 已經識別了用戶的意圖,並試圖嘗試去調用 realtimeWeather的外部方法。但是由於這個方法中的入參 city 是個必填項,而此時它並不知道,因此需要從用戶側了解到該信息。

再來看 #3,用戶回答了 「杭州」,此時我們再來看下發送給 GPT 的消息:

{

"messages": [

{

"role": "user",

"content": "我明天應該穿什麼?"

},

{

"role": "assistant",

"content": "請告訴你所在的城市?"

},

{

"role": "user",

"content": "杭州"

},

],

"functions": [

{

"name": "realtimeWeather",

"deion": "獲取當前天氣情況",

"parameters": {

"type": "object"

"properties": {

"city": {

"deion": "城市名稱",

"type": "string"

}

},

"required": [

"city"

],

}

}

]

}

OK,基於 #1~#2 的分析和 #3 的消息,我們現在已經知道:

  • GPT 準確識別了用戶的意圖,並想要嘗試調用 realtimeWeather的方法;

  • 截止 #3,realtimeWeather 所需要的參數(city),在會話中已經齊全。

GPT 準確識別了用戶的意圖,並想要嘗試調用 realtimeWeather的方法;

那麼接下來就看其中最重要的 #4 。#4 的返回消息如下:

{

"role": "assistant",

"content": "{"name ": "realtimeWeather ","arguments": "{\n \ "city": "杭州"\n}"}

}

可以看到 GPT 在 content 中返回了一串 JSON 內容,將其格式化:

{

"name": "realtimeWeather",

"arguments": {

"city": "杭州"

}

}

通過上述 JSON 可以發現, GPT 準確返回了它想要調用的方法名稱 realtimeWeather 與相應的參數 "city": "杭州"。是不是有點 Amazing ?這就是 Function Call 的特性。

為了讓機器理解人類的意圖,過去我們想方設法去「約束用戶行為」或者「猜測用戶意圖』。但時代已經開始變了,通過 Function Call, 我們只需要在發送給 GPT 請求時加一個 functions的參數,告知 AI 可以調用的外部方法有什麼,然後 AI 就能夠自動分析問題的上下文,並通過多輪對話來收集必要的調用參數,最後拼合返回調用方法的 JSON。

這對於開發者來說已經友好到極致了,研發成本大大降低,但效果又大大提升。

外部 API 調用

OK,接下來再看 #5。這一步其實也是之前非常困惑我的地方。我曾以為是 OpenAI 會在背後幫我們執行一個什麼插件的服務調用。但通過自行實現一遍後才發現其實並不是這樣。

#5 做的事情,本質上就是一次常規的 API 調用。因為當我們獲取到調用方法的指令之後,如何運行這個指令,已經和 GPT 的接口無關了。你可以自行決定這個 API 應該如何調用。

比如在上述天氣預報的查詢結果,就是調用了一下高德的天氣預報接口,返回的結果如下:

[{

"city": "杭州市",

"adcode": "330100",

"province": "浙江",

"reporttime": "2023-08-10 00:02:43",

"casts": [

{

"date": "2023-08-10",

"week": "4",

"dayweather": "雷陣雨",

"nightweather": "多雲",

"daytemp": "35",

"nighttemp": "25",

"daywind": "北",

"nightwind": "北",

"daypower": "≤3",

"nightpower": "≤3",

"daytemp_float": "35.0",

"nighttemp_float": "25.0"

},

{

"date": "2023-08-11",

"week": "5",

"dayweather": "晴",

"nightweather": "晴",

"daytemp": "35",

"nighttemp": "25",

"daywind": "北",

"nightwind": "北",

"daypower": "≤3",

"nightpower": "≤3",

"daytemp_float": "35.0",

"nighttemp_float": "25.0"

},

{

"date": "2023-08-12",

"week": "6",

"dayweather": "多雲",

"nightweather": "多雲",

"daytemp": "35",

"nighttemp": "26",

"daywind": "北",

"nightwind": "北",

"daypower": "≤3",

"nightpower": "≤3",

"daytemp_float": "35.0",

"nighttemp_float": "26.0"

},

{

"date": "2023-08-13",

"week": "7",

"dayweather": "雷陣雨",

"nightweather": "多雲",

"daytemp": "35",

"nighttemp": "25",

"daywind": "西南",

"nightwind": "西南",

"daypower": "≤3",

"nightpower": "≤3",

"daytemp_float": "35.0",

"nighttemp_float": "25.0"

}

]

}]

那返回的結果應該如何和 AI 的會話集成在一起?OpenAI 在 GPT 系列的消息類型中,專門為外部接口的請求結果,定義了 function這樣一種類型,與 usersystemassistant區分開來。(我個人猜測 GPT 應該會針對這類消息做優化,用於提取其中的有效信息)

我們在會話應用中,就需要按照 GPT 的規範,構造出一個 function的消息:

- role: function

name: realtimeWeather

content: "[{ "city": "杭州市", ... }]"

其中 name欄位是必填的,為這個方法的調用名稱。content欄位直接放入接口返回內容(需要轉成字符串)。

解讀 API 返回的結果

構造完成 function消息後,我們需要將 #1~#5 的消息再次發送給 GPT。此時的請求消息如下:

{

"messages": [

{

"role": "user",

"content": "我明天應該穿什麼?"

},

{

"role": "assistant",

"content": "請告訴你所在的城市?"

},

{

"role": "user",

"content": "杭州"

},

{

"role": "assistant",

"content": "{"name ": "realtimeWeather ","arguments ": "{\n \ "city": "杭州"\n}"}

},

{

"role": "function",

"name": "realtimeWeather",

"content": "..."

}

],

}

這一輪消息已經包含了用戶的原始問題(#1)、必要入參的收集(#2、#3)、調用 API 的 JSON 指令(#4)與 外部 API 調用的結果(#5)。另外之前需要傳入的 functions 欄位,已經不需要再傳入了。因為 functions只是為了讓 AI 來決策使用什麼外部方法。當外部方法已經完成調用後,就不再需要傳入。那接下來就是見證魔法的時刻:

{

"role": "assistant",

"content": "當前杭州的天氣是晴天,溫度在36度左右。明天預計會有雷陣雨,溫度在26-35度之間。因此,建議你今天穿短袖和短褲,明天則需要帶一把雨傘,並穿一些防水的衣物,同時也不要忘記防曬。"

}

GPT 在上述 5 條消息的輸入下,告訴我了我明天穿衣的建議,並貼心地提醒我記得帶一把傘。這一輪會話也就此結束。

黎明前的沉思

在 3 月份 OpenAI 開放 GPT-3.5 接口時,我十分激動,立即投入到大模型的交互與實現的研發中。一晃已經小半年過去了,LLM 的實踐也逐漸多了起來,但目之所及,大部分都是 ChatGPT 的復刻,並沒有出現 AI 與現有產品體系做到有機結合,實現 1 + 1 > 2 的效果.

我想其中的核心問題就是在於現有的AI模式,聊天會話的形態與現有的生產體系是格格不入的。如何無縫地將 JSON 化的指令信息與系統消息與集成到會話,如何讓現有的系統中友好地集成會話能力,兩個問題都是難啃的骨頭。

而 Function Call 的發布,我覺得是給社區提供了一種現有系統能力輕鬆集成進會話的實現範式。開發者只需要極低的研發成本,就可以為用戶帶來完全不同的產品體驗,進而引發更巨大的產品形態變革,用戶與介面的交互方式也會產生質的飛躍。

在之前很多人提的「顛覆現有的人機互動」,我認為 Function Call 的出現,才真正讓這個口號有了一個開始。

開源 Chat 應用的插件生態

我和我的小夥伴們,也自己做了一個 Lobe Chat 。本文演示的天氣插件,就是在我們自己做的這個 Chat 應用中實現的。

不過當前 Lobe Chat 仍然處於 0.x 的快速開發階段,暫時還沒有完成全量能力,因此我也不會過多介紹,只在本文提一些相關的插件應用。

上文的天氣插件只是一個示例,用於演示跑通插件會話鏈路。而我從自己的訴求出發,還做了兩個能「聯網」的插件:

一個是「網頁內容提取」,發送給 AI 一個 url,它就會自動幫我總結文章中的內容,並且我可以基於這篇文章進行多輪會話,按照我習慣的方式來獲取文章內容。

另一個則是「搜尋引擎」。讓 GPT 支持調用 Google 搜尋引擎的能力。比如我自定義的一個「前端專家」的角色中,為它開啟了搜尋引擎插件,同時告訴它,在不知道怎麼解決問題時,查一查搜尋引擎。

這樣當我問一些不在 GPT 知識領域的內容時,它會自動調用搜尋引擎,並給我一個總結後的結果。

而遇到一些更複雜的情況下,AI 也會嘗試「搜尋引擎」與「網頁內容提取」搭配使用。先通過搜尋引擎找可能的資料,然後再閱讀單篇內容,最後給我一個總結。

ChatGPT 的插件市場在 OpenAI 內部是被定性為「一個失敗的嘗試」,但我覺得他們在這次實踐後,提供出來的 Function Call ,對於技術應用來說卻是非常有價值的。

API 的開放,讓開源社區誕生不少少仿 ChatGPT 的開源 Chat 應用。如同 stable diffusion 相關的生態插件一般, Function Call 我想也會帶來同等規模的插件生態,還有很多很多的玩法有待社區探索。

開源 LLM 的下一城

當然,在國內守著 ChatGPT 是沒希望的,我們必須要支持本地大模型的才行。那隨著 LLaMA 2、ChatGLM 2、Qianwen 等一眾大模型的開源進程加速,大家現在已經不再愁沒模型可用了。但怎麼和大家已有的業務系統集成,真正落地生產,才是現階段關注的。

天氣插件的例子,大家應該可以發現,其實識別用戶意圖、調用外部 API 與基於 API 內容進行總結,是三個互相獨立的階段。而任何傳統應用,都可以在不大改造的情況下接入「用戶意圖識別」的能力,以增強現有系統的用戶體驗。這是目前階段我覺得性價比最高的大模型接入方案。

所以我覺得開源 LLM 們角逐的下一個領域,我想一定是 Function Call 類似的能力。譬如前段時間看到一個開源模型已經實現了一個 ToolLLaMA,在 16000+ 真實 API 數據集基礎上實現了類似 Function Call 的效果。

如果類似的能力在各大開源模型下默認集成,這個模型的生產可用能力一定會大大加強。

AI 時代的交互技術

在做 Lobe Chat 的天氣插件能力時,我一開始什麼也沒展示,直接讓 AI 做了內容的輸出。

但 AI 返回的內容是一坨純文本,看著費勁。於是乎又將其轉變成了表格(當然,最理想的應該是可視化圖表,偷懶了就沒搞)。

完成這個插件後,我有了一個更加深刻的感觸:人真的是一種很貪心的生物。總是想要別人給你結構化展示,輪到自己卻只想模糊輸出,且越少越好。

AI 時代的來臨,也無法改變人們喜歡直觀、簡潔、甚至偷懶的本性。所以鼓吹「未來所有設計師、前端都要失業,用戶只需要一個輸入框」的言論,大概也不可能成真。

人們喜歡結構化的展示,就意味著曾經的信息展示的範式仍然適用,而 AI 的出現可以幫助用戶實現更好的「模糊輸入」,減少大量頁面流程的跳轉、表單的填寫。用戶體驗可以在 AI 的加持下有個質的提升。

在這樣的時代,關注交互技術的設計師與前端們,甚至比過去更大有可為。

原文連結:

https://sspai.com/post/81986?utm_source=wechat&utm_medium=social

作者:空谷

責編:廣陵止息

/ 更多熱門文章 /

文章來源: https://twgreatdaily.com/zh-tw/77df1dca0e51dc940d896cd9234dae88.html