microsoft / semantic-kernel

Integrate cutting-edge LLM technology quickly and easily into your apps
https://aka.ms/semantic-kernel
MIT License
20.86k stars 3.04k forks source link

.Net Unnecesary kernel verbosity in GetChatMessageContentAsync :) #6468

Open joslat opened 1 month ago

joslat commented 1 month ago

Describe the bug When I get the chatService from a kernel, why do I need to specify again the kernel when using it? IMHO it should use the kernel it is bound to (was generated from it) by default.

To Reproduce Steps to reproduce the behavior: Create a new .net console app, add the following code:

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

Console.WriteLine("Hello, Semantic Kernel!");

var modelDeploymentName = "gpt-4o";
var azureOpenAIEndpoint = Environment.GetEnvironmentVariable("AZUREOPENAI_ENDPOINT");
var azureOpenAIApiKey = Environment.GetEnvironmentVariable("AZUREOPENAI_APIKEY");

Kernel kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion(
        modelDeploymentName,
        azureOpenAIEndpoint,
        azureOpenAIApiKey)
    .Build();
kernel.ImportPluginFromType<WhatDateIsIt>();

var chatService = kernel.GetRequiredService<IChatCompletionService>();
ChatHistory chatHistory = new();

var executionSettings = new OpenAIPromptExecutionSettings
{
    ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};

bool exitnow = false;
while (exitnow == false)
{
    Console.WriteLine("Enter your question or type 'exit' to quit:");
    var userInput = Console.ReadLine();
    chatHistory.AddUserMessage(userInput);

    var response = await chatService.GetChatMessageContentAsync(
        chatHistory, 
        executionSettings);

    Console.WriteLine(response.ToString());
    chatHistory.Add(response);
}

public class WhatDateIsIt
{
    [KernelFunction, Description("Get the current UTC date")]
    public string Date(IFormatProvider? formatProvider = null) =>
        DateTimeOffset.UtcNow.ToString("D", formatProvider);
}

Expected behavior I would like that it does use function calling properly, but it cannot.

For this to happen you have to specify the kernel when the ChatCompletionService is used, when it was created from that same kernel. It's like telling it every single time who his "father" is ;)

For this to work I should use:

    var response = await chatService.GetChatMessageContentAsync(
        chatHistory, 
        executionSettings,
        kernel);

Screenshots N/A

Platform Anywhere

Additional context Add any other context about the problem here.

tyler-suard-parker commented 1 month ago

I made a comment too about unnecessary verbosity.

markwallace-microsoft commented 1 month ago

When I get the chatService from a kernel, why do I need to specify again the kernel when using it? IMHO it should use the kernel it is bound to (was generated from it) by default.

@joslat Our thinking is that the lifecycle of an AI service and a Kernel instance are typically different. E.g. I may instantiate an AI service to be used for the duration of my application whereas the Kernel instance may live for the duration of a request (with the AI service being injected into it).

We previously had the AI service and Kernel linked and this provided problematic because of the different lifecycles so we changed the behaviour. We find the current pattern works well when using DI.

We may need a Blog post for this, let me chat with the team. Thanks for the feedback.