Skip to content

Commit 92471fa

Browse files
authored
Merge pull request #270 from hchen2020/master
Allow task agent to fallback to predefined router.
2 parents 29a2ffe + e1a948c commit 92471fa

File tree

15 files changed

+143
-21
lines changed

15 files changed

+143
-21
lines changed

src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionParametersDef.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace BotSharp.Abstraction.Functions.Models;
55
public class FunctionParametersDef
66
{
77
[JsonPropertyName("type")]
8-
public string Type { get; set; } = "string";
8+
public string Type { get; set; } = "object";
99

1010
/// <summary>
1111
/// ParameterPropertyDef
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace BotSharp.Abstraction.Routing.Enums;
2+
3+
public class RuleType
4+
{
5+
/// <summary>
6+
/// Fallback to redirect agent
7+
/// </summary>
8+
public const string Fallback = "fallback";
9+
10+
/// <summary>
11+
/// Redirect to other agent if data validation failed
12+
/// </summary>
13+
public const string DataValidation = "data-validation";
14+
}

src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,20 @@ public interface IRoutingService
1313
/// <returns></returns>
1414
RoutableAgent[] GetRoutableAgents(List<string> profiles);
1515

16-
RoutingRule[] GetRulesByName(string name);
16+
/// <summary>
17+
/// Get rules by agent name
18+
/// </summary>
19+
/// <param name="name">agent name</param>
20+
/// <returns></returns>
21+
RoutingRule[] GetRulesByAgentName(string name);
22+
23+
/// <summary>
24+
/// Get rules by agent id
25+
/// </summary>
26+
/// <param name="id">agent id </param>
27+
/// <returns></returns>
1728
RoutingRule[] GetRulesByAgentId(string id);
29+
1830
List<RoutingHandlerDef> GetHandlers();
1931
void ResetRecursiveCounter();
2032
Task<bool> InvokeAgent(string agentId, List<RoleDialogModel> dialogs);

src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingContext.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,19 @@ public void Pop()
6868
_stack.Pop();
6969
}
7070

71+
public void Replace(string agentId)
72+
{
73+
if (_stack.Count == 0)
74+
{
75+
_stack.Push(agentId);
76+
}
77+
else if (_stack.Peek() != agentId)
78+
{
79+
_stack.Pop();
80+
_stack.Push(agentId);
81+
}
82+
}
83+
7184
public void Empty()
7285
{
7386
_stack.Clear();

src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingRule.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using BotSharp.Abstraction.Routing.Enums;
2+
13
namespace BotSharp.Abstraction.Routing.Models;
24

35
public class RoutingRule
@@ -8,12 +10,15 @@ public class RoutingRule
810
[JsonIgnore]
911
public string AgentName { get; set; }
1012

13+
public string Type { get; set; } = RuleType.DataValidation;
14+
1115
public string Field { get; set; }
1216
public string Description { get; set; }
17+
1318
/// <summary>
1419
/// Field type: string, number, object
1520
/// </summary>
16-
public string Type { get; set; } = "string";
21+
public string FieldType { get; set; } = "string";
1722

1823
public bool Required { get; set; }
1924

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using BotSharp.Abstraction.Functions;
2+
using BotSharp.Abstraction.Repositories.Filters;
3+
using BotSharp.Abstraction.Routing.Models;
4+
using BotSharp.Abstraction.Routing;
5+
6+
namespace BotSharp.Core.Routing.Functions;
7+
8+
public class FallbackToRouterFn : IFunctionCallback
9+
{
10+
public string Name => "fallback_to_router";
11+
private readonly IServiceProvider _services;
12+
public FallbackToRouterFn(IServiceProvider services)
13+
{
14+
_services = services;
15+
}
16+
17+
public async Task<bool> Execute(RoleDialogModel message)
18+
{
19+
var args = JsonSerializer.Deserialize<RoutingArgs>(message.FunctionArgs);
20+
var agentService = _services.GetRequiredService<IAgentService>();
21+
var agents = await agentService.GetAgents(new AgentFilter
22+
{
23+
AgentName = args.AgentName
24+
});
25+
var targetAgent = agents.Items.FirstOrDefault();
26+
if (targetAgent == null)
27+
{
28+
message.Content = $"Can't find routing agent {args.AgentName}";
29+
return false;
30+
}
31+
32+
var routing = _services.GetRequiredService<RoutingContext>();
33+
routing.Replace(targetAgent.Id);
34+
35+
var router = _services.GetRequiredService<IRoutingService>();
36+
message.CurrentAgentId = targetAgent.Id;
37+
var response = await router.InstructLoop(message);
38+
39+
message.Content = response.Content;
40+
message.StopCompletion = true;
41+
42+
return true;
43+
}
44+
}

src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private bool HasMissingRequiredField(RoleDialogModel message, out string agentId
9191
var args = JsonSerializer.Deserialize<RoutingArgs>(message.FunctionArgs);
9292
var routing = _services.GetRequiredService<IRoutingService>();
9393

94-
var routingRules = routing.GetRulesByName(args.AgentName);
94+
var routingRules = routing.GetRulesByAgentName(args.AgentName);
9595

9696
if (routingRules == null || !routingRules.Any())
9797
{

src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using BotSharp.Abstraction.Functions.Models;
22
using BotSharp.Abstraction.Routing;
3+
using BotSharp.Abstraction.Routing.Enums;
34
using BotSharp.Abstraction.Routing.Settings;
5+
using System.Diagnostics.Metrics;
46

57
namespace BotSharp.Core.Routing.Hooks;
68

@@ -33,11 +35,42 @@ public override bool OnInstructionLoaded(string template, Dictionary<string, obj
3335

3436
public override bool OnFunctionsLoaded(List<FunctionDef> functions)
3537
{
36-
/*functions.Add(new FunctionDef
38+
if (_agent.Type == AgentType.Task)
3739
{
38-
Name = "fallback_to_router",
39-
Description = "If the user's request is beyond your capabilities, you can call this function for help."
40-
});*/
40+
// check if enabled the routing rule
41+
var routing = _services.GetRequiredService<IRoutingService>();
42+
var rule = routing.GetRulesByAgentId(_agent.Id)
43+
.FirstOrDefault(x => x.Type == RuleType.Fallback);
44+
if (rule != null)
45+
{
46+
var agentService = _services.GetRequiredService<IAgentService>();
47+
var redirectAgent = agentService.GetAgent(rule.RedirectTo).Result;
48+
49+
var json = JsonSerializer.Serialize(new
50+
{
51+
user_goal_agent = new
52+
{
53+
type = "string",
54+
description = $"the fixed value is: {_agent.Name}"
55+
},
56+
next_action_agent = new
57+
{
58+
type = "string",
59+
description = $"the fixed value is: {redirectAgent.Name}"
60+
}
61+
});
62+
functions.Add(new FunctionDef
63+
{
64+
Name = "fallback_to_router",
65+
Description = $"If the user's request is beyond your capabilities, you can call this function to handle by other agent ({redirectAgent.Name}).",
66+
Parameters =
67+
{
68+
Properties = JsonSerializer.Deserialize<JsonDocument>(json)
69+
}
70+
});
71+
}
72+
}
73+
4174
return base.OnFunctionsLoaded(functions);
4275
}
4376
}

src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using BotSharp.Abstraction.Agents.Models;
12
using BotSharp.Abstraction.Routing.Models;
23
using BotSharp.Abstraction.Templating;
34

@@ -70,7 +71,7 @@ private async Task<bool> InvokeFunction(RoleDialogModel message, List<RoleDialog
7071
else if (!message.StopCompletion)
7172
{
7273
var routing = _services.GetRequiredService<RoutingContext>();
73-
74+
7475
// Find response template
7576
var templateService = _services.GetRequiredService<IResponseTemplateService>();
7677
var responseTemplate = await templateService.RenderFunctionResponse(message.CurrentAgentId, message);
@@ -83,8 +84,8 @@ private async Task<bool> InvokeFunction(RoleDialogModel message, List<RoleDialog
8384
else
8485
{
8586
// Save to memory dialogs
86-
dialogs.Add(RoleDialogModel.From(message,
87-
role: AgentRole.Function,
87+
dialogs.Add(RoleDialogModel.From(message,
88+
role: AgentRole.Function,
8889
content: message.Content));
8990

9091
// Send to Next LLM
@@ -94,8 +95,8 @@ private async Task<bool> InvokeFunction(RoleDialogModel message, List<RoleDialog
9495
}
9596
else
9697
{
97-
dialogs.Add(RoleDialogModel.From(message,
98-
role: AgentRole.Assistant,
98+
dialogs.Add(RoleDialogModel.From(message,
99+
role: AgentRole.Assistant,
99100
content: message.Content));
100101
}
101102

src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,13 @@ public RoutableAgent[] GetRoutableAgents(List<string> profiles)
172172
Profiles = x.Profiles,
173173
RequiredFields = x.RoutingRules
174174
.Where(p => p.Required)
175-
.Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.Type)
175+
.Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.FieldType)
176176
{
177177
Required = p.Required
178178
}).ToList(),
179179
OptionalFields = x.RoutingRules
180180
.Where(p => !p.Required)
181-
.Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.Type)
181+
.Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.FieldType)
182182
{
183183
Required = p.Required
184184
}).ToList()
@@ -202,7 +202,7 @@ public RoutableAgent[] GetRoutableAgents(List<string> profiles)
202202
return routableAgents;
203203
}
204204

205-
public RoutingRule[] GetRulesByName(string name)
205+
public RoutingRule[] GetRulesByAgentName(string name)
206206
{
207207
return GetRoutingRecords()
208208
.Where(x => x.AgentName.ToLower() == name.ToLower())

src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ You're {{router.name}} ({{router.description}}). Follow these steps to handle us
44
3. Determine which agent is suitable to handle this conversation.
55
4. Re-think on whether the function you chose matches the reason.
66
5. For agent required arguments, leave it as blank object if user doesn't provide it.
7+
6. Response must be in JSON format.
78

89
[FUNCTIONS]
910
{% for handler in routing_handlers %}
@@ -36,4 +37,4 @@ Optional args:
3637
{% endfor %}
3738

3839
[CONVERSATION]
39-
{{ conversation }}
40+
{{ conversation }}
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Break down the user’s most recent needs and figure out the next steps. Response must be in appropriate JSON format.
1+
Break down the user’s most recent needs and figure out the next steps.
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
What is the next step based on the CONVERSATION?
2-
Response must be in required JSON format without any other contents.
32
Route to the Agent that last handled the conversation if necessary.
43
If user wants to speak to customer service, use function human_intervention_needed.
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
In order to execute the instructions listed by the user in the order specified by the user.
2-
What is the next step based on the CONVERSATION?
3-
Response must be in required JSON format.
2+
What is the next step based on the CONVERSATION?

src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public async Task<bool> Execute(RoleDialogModel message)
2727
await _driver.InputUserText(agent, args, message.MessageId);
2828

2929
message.Content = $"Input text \"{args.InputText}\" successfully.";
30+
3031
return true;
3132
}
3233
}

0 commit comments

Comments
 (0)