Aracılığıyla paylaş


Sohbet tamamlama ile işlev çağrısı

Sohbet tamamlamanın en güçlü özelliği, modelden işlevleri çağırabilme özelliğidir. Bu, mevcut kodunuzla etkileşim kurabilen bir sohbet botu oluşturarak iş süreçlerini otomatikleştirmenizi, kod parçacıkları oluşturmanızı ve daha fazlasını yapmanızı sağlar.

Anlam Çekirdeği ile işlevlerinizi ve bunların parametrelerini modele otomatik olarak açıklayarak ve ardından modelle kodunuz arasındaki ileri ve geri iletişimi işleyerek işlev çağrısını kullanma sürecini basitleştiririz.

Ancak işlev çağrısını kullanırken, kodunuzu iyileştirebilmeniz ve bu özelliğin en iyi şekilde kullanılmasını sağlamak için arka planda neler olduğunu anlamanız iyi olur.

Otomatik işlev çağrısı nasıl çalışır?

Not

Aşağıdaki bölümde, otomatik işlev çağrısının Anlam Çekirdeği'nde nasıl çalıştığı açıklanmaktadır. Otomatik işlev çağırma, Anlam Çekirdeği'ndeki varsayılan davranıştır, ancak isterseniz işlevleri el ile de çağırabilirsiniz. El ile işlev çağırma hakkında daha fazla bilgi için lütfen işlev çağırma makalesine bakın.

İşlev çağrısı etkinleştirilmiş bir modele istekte bulunursanız Anlam Çekirdeği aşağıdaki adımları gerçekleştirir:

# Adımlar Açıklama
1 İşlevleri seri hale getirme Çekirdekteki tüm kullanılabilir işlevler (ve giriş parametreleri) JSON şeması kullanılarak serileştirilir.
2 İletileri ve işlevleri modele gönderme Serileştirilmiş işlevler (ve geçerli sohbet geçmişi) girişin bir parçası olarak modele gönderilir.
3 Model girişi işler Model girişi işler ve bir yanıt oluşturur. Yanıt bir sohbet iletisi veya bir veya daha fazla işlev çağrısı olabilir.
4 Yanıtı yönet Yanıt bir sohbet iletisiyse, arayana döndürülür. Ancak yanıt bir işlev çağrısıysa, Anlam Çekirdeği işlev adını ve parametrelerini ayıklar.
5 İşlevi çağırma Ayıklanan işlev adı ve parametreleri, çekirdekteki işlevi çağırmak için kullanılır.
6 İşlev sonucunu döndürme ardından işlevin sonucu sohbet geçmişinin bir parçası olarak modele geri gönderilir. Model bir sohbet iletisi döndürene veya maksimum yineleme numarasına ulaşılana kadar 2-6 arası adımlar yinelenir.

Aşağıdaki diyagramda işlev çağırma işlemi gösterilmektedir:

Semantik Çekirdek işlevi çağrısı

Aşağıdaki bölümde işlev çağrısının pratikte nasıl çalıştığını göstermek için somut bir örnek kullanılacaktır.

Örnek: Pizza sipariş etme

Bir kullanıcının pizza sipariş etmesine izin veren bir eklentiniz olduğunu varsayalım. Eklenti aşağıdaki işlevlere sahiptir:

  1. get_pizza_menu: Kullanılabilir pizzaların listesini döndürür
  2. add_pizza_to_cart: Kullanıcının sepetine pizza ekler
  3. remove_pizza_from_cart: Kullanıcının sepetinden pizza kaldırır
  4. get_pizza_from_cart: Kullanıcının sepetindeki pizzanın belirli ayrıntılarını döndürür
  5. get_cart: Kullanıcının geçerli sepetini verir
  6. checkout: Kullanıcının sepetini kontrol ediyor

C# dilinde eklenti aşağıdaki gibi görünebilir:

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);
    }
}

Daha sonra bu eklentiyi çekirdeğe şu şekilde ekleyin:

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();

Not

Yalnızca özniteliğine KernelFunction sahip işlevler serileştirilir ve modele gönderilir. Bu, modele sunulmayan yardımcı işlevlere sahip olmanıza olanak tanır.

Python'da eklenti şu şekilde görünebilir:

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)

Daha sonra aşağıdaki gibi bu eklentiyi çekerde ekleyebilirsiniz:

from semantic_kernel import Kernel

kernel = Kernel()

# 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")

Not

Yalnızca dekoratöre kernel_function sahip işlevler seri hale getirilir ve modele gönderilir. Bu, modele sunulmayan yardımcı işlevlere sahip olmanıza olanak tanır.

Otomatik İşlev Çağrısı için Ayrılmış Parametre Adları

KernelFunctions içinde otomatik işlev çağrısı kullanılırken, belirli parametre adları ayrılır ve özel işleme alır. Bu ayrılmış adlar, işlev yürütme için gereken anahtar nesnelere otomatik olarak erişmenizi sağlar.

Ayrılmış Adlar

Aşağıdaki parametre adları ayrılmıştır:

  • kernel
  • service
  • execution_settings
  • arguments

Nasıl Çalışırlar?

İşlev çağırma sırasında yöntemi gather_function_parameters her parametreyi inceler. Parametrenin adı ayrılmış adlardan biriyle eşleşiyorsa, belirli nesnelerle doldurulur:

  • kernel: Çekirdek nesnesiyle birlikte eklenir.
  • service: Sağlanan bağımsız değişkenlere göre seçilen yapay zeka hizmetiyle doldurulur.
  • execution_settings: İşlevin yürütülmesiyle ilgili ayarları içerir.
  • arguments: Çağırma sırasında geçirilen çekirdek bağımsız değişkenlerinin tamamını alır.

Bu tasarım, bu parametrelerin otomatik olarak yönetilmesini sağlayarak el ile ayıklama veya atama gereksinimini ortadan kaldırır.

Örnek Kullanım

Aşağıdaki örneği göz önünde bulundurun:

class SimplePlugin:
    @kernel_function(name="GetWeather", description="Get the weather for a location.")
    async def get_the_weather(self, location: str, arguments: KernelArguments) -> str:
        # The 'arguments' parameter is reserved and automatically populated with KernelArguments.
        return f"Received user input: {location}, the weather is nice!"

Otomatik İşlev Çağrısı için Özel Ayrılmış Parametre Adları

Bu davranışı da özelleştirebilirsiniz. Bunu yapmak için işlev çağırma tanımından dışlamak istediğiniz parametreye şu şekilde açıklama eklemeniz gerekir:

class SimplePlugin:
    @kernel_function(name="GetWeather", description="Get the weather for a location.")
    async def get_the_weather(self, location: str, special_arg: Annotated[str, {"include_in_function_choices": False}]) -> str:
        # The 'special_arg' parameter is reserved and you need to ensure it either has a default value or gets passed.

Bu işlevi çağırırken parametresini geçtiğinden special_arg emin olun, aksi takdirde hataya neden olur.

response = await kernel.invoke_async(
    plugin_name=...,
    function_name="GetWeather",
    location="Seattle",
    special_arg="This is a special argument"
)

Alternatif olarak, bunu, örnekteki gibi bir otomatik işlev çağırma özelliği kullanmak için nesneye KernelArguments ekleyin:

arguments = KernelArguments(special_arg="This is a special argument")
response = await agent.get_response(
    messages="what's the weather in Seattle?"
    arguments=arguments)

Java'da eklenti aşağıdaki gibi görünebilir:

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));
    }
}

Daha sonra aşağıdaki gibi bu eklentiyi çekirdeğe eklersiniz.

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();

Not

Yalnızca ek açıklama içeren DefineKernelFunction işlevler seri hale getirilir ve modele gönderilir. Bu, modele sunulmayan yardımcı işlevlere sahip olmanıza olanak tanır.

1) İşlevleri seri hale getirme

ile OrderPizzaPluginbir çekirdek oluşturduğunuzda, çekirdek işlevleri ve parametrelerini otomatik olarak seri hale getirir. Modelin işlevleri ve girişlerini anlayabilmesi için bu gereklidir.

Yukarıdaki eklenti için serileştirilmiş işlevler şöyle görünür:

[
  {
    "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": []
      }
    }
  }
]

Burada, sohbet tamamlamanın hem performansını hem de kalitesini etkileyebileceğiniz birkaç nokta vardır:

  1. İşlev şemasının gereksiz detayları – Modelin kullanması için işlevleri seri hale getirmek bedava değildir. Şema ne kadar ayrıntılı olursa, modelin işlemesi gereken belirteç sayısı o kadar fazla olur ve bu da yanıt süresini yavaşlatabilir ve maliyetleri artırabilir.

    İpucu

    İşlevlerinizi olabildiğince basit tutun. Yukarıdaki örnekte, tüm işlevlerin, işlev adının kendi kendine açık olduğu açıklamalara sahip olmadığını fark edeceksiniz. Bu, belirteç sayısını azaltmak için kasıtlı olarak yapılır. Parametreler de basit tutulur; modelin bilmesi gerekmemesi gereken her şey (veya cartIdgibipaymentId) gizli tutulur. Bu bilgiler bunun yerine iç hizmetler tarafından sağlanır.

    Not

    Endişelenmeniz gerekmeyen tek şey, dönüş türlerinin karmaşıklığıdır. Dönüş türlerinin şemada serileştirilmediğini fark edeceksiniz. Bunun nedeni, modelin yanıt oluşturmak için dönüş türünü bilmesine gerek olmamasıdır. Ancak 6. Adım'da, aşırı ayrıntılı dönüş türlerinin sohbet tamamlama kalitesini nasıl etkileyebileceğini göreceğiz.

  2. Parametre türleri – Şema ile her parametrenin türünü belirtebilirsiniz. Bu, modelin beklenen girişi anlaması için önemlidir. Yukarıdaki örnekte size parametresi bir sabit listesi, toppings parametresi ise bir sabit listesi dizisidir. Bu, modelin daha doğru yanıtlar oluşturmasını sağlar.

    İpucu

    Mümkün olduğunca parametre türü olarak kullanmaktan string kaçının. Model, belirsiz yanıtlara yol açabilecek dize türünü çıkaramaz. Bunun yerine, mümkün olduğunca sabit listeleri veya diğer türleri (örneğin, int, floatve karmaşık türler) kullanın.

  3. Gerekli parametreler - Hangi parametrelerin gerekli olduğunu da belirtebilirsiniz. Bu, modelin işlevin çalışması için hangi parametrelerin gerçekten gerekli olduğunu anlaması için önemlidir. 3. Adım'ın ilerleyen bölümlerinde model, işlevi çağırmak için gereken en düşük bilgileri sağlamak için bu bilgileri kullanacaktır.

    İpucu

    Parametreleri yalnızca gerçekten gerekli olduklarında gerekli olarak işaretleyin. Bu, model çağrısının daha hızlı ve doğru bir şekilde işlev göstermesine yardımcı olur.

  4. İşlev açıklamaları – İşlev açıklamaları isteğe bağlıdır ancak modelin daha doğru yanıtlar üretmesine yardımcı olabilir. Özellikle, dönüş türü şemada serileştirilmediğinden açıklamalar modele yanıttan ne bekleyebileceğinizi söyleyebilir. Model işlevleri yanlış kullanıyorsa, örnekler ve rehberlik sağlamak için açıklamalar da ekleyebilirsiniz.

    Örneğin, işlevinde get_pizza_from_cart , açıklama kullanıcıya önceki iletilere güvenmek yerine bu işlevi kullanmasını söyler. Bu önemlidir çünkü sepet son iletiden sonra değişmiş olabilir.

    İpucu

    Açıklama eklemeden önce, modelin yanıt oluşturmak için bu bilgilere ihtiyacı olup olmadığını kendinize sorun. Aksi takdirde laf kalabalığını azaltmak için dışarıda bırakmayı göz önünde bulundurun. Model işlevi düzgün kullanmakta zorlanıyorsa daha sonra istediğiniz zaman açıklama ekleyebilirsiniz.

  5. Eklenti adı – Serileştirilmiş işlevlerde görebileceğiniz gibi her işlevin bir name özelliği vardır. Anlam Çekirdeği, işlevleri ad alanı olarak kullanmak için eklenti adını kullanır. Aynı ada sahip işlevlere sahip birden çok eklentiye sahip olmanıza olanak sağladığından bu önemlidir. Örneğin, her biri kendi search işlevine sahip olan birden çok arama hizmeti için eklentileriniz olabilir. İşlevleri adlar geçirerek çakışmaları önleyebilir ve modelin hangi işlevi çağıracaklarını anlamasını kolaylaştırabilirsiniz.

    Bunu bilerek, benzersiz ve açıklayıcı bir eklenti adı seçmelisiniz. Yukarıdaki örnekte eklenti adı şeklindedir OrderPizza. Bu, işlevlerin pizza sipariş etmeyle ilgili olduğunu açıkça gösterir.

    İpucu

    Bir eklenti adı seçerken, "eklenti" veya "hizmet" gibi gereksiz sözcükleri kaldırmanızı öneririz. Bu, ayrıntı düzeyini azaltmaya yardımcı olur ve eklenti adının model için anlaşılmasını kolaylaştırır.

    Not

    Varsayılan olarak, işlev adının sınırlayıcısı şeklindedir -. Bu çoğu modelde işe yarasa da, bazıları İkizler gibi farklı gereksinimlere sahip olabilir. Bu işlem çekirdek tarafından otomatik olarak yapılır, ancak serileştirilmiş işlevlerde biraz farklı işlev adları görebilirsiniz.

2) İletileri ve işlevleri modele gönderme

İşlevler seri hale getirildikten sonra, geçerli sohbet geçmişiyle birlikte modele gönderilir. Bu, modelin konuşmanın bağlamını ve kullanılabilir işlevleri anlamasını sağlar.

Bu senaryoda, kullanıcının yardımcıdan sepetine pizza eklemesini istediğini hayal edebiliriz:

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!");

Ardından bu sohbet geçmişini ve serileştirilmiş işlevleri modele gönderebiliriz. Model, yanıt vermenin en iyi yolunu belirlemek için bu bilgileri kullanır.

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

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

ChatResponse response = await chatCompletion.GetChatMessageContentAsync(
    chatHistory,
    executionSettings: openAIPromptExecutionSettings,
    kernel: kernel)

Not

Bu örnek, kullanılabilir birkaç davranışlardan biri olan davranışı kullanır FunctionChoiceBehavior.Auto() . Diğer işlev seçimi davranışları hakkında daha fazla bilgi için işlev seçimi davranışları makalesine bakın.

chat_completion = kernel.get_service(type=ChatCompletionClientBase)

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

response = await chat_completion.get_chat_message_content(
    chat_history=history,
    settings=execution_settings,
    kernel=kernel,
)

Not

Bu örnek, kullanılabilir birkaç davranışlardan biri olan davranışı kullanır FunctionChoiceBehavior.Auto() . Diğer işlev seçimi davranışları hakkında daha fazla bilgi için işlev seçimi davranışları makalesine bakın.

ChatCompletionService chatCompletion = kernel.getService(I)ChatCompletionService.class);

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

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

Önemli

İşlev çağrısının kullanılabilmesi için çekirdeğin hizmete geçirilmesi gerekir. Bunun nedeni eklentilerin çekirdekte kayıtlı olması ve hizmetin hangi eklentilerin kullanılabilir olduğunu bilmesi gerekir.

3) Model girişi işler

Hem sohbet geçmişi hem de serileştirilmiş işlevler sayesinde model, yanıt vermenin en iyi yolunu belirleyebilir. Bu durumda model, kullanıcının pizza sipariş etmek istediğini algılar. Model büyük olasılıkla add_pizza_to_cart, ancak boyut ve topping değerlerini gerekli parametreler olarak belirttiğimiz için model kullanıcıdan şu bilgileri ister:

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?"

Model kullanıcının bir sonraki yanıtını vermesini istediğinden, Anlam Çekirdeği otomatik işlev çağrısını durdurur ve denetimi kullanıcıya döndürür. Bu noktada, kullanıcı sipariş etmek istediği pizzanın boyutu ve toppingleri ile yanıt verebilir:

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_content(
    chat_history=history,
    settings=execution_settings,
    kernel=kernel,
)
chatHistory.addUserMessage("I'd like a medium pizza with cheese and pepperoni, please.");

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

Artık model gerekli bilgilere sahip olduğuna göre kullanıcının girişiyle işlevi çağırabilir add_pizza_to_cart . Arka planda sohbet geçmişine şuna benzer yeni bir ileti ekler:

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

İpucu

Gerekli her argümanın model tarafından oluşturulması gerektiğini unutmayın. Bu, yanıtı oluşturmak için jetonların harcanması anlamına gelir. Çok sayıda belirteç (GUID gibi) gerektiren bağımsız değişkenlerden kaçının. Örneğin, pizzaId için bir int kullandığımıza dikkat edin. Modelin bir-iki basamaklı bir sayı göndermesini istemek, GUID istemekten çok daha kolaydır.

Önemli

İşlev çağırmayı bu kadar güçlü hale getiren adım budur. Daha önce yapay zeka uygulama geliştiricilerinin niyet ve alan doldurma işlevlerini ayıklamak için ayrı işlemler oluşturması gerekiyordu. İşlev çağrısı ile model, bir işlevin ne zaman çağrılabileceğine ve hangi bilgilerin sağlanıp sağlanabileceğine karar verebilir.

4) Yanıtı işleme

Semantik Çekirdek, yanıtı modelden aldığında yanıtın bir işlev çağrısı olup olmadığını denetler. Bu durumda, Anlam Çekirdeği işlev adını ve parametrelerini ayıklar. Bu durumda, işlev adı OrderPizzaPlugin-add_pizza_to_cart ve bağımsız değişkenler pizzanın boyutu ve malzemeleridir.

Bu bilgilerle Anlam Çekirdeği girişleri uygun türlerde sıralayabilir ve içindeki işlevine add_pizza_to_cartOrderPizzaPlugingeçirebilir. Bu örnekte bağımsız değişkenler bir JSON dizesi olarak oluşturulur ancak Anlam Çekirdeği tarafından bir PizzaSize sabit listesi ve bir List<PizzaToppings>olarak seri durumdan çıkarılır.

Not

Girişleri doğru türlerde sıralamak, Anlam Çekirdeği kullanmanın temel avantajlarından biridir. Modeldeki her şey bir JSON nesnesi olarak gelir, ancak Anlam Çekirdeği bu nesneleri otomatik olarak işlevleriniz için doğru türlerde seri durumdan çıkarabilir.

Girişler sıraladıktan sonra Semantik Çekirdek, sohbet geçmişine işlev çağrısını da ekler:

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 için Semantik Çekirdek, otomatik çağırma aracı çağrı davranışı yanlış olduğunda, işlev çağrısını C# ve Python'dan farklı bir şekilde ele alır. Sohbet geçmişine işlev çağrısı içeriği eklemezsiniz; bunun yerine, uygulama işlev çağrılarını çağırmaktan sorumlu hale getirilir. Otomatik çağırma false olduğunda Java'da işlev çağrılarını işleme örneği için sonraki "İşlevi çağır" bölümüne atlayın.

5) İşlevi çağırma

Anlam Çekirdeği doğru türlere sahip olduğunda, son olarak işlevi çağırabilir add_pizza_to_cart . Eklenti bağımlılık enjeksiyonu kullandığından, işlev, pizzaService ve userContext gibi dış hizmetlerle etkileşim kurarak pizzayı kullanıcının sepetine ekleyebilir.

Ancak tüm işlevler başarılı olmayacaktır. İşlev başarısız olursa Anlam Çekirdeği hatayı işleyebilir ve modele varsayılan bir yanıt sağlayabilir. Bu, modelin neyin yanlış gittiğini anlamasına ve kullanıcıya bir yanıt oluşturmaya veya yeniden denemeye karar vermesine olanak tanır.

İpucu

Bir modelin kendi kendine doğru çalıştığından emin olmak için neyin yanlış gittiğini ve nasıl düzeltileceğini açıkça belirten hata iletileri sağlamak önemlidir. Bu, modelin işlev çağrısını doğru bilgilerle yeniden denemesine yardımcı olabilir.

Not

Semantik Çekirdek varsayılan olarak işlevleri otomatik olarak çağırır. Ancak, işlev çağırmayı el ile yönetmeyi tercih ederseniz, el ile işlev çağırma modunu etkinleştirebilirsiniz. Bunun nasıl yapacağı hakkında daha fazla bilgi için lütfen işlev çağırma makalesine bakın.

6) İşlev sonucunu döndürme

İşlev çağrıldıktan sonra, işlev sonucu sohbet geçmişinin bir parçası olarak modele geri gönderilir. Bu, modelin konuşmanın bağlamını anlamasına ve sonraki bir yanıt oluşturmasına olanak tanır.

Semantik Çekirdek, arka planda, araç rolünden sohbet geçmişine şöyle görünen yeni bir ileti ekler:

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\"] } ] }"
            )
        ]
    )
)

Araç çağrısı davranışında otomatik çağırma devre dışı bırakılırsa, java uygulamasının işlev çağrılarını çağırması ve işlev sonucunu sohbet geçmişine ileti AuthorRole.TOOL olarak eklemesi gerekir.

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()));
        });

Sonucun modelin işlemesi gereken bir JSON dizesi olduğuna dikkat edin. Daha önce olduğu gibi, model bu bilgileri işlemeye tokenları harcayacaktır. Bu nedenle, dönüş türlerini mümkün olduğunca basit tutmak önemlidir. Bu durumda, iade yalnızca sepete eklenen yeni ürünleri içerir, sepetin tamamını içermez.

İpucu

İadelerinizle mümkün olduğunca kısa olun. Mümkün olduğunda, modeli döndürmeden önce yalnızca modelin ihtiyaç duyduğu bilgileri döndürün veya başka bir LLM istemi kullanarak bilgileri özetleyin.

2-6 arası adımları yineleyin

Sonuç modele döndürüldükten sonra işlem yineleniyor. Model, en son sohbet geçmişini işler ve bir yanıt oluşturur. Bu durumda model, kullanıcıya sepetine başka bir pizza eklemek isteyip istemediğini veya ödeme yapmak isteyip istemediğini sorabilir.

Paralel işlev çağrıları

Yukarıdaki örnekte, LLM'nin tek bir işlevi nasıl çağırabileceğini gösterdik. Birden çok işlevi sırayla çağırmanız gerekirse bu genellikle yavaş olabilir. İşlemi hızlandırmak için çeşitli LLM'ler paralel işlev çağrılarını destekler. Bu, LLM'nin aynı anda birden çok işlevi çağırmasına olanak sağlayarak işlemi hızlandırmasını sağlar.

Örneğin, bir kullanıcı birden çok pizza sipariş etmek isterse LLM, her pizzanın işlevini aynı anda çağırabilir add_pizza_to_cart . Bu, LLM'ye gidiş dönüş sayısını önemli ölçüde azaltabilir ve sipariş sürecini hızlandırabilir.

Sonraki adımlar

İşlev çağrısının nasıl çalıştığını anladığınıza göre, sonraki adıma geçerek işlev çağrısının belirli senaryolarınıza daha iyi karşılık gelen çeşitli yönlerini yapılandırmayı öğrenebilirsiniz: