在開發 AI 應用時,很多開發者對語言模型的函式呼叫機制存在誤解。實際上,當 LLM 回傳函式呼叫時,它只是提供函式名稱和相關引數,並不會實際執行該函式。這個重要的區別決定了我們如何設計與實作 AI 系統的互動流程。

函式呼叫的完整生命週期

當 LLM 建議呼叫某個函式時,開發者必須在客戶端應用程式中完成一個閉環流程。

函式呼叫生命週期流程圖

此圖詳細描繪了從使用者請求到最終 AI 回應的完整函式呼叫流程。

PlantUML 圖表

這個流程確保了 AI 系統的安全性和可控性,同時也為開發者提供了靈活整合外部工具的能力。

實作函式呼叫:推薦系統範例

讓我們透過一個實際的推薦系統範例來理解函式呼叫的完整流程。

1. 推薦函式的實作

首先,我們需要定義一個推薦函式,它將根據不同主題提供建議。

import json

def recommend(topic, rating="good"):
    """根據主題和評分提供推薦"""
    if "time travel" in topic.lower():
        return json.dumps({"topic": "time travel", "recommendation": "Back to the Future", "rating": rating})
    elif "recipe" in topic.lower():
        return json.dumps({"topic": "recipe", "recommendation": "Classic Carbonara", "rating": rating})
    elif "gift" in topic.lower():
        return json.dumps({"topic": "gift", "recommendation": "A good book", "rating": rating})
    else:
        return json.dumps({"topic": topic, "recommendation": "unknown"})

此函式根據輸入的 topic 提供不同的推薦,並以 JSON 格式回傳結果。

2. 建立對話請求

接下來,我們需要構建傳送給 LLM 的請求,包含使用者訊息和工具定義。

user_request = "請為我推薦以下三樣東西:一部時間旅行電影、一道食譜和一份禮物。"
messages = [{"role": "user", "content": user_request}]
tools = [
    {
        "type": "function",
        "function": {
            "name": "recommend",
            "description": "為任何主題提供推薦。",
            "parameters": {
                "type": "object",
                "properties": {
                    "topic": {"type": "string", "description": "需要推薦的主題。"},
                    "rating": {"type": "string", "description": "推薦的評分。", "enum": ["good", "bad", "terrible"]},
                },
                "required": ["topic"],
            },
        },
    }
]

tools 陣列詳細定義了 recommend 函式的結構,這能幫助 LLM 正確地生成函式呼叫。

3. 處理函式呼叫與執行

當 LLM 決定使用函式時,我們需要提取並執行這些函式呼叫。

from openai import OpenAI
client = OpenAI() # 假設 API 金鑰已設定

# 第一步:讓 LLM 生成函式呼叫
response = client.chat.completions.create(
    model="gpt-3.5-turbo-1106",
    messages=messages,
    tools=tools,
    tool_choice="auto",
)
response_message = response.choices[0].message
tool_calls = response_message.tool_calls

# 第二步:執行函式並收集結果
if tool_calls:
    available_functions = {"recommend": recommend}
    messages.append(response_message) # 將 AI 的回應(包含函式呼叫)加入歷史

    for tool_call in tool_calls:
        function_name = tool_call.function.name
        function_to_call = available_functions[function_name]
        function_args = json.loads(tool_call.function.arguments)
        function_response = function_to_call(**function_args)
        
        messages.append({
            "tool_call_id": tool_call.id,
            "role": "tool",
            "name": function_name,
            "content": function_response,
        })

    # 第三步:將函式結果傳回 LLM 以生成最終回應
    second_response = client.chat.completions.create(
        model="gpt-3.5-turbo-1106",
        messages=messages,
    )
    print(second_response.choices[0].message.content)

這個流程的核心在於兩次 API 呼叫:第一次是為了讓 LLM 生成函式呼叫的指令,第二次是將函式執行的結果傳回,讓 LLM 生成最終的自然語言回應。

進階框架:Microsoft Semantic Kernel

雖然手動處理函式呼叫是可行的,但在複雜系統中會變得繁瑣。Semantic Kernel (SK) 是微軟開發的開源框架,旨在簡化這一過程。

SK 的核心是外掛 (Plugins),它們是技能和函式的封裝。SK 使用與 OpenAI 相容的定義,使其能夠無縫消費和發布外掛,極大地簡化了構建複雜 AI 應用的過程。對於需要整合多個工具和服務的複雜代理系統,採用 SK 這樣的框架是更佳的選擇。

結論

函式呼叫機制為 LLM 提供了與外部世界互動的能力,極大地擴充了 AI 系統的實用性。理解 LLM 僅生成呼叫指令而非直接執行的核心概念,是構建安全、有效 AI 應用的基礎。透過本文的範例,我們展示了從函式定義到結果整合的完整流程。對於更複雜的應用,Semantic Kernel 等框架提供了更結構化、更強大的解決方案,值得開發者深入探索。