Modalità di chiamata di funzione

Quando il modello di intelligenza artificiale riceve un prompt contenente un elenco di funzioni, può scegliere uno o più di essi per completare la richiesta. Quando si seleziona una funzione dal modello, è necessario richiamarla dal kernel semantico.

La funzione che chiama il sottosistema nel kernel semantico ha due modalità di chiamata di funzione: auto e manuale.

A seconda della modalità di chiamata, il kernel semantico esegue una chiamata di funzione end-to-end o fornisce al chiamante il controllo sul processo di chiamata della funzione.

Invocazione automatica di funzione

La chiamata automatica della funzione è la modalità predefinita del sottosistema di chiamata a funzione del kernel semantico. Quando il modello di intelligenza artificiale sceglie una o più funzioni, il kernel semantico richiama automaticamente le funzioni scelte. I risultati di queste chiamate di funzione vengono aggiunti alla cronologia delle chat e inviati automaticamente al modello nelle richieste successive. Il modello quindi ragiona sulla cronologia della chat, sceglie funzioni aggiuntive se necessario oppure genera la risposta finale. Questo approccio è completamente automatizzato e non richiede alcun intervento manuale del chiamante.

Tip

La chiamata alla funzione automatica è diversa dal comportamento di scelta della funzione automatica. Il primo determina se le funzioni devono essere richiamate automaticamente da Kernel semantico, mentre quest'ultima determina se le funzioni devono essere scelte automaticamente dal modello di intelligenza artificiale.

Questo esempio illustra come usare la chiamata alla funzione automatica nel kernel semantico. Il modello di intelligenza artificiale decide quali funzioni chiamare per completare il prompt e il kernel semantico esegue il resto e li richiama automaticamente.

using Microsoft.SemanticKernel;

IKernelBuilder builder = Kernel.CreateBuilder(); 
builder.AddOpenAIChatCompletion("<model-id>", "<api-key>");
builder.Plugins.AddFromType<WeatherForecastUtils>();
builder.Plugins.AddFromType<DateTimeUtils>(); 

Kernel kernel = builder.Build();

// By default, functions are set to be automatically invoked.  
// If you want to explicitly enable this behavior, you can do so with the following code:  
// PromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(autoInvoke: true) };  
PromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }; 

await kernel.InvokePromptAsync("Given the current time of day and weather, what is the likely color of the sky in Boston?", new(settings));
from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.functions.kernel_arguments import KernelArguments
from semantic_kernel.kernel import Kernel

kernel = Kernel()
kernel.add_service(OpenAIChatCompletion())

# Assuming that WeatherPlugin and DateTimePlugin are already implemented
kernel.add_plugin(WeatherPlugin(), "WeatherPlugin")
kernel.add_plugin(DateTimePlugin(), "DateTimePlugin")

query = "What is the weather in Seattle today?"
arguments = KernelArguments(
    settings=PromptExecutionSettings(
        # By default, functions are set to be automatically invoked.
        # If you want to explicitly enable this behavior, you can do so with the following code:
        # function_choice_behavior=FunctionChoiceBehavior.Auto(auto_invoke=True),
        function_choice_behavior=FunctionChoiceBehavior.Auto(),
    )
)

response = await kernel.invoke_prompt(query, arguments=arguments)

Tip

Altri aggiornamenti saranno presto disponibili per Java SDK.

Alcuni modelli di intelligenza artificiale supportano la chiamata a funzioni parallele, in cui il modello sceglie più funzioni per la chiamata. Ciò può essere utile nei casi in cui richiamare le funzioni scelte richiede molto tempo. Ad esempio, l'intelligenza artificiale può scegliere di recuperare simultaneamente le ultime notizie e l'ora corrente, invece di effettuare un'andata e ritorno per ciascuna funzione.

Il kernel semantico può richiamare queste funzioni in due modi diversi:

  • Sequenziale: le funzioni vengono richiamate una dopo l'altra. Questo è il comportamento predefinito.
  • Simultaneamente: le funzioni vengono richiamate contemporaneamente. Questa opzione può essere abilitata impostando la FunctionChoiceBehaviorOptions.AllowConcurrentInvocation proprietà su true, come illustrato nell'esempio seguente.
using Microsoft.SemanticKernel;

IKernelBuilder builder = Kernel.CreateBuilder(); 
builder.AddOpenAIChatCompletion("<model-id>", "<api-key>");
builder.Plugins.AddFromType<NewsUtils>();
builder.Plugins.AddFromType<DateTimeUtils>(); 

Kernel kernel = builder.Build();

// Enable concurrent invocation of functions to get the latest news and the current time.
FunctionChoiceBehaviorOptions options = new() { AllowConcurrentInvocation = true };

PromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(options: options) }; 

await kernel.InvokePromptAsync("Good morning! What is the current time and latest news headlines?", new(settings));

A volte un modello può scegliere più funzioni per la chiamata. Questa operazione viene spesso definita chiamata di funzione parallela . Quando vengono scelte più funzioni dal modello di intelligenza artificiale, Kernel semantico li richiamerà simultaneamente.

Tip

Con il connettore OpenAI o Azure OpenAI, è possibile disabilitare la chiamata di funzione parallela eseguendo le operazioni seguenti:

from semantic_kernel.connectors.ai.open_ai import OpenAIChatPromptExecutionSettings
from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior

settings = OpenAIChatPromptExecutionSettings(
    function_choice_behavior=FunctionChoiceBehavior.Auto(),
    parallel_tool_calls=False
)

Chiamata di funzione manuale

Nei casi in cui il chiamante vuole avere un maggiore controllo sul processo di chiamata della funzione, è possibile usare la chiamata manuale della funzione.

Quando la chiamata manuale della funzione è abilitata, il kernel semantico non richiama automaticamente le funzioni scelte dal modello di intelligenza artificiale. Restituisce invece un elenco di funzioni scelte al chiamante, che può quindi decidere quali funzioni richiamare, richiamarle in sequenza o in parallelo, gestire le eccezioni e così via. I risultati dell'invocazione della funzione devono essere aggiunti alla cronologia della chat e restituiti al modello, che li analizzerà e deciderà se scegliere ulteriori funzioni o generare una risposta finale.

L'esempio seguente illustra come usare la chiamata manuale della funzione.

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;

IKernelBuilder builder = Kernel.CreateBuilder(); 
builder.AddOpenAIChatCompletion("<model-id>", "<api-key>");
builder.Plugins.AddFromType<WeatherForecastUtils>();
builder.Plugins.AddFromType<DateTimeUtils>(); 

Kernel kernel = builder.Build();

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

// Manual function invocation needs to be enabled explicitly by setting autoInvoke to false.
PromptExecutionSettings settings = new() { FunctionChoiceBehavior = Microsoft.SemanticKernel.FunctionChoiceBehavior.Auto(autoInvoke: false) };

ChatHistory chatHistory = [];
chatHistory.AddUserMessage("Given the current time of day and weather, what is the likely color of the sky in Boston?");

while (true)
{
    ChatMessageContent result = await chatCompletionService.GetChatMessageContentAsync(chatHistory, settings, kernel);

    // Check if the AI model has generated a response.
    if (result.Content is not null)
    {
        Console.Write(result.Content);
        // Sample output: "Considering the current weather conditions in Boston with a tornado watch in effect resulting in potential severe thunderstorms,
        // the sky color is likely unusual such as green, yellow, or dark gray. Please stay safe and follow instructions from local authorities."
        break;
    }

    // Adding AI model response containing chosen functions to chat history as it's required by the models to preserve the context.
    chatHistory.Add(result); 

    // Check if the AI model has chosen any function for invocation.
    IEnumerable<FunctionCallContent> functionCalls = FunctionCallContent.GetFunctionCalls(result);
    if (!functionCalls.Any())
    {
        break;
    }

    // Sequentially iterating over each chosen function, invoke it, and add the result to the chat history.
    foreach (FunctionCallContent functionCall in functionCalls)
    {
        try
        {
            // Invoking the function
            FunctionResultContent resultContent = await functionCall.InvokeAsync(kernel);

            // Adding the function result to the chat history
            chatHistory.Add(resultContent.ToChatMessage());
        }
        catch (Exception ex)
        {
            // Adding function exception to the chat history.
            chatHistory.Add(new FunctionResultContent(functionCall, ex).ToChatMessage());
            // or
            //chatHistory.Add(new FunctionResultContent(functionCall, "Error details that the AI model can reason about.").ToChatMessage());
        }
    }
}

Nota

Le classi FunctionCallContent e FunctionResultContent vengono usate rispettivamente per rappresentare le chiamate di funzione del modello di intelligenza artificiale e i risultati della chiamata della funzione semantica del kernel. Contengono informazioni sulla funzione scelta, ad esempio l'ID funzione, il nome e gli argomenti e i risultati della chiamata di funzione, ad esempio l'ID della chiamata di funzione e il risultato.

L'esempio seguente mostra come usare l'invocazione manuale di funzioni con l'API di completamento chat in streaming. Si noti l'utilizzo della FunctionCallContentBuilder classe per compilare chiamate di funzione dal contenuto di streaming. Data la natura di streaming dell'API, anche le chiamate di funzione vengono trasmesse in streaming. Ciò significa che il chiamante deve costruire le chiamate di funzione a partire dal contenuto trasmesso in streaming prima di invocarle.

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;

IKernelBuilder builder = Kernel.CreateBuilder(); 
builder.AddOpenAIChatCompletion("<model-id>", "<api-key>");
builder.Plugins.AddFromType<WeatherForecastUtils>();
builder.Plugins.AddFromType<DateTimeUtils>(); 

Kernel kernel = builder.Build();

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

// Manual function invocation needs to be enabled explicitly by setting autoInvoke to false.
PromptExecutionSettings settings = new() { FunctionChoiceBehavior = Microsoft.SemanticKernel.FunctionChoiceBehavior.Auto(autoInvoke: false) };

ChatHistory chatHistory = [];
chatHistory.AddUserMessage("Given the current time of day and weather, what is the likely color of the sky in Boston?");

while (true)
{
    AuthorRole? authorRole = null;
    FunctionCallContentBuilder fccBuilder = new ();

    // Start or continue streaming chat based on the chat history
    await foreach (StreamingChatMessageContent streamingContent in chatCompletionService.GetStreamingChatMessageContentsAsync(chatHistory, settings, kernel))
    {
        // Check if the AI model has generated a response.
        if (streamingContent.Content is not null)
        {
            Console.Write(streamingContent.Content);
            // Sample streamed output: "The color of the sky in Boston is likely to be gray due to the rainy weather."
        }
        authorRole ??= streamingContent.Role;

        // Collect function calls details from the streaming content
        fccBuilder.Append(streamingContent);
    }

    // Build the function calls from the streaming content and quit the chat loop if no function calls are found
    IReadOnlyList<FunctionCallContent> functionCalls = fccBuilder.Build();
    if (!functionCalls.Any())
    {
        break;
    }

    // Creating and adding chat message content to preserve the original function calls in the chat history.
    // The function calls are added to the chat message a few lines below.
    ChatMessageContent fcContent = new ChatMessageContent(role: authorRole ?? default, content: null);
    chatHistory.Add(fcContent);

    // Iterating over the requested function calls and invoking them.
    // The code can easily be modified to invoke functions concurrently if needed.
    foreach (FunctionCallContent functionCall in functionCalls)
    {
        // Adding the original function call to the chat message content
        fcContent.Items.Add(functionCall);

        // Invoking the function
        FunctionResultContent functionResult = await functionCall.InvokeAsync(kernel);

        // Adding the function result to the chat history
        chatHistory.Add(functionResult.ToChatMessage());
    }
}
from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.contents.function_call_content import FunctionCallContent
from semantic_kernel.contents.function_result_content import FunctionResultContent
from semantic_kernel.kernel import Kernel

kernel = Kernel()
chat_completion_service = OpenAIChatCompletion()

# Assuming that WeatherPlugin is already implemented
kernel.add_plugin(WeatherPlugin(), "WeatherPlugin")

settings = PromptExecutionSettings(
    function_choice_behavior=FunctionChoiceBehavior.Auto(auto_invoke=False),
)

chat_history = ChatHistory()
chat_history.add_user_message("What is the weather in Seattle on 10th of September 2024 at 11:29 AM?")

response = await chat_completion_service.get_chat_message_content(chat_history, settings, kernel=kernel)
function_call_content = response.items[0]
assert isinstance(function_call_content, FunctionCallContent)

# Need to add the response to the chat history to preserve the context
chat_history.add_message(response)

function = kernel.get_function(function_call_content.plugin_name, function_call_content.function_name)
function_result = await function(kernel, function_call_content.to_kernel_arguments())

function_result_content = FunctionResultContent.from_function_call_content_and_result(
    function_call_content, function_result
)

# Adding the function result to the chat history
chat_history.add_message(function_result_content.to_chat_message_content())

# Invoke the model again with the function result
response = await chat_completion_service.get_chat_message_content(chat_history, settings, kernel=kernel)
print(response)
# The weather in Seattle on September 10th, 2024, is expected to be [weather condition].

Nota

Le classi FunctionCallContent e FunctionResultContent vengono usate rispettivamente per rappresentare le chiamate di funzione del modello di intelligenza artificiale e i risultati della chiamata della funzione semantica del kernel. Contengono informazioni sulla funzione scelta, ad esempio l'ID funzione, il nome e gli argomenti e i risultati della chiamata di funzione, ad esempio l'ID della chiamata di funzione e il risultato.

Tip

Altri aggiornamenti saranno presto disponibili per Java SDK.