Add Execute Once.
This commit is contained in:
parent
b97db1b5f0
commit
dd346571b4
|
|
@ -2,7 +2,7 @@
|
|||
<PropertyGroup>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<OutputPath>..\..\..\packages</OutputPath>
|
||||
<BotSharpVersion>0.14.6</BotSharpVersion>
|
||||
<BotSharpVersion>0.14.7</BotSharpVersion>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
using BotSharp.Abstraction.Agents.Enums;
|
||||
|
||||
namespace BotSharp.Abstraction.Conversations.Models;
|
||||
|
||||
public class RoleDialogModel
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public class FunctionCallFromLlm
|
|||
public string? Question { get; set; }
|
||||
|
||||
[JsonPropertyName("answer")]
|
||||
public string? Answer { get; set; }
|
||||
public string Answer { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("args")]
|
||||
public JsonDocument Arguments { get; set; } = JsonDocument.Parse("{}");
|
||||
|
|
|
|||
13
src/Infrastructure/BotSharp.Abstraction/Models/NameDesc.cs
Normal file
13
src/Infrastructure/BotSharp.Abstraction/Models/NameDesc.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
namespace BotSharp.Abstraction.Models;
|
||||
|
||||
public class NameDesc
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
|
||||
public NameDesc(string name, string description)
|
||||
{
|
||||
Name = name;
|
||||
Description = description;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,12 +6,17 @@ public interface IRoutingHandler
|
|||
{
|
||||
string Name { get; }
|
||||
string Description { get; }
|
||||
bool IsReasoning { get; }
|
||||
bool RequireAgent { get; }
|
||||
List<string> Parameters { get; }
|
||||
void SetRouter(Agent router);
|
||||
void SetDialogs(List<RoleDialogModel> dialogs);
|
||||
Task<FunctionCallFromLlm> GetNextInstructionFromReasoner(string prompt);
|
||||
Task<RoleDialogModel> GetResponseFromReasoner();
|
||||
Task<RoleDialogModel> Handle(FunctionCallFromLlm inst);
|
||||
bool IsReasoning { get => false; }
|
||||
bool Enabled { get => true; }
|
||||
List<string> Parameters { get => new List<string>(); }
|
||||
|
||||
void SetRouter(Agent router) { }
|
||||
|
||||
void SetDialogs(List<RoleDialogModel> dialogs) { }
|
||||
|
||||
Task<FunctionCallFromLlm> GetNextInstructionFromReasoner(string prompt)
|
||||
=> throw new NotImplementedException("");
|
||||
|
||||
Task<RoleDialogModel> Handle(FunctionCallFromLlm inst)
|
||||
=> throw new NotImplementedException("");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,5 +4,7 @@ public interface IRoutingService
|
|||
{
|
||||
Agent LoadRouter();
|
||||
List<RoleDialogModel> Dialogs { get; }
|
||||
Task<RoleDialogModel> Enter(Agent agent, List<RoleDialogModel> whileDialogs);
|
||||
void SetDialogs(List<RoleDialogModel> dialogs);
|
||||
Task<RoleDialogModel> InstructLoop(Agent router);
|
||||
Task<RoleDialogModel> ExecuteOnce(Agent agent);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@ namespace BotSharp.Abstraction.Routing.Models;
|
|||
|
||||
public class RoutingArgs
|
||||
{
|
||||
[JsonPropertyName("user_goal")]
|
||||
public string UserGoal { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("reason")]
|
||||
public string Reason { get; set; } = string.Empty;
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ public static class BotSharpServiceCollectionExtensions
|
|||
services.AddScoped<IRoutingHandler, RetrieveDataFromAgentRoutingHandler>();
|
||||
services.AddScoped<IRoutingHandler, TaskEndRoutingHandler>();
|
||||
services.AddScoped<IRoutingHandler, ConversationEndRoutingHandler>();
|
||||
services.AddScoped<IRoutingHandler, TransferToCsrRoutingHandler>();
|
||||
|
||||
if (myDatabaseSettings.Default == "FileRepository")
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,159 +0,0 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
|
||||
namespace BotSharp.Core.Conversations.Services;
|
||||
|
||||
public partial class ConversationService
|
||||
{
|
||||
int currentRecursiveDepth = 0;
|
||||
|
||||
private async Task<bool> GetChatCompletionsAsyncRecursively(Agent agent,
|
||||
List<RoleDialogModel> wholeDialogs,
|
||||
Func<RoleDialogModel, Task> onMessageReceived,
|
||||
Func<RoleDialogModel, Task> onFunctionExecuting,
|
||||
Func<RoleDialogModel, Task> onFunctionExecuted)
|
||||
{
|
||||
var chatCompletion = CompletionProvider.GetChatCompletion(_services);
|
||||
|
||||
currentRecursiveDepth++;
|
||||
if (currentRecursiveDepth > _settings.MaxRecursiveDepth)
|
||||
{
|
||||
_logger.LogWarning($"Exceeded max recursive depth.");
|
||||
|
||||
var latestResponse = wholeDialogs.Last();
|
||||
var text = latestResponse.Content;
|
||||
if (latestResponse.Role == AgentRole.Function)
|
||||
{
|
||||
text = latestResponse.Content.Split("=>").Last();
|
||||
}
|
||||
|
||||
await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, text)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
}, onMessageReceived);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var result = await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs, async msg =>
|
||||
{
|
||||
await HandleAssistantMessage(msg, onMessageReceived);
|
||||
}, async fn =>
|
||||
{
|
||||
var preAgentId = agent.Id;
|
||||
|
||||
await HandleFunctionMessage(fn, onFunctionExecuting, onFunctionExecuted);
|
||||
|
||||
// Function executed has exception
|
||||
if (fn.ExecutionResult == null)
|
||||
{
|
||||
await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, fn.Content)
|
||||
{
|
||||
CurrentAgentId = fn.CurrentAgentId
|
||||
}, onMessageReceived);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (fn.StopCompletion)
|
||||
{
|
||||
await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, fn.Content)
|
||||
{
|
||||
CurrentAgentId = fn.CurrentAgentId,
|
||||
ExecutionData = fn.ExecutionData,
|
||||
ExecutionResult = fn.ExecutionResult
|
||||
}, onMessageReceived);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var content = fn.FunctionArgs.Replace("\r", " ").Replace("\n", " ").Trim() + " => " + fn.ExecutionResult;
|
||||
_logger.LogInformation(content);
|
||||
|
||||
fn.Content = content;
|
||||
|
||||
// Agent has been transferred
|
||||
if (fn.CurrentAgentId != preAgentId)
|
||||
{
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
agent = await agentService.LoadAgent(fn.CurrentAgentId);
|
||||
|
||||
if (fn.FunctionName != "route_to_agent")
|
||||
{
|
||||
wholeDialogs.Add(fn);
|
||||
}
|
||||
|
||||
await GetChatCompletionsAsyncRecursively(agent,
|
||||
wholeDialogs,
|
||||
onMessageReceived,
|
||||
onFunctionExecuting,
|
||||
onFunctionExecuted);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find response template
|
||||
var templateService = _services.GetRequiredService<IResponseTemplateService>();
|
||||
var response = await templateService.RenderFunctionResponse(agent.Id, fn);
|
||||
if (!string.IsNullOrEmpty(response))
|
||||
{
|
||||
await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, response)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
}, onMessageReceived);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Add to dialog history
|
||||
// The server had an error processing your request. Sorry about that!
|
||||
// _storage.Append(conversationId, preAgentId, fn);
|
||||
|
||||
// After function is executed, pass the result to LLM to get a natural response
|
||||
if (fn.FunctionName != "route_to_agent")
|
||||
{
|
||||
wholeDialogs.Add(fn);
|
||||
}
|
||||
|
||||
await GetChatCompletionsAsyncRecursively(agent,
|
||||
wholeDialogs,
|
||||
onMessageReceived,
|
||||
onFunctionExecuting,
|
||||
onFunctionExecuted);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task HandleAssistantMessage(RoleDialogModel message, Func<RoleDialogModel, Task> onMessageReceived)
|
||||
{
|
||||
var hooks = _services.GetServices<IConversationHook>().ToList();
|
||||
|
||||
// After chat completion hook
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
await hook.AfterCompletion(message);
|
||||
}
|
||||
|
||||
var agent = await _services.GetRequiredService<IAgentService>().GetAgent(message.CurrentAgentId);
|
||||
|
||||
_logger.LogInformation($"[{agent?.Name ?? "Router"}] {message.Role}: {message.Content}");
|
||||
|
||||
await onMessageReceived(message);
|
||||
|
||||
// Add to dialog history
|
||||
_storage.Append(_conversationId, message);
|
||||
}
|
||||
|
||||
private async Task HandleFunctionMessage(RoleDialogModel msg,
|
||||
Func<RoleDialogModel, Task> onFunctionExecuting,
|
||||
Func<RoleDialogModel, Task> onFunctionExecuted)
|
||||
{
|
||||
// Save states
|
||||
SaveStateByArgs(msg.FunctionArgs);
|
||||
|
||||
// Call functions
|
||||
await onFunctionExecuting(msg);
|
||||
await CallFunctions(msg);
|
||||
await onFunctionExecuted(msg);
|
||||
}
|
||||
}
|
||||
|
|
@ -51,41 +51,18 @@ public partial class ConversationService
|
|||
}
|
||||
|
||||
// Routing with reasoning
|
||||
var routing = _services.GetRequiredService<IRoutingService>();
|
||||
var settings = _services.GetRequiredService<RoutingSettings>();
|
||||
if (settings.RouterId == agent.Id)
|
||||
{
|
||||
var routing = _services.GetRequiredService<IRoutingService>();
|
||||
var reasonedContext = await routing.Enter(agent, wholeDialogs);
|
||||
|
||||
if (reasonedContext.StopCompletion)
|
||||
{
|
||||
await HandleAssistantMessage(reasonedContext, onMessageReceived);
|
||||
return true;
|
||||
}
|
||||
routing.SetDialogs(wholeDialogs);
|
||||
|
||||
// Switch agent
|
||||
if (reasonedContext.CurrentAgentId != agent.Id)
|
||||
{
|
||||
agent = await agentService.LoadAgent(reasonedContext.CurrentAgentId);
|
||||
}
|
||||
var response = settings.RouterId == agent.Id ?
|
||||
await routing.InstructLoop(agent) :
|
||||
await routing.ExecuteOnce(agent);
|
||||
|
||||
routing.Dialogs.ForEach(x =>
|
||||
{
|
||||
wholeDialogs.Add(x);
|
||||
if (x.Content != null)
|
||||
{
|
||||
_storage.Append(_conversationId, x);
|
||||
}
|
||||
});
|
||||
}
|
||||
await HandleAssistantMessage(response, onMessageReceived);
|
||||
|
||||
var result = await GetChatCompletionsAsyncRecursively(agent,
|
||||
wholeDialogs,
|
||||
onMessageReceived,
|
||||
onFunctionExecuting,
|
||||
onFunctionExecuted);
|
||||
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<Conversation> GetConversationRecord(string agentId)
|
||||
|
|
@ -106,19 +83,23 @@ public partial class ConversationService
|
|||
return converation;
|
||||
}
|
||||
|
||||
private void SaveStateByArgs(string args)
|
||||
private async Task HandleAssistantMessage(RoleDialogModel message, Func<RoleDialogModel, Task> onMessageReceived)
|
||||
{
|
||||
var stateService = _services.GetRequiredService<IConversationStateService>();
|
||||
var jo = JsonSerializer.Deserialize<object>(args);
|
||||
if (jo is JsonElement root)
|
||||
var hooks = _services.GetServices<IConversationHook>().ToList();
|
||||
|
||||
// After chat completion hook
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
foreach (JsonProperty property in root.EnumerateObject())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(property.Value.ToString()))
|
||||
{
|
||||
stateService.SetState(property.Name, property.Value);
|
||||
}
|
||||
}
|
||||
await hook.AfterCompletion(message);
|
||||
}
|
||||
|
||||
var agent = await _services.GetRequiredService<IAgentService>().GetAgent(message.CurrentAgentId);
|
||||
|
||||
_logger.LogInformation($"[{agent?.Name ?? "Router"}] {message.Role}: {message.Content}");
|
||||
|
||||
await onMessageReceived(message);
|
||||
|
||||
// Add to dialog history
|
||||
_storage.Append(_conversationId, message);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ public class ConversationStorage : IConversationStorage
|
|||
|
||||
sb.AppendLine($"{dialog.CreatedAt}|{dialog.Role}|{agentId}|{dialog.FunctionName}|{args}");
|
||||
|
||||
var content = dialog.ExecutionResult.Replace("\r", " ").Replace("\n", " ").Trim();
|
||||
var content = dialog.ExecutionResult ?? dialog.Content;
|
||||
content = content.Replace("\r", " ").Replace("\n", " ").Trim();
|
||||
if (string.IsNullOrEmpty(content))
|
||||
{
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase, IRoutingHan
|
|||
|
||||
public List<string> Parameters => new List<string>
|
||||
{
|
||||
"1. agent_name: the name of the agent",
|
||||
"2. args: required parameters extracted from question",
|
||||
"3. reason: why continue to execute current task"
|
||||
"agent_name: the name of the agent",
|
||||
"args: required parameters extracted from question",
|
||||
"reason: why continue to execute current task"
|
||||
};
|
||||
|
||||
public bool IsReasoning => true;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,6 @@ public class GetNextInstructionRoutingHandler : RoutingHandlerBase, IRoutingHand
|
|||
|
||||
public async Task<RoleDialogModel> Handle(FunctionCallFromLlm inst)
|
||||
{
|
||||
return null;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ public class InterruptTaskExecutionRoutingHandler : RoutingHandlerBase, IRouting
|
|||
|
||||
public List<string> Parameters => new List<string>
|
||||
{
|
||||
"1. reason: the reason why the request is interrupted",
|
||||
"2. answer: the content response to user"
|
||||
"reason: the reason why the request is interrupted",
|
||||
"answer: the content response to user"
|
||||
};
|
||||
|
||||
public bool IsReasoning => true;
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler
|
|||
|
||||
public List<string> Parameters => new List<string>
|
||||
{
|
||||
"1. answer: the content of response",
|
||||
"2. reason: why response to user"
|
||||
"answer: the content of response",
|
||||
"reason: why response to user"
|
||||
};
|
||||
|
||||
public bool IsReasoning => false;
|
||||
|
|
@ -25,8 +25,9 @@ public class ResponseToUserRoutingHandler : RoutingHandlerBase, IRoutingHandler
|
|||
|
||||
public async Task<RoleDialogModel> Handle(FunctionCallFromLlm inst)
|
||||
{
|
||||
var result = new RoleDialogModel(AgentRole.User, inst.Answer)
|
||||
var result = new RoleDialogModel(AgentRole.Assistant, inst.Answer)
|
||||
{
|
||||
CurrentAgentId = _settings.RouterId,
|
||||
FunctionName = inst.Function,
|
||||
StopCompletion = true
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH
|
|||
|
||||
public List<string> Parameters => new List<string>
|
||||
{
|
||||
"1. agent_name: the name of the agent",
|
||||
"2. question: the question you will ask the agent to get the necessary data",
|
||||
"3. reason: why retrieve data",
|
||||
"4. args: required parameters extracted from question and hand over to the next agent. The args should be in JSON format"
|
||||
"agent_name: the name of the agent",
|
||||
"question: the question you will ask the agent to get the necessary data",
|
||||
"reason: why retrieve data",
|
||||
"args: required parameters extracted from question and hand over to the next agent"
|
||||
};
|
||||
|
||||
public bool IsReasoning => true;
|
||||
|
|
@ -36,10 +36,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH
|
|||
// Retrieve information from specific agent
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var record = db.Agents.First(x => x.Name.ToLower() == inst.Route.AgentName.ToLower());
|
||||
var response = await InvokeAgent(record.Id, new List<RoleDialogModel>
|
||||
{
|
||||
new RoleDialogModel(AgentRole.User, inst.Question)
|
||||
});
|
||||
var response = await InvokeAgent(record.Id);
|
||||
|
||||
inst.Answer = response.Content;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,11 +14,9 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
|
|||
|
||||
public List<string> Parameters => new List<string>
|
||||
{
|
||||
"1. agent_name: the name of the agent",
|
||||
"2. reason: why route to this agent",
|
||||
"3. args: parameters extracted from context",
|
||||
"4. answer: if you know how to response without asking to other agent",
|
||||
"5. goal: user's original goal"
|
||||
"agent_name: the name of the agent from AGENTS",
|
||||
"reason: why route to this agent",
|
||||
"args: parameters extracted from context"
|
||||
};
|
||||
|
||||
public bool IsReasoning => false;
|
||||
|
|
@ -47,14 +45,8 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
|
|||
|
||||
var ret = await function.Execute(message);
|
||||
|
||||
var result = await InvokeAgent(message.CurrentAgentId, _dialogs);
|
||||
var result = await InvokeAgent(message.CurrentAgentId);
|
||||
result.ExecutionData = result.ExecutionData ?? message.ExecutionData;
|
||||
|
||||
if (result.Role == AgentRole.Function && !result.StopCompletion)
|
||||
{
|
||||
_dialogs.Add(result);
|
||||
result = await InvokeAgent(message.CurrentAgentId, _dialogs);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
using System.Drawing;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace BotSharp.Core.Routing.Handlers;
|
||||
|
||||
|
|
@ -37,7 +38,7 @@ public abstract class RoutingHandlerBase
|
|||
public async Task<FunctionCallFromLlm> GetNextInstructionFromReasoner(string prompt)
|
||||
{
|
||||
var responseFormat = JsonSerializer.Serialize(new FunctionCallFromLlm());
|
||||
var content = $"{prompt} Response must be in JSON format {responseFormat}.";
|
||||
var content = $"{prompt} Response must be in JSON format {responseFormat}";
|
||||
|
||||
var chatCompletion = CompletionProvider.GetChatCompletion(_services,
|
||||
provider: _settings.Provider,
|
||||
|
|
@ -51,10 +52,16 @@ public abstract class RoutingHandlerBase
|
|||
=> response = msg, fn
|
||||
=> Task.CompletedTask);
|
||||
|
||||
FunctionCallFromLlm args = new FunctionCallFromLlm();
|
||||
var args = new FunctionCallFromLlm();
|
||||
try
|
||||
{
|
||||
#if DEBUG
|
||||
Console.WriteLine(response.Content, Color.Gray);
|
||||
#else
|
||||
_logger.LogInformation(response.Content);
|
||||
#endif
|
||||
var pattern = @"\{(?:[^{}]|(?<open>\{)|(?<-open>\}))+(?(open)(?!))\}";
|
||||
response.Content = Regex.Match(response.Content, pattern).Value;
|
||||
args = JsonSerializer.Deserialize<FunctionCallFromLlm>(response.Content);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -62,7 +69,7 @@ public abstract class RoutingHandlerBase
|
|||
_logger.LogError($"{ex.Message}: {response.Content}");
|
||||
args.Function = "response_to_user";
|
||||
args.Answer = ex.Message;
|
||||
args.Route.AgentName = "";
|
||||
args.Route.AgentName = _settings.RouterName;
|
||||
}
|
||||
|
||||
if (args.Arguments != null)
|
||||
|
|
@ -100,15 +107,23 @@ public abstract class RoutingHandlerBase
|
|||
return response;
|
||||
}
|
||||
|
||||
protected async Task<RoleDialogModel> InvokeAgent(string agentId, List<RoleDialogModel> wholeDialogs)
|
||||
const int MAXIMUM_RECURSION_DEPTH = 2;
|
||||
int CurrentRecursionDepth = 0;
|
||||
protected async Task<RoleDialogModel> InvokeAgent(string agentId)
|
||||
{
|
||||
CurrentRecursionDepth++;
|
||||
if (CurrentRecursionDepth > MAXIMUM_RECURSION_DEPTH)
|
||||
{
|
||||
return _dialogs.Last();
|
||||
}
|
||||
|
||||
var agentService = _services.GetRequiredService<IAgentService>();
|
||||
var agent = await agentService.LoadAgent(agentId);
|
||||
|
||||
var chatCompletion = CompletionProvider.GetChatCompletion(_services);
|
||||
|
||||
RoleDialogModel response = null;
|
||||
await chatCompletion.GetChatCompletionsAsync(agent, wholeDialogs,
|
||||
await chatCompletion.GetChatCompletionsAsync(agent, _dialogs,
|
||||
async msg =>
|
||||
{
|
||||
response = msg;
|
||||
|
|
@ -122,11 +137,33 @@ public abstract class RoutingHandlerBase
|
|||
// Call functions
|
||||
await conversationService.CallFunctions(fn);
|
||||
|
||||
response = fn;
|
||||
|
||||
if (string.IsNullOrEmpty(response.Content))
|
||||
if (string.IsNullOrEmpty(fn.Content))
|
||||
{
|
||||
response.Content = fn.ExecutionResult;
|
||||
fn.Content = fn.ExecutionResult;
|
||||
}
|
||||
|
||||
_dialogs.Add(fn);
|
||||
|
||||
if (!fn.StopCompletion)
|
||||
{
|
||||
// Find response template
|
||||
var templateService = _services.GetRequiredService<IResponseTemplateService>();
|
||||
var quickResponse = await templateService.RenderFunctionResponse(agent.Id, fn);
|
||||
if (!string.IsNullOrEmpty(quickResponse))
|
||||
{
|
||||
response = new RoleDialogModel(AgentRole.Assistant, quickResponse)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
response = await InvokeAgent(fn.CurrentAgentId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
response = fn;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public class TaskEndRoutingHandler : RoutingHandlerBase, IRoutingHandler
|
|||
|
||||
public List<string> Parameters => new List<string>
|
||||
{
|
||||
"1. abandoned_arguments: the arguments next task can't reuse"
|
||||
"abandoned_arguments: the arguments next task can't reuse"
|
||||
};
|
||||
|
||||
public bool IsReasoning => true;
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
|
||||
namespace BotSharp.Core.Routing.Handlers;
|
||||
|
||||
public class TransferToCsrRoutingHandler : RoutingHandlerBase, IRoutingHandler
|
||||
{
|
||||
public string Name => "transfer_to_csr";
|
||||
|
||||
public string Description => "Reach out to a real customer representative to help.";
|
||||
|
||||
public List<string> Parameters => new List<string>
|
||||
{
|
||||
};
|
||||
|
||||
public bool IsReasoning => false;
|
||||
|
||||
public TransferToCsrRoutingHandler(IServiceProvider services, ILogger<TransferToCsrRoutingHandler> logger, RoutingSettings settings)
|
||||
: base(services, logger, settings)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<RoleDialogModel> Handle(FunctionCallFromLlm inst)
|
||||
{
|
||||
var result = new RoleDialogModel(AgentRole.User, "I'm transferring to a customer representative, waiting a moment please.")
|
||||
{
|
||||
CurrentAgentId = _settings.RouterId,
|
||||
FunctionName = inst.Function,
|
||||
StopCompletion = true
|
||||
};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,10 +3,24 @@ namespace BotSharp.Core.Routing;
|
|||
public class PromptConst
|
||||
{
|
||||
public const string ROUTER_PROMPT = @"
|
||||
You're a Router with reasoning, you can dispatch request to different agent to achieve user's goal.
|
||||
You're a Router with reasoning. Follow these steps to handle user's request:
|
||||
1. Read the CONVERSATION context.
|
||||
2. Select a appropriate function from FUNCTIONS.
|
||||
3. Determine which agent from AGENTS is suitable for the current task.
|
||||
|
||||
###
|
||||
Router can decide which of below agents can handle user's request:
|
||||
FUNCTIONS
|
||||
{% for fn in routing_handlers %}
|
||||
* {{ fn.name }}
|
||||
{{ fn.description }}
|
||||
{% if fn.parameters != empty -%}
|
||||
Parameters:
|
||||
{% for arg in fn.parameters -%}
|
||||
{{ arg }};
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
|
||||
AGENTS
|
||||
{% for agent in routing_records %}
|
||||
* {{ agent.name }}
|
||||
{{ agent.description }}
|
||||
|
|
@ -15,19 +29,5 @@ Required: {% for field in agent.required_fields %}{{ field }},{% endfor %}
|
|||
{%- endif %}
|
||||
{% endfor %}
|
||||
|
||||
###
|
||||
Agent can utilize below functions:
|
||||
{% for fn in routing_handlers %}
|
||||
* {{ fn.name }}
|
||||
{{ fn.description }}
|
||||
{% if fn.parameters != empty %}
|
||||
Parameters:
|
||||
{% for arg in fn.parameters -%}
|
||||
{{ arg }};
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
###
|
||||
Conversation context:";
|
||||
CONVERSATION";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Repositories;
|
||||
using BotSharp.Abstraction.Routing;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using BotSharp.Abstraction.Routing.Settings;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BotSharp.Core.Routing;
|
||||
|
||||
|
|
@ -25,16 +25,42 @@ public class RoutingService : IRoutingService
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<RoleDialogModel> Enter(Agent router, List<RoleDialogModel> wholeDialogs)
|
||||
public void SetDialogs(List<RoleDialogModel> dialogs)
|
||||
{
|
||||
_dialogs = dialogs;
|
||||
}
|
||||
|
||||
public async Task<RoleDialogModel> ExecuteOnce(Agent agent)
|
||||
{
|
||||
var message = _dialogs.Last().Content;
|
||||
|
||||
var handlers = _services.GetServices<IRoutingHandler>();
|
||||
|
||||
var handler = handlers.FirstOrDefault(x => x.Name == "route_to_agent");
|
||||
handler.SetDialogs(_dialogs);
|
||||
var result = await handler.Handle(new FunctionCallFromLlm
|
||||
{
|
||||
Function = "route_to_agent",
|
||||
Question = message,
|
||||
Route = new RoutingArgs
|
||||
{
|
||||
Reason = message,
|
||||
AgentName = agent.Name,
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<RoleDialogModel> InstructLoop(Agent router)
|
||||
{
|
||||
_dialogs = new List<RoleDialogModel>();
|
||||
var result = new RoleDialogModel(AgentRole.Assistant, "Can you repeat your request again?")
|
||||
{
|
||||
CurrentAgentId = router.Id
|
||||
};
|
||||
|
||||
var message = wholeDialogs.Last().Content;
|
||||
foreach (var dialog in wholeDialogs.TakeLast(20))
|
||||
var message = _dialogs.Last().Content;
|
||||
foreach (var dialog in _dialogs.TakeLast(20))
|
||||
{
|
||||
router.Instruction += $"\r\n{dialog.Role}: {dialog.Content}";
|
||||
}
|
||||
|
|
@ -43,39 +69,38 @@ public class RoutingService : IRoutingService
|
|||
|
||||
var handler = handlers.FirstOrDefault(x => x.Name == "get_next_instruction");
|
||||
handler.SetRouter(router);
|
||||
handler.SetDialogs(wholeDialogs);
|
||||
handler.SetDialogs(_dialogs);
|
||||
|
||||
int loopCount = 0;
|
||||
while (!result.StopCompletion && loopCount < 5)
|
||||
var stop = false;
|
||||
while (!stop && loopCount < 5)
|
||||
{
|
||||
loopCount++;
|
||||
|
||||
var inst = await handler.GetNextInstructionFromReasoner($"What's the next step to achieve user's goal?");
|
||||
var inst = await handler.GetNextInstructionFromReasoner($"You are the Router, tell me the next step?");
|
||||
inst.Question = inst.Question ?? message;
|
||||
|
||||
handler = handlers.FirstOrDefault(x => x.Name == inst.Function);
|
||||
if (handler == null)
|
||||
{
|
||||
handler = handlers.FirstOrDefault(x => x.Name == "get_next_instruction");
|
||||
router.Instruction += $"\r\n{AgentRole.System}: the function must be one of [{string.Join(",", GetHandlers().Select(x => x.Name))}].";
|
||||
router.Instruction += $"\r\n{AgentRole.System}: the function must be one of {string.Join(",", GetHandlers().Select(x => x.Name))}.";
|
||||
continue;
|
||||
}
|
||||
handler.SetRouter(router);
|
||||
handler.SetDialogs(wholeDialogs);
|
||||
handler.SetDialogs(_dialogs);
|
||||
|
||||
result = await handler.Handle(inst);
|
||||
|
||||
message = result.Content.Replace("\r\n", " ");
|
||||
router.Instruction += $"\r\n{result.Role}: {message}";
|
||||
|
||||
result.StopCompletion = !_settings.EnableReasoning;
|
||||
stop = !_settings.EnableReasoning;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Agent LoadRouter()
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -237,8 +238,8 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
var samplingFactor = float.Parse(state.GetState("sampling_factor", "0.5"));
|
||||
chatCompletionsOptions.Temperature = temperature;
|
||||
chatCompletionsOptions.NucleusSamplingFactor = samplingFactor;
|
||||
chatCompletionsOptions.FrequencyPenalty = 0;
|
||||
chatCompletionsOptions.PresencePenalty = 0;
|
||||
// chatCompletionsOptions.FrequencyPenalty = 0;
|
||||
// chatCompletionsOptions.PresencePenalty = 0;
|
||||
|
||||
var convSetting = _services.GetRequiredService<ConversationSetting>();
|
||||
if (convSetting.ShowVerboseLog)
|
||||
|
|
@ -249,6 +250,7 @@ public class ChatCompletionProvider : IChatCompletion
|
|||
$"{x.Role}: {x.Name} {x.Content}" :
|
||||
$"{x.Role}: {x.Content}";
|
||||
}));
|
||||
|
||||
_logger.LogInformation(verbose);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue