深入探討Function Calling:實現外部函式呼叫的工作原理

董瑞鹏發表於2024-05-28

引言

Function Calling 是一個允許大型語言模型(如 GPT)在生成文字的過程中呼叫外部函式或服務的功能。
Function Calling允許我們以 JSON 格式向 LLM 模型描述函式,並使用模型的固有推理能力來決定在生成響應之前是否呼叫該函式。模型本身不執行函式,而是生成包含函式名稱執行函式所需的引數JSON

function calling 執行原理

現在我們定義提示詞像大語言模型問一下當前北京的天氣?

因為 LLM 大語言模型缺乏實時資料,所以無法回答實時資料這種場景。

我們用SK來測試一下

Console.WriteLine("===>沒有設定function calling=<===");
{
    var kernel = Kernel.CreateBuilder().AddAzureOpenAIChatCompletion(config.ModelId,
            endpoint: config.Endpoint,
    apiKey: config.ApiKey).Build();
    var template = "當前北京的天氣?";
    Console.WriteLine($"User: {template}");
    var function = kernel.CreateFunctionFromPrompt(template);
    var functionResult = await function.InvokeAsync(kernel);
    Console.WriteLine($"Assistant:{functionResult}");
}

輸出:

User: 當前北京的天氣?
Assistant:對不起,作為一個AI,我無法為你提供實時資訊。你可以檢視可信的天氣應用或網站來獲取當前北京的天氣。

這時候就需要用到 LLMFunction Calling 功能來幫助回答使用者的問題

使用 OpenAI API function calling

OpenAIfunction calling的核心是我們將Prompts 提示詞和可用函式列表一起傳送給LLM

OpenAI Chat Completions 介面

{
  "tool_choice": "auto",
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant."
    },
    {
      "role": "user",
      "content": "我想知道現在北京的天氣狀況"
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "Get_Weather_For_City",
        "description": "獲取指定城市的天氣",
        "parameters": {
          "type": "object",
          "properties": {
            "cityName": {
              "type": "string",
              "description": "城市名"
            }
          }
        }
      }
    }
  ]
}

核心引數解釋

tool_choice:

這個引數決定了模型是否應該自動選擇是否呼叫函式。值為 "auto" 表示模型將根據情況自動決定是否呼叫函式。 預設情況下,如果請求中不存在任何函式,則將其設定為“none”,則設定為“auto”

tools

tools 部分定義了一個函式,這個函式可以被 OpenAI 的模型呼叫。以下是 tools 部分引數的簡單解釋:

  • type: 指定了這個工具的型別,這裡是 "function",表示這是一個函式呼叫。

  • function: 包含函式的詳細資訊,是一個物件。

    • name: 函式的名稱,這裡是 "Get_Weather_For_City",這是呼叫時使用的函式名。

    • description: 函式的描述,這裡是 "獲取指定城市的天氣",用於說明這個函式的作用。

    • parameters: 定義了函式呼叫時需要的引數,是一個物件。

      • type: 引數物件的型別,這裡是 "object",表示引數是一個物件型別。

      • properties: 包含具體的引數定義,是一個物件,每個屬性對應一個引數。

        • cityName: 這是一個引數的名稱,表示城市名稱。
          • type: 此引數的型別,這裡是 "string",表示引數應該是一個字串。
          • description: 引數的描述,這裡是 "城市名",用於解釋這個引數的意義。

這個 tools 部分定義了一個名為 Get_Weather_For_City 的函式,它需要一個名為 cityName 的字串引數,用於指定想要查詢天氣的城市。當模型需要呼叫這個函式時,它將使用這個引數來獲取相應的天氣資訊。

function calling 輸出

{
  "id": "chatcmpl-9TOuIqnuMirU3BUDluCrHMTlsjz97",
  "object": "chat.completion",
  "created": 1716794282,
  "model": "gpt-4",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_DQU6OKHWyv3HVLyWVjSRqvwZ",
            "type": "function",
            "function": {
              "name": "Get_Weather_For_City",
              "arguments": "{\n  \"cityName\": \"北京\"\n}"
            }
          }
        ]
      },
      "logprobs": null,
      "finish_reason": "tool_calls"
    }
  ],
  "usage": {
    "prompt_tokens": 83,
    "completion_tokens": 20,
    "total_tokens": 103
  },
  "system_fingerprint": null
}

最核心的方法是tool_calls回參裡面返回了我們需要的方法名和一個 json 引數 比如"{\n \"cityName\": \"北京\"\n}"包含了我們的引數和值。

返回函式結果上下文

{
    "max_tokens": 3000,
    "tool_choice": "auto",
    "messages": [
        {
            "role": "system",
            "content": "You are a helpful assistant."
        },
        {
            "role": "user",
            "content": "我想知道北京的天氣狀況"
        },
        {
            "role": "assistant",
            "function_call": {
                "name": "Get_Weather_For_City",
                "arguments": "{\n  \"cityName\": \"北京\"\n}"
            }
        },
        {
            "role": "function",
            "name": "Get_Weather_For_City",
            "content": "27度,晴朗"
        }
    ],
    "tools": [
        {
            "type": "function",
            "function": {
                "name": "Get_Weather_For_City",
                "description": "獲取指定城市的天氣",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "cityName": {
                            "type": "string",
                            "description": "城市名"
                        }
                    }
                }
            }
        },
        {

需要把上下文資訊和function callingresult回答的資訊傳給LLM

  • ToolCall上下文資訊
{
  "role": "assistant",
  "function_call": {
    "name": "Get_Weather_For_City",
    "arguments": "{\n  \"cityName\": \"北京\"\n}"
  }
}
  • ToolCallResponse
{
  "role": "function",
  "name": "Get_Weather_For_City",
  "content": "27度,晴朗"
}

LLM 輸出

{
  "id": "chatcmpl-9TRZBqCcRMBYIojuZimio6GOpsTi4",
  "object": "chat.completion",
  "created": 1716804505,
  "model": "gpt-4",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "北京的天氣狀況是27度,晴朗。"
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 133,
    "completion_tokens": 19,
    "total_tokens": 152
  },
  "system_fingerprint": null
}

到現在為止簡單的function calling的簡單呼叫已經完成了
具體的流程可以總結為

image

可以看到function calling跟大預言模型至少有兩次互動的的過程

總結

根據文件中的描述,OpenAI的函式呼叫(function calling)過程可以簡化為以下幾個步驟,並且可以用一個流程圖來表示:

image

  1. 使用者提出問題。
  2. 系統接收到問題,並檢查是否有可用的函式可以呼叫。
  3. 如果有,系統會生成一個工具呼叫請求(ToolCall),併傳送給應用程式。
  4. 應用程式執行請求的函式,並返回結果。
  5. 系統將函式的響應(ToolCallResponse)傳送回 LLM 模型。
  6. LLM 模型使用這個響應來生成最終的使用者響應。

下面是一個簡化的流程圖,描述了上述過程:

在這個流程圖中:

  • A 代表使用者。
  • B 是使用者提出的問題。
  • C 是 LLM 模型,它檢查是否有函式可以呼叫。
  • D 是生成工具呼叫(ToolCall)的步驟。
  • E 是應用程式,它接收 ToolCall 並執行相應的函式。
  • F 是應用程式返回的 ToolCallResponse,即函式執行的結果。
  • G 是 LLM 模型,它使用 ToolCallResponse 來生成使用者響應。
  • H 是最終接收到使用者響應的使用者。

這個流程圖是基於文件內容的簡化表示,實際的系統可能包含更多的細節和步驟。

最後

本章的主要了解function calling及其工作原理的簡單介紹。在下一篇部落格中,我們 x 學習在Semantic kernel下使用使用function calling

參考資料

openai-function-calling

相關文章