Merge pull request #330 from hchen2020/master

Add user_goal_description to router.
This commit is contained in:
C. Oceania 2024-03-05 11:38:58 -06:00 committed by GitHub
commit 2ce526dd2e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 69 additions and 20 deletions

View file

@ -6,6 +6,7 @@ namespace BotSharp.Abstraction.Functions.Models;
public class FunctionCallFromLlm : RoutingArgs public class FunctionCallFromLlm : RoutingArgs
{ {
[JsonPropertyName("question")] [JsonPropertyName("question")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Question { get; set; } public string? Question { get; set; }
[JsonPropertyName("args")] [JsonPropertyName("args")]
@ -29,6 +30,7 @@ public class FunctionCallFromLlm : RoutingArgs
/// Conversation summary /// Conversation summary
/// </summary> /// </summary>
[JsonPropertyName("summary")] [JsonPropertyName("summary")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Summary { get; set; } public string? Summary { get; set; }
public override string ToString() public override string ToString()

View file

@ -10,13 +10,21 @@ public interface IRoutingHook
/// <param name="instruct">routing instruction</param> /// <param name="instruct">routing instruction</param>
/// <param name="message">message</param> /// <param name="message">message</param>
/// <returns></returns> /// <returns></returns>
Task OnRoutingInstructionReceived(FunctionCallFromLlm instruct, RoleDialogModel message); Task OnRoutingInstructionReceived(FunctionCallFromLlm instruct, RoleDialogModel message)
=> Task.CompletedTask;
Task OnAgentEnqueued(string agentId, string preAgentId, string? reason = null); Task OnRoutingInstructionRevised(FunctionCallFromLlm instruct, RoleDialogModel message)
=> Task.CompletedTask;
Task OnAgentDequeued(string agentId, string currentAgentId, string? reason = null); Task OnAgentEnqueued(string agentId, string preAgentId, string? reason = null)
=> Task.CompletedTask;
Task OnAgentReplaced(string fromAgentId, string toAgentId, string? reason = null); Task OnAgentDequeued(string agentId, string currentAgentId, string? reason = null)
=> Task.CompletedTask;
Task OnAgentQueueEmptied(string agentId, string? reason = null); Task OnAgentReplaced(string fromAgentId, string toAgentId, string? reason = null)
=> Task.CompletedTask;
Task OnAgentQueueEmptied(string agentId, string? reason = null)
=> Task.CompletedTask;
} }

View file

@ -8,13 +8,15 @@ public class RoutingArgs
/// <summary> /// <summary>
/// The reason why you select this function or agent /// The reason why you select this function or agent
/// </summary> /// </summary>
[JsonPropertyName("reason")] [JsonPropertyName("next_action_reason")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Reason { get; set; } = string.Empty; public string Reason { get; set; } = string.Empty;
/// <summary> /// <summary>
/// The content of replying to user /// The content of replying to user
/// </summary> /// </summary>
[JsonPropertyName("response")] [JsonPropertyName("response")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Response { get; set; } public string Response { get; set; }
/// <summary> /// <summary>
@ -31,6 +33,10 @@ public class RoutingArgs
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string OriginalAgent { get; set; } public string OriginalAgent { get; set; }
[JsonPropertyName("user_goal_description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string UserGoal { get; set; }
public override string ToString() public override string ToString()
{ {
var route = string.IsNullOrEmpty(AgentName) ? "" : $"<Route to {AgentName.ToUpper()} because {Reason}>"; var route = string.IsNullOrEmpty(AgentName) ? "" : $"<Route to {AgentName.ToUpper()} because {Reason}>";

View file

@ -58,15 +58,8 @@ public class RouteToAgentFn : IFunctionCallback
// Push next action agent // Push next action agent
if (!string.IsNullOrEmpty(args.AgentName) && args.AgentName.Length < 32) if (!string.IsNullOrEmpty(args.AgentName) && args.AgentName.Length < 32)
{ {
var db = _services.GetRequiredService<IBotSharpRepository>(); _context.Push(args.AgentName, args.Reason);
var filter = new AgentFilter { AgentName = args.AgentName }; states.SetState("next_action_agent", args.AgentName, isNeedVersion: true);
var actionAgent = db.GetAgents(filter).FirstOrDefault();
if (actionAgent != null)
{
_context.Push(actionAgent.Id, args.Reason);
}
states.SetState("last_action_agent", args.AgentName, isNeedVersion: true);
} }
if (string.IsNullOrEmpty(args.AgentName)) if (string.IsNullOrEmpty(args.AgentName))
@ -93,7 +86,7 @@ public class RouteToAgentFn : IFunctionCallback
if (missingfield && message.CurrentAgentId != agentId) if (missingfield && message.CurrentAgentId != agentId)
{ {
// Stack redirection agent // Stack redirection agent
_context.Push(agentId, reason: $"redirection: {message.Content}"); _context.Push(agentId, reason: $"REDIRECTION {message.Content}");
} }
} }
@ -155,7 +148,7 @@ public class RouteToAgentFn : IFunctionCallback
{ {
// Add field to args // Add field to args
message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "missing_fields", missingFields); message.FunctionArgs = AppendPropertyToArgs(message.FunctionArgs, "missing_fields", missingFields);
message.Content = $"missing some information: {string.Join(',', missingFields)}"; message.Content = $"missing some information: {string.Join(", ", missingFields)}";
// Handle redirect // Handle redirect
var routingRule = routingRules.FirstOrDefault(x => missingFields.Contains(x.Field)); var routingRule = routingRules.FirstOrDefault(x => missingFields.Contains(x.Field));

View file

@ -10,7 +10,7 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
public List<ParameterPropertyDef> Parameters => new List<ParameterPropertyDef> public List<ParameterPropertyDef> Parameters => new List<ParameterPropertyDef>
{ {
new ParameterPropertyDef("reason", "why route to agent") new ParameterPropertyDef("next_action_reason", "the reason why route to this agent")
{ {
Required = true Required = true
}, },
@ -18,6 +18,10 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
{ {
Required = true Required = true
}, },
new ParameterPropertyDef("user_goal_description", "user original goal")
{
Required = true
},
new ParameterPropertyDef("user_goal_agent", "agent who can achieve user original goal") new ParameterPropertyDef("user_goal_agent", "agent who can achieve user original goal")
{ {
Required = true Required = true
@ -38,6 +42,16 @@ public class RouteToAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
message.FunctionArgs = JsonSerializer.Serialize(inst); message.FunctionArgs = JsonSerializer.Serialize(inst);
var ret = await routing.InvokeFunction(message.FunctionName, message); var ret = await routing.InvokeFunction(message.FunctionName, message);
var states = _services.GetRequiredService<IConversationStateService>();
var goalAgent = states.GetState("user_goal_agent");
if (!string.IsNullOrEmpty(goalAgent))
{
inst.OriginalAgent = goalAgent;
}
await HookEmitter.Emit<IRoutingHook>(_services, async hook =>
await hook.OnRoutingInstructionRevised(inst, message)
);
var agentId = routing.Context.GetCurrentAgentId(); var agentId = routing.Context.GetCurrentAgentId();
// Update next action agent's name // Update next action agent's name

View file

@ -102,6 +102,7 @@ public class NaivePlanner : IPlaner
else else
{ {
context.Empty(reason: $"Agent queue is cleared by {nameof(NaivePlanner)}"); context.Empty(reason: $"Agent queue is cleared by {nameof(NaivePlanner)}");
// context.Push(inst.OriginalAgent, "Push user goal agent");
} }
return true; return true;
} }

View file

@ -61,8 +61,23 @@ public class RoutingContext : IRoutingContext
return _stack.Peek(); return _stack.Peek();
} }
/// <summary>
/// Push agent
/// </summary>
/// <param name="agentId">Id or Name</param>
/// <param name="reason"></param>
public void Push(string agentId, string? reason = null) public void Push(string agentId, string? reason = null)
{ {
// Convert id to name
if (!Guid.TryParse(agentId, out _))
{
var agentService = _services.GetRequiredService<IAgentService>();
agentId = agentService.GetAgents(new AgentFilter
{
AgentName = agentId
}).Result.Items.First().Id;
}
if (_stack.Count == 0 || _stack.Peek() != agentId) if (_stack.Count == 0 || _stack.Peek() != agentId)
{ {
var preAgentId = _stack.Count == 0 ? agentId : _stack.Peek(); var preAgentId = _stack.Count == 0 ? agentId : _stack.Peek();

View file

@ -41,7 +41,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
PropertyNameCaseInsensitive = true, PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
AllowTrailingCommas = true, AllowTrailingCommas = true,
WriteIndented = true WriteIndented = true,
}; };
} }
@ -66,6 +66,11 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
public override async Task OnFunctionExecuted(RoleDialogModel message) public override async Task OnFunctionExecuted(RoleDialogModel message)
{ {
if (message.FunctionName == "route_to_agent")
{
return;
}
var conversationId = _state.GetConversationId(); var conversationId = _state.GetConversationId();
var agent = await _agentService.LoadAgent(message.CurrentAgentId); var agent = await _agentService.LoadAgent(message.CurrentAgentId);
message.FunctionArgs = message.FunctionArgs ?? "{}"; message.FunctionArgs = message.FunctionArgs ?? "{}";
@ -128,6 +133,11 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
log += $"\r\n```json\r\n{richContent}\r\n```"; log += $"\r\n```json\r\n{richContent}\r\n```";
} }
if (!string.IsNullOrEmpty(message.FunctionName))
{
log += $"\r\n\r\n**{message.FunctionName}**";
}
var input = new ContentLogInputModel(conv.ConversationId, message) var input = new ContentLogInputModel(conv.ConversationId, message)
{ {
Name = agent?.Name, Name = agent?.Name,
@ -223,7 +233,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input)); await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(input));
} }
public async Task OnRoutingInstructionReceived(FunctionCallFromLlm instruct, RoleDialogModel message) public async Task OnRoutingInstructionRevised(FunctionCallFromLlm instruct, RoleDialogModel message)
{ {
var conversationId = _state.GetConversationId(); var conversationId = _state.GetConversationId();
var agent = await _agentService.LoadAgent(message.CurrentAgentId); var agent = await _agentService.LoadAgent(message.CurrentAgentId);