Try new routing.
This commit is contained in:
parent
3df0565a63
commit
3e68cf0311
|
|
@ -2,7 +2,7 @@
|
|||
<PropertyGroup>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<OutputPath>..\..\..\packages</OutputPath>
|
||||
<PackageVersion>0.12.3</PackageVersion>
|
||||
<PackageVersion>0.13.0</PackageVersion>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -11,4 +11,9 @@ public class FunctionCallFromLlm
|
|||
|
||||
[JsonPropertyName("parameters")]
|
||||
public RetrievalArgs Parameters { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Function}: {Parameters}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@ namespace BotSharp.Abstraction.Routing;
|
|||
|
||||
public interface IRoutingService
|
||||
{
|
||||
Task DeleteRoutingItems();
|
||||
Task DeleteRoutingProfiles();
|
||||
List<RoleDialogModel> Dialogs { get; }
|
||||
Task<RoleDialogModel> Enter(Agent agent, List<RoleDialogModel> whileDialogs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,4 +16,9 @@ public class RetrievalArgs : RoutingArgs
|
|||
|
||||
[JsonPropertyName("args")]
|
||||
public JsonDocument Arguments { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{AgentName} {Question} ({JsonSerializer.Serialize(Arguments)}) => {Answer} ({Reason})";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ public static class BotSharpServiceCollectionExtensions
|
|||
services.AddScoped<IAgentHook, RoutingHook>();
|
||||
|
||||
services.AddScoped<Simulator>();
|
||||
services.AddScoped<IRoutingService, RoutingService>();
|
||||
|
||||
if (myDatabaseSettings.Default == "FileRepository")
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
using BotSharp.Core.Routing;
|
||||
|
||||
namespace BotSharp.Core.Conversations.Services;
|
||||
|
||||
|
|
@ -52,10 +52,10 @@ public partial class ConversationService
|
|||
|
||||
// reasoning
|
||||
var settings = _services.GetRequiredService<RoutingSettings>();
|
||||
if (settings.ReasonerId == agent.Id)
|
||||
if (settings.RouterId == agent.Id)
|
||||
{
|
||||
var simulator = _services.GetRequiredService<Simulator>();
|
||||
var reasonedContext = await simulator.Enter(agent, wholeDialogs);
|
||||
var routing = _services.GetRequiredService<IRoutingService>();
|
||||
var reasonedContext = await routing.Enter(agent, wholeDialogs);
|
||||
|
||||
if (reasonedContext.FunctionName == "interrupt_task_execution")
|
||||
{
|
||||
|
|
@ -83,7 +83,7 @@ public partial class ConversationService
|
|||
}
|
||||
}
|
||||
|
||||
simulator.Dialogs.ForEach(x =>
|
||||
routing.Dialogs.ForEach(x =>
|
||||
{
|
||||
wholeDialogs.Add(x);
|
||||
if (x.Content != null)
|
||||
|
|
|
|||
183
src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs
Normal file
183
src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Functions;
|
||||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Repositories;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
|
||||
namespace BotSharp.Core.Routing;
|
||||
|
||||
public class RoutingService : IRoutingService
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger _logger;
|
||||
private List<RoleDialogModel> _dialogs;
|
||||
public List<RoleDialogModel> Dialogs => _dialogs;
|
||||
|
||||
public RoutingService(IServiceProvider services, ILogger<RoutingService> logger)
|
||||
{
|
||||
_services = services;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<RoleDialogModel> Enter(Agent agent, List<RoleDialogModel> whileDialogs)
|
||||
{
|
||||
_dialogs = new List<RoleDialogModel>();
|
||||
RoleDialogModel result = new RoleDialogModel(AgentRole.Assistant, "not handled");
|
||||
|
||||
foreach (var dialog in whileDialogs.TakeLast(10))
|
||||
{
|
||||
agent.Instruction += $"\r\n{dialog.Role}: {dialog.Content}";
|
||||
}
|
||||
|
||||
var inst = await GetNextInstructionFromReasoner(agent);
|
||||
int loopCount = 0;
|
||||
while (loopCount < 3)
|
||||
{
|
||||
loopCount++;
|
||||
if (inst.Function == "continue_execute_task")
|
||||
{
|
||||
var router = _services.GetRequiredService<IAgentRouting>();
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var record = db.Agents.First(x => x.Name.ToLower() == inst.Parameters.AgentName.ToLower());
|
||||
|
||||
result = new RoleDialogModel(AgentRole.Function, inst.Parameters.Question)
|
||||
{
|
||||
FunctionName = inst.Function,
|
||||
FunctionArgs = JsonSerializer.Serialize(inst.Parameters.Arguments),
|
||||
CurrentAgentId = record.Id,
|
||||
};
|
||||
break;
|
||||
}
|
||||
// Compatible with previous Router, can be removed in the future.
|
||||
else if (inst.Function == "route_to_agent")
|
||||
{
|
||||
var function = _services.GetServices<IFunctionCallback>().FirstOrDefault(x => x.Name == inst.Function);
|
||||
result = new RoleDialogModel(AgentRole.Function, inst.Parameters.Question)
|
||||
{
|
||||
FunctionName = inst.Function,
|
||||
FunctionArgs = JsonSerializer.Serialize(new RoutingArgs
|
||||
{
|
||||
AgentName = inst.Parameters.AgentName
|
||||
}),
|
||||
};
|
||||
var ret = await function.Execute(result);
|
||||
break;
|
||||
}
|
||||
else if (inst.Function == "interrupt_task_execution")
|
||||
{
|
||||
result = new RoleDialogModel(AgentRole.User, inst.Parameters.Reason)
|
||||
{
|
||||
FunctionName = inst.Function
|
||||
};
|
||||
break;
|
||||
}
|
||||
else if (inst.Function == "response_to_user")
|
||||
{
|
||||
result = new RoleDialogModel(AgentRole.User, inst.Parameters.Answer)
|
||||
{
|
||||
FunctionName = inst.Function
|
||||
};
|
||||
break;
|
||||
}
|
||||
else if (inst.Function == "retrieve_data_from_agent")
|
||||
{
|
||||
// Retrieve information from specific agent
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var record = db.Agents.First(x => x.Name.ToLower() == inst.Parameters.AgentName.ToLower());
|
||||
var response = await RetrieveDataFromAgent(record.Id, new List<RoleDialogModel>
|
||||
{
|
||||
new RoleDialogModel(AgentRole.User, inst.Parameters.Question)
|
||||
});
|
||||
|
||||
response.Content += $"\r\nDo you want to continue current task?";
|
||||
_dialogs.Add(new RoleDialogModel(AgentRole.Function, $"{record.Name}: {response.Content}")
|
||||
{
|
||||
FunctionName = inst.Function,
|
||||
FunctionArgs = JsonSerializer.Serialize(inst.Parameters.Arguments),
|
||||
ExecutionResult = response.Content,
|
||||
CurrentAgentId = record.Id
|
||||
});
|
||||
|
||||
agent.Instruction += $"\r\n{record.Name}: {response.Content}";
|
||||
|
||||
// Got the response from agent, then send to reasoner again to make the decision
|
||||
inst = await GetNextInstructionFromReasoner(agent);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<FunctionCallFromLlm> GetNextInstructionFromReasoner(Agent reasoner)
|
||||
{
|
||||
var wholeDialogs = new List<RoleDialogModel>
|
||||
{
|
||||
new RoleDialogModel(AgentRole.User, @"What's the next step? Response in JSON format with ""function"" and ""parameters"".")
|
||||
};
|
||||
|
||||
var chatCompletion = CompletionProvider.GetChatCompletion(_services);
|
||||
|
||||
RoleDialogModel response = null;
|
||||
await chatCompletion.GetChatCompletionsAsync(reasoner, wholeDialogs, async msg
|
||||
=> response = msg, fn
|
||||
=> Task.CompletedTask);
|
||||
|
||||
var args = JsonSerializer.Deserialize<FunctionCallFromLlm>(response.Content);
|
||||
|
||||
if (args.Parameters.Arguments != null)
|
||||
{
|
||||
SaveStateByArgs(args.Parameters.Arguments);
|
||||
}
|
||||
|
||||
args.Function = args.Function.Split('.').Last();
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
private async Task<RoleDialogModel> RetrieveDataFromAgent(string agentId, List<RoleDialogModel> wholeDialogs)
|
||||
{
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
var agent = await agentService.LoadAgent(agentId);
|
||||
|
||||
var chatCompletion = CompletionProvider.GetChatCompletion(_services);
|
||||
|
||||
RoleDialogModel response = null;
|
||||
await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg
|
||||
=> response = msg, async fn
|
||||
=>
|
||||
{
|
||||
// execute function
|
||||
// Save states
|
||||
SaveStateByArgs(JsonSerializer.Deserialize<JsonDocument>(fn.FunctionArgs));
|
||||
|
||||
var conversationService = _services.GetRequiredService<IConversationService>();
|
||||
// Call functions
|
||||
await conversationService.CallFunctions(fn);
|
||||
|
||||
response = fn;
|
||||
response.Content = fn.ExecutionResult;
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
private void SaveStateByArgs(JsonDocument args)
|
||||
{
|
||||
if (args == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var stateService = _services.GetRequiredService<IConversationStateService>();
|
||||
if (args.RootElement is JsonElement root)
|
||||
{
|
||||
foreach (JsonProperty property in root.EnumerateObject())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(property.Value.ToString()))
|
||||
{
|
||||
stateService.SetState(property.Name, property.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
using BotSharp.Abstraction.Agents.Enums;
|
||||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using BotSharp.Abstraction.Functions;
|
||||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.MLTasks;
|
||||
using BotSharp.Abstraction.Repositories;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
|
||||
namespace BotSharp.Core.Routing;
|
||||
|
||||
|
|
@ -33,6 +32,11 @@ public class Simulator
|
|||
}
|
||||
|
||||
var response = await SendMessageToReasoner(agent);
|
||||
if (response.Role == AgentRole.Function)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
var args = JsonSerializer.Deserialize<FunctionCallFromLlm>(response.Content);
|
||||
response.FunctionName = args.Function;
|
||||
|
||||
|
|
@ -42,7 +46,7 @@ public class Simulator
|
|||
|
||||
var router = _services.GetRequiredService<IAgentRouting>();
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var record = db.Agents.First(x => x.Name.ToLower() == args.Parameters.AgentName);
|
||||
var record = db.Agents.First(x => x.Name.ToLower() == args.Parameters.AgentName.ToLower());
|
||||
response.CurrentAgentId = record.Id;
|
||||
}
|
||||
else if (args.Function == "interrupt_task_execution")
|
||||
|
|
@ -75,7 +79,7 @@ public class Simulator
|
|||
|
||||
var args = JsonSerializer.Deserialize<FunctionCallFromLlm>(response.Content);
|
||||
|
||||
if (args.Function == "retrieve_data_from_agent")
|
||||
if (args.Parameters.Arguments != null)
|
||||
{
|
||||
SaveStateByArgs(args.Parameters.Arguments);
|
||||
}
|
||||
|
|
@ -84,10 +88,28 @@ public class Simulator
|
|||
return response;
|
||||
}
|
||||
|
||||
if (args.Function == "route_to_agent")
|
||||
{
|
||||
var function = _services.GetServices<IFunctionCallback>().FirstOrDefault(x => x.Name == args.Function);
|
||||
var message = new RoleDialogModel(AgentRole.Function, args.Parameters.Question)
|
||||
{
|
||||
FunctionName = args.Function,
|
||||
FunctionArgs = JsonSerializer.Serialize(new RoutingArgs
|
||||
{
|
||||
AgentName = args.Parameters.AgentName
|
||||
}),
|
||||
};
|
||||
var ret = await function.Execute(message);
|
||||
if (ret)
|
||||
{
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve information from specific agent
|
||||
var router = _services.GetRequiredService<IAgentRouting>();
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var record = db.Agents.First(x => x.Name.ToLower() == args.Parameters.AgentName);
|
||||
var record = db.Agents.First(x => x.Name.ToLower() == args.Parameters.AgentName.ToLower());
|
||||
response = await SendMessageToAgent(record.Id, new List<RoleDialogModel>
|
||||
{
|
||||
new RoleDialogModel(AgentRole.User, args.Parameters.Question)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ using BotSharp.Abstraction.Conversations.Models;
|
|||
|
||||
namespace BotSharp.Plugin.PizzaBot.Functions;
|
||||
|
||||
public class GetBakingTimeFn : IFunctionCallback
|
||||
public class GetDeliveryTimeFn : IFunctionCallback
|
||||
{
|
||||
public string Name => "get_cooking_remaining_time";
|
||||
public string Name => "get_delivery_time";
|
||||
|
||||
public async Task<bool> Execute(RoleDialogModel message)
|
||||
{
|
||||
|
|
@ -12,7 +12,7 @@ public class PizzaBotPlugin : IBotSharpPlugin
|
|||
services.AddScoped<IFunctionCallback, GetPizzaPricesFn>();
|
||||
services.AddScoped<IFunctionCallback, PlaceOrderFn>();
|
||||
services.AddScoped<IFunctionCallback, OrderFoundFn>();
|
||||
services.AddScoped<IFunctionCallback, GetBakingTimeFn>();
|
||||
services.AddScoped<IFunctionCallback, GetDeliveryTimeFn>();
|
||||
|
||||
// Register hooks
|
||||
services.AddScoped<IAgentHook, PizzaBotAgentHook>();
|
||||
|
|
|
|||
Loading…
Reference in a new issue