Refactor to support agent router.
This commit is contained in:
parent
0fe0f1db79
commit
77960842d9
|
|
@ -1,3 +1,5 @@
|
|||
using BotSharp.Abstraction.Conversations.Models;
|
||||
|
||||
namespace BotSharp.Abstraction.Agents;
|
||||
|
||||
public abstract class AgentHookBase : IAgentHook
|
||||
|
|
@ -33,8 +35,12 @@ public abstract class AgentHookBase : IAgentHook
|
|||
return true;
|
||||
}
|
||||
|
||||
public virtual Agent OnAgentLoaded()
|
||||
public virtual void OnAgentLoaded(Agent agent)
|
||||
{
|
||||
return _agent;
|
||||
}
|
||||
|
||||
public virtual bool OnAgentRouting(RoleDialogModel message, ref string id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,5 +24,5 @@ public interface IAgentHook
|
|||
/// </summary>
|
||||
/// <param name="agent"></param>
|
||||
/// <returns></returns>
|
||||
Agent OnAgentLoaded();
|
||||
void OnAgentLoaded(Agent agent);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
namespace BotSharp.Abstraction.Agents;
|
||||
|
||||
public interface IAgentRouting
|
||||
{
|
||||
Task<Agent> LoadCurrentAgent();
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
using BotSharp.Abstraction.Conversations.Models;
|
||||
|
||||
namespace BotSharp.Abstraction.Agents.Models;
|
||||
|
||||
public class Agent
|
||||
|
|
@ -29,4 +27,7 @@ public class Agent
|
|||
/// Domain knowledges
|
||||
/// </summary>
|
||||
public string Knowledges { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
=> $"{Name} {Id}";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,5 +2,9 @@ namespace BotSharp.Abstraction.Agents.Settings;
|
|||
|
||||
public class AgentSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Router Agent Id
|
||||
/// </summary>
|
||||
public string RouterId { get; set; }
|
||||
public string DataDir { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,25 +8,25 @@ public class RoleDialogModel
|
|||
public string Role { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public string Content { get; set; }
|
||||
public string CurrentAgentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Function name if LLM response function call
|
||||
/// </summary>
|
||||
public string? FunctionName { get; set; }
|
||||
|
||||
public string? FunctionArgs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Function execution result
|
||||
/// </summary>
|
||||
public string? ExecutionResult { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When function callback has been executed, system will pass result to LLM again,
|
||||
/// Set this property to True to stop calling LLM.
|
||||
/// </summary>
|
||||
public bool StopSubsequentInteraction { get; set; }
|
||||
|
||||
public bool IsConversationEnd { get; set; }
|
||||
|
||||
public bool NeedReloadAgent { get; set; }
|
||||
public bool StopPropagate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Channel name
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ namespace BotSharp.Abstraction.MLTasks;
|
|||
|
||||
public interface IChatCompletion
|
||||
{
|
||||
string GetChatCompletions(Agent agent, List<RoleDialogModel> conversations);
|
||||
// string GetChatCompletions(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived);
|
||||
Task<bool> GetChatCompletionsAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived);
|
||||
Task<bool> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace BotSharp.Abstraction.Utilities;
|
||||
|
||||
public static class StringExtensions
|
||||
|
|
@ -15,4 +18,19 @@ public static class StringExtensions
|
|||
else
|
||||
return str;
|
||||
}
|
||||
|
||||
public static string CleanPhoneNumber(this string phoneNumber)
|
||||
{
|
||||
if (phoneNumber != null && !phoneNumber.All(char.IsDigit))
|
||||
{
|
||||
phoneNumber = Regex.Replace(phoneNumber, @"[^\d]", "");
|
||||
}
|
||||
|
||||
if (phoneNumber != null && phoneNumber.Length > 10)
|
||||
{
|
||||
phoneNumber = phoneNumber.Substring(1);
|
||||
}
|
||||
|
||||
return phoneNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
|
||||
namespace BotSharp.Core.Agents.Services;
|
||||
|
||||
public class AgentRouter : IAgentRouting
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger _logger;
|
||||
private readonly AgentSettings _settings;
|
||||
|
||||
public AgentRouter(IServiceProvider services,
|
||||
ILogger<AgentRouter> logger,
|
||||
AgentSettings settings)
|
||||
{
|
||||
_services = services;
|
||||
_logger = logger;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public async Task<Agent> LoadCurrentAgent()
|
||||
{
|
||||
// Load current agent from state
|
||||
var stateService = _services.GetRequiredService<IConversationStateService>();
|
||||
var currentAgentId = stateService.GetState("agentId");
|
||||
if (string.IsNullOrEmpty(currentAgentId))
|
||||
{
|
||||
currentAgentId = _settings.RouterId;
|
||||
}
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
var agent = await agentService.LoadAgent(currentAgentId);
|
||||
|
||||
// Set agent and trigger state changed
|
||||
stateService.SetState("agentId", currentAgentId);
|
||||
|
||||
return agent;
|
||||
}
|
||||
}
|
||||
|
|
@ -39,9 +39,11 @@ public partial class AgentService
|
|||
hook.OnSamplesLoaded(ref samples);
|
||||
}
|
||||
|
||||
hook.OnAgentLoaded();
|
||||
hook.OnAgentLoaded(agent);
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Loaded agent {agent}.");
|
||||
|
||||
return agent;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ public static class BotSharpServiceCollectionExtensions
|
|||
|
||||
RegisterPlugins(services, config);
|
||||
|
||||
services.AddScoped<IAgentRouting, AgentRouter>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public class ConversationController : ControllerBase, IApiAdapter
|
|||
await conv.SendMessage(agentId, conversationId,
|
||||
new RoleDialogModel("user", input.Text),
|
||||
async msg =>
|
||||
response.Text += msg.Content,
|
||||
response.Text = msg.Content,
|
||||
async fn
|
||||
=> await Task.CompletedTask);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
using Amazon.SecurityToken.Model.Internal.MarshallTransformations;
|
||||
using BotSharp.Abstraction.Agents.Enums;
|
||||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using BotSharp.Abstraction.Functions;
|
||||
using BotSharp.Abstraction.MLTasks;
|
||||
|
|
@ -12,7 +15,7 @@ public class ConversationService : IConversationService
|
|||
private readonly ConversationSetting _settings;
|
||||
private readonly IConversationStorage _storage;
|
||||
|
||||
public ConversationService(IServiceProvider services,
|
||||
public ConversationService(IServiceProvider services,
|
||||
IUserIdentity user,
|
||||
ConversationSetting settings,
|
||||
IConversationStorage storage,
|
||||
|
|
@ -69,11 +72,12 @@ public class ConversationService : IConversationService
|
|||
return record.ToConversation();
|
||||
}
|
||||
|
||||
public async Task<bool> SendMessage(string agentId, string conversationId,
|
||||
RoleDialogModel lastDalog,
|
||||
Func<RoleDialogModel, Task> onMessageReceived,
|
||||
public async Task<bool> SendMessage(string agentId, string conversationId,
|
||||
RoleDialogModel lastDalog,
|
||||
Func<RoleDialogModel, Task> onMessageReceived,
|
||||
Func<RoleDialogModel, Task> onFunctionExecuting)
|
||||
{
|
||||
lastDalog.CurrentAgentId = agentId;
|
||||
_storage.Append(conversationId, lastDalog);
|
||||
|
||||
var wholeDialogs = GetDialogHistory(conversationId);
|
||||
|
|
@ -95,11 +99,9 @@ public class ConversationService : IConversationService
|
|||
var stateService = _services.GetRequiredService<IConversationStateService>();
|
||||
stateService.SetConversation(conversationId);
|
||||
stateService.Load();
|
||||
stateService.SetState("agentId", agentId);
|
||||
|
||||
// load agent
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
var agent = await agentService.LoadAgent(agentId);
|
||||
var router = _services.GetRequiredService<IAgentRouting>();
|
||||
var agent = await router.LoadCurrentAgent();
|
||||
|
||||
// Get relevant domain knowledge
|
||||
/*if (_settings.EnableKnowledgeBase)
|
||||
|
|
@ -127,39 +129,100 @@ public class ConversationService : IConversationService
|
|||
var chatCompletion = GetChatCompletion();
|
||||
var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg =>
|
||||
{
|
||||
if (msg.Role == "function")
|
||||
await HandleMessage(conversationId, agent, msg, onMessageReceived, onFunctionExecuting);
|
||||
|
||||
if (msg.NeedReloadAgent)
|
||||
{
|
||||
// Save states
|
||||
SaveStateByArgs(msg.Content);
|
||||
|
||||
// Call functions
|
||||
await onFunctionExecuting(msg);
|
||||
await CallFunctions(conversationId, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add to dialog history
|
||||
_storage.Append(conversationId, new RoleDialogModel(msg.Role, msg.Content));
|
||||
|
||||
// After chat completion hook
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
await hook.AfterCompletion(msg);
|
||||
}
|
||||
|
||||
await onMessageReceived(msg);
|
||||
}
|
||||
|
||||
// Clean conversation
|
||||
if (msg.IsConversationEnd)
|
||||
{
|
||||
stateService.CleanState();
|
||||
await HandleMessageIfAgentReloaded(conversationId, agent, msg, wholeDialogs, onMessageReceived, onFunctionExecuting);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task HandleMessage(string conversationId, Agent agent, RoleDialogModel msg,
|
||||
Func<RoleDialogModel, Task> onMessageReceived,
|
||||
Func<RoleDialogModel, Task> onFunctionExecuting)
|
||||
{
|
||||
if (msg.Role == "function")
|
||||
{
|
||||
// Save states
|
||||
SaveStateByArgs(msg.FunctionArgs);
|
||||
|
||||
// Call functions
|
||||
await onFunctionExecuting(msg);
|
||||
await CallFunctions(msg);
|
||||
|
||||
// Add to dialog history
|
||||
if (msg.ExecutionResult != null)
|
||||
{
|
||||
if (msg.NeedReloadAgent)
|
||||
{
|
||||
_logger.LogInformation($"Skipped append dialog log: {msg.FunctionName}\n{msg.FunctionArgs}\n{msg.ExecutionResult}");
|
||||
return;
|
||||
}
|
||||
|
||||
_storage.Append(conversationId, new RoleDialogModel(msg.Role, msg.Content)
|
||||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
FunctionName = msg.FunctionName,
|
||||
FunctionArgs = msg.FunctionArgs,
|
||||
ExecutionResult = msg.ExecutionResult
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add to dialog history
|
||||
_storage.Append(conversationId, new RoleDialogModel(msg.Role, msg.Content)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
});
|
||||
|
||||
var hooks = _services.GetServices<IConversationHook>().ToList();
|
||||
// After chat completion hook
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
await hook.AfterCompletion(msg);
|
||||
}
|
||||
|
||||
await onMessageReceived(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleMessageIfAgentReloaded(string conversationId, Agent agent,
|
||||
RoleDialogModel msg,
|
||||
List<RoleDialogModel> wholeDialogs,
|
||||
Func<RoleDialogModel, Task> onMessageReceived,
|
||||
Func<RoleDialogModel, Task> onFunctionExecuting)
|
||||
{
|
||||
var state = _services.GetRequiredService<IConversationStateService>();
|
||||
var currentAgentId = state.GetState("agentId");
|
||||
|
||||
// Send to LLM to get final response when agent is switched.
|
||||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
var chatCompletion = conv.GetChatCompletion();
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
var newAgent = await agentService.LoadAgent(currentAgentId);
|
||||
await chatCompletion.GetChatCompletionsAsync(newAgent, wholeDialogs, async newMsg =>
|
||||
{
|
||||
if (newMsg.Role == AgentRole.Function)
|
||||
{
|
||||
await HandleMessage(conversationId, agent, newMsg, onMessageReceived, onFunctionExecuting);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.StopPropagate = true;
|
||||
await onMessageReceived(newMsg);
|
||||
|
||||
_storage.Append(conversationId, new RoleDialogModel(newMsg.Role, newMsg.Content)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void SaveStateByArgs(string args)
|
||||
{
|
||||
var stateService = _services.GetRequiredService<IConversationStateService>();
|
||||
|
|
@ -173,7 +236,7 @@ public class ConversationService : IConversationService
|
|||
}
|
||||
}
|
||||
|
||||
private async Task CallFunctions(string conversationId, RoleDialogModel msg)
|
||||
private async Task CallFunctions(RoleDialogModel msg)
|
||||
{
|
||||
var hooks = _services.GetServices<IConversationHook>().ToList();
|
||||
|
||||
|
|
@ -182,6 +245,12 @@ public class ConversationService : IConversationService
|
|||
.Where(x => x.Name == msg.FunctionName)
|
||||
.ToList();
|
||||
|
||||
if (functions.Count == 0)
|
||||
{
|
||||
_logger.LogError($"Can't find function implementation of {msg.FunctionName}.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var fn in functions)
|
||||
{
|
||||
// Before executing functions
|
||||
|
|
@ -193,12 +262,6 @@ public class ConversationService : IConversationService
|
|||
// Execute function
|
||||
await fn.Execute(msg);
|
||||
|
||||
// Add to dialog history
|
||||
_storage.Append(conversationId, new RoleDialogModel(msg.Role, msg.ExecutionResult)
|
||||
{
|
||||
FunctionName = msg.FunctionName,
|
||||
});
|
||||
|
||||
// After functions have been executed
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using BotSharp.Abstraction.Agents.Enums;
|
||||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using System.IO;
|
||||
|
||||
|
|
@ -15,15 +16,40 @@ public class ConversationStorage : IConversationStorage
|
|||
{
|
||||
var conversationFile = GetStorageFile(conversationId);
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"{dialog.Role}|{dialog.CreatedAt}|{dialog.FunctionName}");
|
||||
|
||||
var content = dialog.Content.Trim().Replace("\r", " ").Replace("\n", " ");
|
||||
if (string.IsNullOrEmpty(content))
|
||||
if (dialog.Role == AgentRole.Function)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var args = dialog.FunctionArgs.Replace("\r", " ").Replace("\n", " ").Trim();
|
||||
|
||||
sb.AppendLine($" - {content}");
|
||||
sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|{dialog.CurrentAgentId}|{dialog.FunctionName}|{args}");
|
||||
|
||||
var content = dialog.ExecutionResult.Replace("\r", " ").Replace("\n", " ").Trim();
|
||||
if (string.IsNullOrEmpty(content))
|
||||
{
|
||||
return;
|
||||
}
|
||||
sb.AppendLine($" - {content}");
|
||||
}
|
||||
else if (dialog.Role == AgentRole.Assistant)
|
||||
{
|
||||
sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|||");
|
||||
var content = dialog.Content.Replace("\r", " ").Replace("\n", " ").Trim();
|
||||
if (string.IsNullOrEmpty(content))
|
||||
{
|
||||
return;
|
||||
}
|
||||
sb.AppendLine($" - {content}");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|{dialog.CurrentAgentId}||");
|
||||
var content = dialog.Content.Replace("\r", " ").Replace("\n", " ").Trim();
|
||||
if (string.IsNullOrEmpty(content))
|
||||
{
|
||||
return;
|
||||
}
|
||||
sb.AppendLine($" - {content}");
|
||||
}
|
||||
|
||||
var conversation = sb.ToString();
|
||||
File.AppendAllText(conversationFile, conversation);
|
||||
|
|
@ -39,13 +65,18 @@ public class ConversationStorage : IConversationStorage
|
|||
{
|
||||
var meta = dialogs[i];
|
||||
var dialog = dialogs[i + 1];
|
||||
var role = meta.Split('|')[0];
|
||||
var createdAt = DateTime.Parse(meta.Split('|')[1]);
|
||||
var createdAt = DateTime.Parse(meta.Split('|')[0]);
|
||||
var role = meta.Split('|')[1];
|
||||
var currentAgentId = meta.Split('|')[2];
|
||||
var funcName = meta.Split('|')[3];
|
||||
var funcArgs= meta.Split('|')[4];
|
||||
var text = dialog.Substring(4);
|
||||
var funcName = meta.Split('|')[2];
|
||||
|
||||
results.Add(new RoleDialogModel(role, text)
|
||||
{
|
||||
CurrentAgentId = currentAgentId,
|
||||
FunctionName = funcName,
|
||||
FunctionArgs = funcArgs,
|
||||
CreatedAt = createdAt
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
_services = services;
|
||||
}
|
||||
|
||||
public string GetChatCompletions(Agent agent, List<RoleDialogModel> conversations)
|
||||
public string GetChatCompletions(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,27 +25,41 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public string GetChatCompletions(Agent agent, List<RoleDialogModel> conversations)
|
||||
private OpenAIClient GetClient()
|
||||
{
|
||||
var client = new OpenAIClient(new Uri(_settings.Endpoint), new AzureKeyCredential(_settings.ApiKey));
|
||||
return client;
|
||||
}
|
||||
|
||||
/*public string GetChatCompletions(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived)
|
||||
{
|
||||
var client = GetClient();
|
||||
var chatCompletionsOptions = PrepareOptions(agent, conversations);
|
||||
|
||||
var response = client.GetChatCompletions(_settings.DeploymentModel.ChatCompletionModel, chatCompletionsOptions);
|
||||
var choice = response.Value.Choices[0];
|
||||
var message = choice.Message;
|
||||
|
||||
string output = "";
|
||||
foreach (var choice in response.Value.Choices)
|
||||
if (choice.FinishReason == CompletionsFinishReason.FunctionCall)
|
||||
{
|
||||
var message = choice.Message;
|
||||
if (message.Content == null)
|
||||
continue;
|
||||
Console.Write(message.Content);
|
||||
output += message.Content;
|
||||
response = HandleFunctionCall(message,
|
||||
onMessageReceived,
|
||||
chatCompletionsOptions).Result;
|
||||
}
|
||||
|
||||
_logger.LogInformation(output);
|
||||
choice = response.Value.Choices[0];
|
||||
message = choice.Message;
|
||||
|
||||
return output.Trim();
|
||||
}
|
||||
_logger.LogInformation(message.Content);
|
||||
|
||||
if (!string.IsNullOrEmpty(message.Content))
|
||||
{
|
||||
onMessageReceived(new RoleDialogModel(ChatRole.Assistant.ToString(), message.Content))
|
||||
.Wait();
|
||||
}
|
||||
|
||||
return message.Content.Trim();
|
||||
}*/
|
||||
|
||||
public List<RoleDialogModel> GetChatSamples(string sampleText)
|
||||
{
|
||||
|
|
@ -95,7 +109,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
|
||||
public async Task<bool> GetChatCompletionsAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived)
|
||||
{
|
||||
var client = new OpenAIClient(new Uri(_settings.Endpoint), new AzureKeyCredential(_settings.ApiKey));
|
||||
var client = GetClient();
|
||||
var chatCompletionsOptions = PrepareOptions(agent, conversations);
|
||||
|
||||
var response = await client.GetChatCompletionsAsync(_settings.DeploymentModel.ChatCompletionModel, chatCompletionsOptions);
|
||||
|
|
@ -104,51 +118,24 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
|
||||
if (choice.FinishReason == CompletionsFinishReason.FunctionCall)
|
||||
{
|
||||
if (message.FunctionCall == null || message.FunctionCall.Arguments == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_logger.LogInformation($"{message.FunctionCall.Name}: {message.FunctionCall.Arguments}");
|
||||
var funcContextIn = new RoleDialogModel(ChatRole.Function.ToString(), message.FunctionCall.Arguments)
|
||||
{
|
||||
FunctionName = message.FunctionCall.Name
|
||||
};
|
||||
|
||||
// Execute functions
|
||||
await onMessageReceived(funcContextIn);
|
||||
|
||||
if (funcContextIn.StopSubsequentInteraction)
|
||||
{
|
||||
// Emit a fake message that should be populated by whom set StopSubsequentInteraction as True.
|
||||
await onMessageReceived(new RoleDialogModel(ChatRole.Assistant.ToString(), ""));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (funcContextIn.IsConversationEnd)
|
||||
{
|
||||
await onMessageReceived(new RoleDialogModel(ChatRole.Assistant.ToString(), funcContextIn.Content)
|
||||
{
|
||||
IsConversationEnd = true
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// After function is executed, pass the result to LLM
|
||||
chatCompletionsOptions.Messages.Add(new ChatMessage(ChatRole.Function, funcContextIn.ExecutionResult)
|
||||
{
|
||||
Name = funcContextIn.FunctionName
|
||||
});
|
||||
response = client.GetChatCompletions(_settings.DeploymentModel.ChatCompletionModel, chatCompletionsOptions);
|
||||
response = await HandleFunctionCall(agent,
|
||||
message,
|
||||
onMessageReceived,
|
||||
chatCompletionsOptions);
|
||||
}
|
||||
|
||||
choice = response.Value.Choices[0];
|
||||
message = choice.Message;
|
||||
|
||||
_logger.LogInformation(message.Content);
|
||||
|
||||
if (!string.IsNullOrEmpty(message.Content))
|
||||
if (response != null)
|
||||
{
|
||||
await onMessageReceived(new RoleDialogModel(ChatRole.Assistant.ToString(), message.Content));
|
||||
choice = response.Value.Choices[0];
|
||||
message = choice.Message;
|
||||
|
||||
_logger.LogInformation(message.Content);
|
||||
|
||||
if (!string.IsNullOrEmpty(message.Content))
|
||||
{
|
||||
var msgByLlm = new RoleDialogModel(ChatRole.Assistant.ToString(), message.Content);
|
||||
await onMessageReceived(msgByLlm);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -198,10 +185,61 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
return true;
|
||||
}
|
||||
|
||||
private async Task<Response<ChatCompletions>> HandleFunctionCall(Agent agent,
|
||||
ChatMessage message,
|
||||
Func<RoleDialogModel, Task> onMessageReceived,
|
||||
ChatCompletionsOptions chatCompletionsOptions)
|
||||
{
|
||||
Response<ChatCompletions> response = default;
|
||||
|
||||
if (message.FunctionCall == null || message.FunctionCall.Arguments == null)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"{message.FunctionCall.Name}: {message.FunctionCall.Arguments}");
|
||||
var funcContextIn = new RoleDialogModel(ChatRole.Function.ToString(), message.Content)
|
||||
{
|
||||
CurrentAgentId = agent.Id,
|
||||
FunctionName = message.FunctionCall.Name,
|
||||
FunctionArgs = message.FunctionCall.Arguments
|
||||
};
|
||||
|
||||
// Execute functions
|
||||
await onMessageReceived(funcContextIn);
|
||||
|
||||
if (funcContextIn.StopPropagate)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
||||
if (funcContextIn.IsConversationEnd)
|
||||
{
|
||||
await onMessageReceived(new RoleDialogModel(ChatRole.Assistant.ToString(), funcContextIn.Content)
|
||||
{
|
||||
IsConversationEnd = true
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
// After function is executed, pass the result to LLM
|
||||
if (funcContextIn.ExecutionResult != null)
|
||||
{
|
||||
chatCompletionsOptions.Messages.Add(new ChatMessage(ChatRole.Function, funcContextIn.ExecutionResult)
|
||||
{
|
||||
Name = funcContextIn.FunctionName
|
||||
});
|
||||
var client = GetClient();
|
||||
response = client.GetChatCompletions(_settings.DeploymentModel.ChatCompletionModel, chatCompletionsOptions);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private ChatCompletionsOptions PrepareOptions(Agent agent, List<RoleDialogModel> conversations)
|
||||
{
|
||||
var chatCompletionsOptions = new ChatCompletionsOptions();
|
||||
|
||||
|
||||
if (!string.IsNullOrEmpty(agent.Instruction))
|
||||
{
|
||||
chatCompletionsOptions.Messages.Add(new ChatMessage(ChatRole.System, agent.Instruction));
|
||||
|
|
@ -244,6 +282,10 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
}
|
||||
}
|
||||
|
||||
// https://community.openai.com/t/cheat-sheet-mastering-temperature-and-top-p-in-chatgpt-api-a-few-tips-and-tricks-on-controlling-the-creativity-deterministic-output-of-prompt-responses/172683
|
||||
chatCompletionsOptions.Temperature = 0.5f;
|
||||
chatCompletionsOptions.NucleusSamplingFactor = 0.5f;
|
||||
|
||||
_logger.LogInformation(string.Join("\n", chatCompletionsOptions.Messages.Select(x => $"{x.Role}: {x.Content}")));
|
||||
return chatCompletionsOptions;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue