Skip to content

Some issues related to support for an Agent declarative format #10237

@markwallace-microsoft

Description

@markwallace-microsoft

1. Use KernelFunction return value

The default KernelFunctionTerminationStrategy.ResultParser is:
public Func<FunctionResult, bool> ResultParser { get; init; } = (_) => true;

If the KernelFunction returns false then this will override this return value and cause termination.

Why doesn't the default implementation change the function result?

2. Pass Agent and ChatHistory in KernelArguments

Consider this code

    protected sealed override async Task<bool> ShouldAgentTerminateAsync(Agent agent, IReadOnlyList<ChatMessageContent> history, CancellationToken cancellationToken = default)
    {
        history = await history.ReduceAsync(this.HistoryReducer, cancellationToken).ConfigureAwait(false);

        KernelArguments originalArguments = this.Arguments ?? [];
        KernelArguments arguments =
            new(originalArguments, originalArguments.ExecutionSettings?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value))
            {
                { this.AgentVariableName, agent.Name ?? agent.Id },
                { this.HistoryVariableName, ChatMessageForPrompt.Format(history, this.EvaluateNameOnly) },
            };

        this.Logger.LogKernelFunctionTerminationStrategyInvokingFunction(nameof(ShouldAgentTerminateAsync), this.Function.PluginName, this.Function.Name);

        FunctionResult result = await this.Function.InvokeAsync(this.Kernel, arguments, cancellationToken).ConfigureAwait(false);

        this.Logger.LogKernelFunctionTerminationStrategyInvokedFunction(nameof(ShouldAgentTerminateAsync), this.Function.PluginName, this.Function.Name, result.ValueType);

        return this.ResultParser.Invoke(result);
    }

It would be more convenient to:

  1. Pass Agent instance rather than the name or id
  2. Pass the ChatHistory rather than a string (which cannot be parsed back into a ChatHistory
  3. Allow for KernelFunctions which has a signature which takes typed Agent and ChatHistory parameters

3. Should we have a standard way to Invoke an Agent

When we support declarative agents the follow will look something like this

  1. Load the Agent instance from a file which contains a declarative definition of an Agent
  2. Invoke the Agent instance optionally using a provided input and other arguments
  3. Capture the result which should contain everything needed to invoke the Agent instance again and maintain context

Here's some psuedo code:

Kernel kernel = ...
string agentYaml = EmbeddedResource.Read("MyAgent.yaml");
AgentFactory agentFactory = new AggregatorAgentFactory(
    new ChatCompletionFactory(),
    new OpenAIAssistantAgentFactory(),
    new XXXAgentFactory());
Agent agent = kernel.LoadAgentFromYaml(agentYaml); // What return type should we use to standardise?

// Should we have a unified pattern here?
ChatHistory chatHistory = new();
chatHistory.AddUserMessage(input);
await foreach (ChatMessageContent content in agent.InvokeAsync(chatHistory))
{
    chatHistory.Add(content);
}

At the moment the code to "Invoke" a ChatCompletionAgent versus a OpenAIAssistantAgent is different. Should we have an abstraction in the Agent framework that allows us to standardise?

Metadata

Metadata

Assignees

Labels

.NETIssue or Pull requests regarding .NET codeBuildFeatures planned for next Build conferenceagentsexperimentalAssociated with an experimental featurequestionFurther information is requestedsk team issueA tag to denote issues that where created by the Semantic Kernel team (i.e., not the community)staleIssue is stale because it has been open for a while and has no activity

Type

No type

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions