Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Nejvýkonnější funkcí dokončování chatu je možnost volat funkce z modelu. Díky tomu můžete vytvořit chatovacího robota, který může pracovat s vaším stávajícím kódem, což umožňuje automatizovat obchodní procesy, vytvářet fragmenty kódu a provádět další činnosti.
Sémantickým jádrem zjednodušujeme proces použití volání funkcí tím, že automaticky popíšeme funkce a jejich parametry modelu a pak zpracujeme komunikaci mezi modelem a vaším kódem.
Při volání funkcí je ale dobré pochopit, co se skutečně děje na pozadí, abyste mohli optimalizovat kód a využít tuto funkci na maximum.
Jak funguje automatické volání funkcí
Poznámka:
Následující část popisuje, jak funguje automatické volání funkcí v sémantickém jádru. Automatické volání funkcí je výchozí chování v sémantickém jádru, ale pokud chcete, můžete také ručně vyvolat funkce. Další informace o ručním vyvolání funkce najdete v článku o vyvolání funkce.
Když provedete požadavek na model s povoleným voláním funkce, provede sémantické jádro následující kroky:
| # | Krok | Description |
|---|---|---|
| 1 | Serializace funkcí | Všechny dostupné funkce (a její vstupní parametry) v jádru se serializují pomocí schématu JSON. |
| 2 | Odeslání zpráv a funkcí do modelu | Serializované funkce (a aktuální historie chatu) se do modelu odesílají jako součást vstupu. |
| 3 | Model zpracovává vstup. | Model zpracuje vstup a vygeneruje odpověď. Odpověď může být buď chatová zpráva, nebo jedno nebo více volání funkce. |
| 4 | Zpracování odpovědi | Pokud je odpovědí zpráva chatu, vrátí se volajícímu. Pokud je odpovědí volání funkce, sémantické jádro extrahuje název funkce a jejich parametry. |
| 5 | Vyvolání funkce | Extrahovaný název a parametry funkce se používají k vyvolání funkce v jádru. |
| 6 | Vrácení výsledku funkce | Výsledek funkce se pak odešle zpět do modelu jako součást historie chatu. Kroky 2 až 6 se pak opakují, dokud model nevrátí chatovou zprávu nebo se dosáhne maximálního počtu iterací. |
Následující diagram znázorňuje proces volání funkce:
Následující část použije konkrétní příklad k ilustraci fungování volání funkcí v praxi.
Příklad: Objednání pizzy
Předpokládejme, že máte modul plug-in, který uživateli umožňuje objednat pizzu. Modul plug-in má následující funkce:
-
get_pizza_menu: Vrátí seznam dostupných pizz -
add_pizza_to_cart: Přidá pizzu do košíku uživatele. -
remove_pizza_from_cart: Odebere pizzu z košíku uživatele. -
get_pizza_from_cart: Vrátí konkrétní podrobnosti pizzy v košíku uživatele. -
get_cart: Vrátí aktuální košík uživatele. -
checkout: Zkontroluje košík uživatele.
V jazyce C# může modul plug-in vypadat takto:
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);
}
}
Pak byste tento modul plug-in přidali do jádra takto:
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();
Poznámka:
Do modelu budou serializovány a odeslány pouze funkce s KernelFunction atributem. To vám umožní mít pomocné funkce, které nejsou vystavené modelu.
V Pythonu může modul plug-in vypadat takto:
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)
Pak byste tento modul plug-in přidali do jádra takto:
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")
Poznámka:
Do modelu budou serializovány a odeslány pouze funkce s kernel_function dekorátorem. To vám umožní mít pomocné funkce, které nejsou vystavené modelu.
Rezervované názvy parametrů pro automatické volání funkcí
Při použití automatické funkce volání v KernelFunctions jsou určité názvy parametrů vyhrazeny a přijímají zvláštní zpracování. Tyto rezervované názvy umožňují automaticky přistupovat k objektům klíčů požadovaným pro provádění funkce.
Rezervované názvy
Jsou vyhrazeny následující názvy parametrů:
kernelserviceexecution_settingsarguments
Jak fungují
Během vyvolání funkce metoda gather_function_parameters zkontroluje jednotlivé parametry. Pokud název parametru odpovídá jednomu z vyhrazených názvů, naplní se konkrétními objekty:
-
kernel: Vloženo pomocí objektu jádra. -
service: Naplněno vybranou službou AI na základě zadaných argumentů. -
execution_settings: Obsahuje nastavení související s prováděním funkce. -
arguments: Přijímá celou sadu argumentů jádra předaných během vyvolání.
Tento návrh zajišťuje automatické spravování těchto parametrů a eliminuje potřebu ruční extrakce nebo přiřazení.
Příklad použití
Podívejte se na následující příklad:
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!"
Vlastní názvy vyhrazených parametrů pro automatické volání funkcí
Toto chování můžete také přizpůsobit. Abyste to mohli udělat, musíte přidat poznámky k parametru, který chcete vyloučit z definice volání funkce, například takto:
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.
Při volání této funkce nezapomeňte předat special_arg parametr, jinak vyvolá chybu.
response = await kernel.invoke_async(
plugin_name=...,
function_name="GetWeather",
location="Seattle",
special_arg="This is a special argument"
)
Nebo ho přidejte do objektu KernelArguments , abyste ho mohli použít s automatickým voláním funkce v agentu takto:
arguments = KernelArguments(special_arg="This is a special argument")
response = await agent.get_response(
messages="what's the weather in Seattle?"
arguments=arguments)
V Javě může modul plug-in vypadat takto:
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));
}
}
Pak byste tento modul plug-in přidali do jádra takto:
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();
Poznámka:
Do modelu budou serializovány a odeslány pouze funkce s DefineKernelFunction poznámkami. To vám umožní mít pomocné funkce, které nejsou vystavené modelu.
1) Serializace funkcí
Když vytvoříte jádro s jádrem OrderPizzaPlugin, jádro automaticky serializuje funkce a jejich parametry. To je nezbytné, aby model rozuměl funkcím a jejich vstupům.
U výše uvedeného modulu plug-in by serializované funkce vypadaly takto:
[
{
"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": []
}
}
}
]
Tady je několik věcí, které můžou ovlivnit výkon i kvalitu dokončení chatu:
Výmluvnost schématu funkcí – Serializace funkcí pro použití modelem není zadarmo. Čím více podrobného schématu, tím více tokenů musí model zpracovat, což může zpomalit dobu odezvy a zvýšit náklady.
Doporučení
Udržujte funkce co nejjednodušší. V předchozím příkladu si všimnete, že ne všechny funkce mají popisy, ve kterých je název funkce vysvětlovaný. To je úmyslné snížit počet tokenů. Parametry jsou také jednoduché; cokoli, co by model neměl vědět (jako je
cartIdnebopaymentId), zůstává skryto. Tyto informace jsou místo toho poskytovány interními službami.Poznámka:
Jedna věc, se kterou si nemusíte dělat starosti, je složitost návratových typů. Všimněte si, že návratové typy nejsou serializovány ve schématu. Důvodem je to, že model nepotřebuje znát návratový typ pro vygenerování odpovědi. V kroku 6 ale uvidíme, jak příliš podrobné návratové typy můžou ovlivnit kvalitu dokončení chatu.
Typy parametrů – Pomocí schématu můžete zadat typ každého parametru. To je důležité pro model, aby porozuměl očekávanému vstupu. V předchozím příkladu
sizeje parametr výčtem atoppingsparametr je pole výčtů. To pomáhá modelu generovat přesnější odpovědi.Tip
Pokud je to možné, nepoužívejte
stringjako typ parametru. Model nemůže odvodit typ řetězce, což může vést k nejednoznačným odpovědím. Místo toho použijte výčty nebo jiné typy (napřint. ,float, a komplexní typy), pokud je to možné.Požadované parametry – Můžete také určit, které parametry se vyžadují. To je důležité pro model, aby porozuměl tomu, které parametry jsou skutečně nezbytné pro fungování funkce. Později v kroku 3 použije model tyto informace k zadání minimálních informací, které jsou potřeba k volání funkce.
Tip
Parametry označte jako povinné pouze v případě, že jsou skutečně povinné. To pomáhá modelu volat funkce rychleji a přesněji.
Popisy funkcí – Popisy funkcí jsou volitelné, ale můžou modelu pomoct generovat přesnější odpovědi. Konkrétně popisy můžou modelu sdělit, co očekávat od odpovědi, protože návratový typ není serializován ve schématu. Pokud model nesprávně používá funkce, můžete také přidat popisy, které vám poskytnou příklady a pokyny.
Například ve
get_pizza_from_cartfunkci popis uživateli říká, aby tuto funkci používal, místo aby se spoléhal na předchozí zprávy. To je důležité, protože se košík mohl od poslední zprávy změnit.Tip
Než přidáte popis, zeptejte se sami sebe, jestli model potřebuje tyto informace k vygenerování odpovědi. Pokud ne, zvažte jeho vynechání, abyste snížili slovnost. Popisy můžete kdykoli později přidat, pokud se model snaží funkci správně používat.
Název modulu plug-in – jak vidíte v serializovaných funkcích, každá funkce má
namevlastnost. Sémantické jádro používá název plug-inu pro vytváření jmenných prostorů funkcí. To je důležité, protože umožňuje mít více modulů plug-in s funkcemi stejného názvu. Můžete mít například moduly plug-in pro více vyhledávacích služeb, z nichž každá má vlastnísearchfunkci. Při použití namespacingu funkcí se můžete vyhnout konfliktům a usnadnit modelu pochopení, kterou funkci má volat.S ohledem na to byste měli zvolit název pluginu, který je jedinečný a popisný. V předchozím příkladu je název plug-inu
OrderPizza. To znamená, že funkce souvisí s objednávání pizzy.Tip
Při výběru názvu modulu plug-in doporučujeme odebrat nadbytečná slova, jako je "plugin" nebo "služba". Tím se pomáhá snížit rozvláčnost a usnadňuje modelu pochopení jména pluginu.
Poznámka:
Ve výchozím nastavení je
-oddělovač názvu funkce . I když to funguje u většiny modelů, některé z nich můžou mít jiné požadavky, například Gemini. O to se postará jádro automaticky, ale v serializovaných funkcích se můžou zobrazovat mírně odlišné názvy funkcí.
2) Odesílání zpráv a funkcí do modelu
Jakmile jsou funkce serializovány, posílají se do modelu spolu s aktuální historií chatu. To umožňuje modelu porozumět kontextu konverzace a dostupným funkcím.
V tomto scénáři si můžeme představit, že uživatel požádá asistenta o přidání pizzy do košíku:
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!");
Pak můžeme do modelu odeslat tuto historii chatu a serializované funkce. Tento model použije tyto informace k určení nejlepšího způsobu reakce.
IChatCompletionService chatCompletion = kernel.GetRequiredService<IChatCompletionService>();
OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};
ChatResponse response = await chatCompletion.GetChatMessageContentAsync(
chatHistory,
executionSettings: openAIPromptExecutionSettings,
kernel: kernel)
Poznámka:
Tento příklad používá chování FunctionChoiceBehavior.Auto(), jedno z mála dostupných. Další informace o chování ostatních voleb funkcí najdete v článku o chování funkcí.
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,
)
Poznámka:
Tento příklad používá FunctionChoiceBehavior.Auto() chování, jeden z několika dostupných. Další informace o chování ostatních voleb funkcí najdete v článku o chování funkcí.
ChatCompletionService chatCompletion = kernel.getService(I)ChatCompletionService.class);
InvocationContext invocationContext = InvocationContext.builder()
.withToolCallBehavior(ToolCallBehavior.allowAllKernelFunctions(false));
List<ChatResponse> responses = chatCompletion.getChatMessageContentsAsync(
chatHistory,
kernel,
invocationContext).block();
Důležité
Jádro musí být předáno službě, aby bylo možné použít volání funkce. Je to proto, že moduly plug-in jsou zaregistrované v jádru a služba potřebuje vědět, které moduly plug-in jsou k dispozici.
3) Model zpracovává vstup
S historií chatu i serializovanými funkcemi může model určit nejlepší způsob, jak reagovat. V tomto případě model rozpozná, že uživatel chce objednat pizzu. Model by pravděpodobně chtěl volat add_pizza_to_cart funkci, ale protože jsme zadali velikost a přílohy jako povinné parametry, model požádá uživatele o tyto informace:
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?"
Vzhledem k tomu, že model chce, aby uživatel reagoval příště, sémantické jádro zastaví automatické volání funkce a vrátí řízení uživateli. V tomto okamžiku může uživatel upřesnit velikost a přísady pizzy, kterou chce objednat.
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();
Teď, když má model potřebné informace, teď může volat add_pizza_to_cart funkci se vstupem uživatele. Na pozadí přidá novou zprávu do historie chatu, která vypadá takto:
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "OrderPizzaPlugin-add_pizza_to_cart",
"arguments": "{\n\"size\": \"Medium\",\n\"toppings\": [\"Cheese\", \"Pepperoni\"]\n}"
}
}
]
Tip
Je dobré si uvědomit, že každý argument, který požadujete, musí být vygenerován modelem. To znamená, že utrácení tokenů pro vygenerování odpovědi. Vyhněte se argumentům, které vyžadují mnoho tokenů (například IDENTIFIKÁTOR GUID). Všimněte si například, že používáme int pro pizzaId. Žádost modelu o odeslání čísla o jednu až dvě číslice je mnohem jednodušší než požádat o identifikátor GUID.
Důležité
Tento krok znamená, že volání funkcí je tak výkonné. Vývojáři aplikací AI dříve museli vytvářet samostatné procesy pro extrakci záměru a vyplňování slotů. Při volání funkce se model může rozhodnout , kdy volat funkci a jaké informace poskytnout.
4) Zpracování odpovědi
Když sémantické jádro obdrží odpověď z modelu, zkontroluje, jestli odpověď je volání funkce. Pokud ano, sémantické jádro extrahuje název funkce a jeho parametry. V tomto případě je název funkce OrderPizzaPlugin-add_pizza_to_cart a argumenty jsou velikost a přílohy pizzy.
S touto informací může sémantické jádro zařazovat vstupy do příslušných typů a předat je add_pizza_to_cart funkci v OrderPizzaPlugin. V tomto příkladu pocházejí argumenty jako řetězec JSON, ale jsou deserializovány sémantickým jádrem do výčtu PizzaSize a .List<PizzaToppings>
Poznámka:
Zařazování vstupů do správných typů je jednou z klíčových výhod použití sémantického jádra. Všechno od modelu přichází jako objekt JSON, ale sémantické jádro může tyto objekty automaticky deserializovat do správných typů vašich funkcí.
Po zařazování vstupů přidá sémantické jádro také volání funkce do historie chatu:
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"]})
)
]
)
)
Sémantické jádro pro Javu zpracovává volání funkcí jinak než C# a Python, když je chování automatického vyvolání nástroje nastaveno na nepravdivé. Obsah volání funkcí nepřidáváte do historie chatu; místo toho je odpovědnost za vyvolání volání funkcí přiřazena aplikaci. Přejděte k další části "Invoke the function" (Vyvolání funkce), například zpracování volání funkcí v Javě, pokud je automatické vyvolání false.
5) Vyvolání funkce
Jakmile má sémantické jádro správné typy, může funkci nakonec vyvolat add_pizza_to_cart . Vzhledem k tomu, že modul plug-in používá injektáž závislostí, může funkce pracovat s externími službami, jako je pizzaServiceuserContext a přidat pizzu do košíku uživatele.
Ne všechny funkce však budou úspěšné. Pokud funkce selže, může sémantické jádro chybu zpracovat a poskytnout výchozí odpověď na model. To umožňuje modelu pochopit, co se nepovedlo, a rozhodnout se zopakovat nebo vygenerovat odpověď na uživatele.
Tip
Aby se zajistilo, že model se dokáže sám opravit, je důležité poskytnout chybové zprávy, které jasně vysvětlují, co se pokazilo a jak to napravit. To může pomoci modelu zopakovat volání funkce se správnými informacemi.
Poznámka:
Sémantické jádro automaticky vyvolá funkce ve výchozím nastavení. Pokud ale dáváte přednost ruční správě vyvolání funkce, můžete povolit režim ručního vyvolání funkce. Další podrobnosti o tom, jak to udělat, najdete v článku o vyvolání funkce.
6) Vrátí výsledek funkce.
Po vyvolání funkce se výsledek funkce odešle zpět do modelu jako součást historie chatu. Díky tomu může model pochopit kontext konverzace a vygenerovat následnou odpověď.
Na pozadí přidá sémantické jádro do historie chatu novou zprávu z role nástroje, která vypadá takto:
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\"] } ] }"
)
]
)
)
Pokud je automatické vyvolání zakázané v chování volání nástroje, aplikace v Javě musí vyvolat volání funkce a přidat výsledek funkce jako AuthorRole.TOOL zprávu do historie chatu.
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()));
});
Všimněte si, že výsledkem je řetězec JSON, který pak model potřebuje zpracovat. Stejně jako předtím bude model muset investovat tokeny na spotřebování těchto informací. Proto je důležité, aby návratové typy byly co nejjednodušší. V tomto případě vrácení zahrnuje pouze nové položky přidané do košíku, ne celý košík.
Tip
Buďte co nejstručnější s vašimi návraty. Pokud je to možné, vraťte pouze informace, které model potřebuje, nebo shrňte informace pomocí jiné výzvy LLM před jejich vrácením.
Opakování kroků 2–6
Po vrácení výsledku do modelu se proces opakuje. Model zpracuje nejnovější historii chatu a vygeneruje odpověď. V tomto případě se model může zeptat uživatele, jestli chce do košíku přidat další pizzu nebo jestli si chce rezervovat.
Paralelní volání funkcí
V předchozím příkladu jsme ukázali, jak LLM může volat jednu funkci. Často to může být pomalé, pokud potřebujete volat více funkcí v posloupnosti. Aby se proces urychlil, podporuje několik LLM paralelní volání funkcí. LLM tak může volat více funkcí najednou a urychlit proces.
Pokud například chce uživatel objednat více pizz, může LLM volat add_pizza_to_cart funkci pro každou pizzu najednou. To může výrazně snížit počet odezvy do LLM a urychlit proces řazení.
Další kroky
Teď, když víte, jak funguje volání funkcí, můžete pokračovat v tom, jak nakonfigurovat různé aspekty volání funkcí, které lépe odpovídají vašim konkrétním scénářům, a to tak, že přejdete k dalšímu kroku: