共用方式為


使用聊天完成呼叫函式

聊天完成功能最強大的功能是從模型呼叫函式的能力。 這可讓您建立可與現有程式代碼互動的聊天機器人,讓商務程式自動化、建立代碼段等等。

透過 Semantic Kernel,我們藉由自動將函式及其參數描述至模型,然後處理模型與程式碼之間的來回通訊,來簡化使用函式呼叫的程式。

不過,使用函式呼叫時,最好瞭解幕後實際發生的情況,以便您將程式代碼優化並充分利用此功能。

函式呼叫的運作方式

當您對已啟用函式呼叫的模型提出要求時,Semantic Kernel 會執行下列步驟:

步驟 描述:
1 串行化函式 核心中的所有可用函式(及其輸入參數)都會使用 JSON 架構串行化。
2 將訊息和函式傳送至模型 串行化函式(和目前的聊天記錄)會以輸入的一部分傳送至模型。
3 模型會處理輸入 模型會處理輸入併產生回應。 回應可以是聊天訊息或函式呼叫
4 處理回應 如果回應是聊天訊息,則會傳回給開發人員,以將回應列印到畫面。 不過,如果回應是函數調用,Semantic Kernel 會擷取函式名稱和其參數。
5 叫用函式 擷取的函式名稱和參數可用來叫用核心中的函式。
6 傳回函式結果 然後,函式的結果會傳回至模型,作為聊天記錄的一部分。 然後重複步驟 2-6,直到模型傳送終止訊號為止

下圖說明函式呼叫的程式:

語意核心函式呼叫

下一節會使用具體範例來說明函式呼叫的運作方式。

範例:訂購披薩

假設您有一個外掛程式,可讓使用者訂購披薩。 外掛程式具有下列功能:

  1. get_pizza_menu:傳回可用的披薩清單
  2. add_pizza_to_cart:將披薩新增至使用者的購物車
  3. remove_pizza_from_cart:從使用者的購物車中移除披薩
  4. get_pizza_from_cart:傳回使用者購物車中披薩的特定詳細數據
  5. get_cart:傳回使用者的目前購物車
  6. checkout:查看使用者的購物車

在 C# 中,外掛程式看起來可能如下所示:

public class OrderPizzaPlugin(
    IPizzaService pizzaService,
    IUserContext userContext,
    IPaymentService paymentService)
{
    [KernelFunction("get_pizza_menu")]
    public async Task<Menu> GetPizzaMenuAsync()
    {
        return await pizzaService.GetMenu();
    }

    [KernelFunction("add_pizza_to_cart")]
    [Description("Add a pizza to the user's cart; returns the new item and updated cart")]
    public async Task<CartDelta> AddPizzaToCart(
        PizzaSize size,
        List<PizzaToppings> toppings,
        int quantity = 1,
        string specialInstructions = ""
    )
    {
        Guid cartId = userContext.GetCartId();
        return await pizzaService.AddPizzaToCart(
            cartId: cartId,
            size: size,
            toppings: toppings,
            quantity: quantity,
            specialInstructions: specialInstructions);
    }

    [KernelFunction("remove_pizza_from_cart")]
    public async Task<RemovePizzaResponse> RemovePizzaFromCart(int pizzaId)
    {
        Guid cartId = userContext.GetCartId();
        return await pizzaService.RemovePizzaFromCart(cartId, pizzaId);
    }

    [KernelFunction("get_pizza_from_cart")]
    [Description("Returns the specific details of a pizza in the user's cart; use this instead of relying on previous messages since the cart may have changed since then.")]
    public async Task<Pizza> GetPizzaFromCart(int pizzaId)
    {
        Guid cartId = await userContext.GetCartIdAsync();
        return await pizzaService.GetPizzaFromCart(cartId, pizzaId);
    }

    [KernelFunction("get_cart")]
    [Description("Returns the user's current cart, including the total price and items in the cart.")]
    public async Task<Cart> GetCart()
    {
        Guid cartId = await userContext.GetCartIdAsync();
        return await pizzaService.GetCart(cartId);
    }

    [KernelFunction("checkout")]
    [Description("Checkouts the user's cart; this function will retrieve the payment from the user and complete the order.")]
    public async Task<CheckoutResponse> Checkout()
    {
        Guid cartId = await userContext.GetCartIdAsync();
        Guid paymentId = await paymentService.RequestPaymentFromUserAsync(cartId);

        return await pizzaService.Checkout(cartId, paymentId);
    }
}

接著,您會將此外掛程式新增至核心,如下所示:

IKernelBuilder kernelBuilder = new KernelBuilder();
kernelBuilder..AddAzureOpenAIChatCompletion(
    deploymentName: "NAME_OF_YOUR_DEPLOYMENT",
    apiKey: "YOUR_API_KEY",
    endpoint: "YOUR_AZURE_ENDPOINT"
);
kernelBuilder.Plugins.AddFromType<OrderPizzaPlugin>("OrderPizza");
Kernel kernel = kernelBuilder.Build();

在 Python 中,外掛程式看起來可能如下所示:

from semantic_kernel.functions import kernel_function

class OrderPizzaPlugin:
    def __init__(self, pizza_service, user_context, payment_service):
        self.pizza_service = pizza_service
        self.user_context = user_context
        self.payment_service = payment_service

    @kernel_function
    async def get_pizza_menu(self):
        return await self.pizza_service.get_menu()

    @kernel_function(
        description="Add a pizza to the user's cart; returns the new item and updated cart"
    )
    async def add_pizza_to_cart(self, size: PizzaSize, toppings: List[PizzaToppings], quantity: int = 1, special_instructions: str = ""):
        cart_id = await self.user_context.get_cart_id()
        return await self.pizza_service.add_pizza_to_cart(cart_id, size, toppings, quantity, special_instructions)

    @kernel_function(
        description="Remove a pizza from the user's cart; returns the updated cart"
    )
    async def remove_pizza_from_cart(self, pizza_id: int):
        cart_id = await self.user_context.get_cart_id()
        return await self.pizza_service.remove_pizza_from_cart(cart_id, pizza_id)

    @kernel_function(
        description="Returns the specific details of a pizza in the user's cart; use this instead of relying on previous messages since the cart may have changed since then."
    )
    async def get_pizza_from_cart(self, pizza_id: int):
        cart_id = await self.user_context.get_cart_id()
        return await self.pizza_service.get_pizza_from_cart(cart_id, pizza_id)

    @kernel_function(
        description="Returns the user's current cart, including the total price and items in the cart."
    )
    async def get_cart(self):
        cart_id = await self.user_context.get_cart_id()
        return await self.pizza_service.get_cart(cart_id)

    @kernel_function(
        description="Checkouts the user's cart; this function will retrieve the payment from the user and complete the order."
    )
    async def checkout(self):
        cart_id = await self.user_context.get_cart_id()
        payment_id = await self.payment_service.request_payment_from_user(cart_id)
        return await self.pizza_service.checkout(cart_id, payment_id)

接著,您會將此外掛程式新增至核心,如下所示:

from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.connectors.ai.chat_completion_client_base import ChatCompletionClientBase

kernel = Kernel()
kernel.add_service(AzureChatCompletion(model_id, endpoint, api_key))

# Create the services needed for the plugin: pizza_service, user_context, and payment_service
# ...

# Add the plugin to the kernel
kernel.add_plugin(OrderPizzaPlugin(pizza_service, user_context, payment_service), plugin_name="OrderPizza")

在 Java 中,外掛程式看起來可能如下所示:

public class OrderPizzaPlugin {

    private final PizzaService pizzaService;
    private final HttpSession userContext;
    private final PaymentService paymentService;

    public OrderPizzaPlugin(
        PizzaService pizzaService,
        UserContext userContext,
        PaymentService paymentService)
    {
      this.pizzaService = pizzaService;
      this.userContext = userContext;
      this.paymentService = paymentService;
    }

    @DefineKernelFunction(name = "get_pizza_menu", description = "Get the pizza menu.", returnType = "com.pizzashop.Menu")
    public Mono<Menu> getPizzaMenuAsync()
    {
        return pizzaService.getMenu();
    }

    @DefineKernelFunction(
        name = "add_pizza_to_cart", 
        description = "Add a pizza to the user's cart",
        returnDescription = "Returns the new item and updated cart", 
        returnType = "com.pizzashop.CartDelta")
    public Mono<CartDelta> addPizzaToCart(
        @KernelFunctionParameter(name = "size", description = "The size of the pizza", type = com.pizzashopo.PizzaSize.class, required = true)
        PizzaSize size,
        @KernelFunctionParameter(name = "toppings", description = "The toppings to add to the the pizza", type = com.pizzashopo.PizzaToppings.class)
        List<PizzaToppings> toppings,
        @KernelFunctionParameter(name = "quantity", description = "How many of this pizza to order", type = Integer.class, defaultValue = "1")
        int quantity,
        @KernelFunctionParameter(name = "specialInstructions", description = "Special instructions for the order",)
        String specialInstructions
    )
    {
        UUID cartId = userContext.getCartId();
        return pizzaService.addPizzaToCart(
            cartId,
            size,
            toppings,
            quantity,
            specialInstructions);
    }

    @DefineKernelFunction(name = "remove_pizza_from_cart", description = "Remove a pizza from the cart.", returnType = "com.pizzashop.RemovePizzaResponse")
    public Mono<RemovePizzaResponse> removePizzaFromCart(
        @KernelFunctionParameter(name = "pizzaId", description = "Id of the pizza to remove from the cart", type = Integer.class, required = true)
        int pizzaId)
    {
        UUID cartId = userContext.getCartId();
        return pizzaService.removePizzaFromCart(cartId, pizzaId);
    }

    @DefineKernelFunction(
        name = "get_pizza_from_cart", 
        description = "Returns the specific details of a pizza in the user's cart; use this instead of relying on previous messages since the cart may have changed since then.",
        returnType = "com.pizzashop.Pizza")
    public Mono<Pizza> getPizzaFromCart(
        @KernelFunctionParameter(name = "pizzaId", description = "Id of the pizza to get from the cart", type = Integer.class, required = true)
        int pizzaId)
    {

        UUID cartId = userContext.getCartId();
        return pizzaService.getPizzaFromCart(cartId, pizzaId);
    }

    @DefineKernelFunction(
        name = "get_cart", 
        description = "Returns the user's current cart, including the total price and items in the cart.",
        returnType = "com.pizzashop.Cart")

    public Mono<Cart> getCart()
    {
        UUID cartId = userContext.getCartId();
        return pizzaService.getCart(cartId);
    }


    @DefineKernelFunction(
        name = "checkout", 
        description = "Checkouts the user's cart; this function will retrieve the payment from the user and complete the order.",
        returnType = "com.pizzashop.CheckoutResponse")
    public Mono<CheckoutResponse> Checkout()
    {
        UUID cartId = userContext.getCartId();
        return paymentService.requestPaymentFromUser(cartId)
                .flatMap(paymentId -> pizzaService.checkout(cartId, paymentId));
    }
}

接著,您會將此外掛程式新增至核心,如下所示:

OpenAIAsyncClient client = new OpenAIClientBuilder()
  .credential(openAIClientCredentials)
  .buildAsyncClient();

ChatCompletionService chat = OpenAIChatCompletion.builder()
  .withModelId(modelId)
  .withOpenAIAsyncClient(client)
  .build();

KernelPlugin plugin = KernelPluginFactory.createFromObject(
  new OrderPizzaPlugin(pizzaService, userContext, paymentService),
  "OrderPizzaPlugin"
);

Kernel kernel = Kernel.builder()
    .withAIService(ChatCompletionService.class, chat)
    .withPlugin(plugin)
    .build();

1) 串行化函式

當您使用 OrderPizzaPlugin建立核心時,核心會自動串行化函式及其參數。 這是必要的,讓模型可以瞭解函式及其輸入。

針對上述外掛程式,串行化函式看起來會像這樣:

[
  {
    "type": "function",
    "function": {
      "name": "OrderPizza-get_pizza_menu",
      "parameters": {
        "type": "object",
        "properties": {},
        "required": []
      }
    }
  },
  {
    "type": "function",
    "function": {
      "name": "OrderPizza-add_pizza_to_cart",
      "description": "Add a pizza to the user's cart; returns the new item and updated cart",
      "parameters": {
        "type": "object",
        "properties": {
          "size": {
            "type": "string",
            "enum": ["Small", "Medium", "Large"]
          },
          "toppings": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": ["Cheese", "Pepperoni", "Mushrooms"]
            }
          },
          "quantity": {
            "type": "integer",
            "default": 1,
            "description": "Quantity of pizzas"
          },
          "specialInstructions": {
            "type": "string",
            "default": "",
            "description": "Special instructions for the pizza"
          }
        },
        "required": ["size", "toppings"]
      }
    }
  },
  {
    "type": "function",
    "function": {
      "name": "OrderPizza-remove_pizza_from_cart",
      "parameters": {
        "type": "object",
        "properties": {
          "pizzaId": {
            "type": "integer"
          }
        },
        "required": ["pizzaId"]
      }
    }
  },
  {
    "type": "function",
    "function": {
      "name": "OrderPizza-get_pizza_from_cart",
      "description": "Returns the specific details of a pizza in the user's cart; use this instead of relying on previous messages since the cart may have changed since then.",
      "parameters": {
        "type": "object",
        "properties": {
          "pizzaId": {
            "type": "integer"
          }
        },
        "required": ["pizzaId"]
      }
    }
  },
  {
    "type": "function",
    "function": {
      "name": "OrderPizza-get_cart",
      "description": "Returns the user's current cart, including the total price and items in the cart.",
      "parameters": {
        "type": "object",
        "properties": {},
        "required": []
      }
    }
  },
  {
    "type": "function",
    "function": {
      "name": "OrderPizza-checkout",
      "description": "Checkouts the user's cart; this function will retrieve the payment from the user and complete the order.",
      "parameters": {
        "type": "object",
        "properties": {},
        "required": []
      }
    }
  }
]

這裡有一些值得注意的事項,可能會影響聊天完成的效能和品質:

  1. 函式架構 的詳細資訊 – 將模型使用的函式串行化不會免費使用。 架構越詳細,模型必須處理的令牌越多,這可能會減緩回應時間並增加成本。

    提示

    盡可能讓函式保持簡單。 在上述範例中,您會發現並非所有函式都有函式名稱為自我說明的描述。 這是刻意減少令牌數目。 參數也保持簡單;模型不需要知道的任何專案(例如 cartIdpaymentId)都隱藏起來。 這項資訊是由內部服務所提供。

    注意

    您不需要擔心的其中一件事就是傳回類型的複雜度。 您會發現傳回型別不會在架構中串行化。 這是因為模型不需要知道傳回類型來產生回應。 不過,在步驟 6 中,我們將瞭解如何過度詳細傳回類型會影響聊天完成的品質。

  2. 參數類型 – 使用架構,您可以指定每個參數的類型。 對於模型瞭解預期的輸入而言,這很重要。 在上述範例中 size ,參數是列舉,而 toppings 參數是列舉的陣列。 這有助於模型產生更精確的回應。

    提示

    盡可能避免使用 string 做為參數類型。 模型無法推斷字串的類型,這可能會導致模棱兩可的回應。 請盡可能使用列舉或其他類型(例如、 intfloat和複雜類型)。

  3. 必要參數 - 您也可以指定需要哪些參數。 這對模型而言很重要,以瞭解函式實際需要哪些參數才能運作。 稍後在步驟 3 中,模型會使用這項資訊,視需要提供最少的資訊來呼叫函式。

    提示

    只有在實際需要參數時,才將參數標示為必要。 這有助於模型更快速且準確地呼叫函式。

  4. 函式描述 – 函式描述是選擇性的 ,但可協助模型產生更精確的回應。 特別是,描述可以告訴模型回應預期的內容,因為傳回型別未在架構中串行化。 如果模型不正確地使用函式,您也可以新增描述來提供範例和指引。

    例如,在函式中 get_pizza_from_cart ,描述會告知使用者使用此函式,而不是依賴先前的訊息。 這很重要,因為購物車可能自上次訊息後變更過。

    提示

    新增描述之前,請先詢問模型是否需要此資訊來產生回應。 如果沒有,請考慮將其排除以降低詳細資訊。 如果模型難以正確使用函式,您隨時都可以稍後新增描述。

  5. 外掛程式名稱 – 如您在串行化函式中所見,每個函式都有 屬性 name 。 語意核心會使用外掛程式名稱來命名空間函式。 這很重要,因為它可讓您有多個具有相同名稱函式的外掛程式。 例如,您可能有多個搜尋服務的外掛程式,每個都有自己的 search 功能。 藉由命名函式,您可以避免衝突,並讓模型更容易瞭解要呼叫的函式。

    知道這一點,您應該選擇唯一且描述性的外掛程式名稱。 在上述範例中,外掛程式名稱為 OrderPizza。 這清楚說明函式與訂購披薩有關。

    提示

    選擇外掛程式名稱時,建議您移除「外掛程式」或「服務」等多餘的字組。 這有助於減少詳細資訊,並讓外掛程式名稱更容易瞭解模型。

2) 將訊息和函式傳送至模型

一旦函式串行化,它們就會連同目前的聊天記錄一起傳送至模型。 這可讓模型瞭解交談的內容和可用的函式。

在此案例中,我們可以想像使用者要求助理在購物車中新增披薩:

ChatHistory chatHistory = [];
chatHistory.AddUserMessage("I'd like to order a pizza!");
chat_history = ChatHistory()
chat_history.add_user_message("I'd like to order a pizza!")
ChatHistory chatHistory = new ChatHistory();
chatHistory.addUserMessage("I'd like to order a pizza!");

然後,我們可以將此聊天記錄和串行化函式傳送至模型。 模型會使用這項信息來判斷回應的最佳方式。

IChatCompletionService chatCompletion = kernel.GetRequiredService<IChatCompletionService>();

OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new() 
{
    FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};

ChatResponse response = await chatCompletion.GetChatMessageContentAsync(
    chatHistory,
    executionSettings: openAIPromptExecutionSettings,
    kernel: kernel)
chat_completion = kernel.get_service(type=ChatCompletionClientBase)

execution_settings = AzureChatPromptExecutionSettings()
execution_settings.function_choice_behavior = FunctionChoiceBehavior.Auto()

response = (await chat_completion.get_chat_message_contents(
      chat_history=history,
      settings=execution_settings,
      kernel=kernel,
      arguments=KernelArguments(),
  ))[0]
ChatCompletionService chatCompletion = kernel.getService(I)ChatCompletionService.class);

InvocationContext invocationContext = InvocationContext.builder()
    .withToolCallBehavior(ToolCallBehavior.allowAllKernelFunctions(false));

List<ChatResponse> responses = chatCompletion.getChatMessageContentsAsync(
    chatHistory,
    kernel,
    invocationContext).block();

注意

此範例會 FunctionChoiceBehavior.Auto() 使用行為,這是少數可用的行為之一。 如需其他函式選擇行為的詳細資訊,請參閱函 式選擇行為一文

3) 模型會處理輸入

透過聊天記錄和串行化函式,模型可以判斷回應的最佳方式。 在此情況下,模型會辨識使用者想要訂購披薩。 模型可能會 想要 呼叫 add_pizza_to_cart 函式,但因為我們將大小和配料指定為必要參數,因此模型會要求使用者提供這項資訊:

Console.WriteLine(response);
chatHistory.AddAssistantMessage(response);

// "Before I can add a pizza to your cart, I need to
// know the size and toppings. What size pizza would
// you like? Small, medium, or large?"
print(response)
chat_history.add_assistant_message(response)

# "Before I can add a pizza to your cart, I need to
# know the size and toppings. What size pizza would
# you like? Small, medium, or large?"
responses.forEach(response -> System.out.printlin(response.getContent());
chatHistory.addAll(responses);

// "Before I can add a pizza to your cart, I need to
// know the size and toppings. What size pizza would
// you like? Small, medium, or large?"

由於模型希望使用者下一步回應,因此終止訊號會傳送至 Semantic Kernel,以停止自動呼叫函式,直到使用者響應為止。

此時,使用者可以回應他們想要訂購的披薩大小和配料:

chatHistory.AddUserMessage("I'd like a medium pizza with cheese and pepperoni, please.");

response = await chatCompletion.GetChatMessageContentAsync(
    chatHistory,
    kernel: kernel)
chat_history.add_user_message("I'd like a medium pizza with cheese and pepperoni, please.")

response = (await chat_completion.get_chat_message_contents(
    chat_history=history,
    settings=execution_settings,
    kernel=kernel,
    arguments=KernelArguments(),
))[0]
chatHistory.addUserMessage("I'd like a medium pizza with cheese and pepperoni, please.");

responses = chatCompletion.GetChatMessageContentAsync(
    chatHistory,
    kernel,
    null).block();

模型現在具有必要的資訊,現在可以使用使用者的輸入來呼叫 add_pizza_to_cart 函式。 在幕後,它會將新訊息新增至聊天記錄,如下所示:

"tool_calls": [
    {
        "id": "call_abc123",
        "type": "function",
        "function": {
            "name": "OrderPizzaPlugin-add_pizza_to_cart",
            "arguments": "{\n\"size\": \"Medium\",\n\"toppings\": [\"Cheese\", \"Pepperoni\"]\n}"
        }
    }
]

提示

請務必記住,您需要的每個自變數都必須由模型產生。 這表示花費令牌來產生回應。 避免需要許多令牌的自變數(例如 GUID)。 例如,請注意,我們使用 intpizzaId。 要求模型傳送一到兩位數的數位比要求 GUID 要容易得多。

重要

此步驟讓函式呼叫如此強大。 先前,AI 應用程式開發人員必須建立個別的程式,以擷取意圖和位置填滿函式。 透過函式呼叫,模型可以決定 何時 呼叫函式,以及 要提供哪些 資訊。

4) 處理回應

當 Semantic Kernel 收到來自模型的回應時,它會檢查回應是否為函式呼叫。 如果是,語意核心會擷取函式名稱和其參數。 在此情況下,函式名稱為 OrderPizzaPlugin-add_pizza_to_cart,而自變數是披薩的大小和配料。

利用這項資訊,Semantic Kernel 可以將輸入封送處理至適當的類型,並將其傳遞至 中的OrderPizzaPluginadd_pizza_to_cart式。 在這裡範例中,自變數會以 JSON 字串的形式產生,但由 Semantic Kernel 還原串行化為 PizzaSize 列舉和 List<PizzaToppings>

注意

將輸入封送處理到正確的類型是使用語意核心的主要優點之一。 模型的所有專案都以 JSON 物件的形式出現,但 Semantic Kernel 可以自動將這些物件還原串行化為函式的正確類型。

封送處理輸入之後,Semantic Kernel 也可以將函式呼叫新增至聊天記錄:

chatHistory.Add(
    new() {
        Role = AuthorRole.Assistant,
        Items = [
            new FunctionCallContent(
                functionName: "add_pizza_to_cart",
                pluginName: "OrderPizza",
                id: "call_abc123",
                arguments: new () { {"size", "Medium"}, {"toppings", ["Cheese", "Pepperoni"]} }
            )
        ]
    }
);
from semantic_kernel.contents import ChatMessageContent, FunctionCallContent
from semantic_kernel.contents.utils.author_role import AuthorRole

chat_history.add_message(
    ChatMessageContent(
        role=AuthorRole.ASSISTANT,
        items=[
            FunctionCallContent(
                name="OrderPizza-add_pizza_to_cart",
                id="call_abc123",
                arguments=str({"size": "Medium", "toppings": ["Cheese", "Pepperoni"]})
            )
        ]
    )
)

Java 的語意核心會在自動叫用工具呼叫行為為 false 時處理與 C# 和 Python 不同的函式。 您不會將函數調用內容新增至聊天記錄;相反地,應用程式會負責叫用函式呼叫。 跳至下一節「叫用函式」,以在自動叫用為 false 時處理 Java 中的函式呼叫範例。

5) 叫用函式

一旦 Semantic Kernel 具有正確的類型,它最後就可以叫用函 add_pizza_to_cart 式。 因為外掛程式使用相依性插入,因此函式可以與外部服務互動,例如 pizzaService ,並將 userContext 披薩新增至使用者的購物車。

不過,並非所有函式都會成功。 如果函式失敗,語意核心可以處理錯誤,並提供模型的默認回應。 這可讓模型了解發生錯誤,併產生對用戶的回應。

提示

為了確保模型可以自行更正,請務必提供錯誤訊息,以清楚傳達發生錯誤的內容,以及如何修正錯誤。 這可協助模型使用正確的資訊重試函式呼叫。

注意

語意核心預設會自動叫用函式。 不過,如果您想要手動管理函式調用,您可以啟用手動函式調用模式。 如需如何執行這項操作的詳細資訊,請參閱函 式調用一文

6) 傳回函式結果

叫用函式之後,函式結果會傳回至模型作為聊天記錄的一部分。 這可讓模型瞭解交談的內容,併產生後續的回應。

在幕後,Semantic Kernel 會從工具角色將新訊息新增至聊天記錄,如下所示:

chatHistory.Add(
    new() {
        Role = AuthorRole.Tool,
        Items = [
            new FunctionResultContent(
                functionName: "add_pizza_to_cart",
                pluginName: "OrderPizza",
                id: "0001",
                result: "{ \"new_items\": [ { \"id\": 1, \"size\": \"Medium\", \"toppings\": [\"Cheese\",\"Pepperoni\"] } ] }"
            )
        ]
    }
);
from semantic_kernel.contents import ChatMessageContent, FunctionResultContent
from semantic_kernel.contents.utils.author_role import AuthorRole

chat_history.add_message(
    ChatMessageContent(
        role=AuthorRole.TOOL,
        items=[
            FunctionResultContent(
                name="OrderPizza-add_pizza_to_cart",
                id="0001",
                result="{ \"new_items\": [ { \"id\": 1, \"size\": \"Medium\", \"toppings\": [\"Cheese\",\"Pepperoni\"] } ] }"
            )
        ]
    )
)

如果在工具呼叫行為中停用自動叫用,Java 應用程式必須叫用函式呼叫,並將函式結果新增為 AuthorRole.TOOL 聊天記錄的訊息。

messages.stream()
    .filter (it -> it instanceof OpenAIChatMessageContent)
        .map(it -> ((OpenAIChatMessageContent<?>) it).getToolCall())
        .flatMap(List::stream)
        .forEach(toolCall -> {
            String content;
            try {
                // getFunction will throw an exception if the function is not found
                var fn = kernel.getFunction(toolCall.getPluginName(),
                        toolCall.getFunctionName());
                FunctionResult<?> fnResult = fn
                        .invokeAsync(kernel, toolCall.getArguments(), null, null).block();
                content = (String) fnResult.getResult();
            } catch (IllegalArgumentException e) {
                content = "Unable to find function. Please try again!";
            }

            chatHistory.addMessage(
                    AuthorRole.TOOL,
                    content,
                    StandardCharsets.UTF_8,
                    FunctionResultMetadata.build(toolCall.getId()));
        });

請注意,結果是模型接著需要處理的 JSON 字串。 和之前一樣,模型需要花費令牌來取用這項資訊。 這就是為什麼盡可能讓傳回型別保持簡單很重要的原因。 在此情況下,傳回只會包含新增至購物車的新專案,而不是整個購物車。

提示

盡可能簡潔地與你的回報。 可能的話,只會傳回模型需要的資訊,或先使用另一個 LLM 提示來摘要資訊,再傳回它。

重複步驟 2-6

將結果傳回模型之後,程式會重複。 此模型會處理最新的聊天記錄,併產生回應。 在此情況下,模型可能會詢問使用者是否想要將另一個披薩新增至購物車,或是否想要簽出。

平行函式呼叫

在上述範例中,我們示範 LLM 如何呼叫單一函式。 如果您需要依序呼叫多個函式,通常可能會很慢。 為了加速程式,數個 LLM 支援平行函數調用。 這可讓 LLM 一次呼叫多個函式,以加速程式。

例如,如果使用者想要訂購多個披薩,LLM 可以同時呼叫 add_pizza_to_cart 每個披薩的 函式。 這可大幅減少 LLM 往返次數,並加快訂購程式。

下一步

既然您已瞭解函式呼叫的運作方式,您可以繼續瞭解如何透過參考 函式選擇行為一文,設定函式呼叫的各種層面,以更妥善地對應至您的特定案例