Skip to content

Commit c7ff0e4

Browse files
committed
Rename to ReasoningPlanner.
1 parent 513ad58 commit c7ff0e4

File tree

15 files changed

+133
-124
lines changed

15 files changed

+133
-124
lines changed

src/Infrastructure/BotSharp.Abstraction/Planning/IPlaner.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace BotSharp.Abstraction.Planning;
77
/// </summary>
88
public interface IPlaner
99
{
10-
Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string conversation);
10+
Task<FunctionCallFromLlm> GetNextInstruction(Agent router);
11+
Task<bool> AgentExecuting(FunctionCallFromLlm inst, RoleDialogModel message);
1112
Task<bool> AgentExecuted(FunctionCallFromLlm inst, RoleDialogModel message);
1213
}

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public RoutingContext(RoutingSettings setting)
2424
public string OriginAgentId
2525
=> _stack.Where(x => x != _setting.RouterId).Last();
2626

27+
public bool IsEmpty => !_stack.Any();
2728
public string GetCurrentAgentId()
2829
{
2930
if (_stack.Count == 0)
@@ -44,14 +45,13 @@ public void Push(string agentId)
4445
/// <summary>
4546
/// Pop current agent
4647
/// </summary>
47-
/// <returns>Return next agent</returns>
48-
public string Pop()
48+
public void Pop()
4949
{
50-
if (_stack.Count > 1)
51-
{
52-
_stack.Pop();
53-
}
50+
_stack.Pop();
51+
}
5452

55-
return _stack.Peek();
53+
public void Empty()
54+
{
55+
_stack.Clear();
5656
}
5757
}

src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,13 @@ public static IServiceCollection AddBotSharp(this IServiceCollection services, I
7171
services.AddSingleton((IServiceProvider x) => routingSettings);
7272

7373
services.AddScoped<NaivePlanner>();
74-
services.AddScoped<FeedbackReasoningPlanner>();
74+
services.AddScoped<ReasoningPlanner>();
7575
services.AddScoped<IPlaner>(provider =>
7676
{
77-
if (routingSettings.Planner == "NaivePlanner")
77+
if (routingSettings.Planner == nameof(ReasoningPlanner))
78+
return provider.GetRequiredService<ReasoningPlanner>();
79+
else
7880
return provider.GetRequiredService<NaivePlanner>();
79-
else if (routingSettings.Planner == "FeedbackReasoningPlanner")
80-
return provider.GetRequiredService<FeedbackReasoningPlanner>();
81-
throw new NotImplementedException();
8281
});
8382

8483
services.AddScoped<IExecutor, InstructExecutor>();

src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using BotSharp.Abstraction.Agents.Models;
22
using BotSharp.Abstraction.Functions.Models;
33
using BotSharp.Abstraction.Planning;
4+
using BotSharp.Abstraction.Routing.Models;
45
using BotSharp.Abstraction.Templating;
56

67
namespace BotSharp.Core.Planning;
@@ -16,7 +17,7 @@ public NaivePlanner(IServiceProvider services, ILogger<NaivePlanner> logger)
1617
_logger = logger;
1718
}
1819

19-
public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string conversation)
20+
public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router)
2021
{
2122
var next = GetNextStepPrompt(router);
2223

@@ -25,7 +26,7 @@ public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string c
2526

2627
var agentService = _services.GetRequiredService<IAgentService>();
2728
var instruction = agentService.RenderedInstruction(router);
28-
var content = $"{instruction}\r\n{conversation}\r\n###\r\n{next}";
29+
var content = $"{instruction}\r\n###\r\n{next}";
2930

3031
// text completion
3132
content = content + "\r\nResponse: ";
@@ -54,13 +55,22 @@ public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string c
5455
retryCount++;
5556
}
5657
}
57-
58+
59+
// Fix LLM malformed response
60+
FixMalformedResponse(inst);
61+
5862
return inst;
5963
}
6064

65+
public async Task<bool> AgentExecuting(FunctionCallFromLlm inst, RoleDialogModel message)
66+
{
67+
return true;
68+
}
69+
6170
public async Task<bool> AgentExecuted(FunctionCallFromLlm inst, RoleDialogModel message)
6271
{
63-
inst.AgentName = null;
72+
var context = _services.GetRequiredService<RoutingContext>();
73+
context.Empty();
6474
return true;
6575
}
6676

@@ -73,4 +83,54 @@ private string GetNextStepPrompt(Agent router)
7383
{
7484
});
7585
}
86+
87+
/// <summary>
88+
/// Sometimes LLM hallucinates and fails to set function names correctly.
89+
/// </summary>
90+
/// <param name="args"></param>
91+
private void FixMalformedResponse(FunctionCallFromLlm args)
92+
{
93+
var agentService = _services.GetRequiredService<IAgentService>();
94+
var agents = agentService.GetAgents(allowRouting: true).Result;
95+
var malformed = false;
96+
97+
// Sometimes it populate malformed Function in Agent name
98+
if (!string.IsNullOrEmpty(args.Function) &&
99+
args.Function == args.AgentName)
100+
{
101+
args.Function = "route_to_agent";
102+
malformed = true;
103+
}
104+
105+
// Another case of malformed response
106+
if (string.IsNullOrEmpty(args.AgentName) &&
107+
agents.Select(x => x.Name).Contains(args.Function))
108+
{
109+
args.AgentName = args.Function;
110+
args.Function = "route_to_agent";
111+
malformed = true;
112+
}
113+
114+
// It should be Route to agent, but it is used as Response to user.
115+
if (!string.IsNullOrEmpty(args.AgentName) &&
116+
agents.Select(x => x.Name).Contains(args.AgentName) &&
117+
args.Function != "route_to_agent")
118+
{
119+
args.Function = "route_to_agent";
120+
malformed = true;
121+
}
122+
123+
// Function name shouldn't contain dot symbol
124+
if (!string.IsNullOrEmpty(args.Function) &&
125+
args.Function.Contains('.'))
126+
{
127+
args.Function = args.Function.Split('.').Last();
128+
malformed = true;
129+
}
130+
131+
if (malformed)
132+
{
133+
_logger.LogWarning($"Captured LLM malformed response");
134+
}
135+
}
76136
}

src/Infrastructure/BotSharp.Core/Planning/FeedbackReasoningPlanner.cs renamed to src/Infrastructure/BotSharp.Core/Planning/ReasoningPlanner.cs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
using BotSharp.Abstraction.Agents.Models;
22
using BotSharp.Abstraction.Functions.Models;
33
using BotSharp.Abstraction.Planning;
4+
using BotSharp.Abstraction.Repositories;
5+
using BotSharp.Abstraction.Routing.Models;
46
using BotSharp.Abstraction.Templating;
57

68
namespace BotSharp.Core.Planning;
79

8-
public class FeedbackReasoningPlanner : IPlaner
10+
public class ReasoningPlanner : IPlaner
911
{
1012
private readonly IServiceProvider _services;
1113
private readonly ILogger _logger;
1214

13-
public FeedbackReasoningPlanner(IServiceProvider services, ILogger<FeedbackReasoningPlanner> logger)
15+
public ReasoningPlanner(IServiceProvider services, ILogger<ReasoningPlanner> logger)
1416
{
1517
_services = services;
1618
_logger = logger;
1719
}
1820

19-
public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string conversation)
21+
public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router)
2022
{
2123
var next = GetNextStepPrompt(router);
2224

2325
RoleDialogModel response = default;
2426
var inst = new FunctionCallFromLlm();
2527

26-
var content = $"{conversation}\r\n###\r\n{next}";
27-
2828
var completion = CompletionProvider.GetChatCompletion(_services,
2929
model: "llm-gpt4");
3030

@@ -35,7 +35,7 @@ public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string c
3535
{
3636
response = completion.GetChatCompletions(router, new List<RoleDialogModel>
3737
{
38-
new RoleDialogModel(AgentRole.User, content)
38+
new RoleDialogModel(AgentRole.User, next)
3939
});
4040

4141
inst = response.Content.JsonContent<FunctionCallFromLlm>();
@@ -57,9 +57,28 @@ public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string c
5757
return inst;
5858
}
5959

60+
public async Task<bool> AgentExecuting(FunctionCallFromLlm inst, RoleDialogModel message)
61+
{
62+
message.Content = inst.Question;
63+
message.FunctionArgs = JsonSerializer.Serialize(inst.Arguments);
64+
65+
var db = _services.GetRequiredService<IBotSharpRepository>();
66+
var agent = db.GetAgents(inst.AgentName).FirstOrDefault();
67+
68+
var context = _services.GetRequiredService<RoutingContext>();
69+
context.Push(agent.Id);
70+
71+
return true;
72+
}
73+
6074
public async Task<bool> AgentExecuted(FunctionCallFromLlm inst, RoleDialogModel message)
6175
{
62-
inst.AgentName = null;
76+
var context = _services.GetRequiredService<RoutingContext>();
77+
context.Pop();
78+
79+
// push Router to continue
80+
// Make decision according to last agent's response
81+
6382
return true;
6483
}
6584

src/Infrastructure/BotSharp.Core/Routing/Handlers/ContinueExecuteTaskRoutingHandler.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using BotSharp.Abstraction.Repositories;
44
using BotSharp.Abstraction.Routing;
55
using BotSharp.Abstraction.Routing.Settings;
6+
using BotSharp.Core.Planning;
67

78
namespace BotSharp.Core.Routing.Handlers;
89

@@ -24,7 +25,7 @@ public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase, IRoutingHan
2425

2526
public List<string> Planers => new List<string>
2627
{
27-
"FeedbackReasoningPlanner"
28+
nameof(ReasoningPlanner)
2829
};
2930

3031
public ContinueExecuteTaskRoutingHandler(IServiceProvider services, ILogger<ContinueExecuteTaskRoutingHandler> logger, RoutingSettings settings)

src/Infrastructure/BotSharp.Core/Routing/Handlers/InterruptTaskExecutionRoutingHandler.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using BotSharp.Abstraction.Functions.Models;
22
using BotSharp.Abstraction.Routing;
33
using BotSharp.Abstraction.Routing.Settings;
4+
using BotSharp.Core.Planning;
45

56
namespace BotSharp.Core.Routing.Handlers;
67

@@ -18,7 +19,7 @@ public class InterruptTaskExecutionRoutingHandler : RoutingHandlerBase, IRouting
1819

1920
public List<string> Planers => new List<string>
2021
{
21-
"FeedbackReasoningPlanner"
22+
nameof(ReasoningPlanner)
2223
};
2324

2425
public InterruptTaskExecutionRoutingHandler(IServiceProvider services, ILogger<InterruptTaskExecutionRoutingHandler> logger, RoutingSettings settings)
Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
using BotSharp.Abstraction.Functions.Models;
22
using BotSharp.Abstraction.Repositories;
33
using BotSharp.Abstraction.Routing;
4+
using BotSharp.Abstraction.Routing.Models;
45
using BotSharp.Abstraction.Routing.Settings;
6+
using BotSharp.Core.Planning;
57

68
namespace BotSharp.Core.Routing.Handlers;
79

10+
/// <summary>
11+
/// Retrieve information from specific agent
12+
/// </summary>
813
public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingHandler
914
{
1015
public string Name => "retrieve_data_from_agent";
@@ -13,9 +18,9 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH
1318

1419
public List<ParameterPropertyDef> Parameters => new List<ParameterPropertyDef>
1520
{
16-
new ParameterPropertyDef("agent", "the name of the agent"),
17-
new ParameterPropertyDef("question", "the question you will ask the agent to get the necessary data"),
1821
new ParameterPropertyDef("reason", "why retrieve data"),
22+
new ParameterPropertyDef("question", "the question you will ask the agent to get the necessary data"),
23+
new ParameterPropertyDef("next_action_agent", "agent that can handle the question"),
1924
new ParameterPropertyDef("args", "required parameters extracted from question and hand over to the next agent")
2025
{
2126
Type = "object"
@@ -24,7 +29,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase, IRoutingH
2429

2530
public List<string> Planers => new List<string>
2631
{
27-
"FeedbackReasoningPlanner"
32+
nameof(ReasoningPlanner)
2833
};
2934

3035
public RetrieveDataFromAgentRoutingHandler(IServiceProvider services, ILogger<RetrieveDataFromAgentRoutingHandler> logger, RoutingSettings settings)
@@ -34,33 +39,9 @@ public RetrieveDataFromAgentRoutingHandler(IServiceProvider services, ILogger<Re
3439

3540
public async Task<bool> Handle(IRoutingService routing, FunctionCallFromLlm inst, RoleDialogModel message)
3641
{
37-
// Retrieve information from specific agent
38-
var db = _services.GetRequiredService<IBotSharpRepository>();
39-
var record = db.GetAgents(inst.AgentName).FirstOrDefault();
40-
var ret = await routing.InvokeAgent(record.Id, message);
41-
42-
/*_dialogs.Add(new RoleDialogModel(AgentRole.Assistant, inst.Parameters.Question)
43-
{
44-
CurrentAgentId = record.Id
45-
});*/
46-
47-
_router.Instruction += $"\r\n{AgentRole.Assistant}: {inst.Question}";
48-
49-
/*_dialogs.Add(new RoleDialogModel(AgentRole.Function, inst.Parameters.Answer)
50-
{
51-
MessageId = inst.MessageId,
52-
FunctionName = inst.Function,
53-
FunctionArgs = JsonSerializer.Serialize(inst.Parameters.Arguments),
54-
ExecutionResult = inst.Parameters.Answer,
55-
ExecutionData = response.ExecutionData,
56-
CurrentAgentId = record.Id
57-
});*/
58-
59-
_router.Instruction += $"\r\n{AgentRole.Function}: {message.Content}";
60-
61-
// Got the response from agent, then send to reasoner again to make the decision
62-
// inst = await GetNextInstructionFromReasoner($"What's the next step based on user's original goal and function result?");
42+
var context = _services.GetRequiredService<RoutingContext>();
43+
var ret = await routing.InvokeAgent(context.GetCurrentAgentId(), message);
6344

64-
return true;
45+
return ret;
6546
}
6647
}

src/Infrastructure/BotSharp.Core/Routing/Handlers/TaskEndRoutingHandler.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using BotSharp.Abstraction.Functions.Models;
22
using BotSharp.Abstraction.Routing;
33
using BotSharp.Abstraction.Routing.Settings;
4+
using BotSharp.Core.Planning;
45

56
namespace BotSharp.Core.Routing.Handlers;
67

@@ -17,7 +18,7 @@ public class TaskEndRoutingHandler : RoutingHandlerBase, IRoutingHandler
1718

1819
public List<string> Planers => new List<string>
1920
{
20-
"FeedbackReasoningPlanner"
21+
nameof(ReasoningPlanner)
2122
};
2223

2324
public TaskEndRoutingHandler(IServiceProvider services, ILogger<TaskEndRoutingHandler> logger, RoutingSettings settings)

0 commit comments

Comments
 (0)