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:
- Pass
Agent instance rather than the name or id
- Pass the
ChatHistory rather than a string (which cannot be parsed back into a ChatHistory
- 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
- Load the
Agent instance from a file which contains a declarative definition of an Agent
- Invoke the
Agent instance optionally using a provided input and other arguments
- 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?
1. Use KernelFunction return value
The default
KernelFunctionTerminationStrategy.ResultParseris:public Func<FunctionResult, bool> ResultParser { get; init; } = (_) => true;If the
KernelFunctionreturns false then this will override this return value and cause termination.Why doesn't the default implementation change the function result?
2. Pass
AgentandChatHistoryinKernelArgumentsConsider this code
It would be more convenient to:
Agentinstance rather than the name or idChatHistoryrather than a string (which cannot be parsed back into a ChatHistoryKernelFunctionswhich has a signature which takes typedAgentandChatHistoryparameters3. Should we have a standard way to Invoke an
AgentWhen we support declarative agents the follow will look something like this
Agentinstance from a file which contains a declarative definition of anAgentAgentinstance optionally using a provided input and other argumentsAgentinstance again and maintain contextHere's some psuedo code:
At the moment the code to "Invoke" a
ChatCompletionAgentversus aOpenAIAssistantAgentis different. Should we have an abstraction in the Agent framework that allows us to standardise?