함수 호출 모드(Function Invocation Modes)

함수 호출 모드(Function Invocation Modes)

AI 모델이 함수 목록을 담은 프롬프트를 받으면, 프롬프트를 완성하기 위해 그중 하나 이상을 호출 대상으로 선택할 수 있어요. 모델이 함수를 선택하면 Semantic Kernel이 그것을 호출(invoke) 해야 합니다.

Semantic Kernel의 함수 호출 서브시스템에는 auto(자동)manual(수동) 두 가지 함수 호출 모드가 있습니다. 호출 모드에 따라 Semantic Kernel이 end-to-end 함수 호출을 수행하거나, 호출자에게 함수 호출 과정의 제어권을 넘깁니다.

출처: 공식 문서 — Function Invocation

자동 함수 호출(Auto Function Invocation)

자동 함수 호출은 Semantic Kernel 함수 호출 서브시스템의 기본 모드예요. AI 모델이 함수 하나 이상을 선택하면 Semantic Kernel이 선택된 함수들을 자동으로 호출합니다. 이 함수 호출들의 결과는 채팅 기록에 추가되고 후속 요청에서 모델에 자동으로 전송됩니다. 그러면 모델이 채팅 기록을 추론하고, 필요하면 추가 함수를 고르거나 최종 응답을 생성합니다. 이 접근법은 완전히 자동화되어 호출자의 수동 개입이 필요 없어요.

팁. 자동 함수 호출은 자동 함수 선택 동작과는 달라요. 전자는 함수를 Semantic Kernel이 자동으로 호출해야 하는지 여부를, 후자는 AI 모델이 함수를 자동으로 선택해야 하는지 여부를 결정합니다.

아래 예시는 자동 함수 호출을 사용하는 방법을 보여줘요. AI 모델이 프롬프트를 완성하기 위해 호출할 함수를 결정하고, Semantic Kernel이 나머지를 처리해 그것들을 자동으로 호출합니다.

C#

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

Python

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)

팁(Java). Java SDK에 더 많은 업데이트가 곧 제공될 예정이에요.

일부 AI 모델은 병렬 함수 호출을 지원합니다. 이는 모델이 여러 함수를 호출 대상으로 선택하는 것을 뜻해요. 선택된 함수를 호출하는 데 오래 걸리는 경우 유용합니다. 예를 들어 AI가 함수마다 왕복하는 대신 최신 뉴스와 현재 시간을 동시에 검색하도록 선택할 수 있어요.

Semantic Kernel은 이런 함수들을 두 가지 방식으로 호출할 수 있습니다.

  • 순차적(Sequentially) — 함수를 하나씩 차례로 호출합니다. 기본 동작이에요.
  • 동시적(Concurrently) — 함수를 동시에 호출합니다. 아래 예시처럼 FunctionChoiceBehaviorOptions.AllowConcurrentInvocation 속성을 true로 설정하면 활성화됩니다.
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));

Python에서의 병렬 함수 호출. 때로 모델이 호출 대상으로 여러 함수를 선택할 수 있어요. 흔히 병렬 함수 호출이라고 부릅니다. AI 모델이 여러 함수를 선택하면 Semantic Kernel은 그것들을 동시에 호출합니다.

팁(Python). OpenAI 또는 Azure OpenAI 커넥터에서 다음처럼 병렬 함수 호출을 비활성화할 수 있어요.

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
)

수동 함수 호출(Manual Function Invocation)

호출자가 함수 호출 과정을 더 많이 제어하고 싶을 때는 수동 함수 호출을 쓸 수 있습니다.

수동 함수 호출을 활성화하면 Semantic Kernel은 AI 모델이 선택한 함수를 자동으로 호출하지 않아요. 대신 선택된 함수 목록을 호출자에게 반환합니다. 호출자는 어떤 함수를 호출할지 결정하고, 순차적으로 또는 병렬로 호출하며, 예외를 처리하는 등의 제어를 할 수 있습니다. 함수 호출 결과는 채팅 기록에 추가되어 모델에 반환되어야 해요. 모델이 그것을 추론하고 추가 함수를 고를지 최종 응답을 생성할지 결정합니다.

아래 예시는 수동 함수 호출을 사용하는 방법을 보여줘요.

C#

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

참고. FunctionCallContentFunctionResultContent 클래스는 각각 AI 모델 함수 호출과 Semantic Kernel 함수 호출 결과를 나타내는 데 쓰입니다. 여기에는 선택된 함수에 대한 정보(예: 함수 ID, 이름, 인자)와 함수 호출 결과(예: 함수 호출 ID와 결과)가 들어 있어요.

C#에서 스트리밍 채팅 완성 API로 수동 함수 호출. 아래 예시는 스트리밍 채팅 완성 API로 수동 함수 호출을 사용하는 방법을 보여줘요. 스트리밍 콘텐츠에서 함수 호출을 만들기 위해 FunctionCallContentBuilder 클래스를 쓰는 점을 주목하세요. API의 스트리밍 특성 때문에 함수 호출도 스트리밍됩니다. 따라서 호출자는 호출하기 전에 스트리밍 콘텐츠에서 함수 호출을 만들어야 합니다.

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

Python

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].

참고. FunctionCallContentFunctionResultContent 클래스는 각각 AI 모델 함수 호출과 Semantic Kernel 함수 호출 결과를 나타내는 데 쓰입니다. 여기에는 선택된 함수에 대한 정보(예: 함수 ID, 이름, 인자)와 함수 호출 결과(예: 함수 호출 ID와 결과)가 들어 있어요.

더 알아보기 (Learn more)