Merge pull request #330 from hchen2020/master
Add user_goal_description to router.
This commit is contained in:
commit
2ce526dd2e
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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}>";
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue