Refactor reasoner and planner

This commit is contained in:
Haiping Chen 2024-11-25 18:06:26 -06:00
parent b6f83e3722
commit 4507d99bfa
34 changed files with 426 additions and 242 deletions

View file

@ -1,6 +1,7 @@
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Routing;
namespace BotSharp.Abstraction.Routing.Planning;
namespace BotSharp.Abstraction.Planning;
public interface IExecutor
{

View file

@ -1,9 +1,19 @@
using BotSharp.Abstraction.Functions.Models;
namespace BotSharp.Abstraction.Planning;
/// <summary>
/// Planning process for Task Agent
/// https://www.promptingguide.ai/techniques/cot
/// </summary>
public class ITaskPlanner
public interface ITaskPlanner
{
Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId, List<RoleDialogModel> dialogs);
Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs);
Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs);
List<RoleDialogModel> BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
=> dialogs;
bool AfterHandleContext(List<RoleDialogModel> dialogs, List<RoleDialogModel> taskAgentDialogs)
=> true;
int MaxLoopCount => 5;
}

View file

@ -12,6 +12,11 @@ public class RuleType
/// </summary>
public const string DataValidation = "data-validation";
/// <summary>
/// The reasoning approach name for next step
/// </summary>
public const string Reasoner = "reasoner";
/// <summary>
/// The planning approach name for next step
/// </summary>

View file

@ -1,19 +0,0 @@
using BotSharp.Abstraction.Functions.Models;
namespace BotSharp.Abstraction.Routing.Planning;
/// <summary>
/// Task breakdown and execution plan
/// https://www.promptingguide.ai/techniques/cot
/// </summary>
public interface IRoutingPlaner
{
Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId, List<RoleDialogModel> dialogs);
Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs);
Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs);
List<RoleDialogModel> BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
=> dialogs;
bool AfterHandleContext(List<RoleDialogModel> dialogs, List<RoleDialogModel> taskAgentDialogs)
=> true;
int MaxLoopCount => 5;
}

View file

@ -0,0 +1,30 @@
using BotSharp.Abstraction.Functions.Models;
namespace BotSharp.Abstraction.Routing.Reasoning;
/// <summary>
/// Reasoning approaches for large language models (LLMs) help enhance their ability to solve complex problems,
/// handle tasks requiring logic, and provide accurate and contextually appropriate responses.
/// </summary>
public interface IRoutingReasoner
{
string Name => "Unnamed Reasoner";
string Description => "Each of these approaches leverages the capabilities of LLMs to reason more effectively, " +
"ensuring better performance and more coherent outputs across various types of complex tasks.";
int MaxLoopCount => 5;
Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId, List<RoleDialogModel> dialogs);
Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
=> Task.FromResult(true);
Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
=> Task.FromResult(true);
List<RoleDialogModel> BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
=> dialogs;
bool AfterHandleContext(List<RoleDialogModel> dialogs, List<RoleDialogModel> taskAgentDialogs)
=> true;
}

View file

@ -12,7 +12,6 @@ global using BotSharp.Abstraction.Agents.Enums;
global using BotSharp.Abstraction.Infrastructures.Enums;
global using BotSharp.Abstraction.Models;
global using BotSharp.Abstraction.Routing.Models;
global using BotSharp.Abstraction.Routing.Planning;
global using BotSharp.Abstraction.Templating;
global using BotSharp.Abstraction.Translation.Attributes;
global using BotSharp.Abstraction.Messaging.Enums;

View file

@ -59,6 +59,11 @@
<ItemGroup>
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\database_knowledge.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.hf.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.naive.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.one-step-forward.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.sequential.get_remaining_task.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.sequential.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\agent.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\instructions\instruction.liquid" />
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\agent.json" />
@ -73,10 +78,6 @@
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\instructions\instruction.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\.welcome.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\conversation.summary.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.hf.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.naive.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.sequential.get_remaining_task.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.sequential.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\response_with_function.liquid" />
<None Remove="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\translation_prompt.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\select_file_prompt.liquid" />
@ -120,16 +121,19 @@
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\database_knowledge.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.sequential.get_remaining_task.liquid">
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.sequential.get_remaining_task.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.sequential.liquid">
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.sequential.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.hf.liquid">
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.hf.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\planner_prompt.naive.liquid">
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.naive.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\reasoner.one-step-forward.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a\templates\response_with_function.liquid">

View file

@ -1,13 +1,13 @@
using BotSharp.Abstraction.Google.Settings;
using BotSharp.Abstraction.Instructs;
using BotSharp.Abstraction.Messaging;
using BotSharp.Abstraction.Planning;
using BotSharp.Abstraction.Plugins.Models;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Settings;
using BotSharp.Abstraction.Templating;
using BotSharp.Core.Instructs;
using BotSharp.Core.Messaging;
using BotSharp.Core.Routing.Planning;
using BotSharp.Core.Routing.Reasoning;
using BotSharp.Core.Templating;
using BotSharp.Core.Translation;
using Microsoft.Extensions.Configuration;

View file

@ -4,7 +4,7 @@ using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Core.Routing.Planning;
using BotSharp.Core.Routing.Reasoning;
namespace BotSharp.Core.Routing.Handlers;
@ -27,7 +27,7 @@ public class ContinueExecuteTaskRoutingHandler : RoutingHandlerBase//, IRoutingH
public List<string> Planers => new List<string>
{
nameof(HFPlanner)
nameof(HFReasoner)
};
public ContinueExecuteTaskRoutingHandler(IServiceProvider services, ILogger<ContinueExecuteTaskRoutingHandler> logger, RoutingSettings settings)

View file

@ -1,7 +1,7 @@
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Core.Routing.Planning;
using BotSharp.Core.Routing.Reasoning;
namespace BotSharp.Core.Routing.Handlers;
@ -19,7 +19,7 @@ public class InterruptTaskExecutionRoutingHandler : RoutingHandlerBase//, IRouti
public List<string> Planers => new List<string>
{
nameof(HFPlanner)
nameof(HFReasoner)
};
public InterruptTaskExecutionRoutingHandler(IServiceProvider services, ILogger<InterruptTaskExecutionRoutingHandler> logger, RoutingSettings settings)

View file

@ -1,5 +1,5 @@
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Core.Routing.Planning;
using BotSharp.Core.Routing.Reasoning;
namespace BotSharp.Core.Routing.Handlers;
@ -26,7 +26,7 @@ public class RetrieveDataFromAgentRoutingHandler : RoutingHandlerBase//, IRoutin
public List<string> Planers => new List<string>
{
nameof(HFPlanner)
nameof(HFReasoner)
};
public RetrieveDataFromAgentRoutingHandler(IServiceProvider services, ILogger<RetrieveDataFromAgentRoutingHandler> logger, RoutingSettings settings)

View file

@ -1,15 +0,0 @@
using System.Text.Json.Serialization;
public class FirstStagePlanParameter
{
[JsonPropertyName("input_args")]
public JsonDocument[] Parameters { get; set; } = new JsonDocument[0];
[JsonPropertyName("output_results")]
public string[] Results { get; set; } = new string[0];
public override string ToString()
{
return $"INPUTS:\r\n{JsonSerializer.Serialize(Parameters)}\r\n\r\nOUTPUTS:\r\n{JsonSerializer.Serialize(Results)}";
}
}

View file

@ -1,21 +0,0 @@
using System.Text.Json.Serialization;
namespace BotSharp.Core.Routing.Planning;
public class SecondStagePlan
{
[JsonPropertyName("related_tables")]
public string[] Tables { get; set; } = new string[0];
[JsonPropertyName("description")]
public string Description { get; set; } = "";
[JsonPropertyName("tool_name")]
public string Tool { get; set; } = "";
[JsonPropertyName("input_args")]
public JsonDocument[] Parameters { get; set; } = new JsonDocument[0];
[JsonPropertyName("output_results")]
public string[] Results { get; set; } = new string[0];
}

View file

@ -1,4 +0,0 @@
public class SecondStagePlanParameter : FirstStagePlanParameter
{
}

View file

@ -1,17 +1,33 @@
using BotSharp.Abstraction.Routing.Planning;
/*****************************************************************************
Copyright 2024 Written by Haiping Chen. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
******************************************************************************/
using BotSharp.Abstraction.Routing.Reasoning;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core.Routing.Planning;
namespace BotSharp.Core.Routing.Reasoning;
/// <summary>
/// Human feedback based planner
/// Human feedback based reasoner
/// </summary>
public class HFPlanner : IRoutingPlaner
public class HFReasoner : IRoutingReasoner
{
private readonly IServiceProvider _services;
private readonly ILogger _logger;
public HFPlanner(IServiceProvider services, ILogger<HFPlanner> logger)
public HFReasoner(IServiceProvider services, ILogger<HFReasoner> logger)
{
_services = services;
_logger = logger;
@ -37,7 +53,7 @@ public class HFPlanner : IRoutingPlaner
{
new RoleDialogModel(AgentRole.User, next)
{
FunctionName = nameof(HFPlanner),
FunctionName = nameof(HFReasoner),
MessageId = messageId
}
};
@ -60,7 +76,7 @@ public class HFPlanner : IRoutingPlaner
}
// Fix LLM malformed response
PlannerHelper.FixMalformedResponse(_services, inst);
ReasonerHelper.FixMalformedResponse(_services, inst);
return inst;
}
@ -87,13 +103,13 @@ public class HFPlanner : IRoutingPlaner
public async Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
{
var context = _services.GetRequiredService<IRoutingContext>();
context.Empty(reason: $"Agent queue is cleared by {nameof(HFPlanner)}");
context.Empty(reason: $"Agent queue is cleared by {nameof(HFReasoner)}");
return true;
}
private string GetNextStepPrompt(Agent router)
{
var template = router.Templates.First(x => x.Name == "planner_prompt.hf").Content;
var template = router.Templates.First(x => x.Name == "reasoner.hf").Content;
var render = _services.GetRequiredService<ITemplateRender>();
// update states
var conv = _services.GetRequiredService<IConversationService>();

View file

@ -1,6 +1,6 @@
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Planning;
namespace BotSharp.Core.Routing.Planning;
namespace BotSharp.Core.Routing.Reasoning;
public class InstructExecutor : IExecutor
{

View file

@ -1,16 +1,35 @@
/*****************************************************************************
Copyright 2024 Written by Haiping Chen. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
******************************************************************************/
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Routing.Reasoning;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core.Routing.Planning;
namespace BotSharp.Core.Routing.Reasoning;
public class NaivePlanner : IRoutingPlaner
/// <summary>
/// simple or unsophisticated methods used to decide which specialized model or module in a system to engage for a given task.
/// </summary>
public class NaiveReasoner : IRoutingReasoner
{
private readonly IServiceProvider _services;
private readonly ILogger _logger;
public NaivePlanner(IServiceProvider services, ILogger<NaivePlanner> logger)
public NaiveReasoner(IServiceProvider services, ILogger<NaiveReasoner> logger)
{
_services = services;
_logger = logger;
@ -46,7 +65,7 @@ public class NaivePlanner : IRoutingPlaner
{
new RoleDialogModel(AgentRole.User, next)
{
FunctionName = nameof(NaivePlanner),
FunctionName = nameof(NaiveReasoner),
MessageId = messageId
}
};
@ -69,7 +88,7 @@ public class NaivePlanner : IRoutingPlaner
}
// Fix LLM malformed response
PlannerHelper.FixMalformedResponse(_services, inst);
ReasonerHelper.FixMalformedResponse(_services, inst);
return inst;
}
@ -99,7 +118,7 @@ public class NaivePlanner : IRoutingPlaner
}
else
{
context.Empty(reason: $"Agent queue is cleared by {nameof(NaivePlanner)}");
context.Empty(reason: $"Agent queue is cleared by {nameof(NaiveReasoner)}");
// context.Push(inst.OriginalAgent, "Push user goal agent");
}
return true;
@ -107,7 +126,7 @@ public class NaivePlanner : IRoutingPlaner
private string GetNextStepPrompt(Agent router)
{
var template = router.Templates.First(x => x.Name == "planner_prompt.naive").Content;
var template = router.Templates.First(x => x.Name == "reasoner.naive").Content;
var states = _services.GetRequiredService<IConversationStateService>();
var render = _services.GetRequiredService<ITemplateRender>();

View file

@ -0,0 +1,137 @@
/*****************************************************************************
Copyright 2024 Written by Haiping Chen. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
******************************************************************************/
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Reasoning;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core.Routing.Reasoning;
/// <summary>
/// One-step forward reasoning is a straightforward reasoning approach where the model or agent evaluates its current state
/// and takes the next best logical step toward the solution without extensive lookahead or planning.
/// This type of reasoning involves making a decision based on the current situation and immediate context
/// rather than considering multiple future steps or possibilities.
/// </summary>
public class OneStepForwardReasoner : IRoutingReasoner
{
public string Name => "one-step-forward";
private readonly IServiceProvider _services;
private readonly ILogger _logger;
public OneStepForwardReasoner(IServiceProvider services, ILogger<NaiveReasoner> logger)
{
_services = services;
_logger = logger;
}
public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId, List<RoleDialogModel> dialogs)
{
var next = GetNextStepPrompt(router);
var inst = new FunctionCallFromLlm();
// chat completion
var completion = CompletionProvider.GetChatCompletion(_services,
provider: router?.LlmConfig?.Provider,
model: router?.LlmConfig?.Model);
int retryCount = 0;
while (retryCount < 3)
{
string text = string.Empty;
try
{
// text completion
// text = await completion.GetCompletion(content, router.Id, messageId);
dialogs = new List<RoleDialogModel>
{
new RoleDialogModel(AgentRole.User, next)
{
FunctionName = Name,
MessageId = messageId
}
};
var response = await completion.GetChatCompletions(router, dialogs);
inst = response.Content.JsonContent<FunctionCallFromLlm>();
break;
}
catch (Exception ex)
{
_logger.LogError($"{ex.Message}: {text}");
inst.Function = "response_to_user";
inst.Response = ex.Message;
inst.AgentName = "Router";
}
finally
{
retryCount++;
}
}
// Fix LLM malformed response
ReasonerHelper.FixMalformedResponse(_services, inst);
return inst;
}
public async Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
{
// Set user content as Planner's question
message.FunctionName = inst.Function;
message.FunctionArgs = inst.Arguments == null ? "{}" : JsonSerializer.Serialize(inst.Arguments);
return true;
}
public async Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
{
var context = _services.GetRequiredService<IRoutingContext>();
if (inst.UnmatchedAgent)
{
var unmatchedAgentId = context.GetCurrentAgentId();
// Exclude the wrong routed agent
var agents = router.TemplateDict["routing_agents"] as RoutableAgent[];
router.TemplateDict["routing_agents"] = agents.Where(x => x.AgentId != unmatchedAgentId).ToArray();
// Handover to Router;
context.Pop();
}
else
{
context.Empty(reason: $"Agent queue is cleared by {nameof(NaiveReasoner)}");
// context.Push(inst.OriginalAgent, "Push user goal agent");
}
return true;
}
private string GetNextStepPrompt(Agent router)
{
var template = router.Templates.First(x => x.Name == "reasoner.one-step-forward").Content;
var states = _services.GetRequiredService<IConversationStateService>();
var render = _services.GetRequiredService<ITemplateRender>();
return render.Render(template, new Dictionary<string, object>
{
{ StateConst.EXPECTED_ACTION_AGENT, states.GetState(StateConst.EXPECTED_ACTION_AGENT) },
{ StateConst.EXPECTED_GOAL_AGENT, states.GetState(StateConst.EXPECTED_GOAL_AGENT) }
});
}
}

View file

@ -1,6 +1,6 @@
namespace BotSharp.Core.Routing.Planning;
namespace BotSharp.Core.Routing.Reasoning;
public static class PlannerHelper
public static class ReasonerHelper
{
/// <summary>
/// Sometimes LLM hallucinates and fails to set function names correctly.

View file

@ -1,11 +1,30 @@
/*****************************************************************************
Copyright 2024 Written by Haiping Chen. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
******************************************************************************/
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Routing.Reasoning;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core.Routing.Planning;
namespace BotSharp.Core.Routing.Reasoning;
public class SequentialPlanner : IRoutingPlaner
/// <summary>
/// Sequential tasks focused reasoning approach
/// </summary>
public class SequentialReasoner : IRoutingReasoner
{
private readonly IServiceProvider _services;
private readonly ILogger _logger;
@ -14,7 +33,7 @@ public class SequentialPlanner : IRoutingPlaner
public int MaxLoopCount => 100;
private FunctionCallFromLlm _lastInst;
public SequentialPlanner(IServiceProvider services, ILogger<NaivePlanner> logger)
public SequentialReasoner(IServiceProvider services, ILogger<NaiveReasoner> logger)
{
_services = services;
_logger = logger;
@ -72,7 +91,7 @@ public class SequentialPlanner : IRoutingPlaner
{
new RoleDialogModel(AgentRole.User, next)
{
FunctionName = nameof(SequentialPlanner),
FunctionName = nameof(SequentialReasoner),
MessageId = messageId
}
};
@ -139,7 +158,7 @@ public class SequentialPlanner : IRoutingPlaner
if (message.StopCompletion)
{
context.Empty(reason: $"Agent queue is cleared by {nameof(SequentialPlanner)}");
context.Empty(reason: $"Agent queue is cleared by {nameof(SequentialReasoner)}");
return false;
}
@ -154,7 +173,7 @@ public class SequentialPlanner : IRoutingPlaner
private string GetNextStepPrompt(Agent router)
{
var template = router.Templates.First(x => x.Name == "planner_prompt.sequential").Content;
var template = router.Templates.First(x => x.Name == "reasoner.sequential").Content;
var render = _services.GetRequiredService<ITemplateRender>();
return render.Render(template, new Dictionary<string, object>
@ -169,11 +188,11 @@ public class SequentialPlanner : IRoutingPlaner
var inst = new DecomposedStep();
var llmProviderService = _services.GetRequiredService<ILlmProviderService>();
var model = llmProviderService.GetProviderModel("azure-openai", "gpt-4");
var model = llmProviderService.GetProviderModel("openai", "gpt-4o");
// chat completion
var completion = CompletionProvider.GetChatCompletion(_services,
provider: "azure-openai",
provider: "openai",
model: model.Name);
int retryCount = 0;
@ -185,7 +204,7 @@ public class SequentialPlanner : IRoutingPlaner
var response = await completion.GetChatCompletions(new Agent
{
Id = router.Id,
Name = nameof(SequentialPlanner),
Name = nameof(SequentialReasoner),
Instruction = systemPrompt
}, dialogs);
@ -208,7 +227,7 @@ public class SequentialPlanner : IRoutingPlaner
private string GetDecomposeTaskPrompt(Agent router)
{
var template = router.Templates.First(x => x.Name == "planner_prompt.sequential.get_remaining_task").Content;
var template = router.Templates.First(x => x.Name == "reasoner.sequential.get_remaining_task").Content;
var render = _services.GetRequiredService<ITemplateRender>();
return render.Render(template, new Dictionary<string, object>

View file

@ -1,10 +1,8 @@
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Routing.Reasoning;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Abstraction.Settings;
using BotSharp.Core.Routing.Hooks;
using BotSharp.Core.Routing.Planning;
using BotSharp.Core.Routing.Reasoning;
using Microsoft.Extensions.Configuration;
namespace BotSharp.Core.Routing;
@ -35,8 +33,10 @@ public class RoutingPlugin : IBotSharpPlugin
services.AddScoped<IRoutingService, RoutingService>();
services.AddScoped<IAgentHook, RoutingAgentHook>();
services.AddScoped<IRoutingPlaner, NaivePlanner>();
services.AddScoped<IRoutingPlaner, HFPlanner>();
services.AddScoped<IRoutingPlaner, SequentialPlanner>();
services.AddScoped<IRoutingReasoner, NaiveReasoner>();
services.AddScoped<IRoutingReasoner, HFReasoner>();
services.AddScoped<IRoutingReasoner, SequentialReasoner>();
services.AddScoped<IRoutingReasoner, OneStepForwardReasoner>();
}
}

View file

@ -1,24 +0,0 @@
using BotSharp.Abstraction.Routing.Enums;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Core.Routing.Planning;
namespace BotSharp.Core.Routing;
public partial class RoutingService
{
public IRoutingPlaner GetPlanner(Agent router)
{
var rule = router.RoutingRules.FirstOrDefault(x => x.Type == RuleType.Planner);
var planner = _services.GetServices<IRoutingPlaner>().
FirstOrDefault(x => x.GetType().Name.EndsWith(rule.Field));
if (planner == null)
{
_logger.LogError($"Can't find specific planner named {rule.Field}");
return _services.GetRequiredService<NaivePlanner>();
}
return planner;
}
}

View file

@ -0,0 +1,112 @@
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Planning;
using BotSharp.Abstraction.Routing.Enums;
using BotSharp.Abstraction.Routing.Reasoning;
using BotSharp.Core.Routing.Reasoning;
namespace BotSharp.Core.Routing;
public partial class RoutingService
{
public async Task<RoleDialogModel> InstructLoop(RoleDialogModel message, List<RoleDialogModel> dialogs)
{
RoleDialogModel response = default;
var agentService = _services.GetRequiredService<IAgentService>();
var convService = _services.GetRequiredService<IConversationService>();
var storage = _services.GetRequiredService<IConversationStorage>();
_router = await agentService.LoadAgent(message.CurrentAgentId);
var states = _services.GetRequiredService<IConversationStateService>();
var executor = _services.GetRequiredService<IExecutor>();
var planner = GetReasoner(_router);
_context.Push(_router.Id);
// Handle multi-language for input
var agentSettings = _services.GetRequiredService<AgentSettings>();
if (agentSettings.EnableTranslator)
{
var translator = _services.GetRequiredService<ITranslationService>();
var language = states.GetState(StateConst.LANGUAGE, LanguageType.ENGLISH);
if (language != LanguageType.ENGLISH)
{
message.SecondaryContent = message.Content;
message.Content = await translator.Translate(_router, message.MessageId, message.Content,
language: LanguageType.ENGLISH,
clone: false);
}
}
dialogs.Add(message);
storage.Append(convService.ConversationId, message);
// Get first instruction
_router.TemplateDict["conversation"] = await GetConversationContent(dialogs);
var inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs);
int loopCount = 1;
while (true)
{
await HookEmitter.Emit<IRoutingHook>(_services, async hook =>
await hook.OnRoutingInstructionReceived(inst, message)
);
// Save states
states.SaveStateByArgs(inst.Arguments);
#if DEBUG
Console.WriteLine($"*** Next Instruction *** {inst}");
#else
_logger.LogInformation($"*** Next Instruction *** {inst}");
#endif
await planner.AgentExecuting(_router, inst, message, dialogs);
// Handover to Task Agent
if (inst.HandleDialogsByPlanner)
{
var dialogWithoutContext = planner.BeforeHandleContext(inst, message, dialogs);
response = await executor.Execute(this, inst, message, dialogWithoutContext);
planner.AfterHandleContext(dialogs, dialogWithoutContext);
}
else
{
response = await executor.Execute(this, inst, message, dialogs);
}
await planner.AgentExecuted(_router, inst, response, dialogs);
if (loopCount >= planner.MaxLoopCount || _context.IsEmpty)
{
break;
}
// Get next instruction from Planner
_router.TemplateDict["conversation"] = await GetConversationContent(dialogs);
inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs);
loopCount++;
}
return response;
}
public IRoutingReasoner GetReasoner(Agent router)
{
var rule = router.RoutingRules.FirstOrDefault(x => x.Type == RuleType.Reasoner);
var reasoner = _services.GetServices<IRoutingReasoner>().
FirstOrDefault(x => x.GetType().Name.EndsWith(rule.Field));
if (reasoner == null)
{
_logger.LogError($"Can't find specific planner named {rule.Field}");
// Default use NaiveReasoner
return _services.GetRequiredService<NaiveReasoner>();
}
return reasoner;
}
}

View file

@ -1,6 +1,4 @@
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Routing.Settings;
namespace BotSharp.Core.Routing;
@ -75,97 +73,12 @@ public partial class RoutingService : IRoutingService
return response;
}
public async Task<RoleDialogModel> InstructLoop(RoleDialogModel message, List<RoleDialogModel> dialogs)
{
RoleDialogModel response = default;
var agentService = _services.GetRequiredService<IAgentService>();
var convService = _services.GetRequiredService<IConversationService>();
var storage = _services.GetRequiredService<IConversationStorage>();
_router = await agentService.LoadAgent(message.CurrentAgentId);
var states = _services.GetRequiredService<IConversationStateService>();
var executor = _services.GetRequiredService<IExecutor>();
var planner = GetPlanner(_router);
_context.Push(_router.Id);
// Handle multi-language for input
var agentSettings = _services.GetRequiredService<AgentSettings>();
if (agentSettings.EnableTranslator)
{
var translator = _services.GetRequiredService<ITranslationService>();
var language = states.GetState(StateConst.LANGUAGE, LanguageType.ENGLISH);
if (language != LanguageType.ENGLISH)
{
message.SecondaryContent = message.Content;
message.Content = await translator.Translate(_router, message.MessageId, message.Content,
language: LanguageType.ENGLISH,
clone: false);
}
}
dialogs.Add(message);
storage.Append(convService.ConversationId, message);
// Get first instruction
_router.TemplateDict["conversation"] = await GetConversationContent(dialogs);
var inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs);
int loopCount = 1;
while (true)
{
await HookEmitter.Emit<IRoutingHook>(_services, async hook =>
await hook.OnRoutingInstructionReceived(inst, message)
);
// Save states
states.SaveStateByArgs(inst.Arguments);
#if DEBUG
Console.WriteLine($"*** Next Instruction *** {inst}");
#else
_logger.LogInformation($"*** Next Instruction *** {inst}");
#endif
await planner.AgentExecuting(_router, inst, message, dialogs);
// Handover to Task Agent
if (inst.HandleDialogsByPlanner)
{
var dialogWithoutContext = planner.BeforeHandleContext(inst, message, dialogs);
response = await executor.Execute(this, inst, message, dialogWithoutContext);
planner.AfterHandleContext(dialogs, dialogWithoutContext);
}
else
{
response = await executor.Execute(this, inst, message, dialogs);
}
await planner.AgentExecuted(_router, inst, response, dialogs);
if (loopCount >= planner.MaxLoopCount || _context.IsEmpty)
{
break;
}
// Get next instruction from Planner
_router.TemplateDict["conversation"] = await GetConversationContent(dialogs);
inst = await planner.GetNextInstruction(_router, message.MessageId, dialogs);
loopCount++;
}
return response;
}
public List<RoutingHandlerDef> GetHandlers(Agent router)
{
var planer = GetPlanner(router);
var reasoner = GetReasoner(router);
return _services.GetServices<IRoutingHandler>()
.Where(x => x.Planers == null || x.Planers.Contains(planer.GetType().Name))
.Where(x => x.Planers == null || x.Planers.Contains(reasoner.GetType().Name))
.Where(x => !string.IsNullOrEmpty(x.Description))
.Select((x, i) => new RoutingHandlerDef
{

View file

@ -11,8 +11,8 @@
"profiles": [ "tool" ],
"routingRules": [
{
"type": "planner",
"field": "HFPlanner"
"type": "reasoner",
"field": "HFReasoner"
}
]
}

View file

@ -0,0 +1,2 @@
Analyze the user's problem. Which prerequisite task needs to be completed? Output the next step of routing instructions.
Check the job responsibilities of the routable Agent and do not transfer to an Agent that exceeds the scope of responsibility.

View file

@ -8,7 +8,7 @@
"updatedDateTime": "2024-11-23T00:00:00Z",
"disabled": false,
"isPublic": true,
"profiles": [ "database" ],
"profiles": [ "coding" ],
"llmConfig": {
"provider": "openai",
"model": "gpt-4o",

View file

@ -1,4 +1,4 @@
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Abstraction.Planning;
using BotSharp.Plugin.Planner.TwoStaging;
namespace BotSharp.Plugin.Planner;
@ -17,7 +17,7 @@ public class PlannerPlugin : IBotSharpPlugin
public void RegisterDI(IServiceCollection services, IConfiguration config)
{
services.AddScoped<IRoutingPlaner, TwoStageTaskPlanner>();
services.AddScoped<ITaskPlanner, TwoStageTaskPlanner>();
services.AddScoped<IAgentHook, PlannerAgentHook>();
services.AddScoped<IAgentUtilityHook, PlannerUtilityHook>();
}

View file

@ -1,10 +1,10 @@
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Core.Routing.Planning;
using BotSharp.Abstraction.Planning;
using BotSharp.Core.Routing.Reasoning;
namespace BotSharp.Plugin.Planner.TwoStaging;
public partial class TwoStageTaskPlanner : IRoutingPlaner
public partial class TwoStageTaskPlanner : ITaskPlanner
{
private readonly IServiceProvider _services;
private readonly ILogger _logger;
@ -40,7 +40,7 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner
inst = response.Content.JsonContent<FunctionCallFromLlm>();
// Fix LLM malformed response
PlannerHelper.FixMalformedResponse(_services, inst);
ReasonerHelper.FixMalformedResponse(_services, inst);
return inst;
}

View file

@ -1,7 +1,7 @@
{
"id": "beda4c12-e1ec-4b4b-b328-3df4a6687c4f",
"name": "SQL Driver",
"description": "Execute the sql query in database from the latest dialog.",
"description": "Transfer to this Agent is allowed only when executable SQL statements are provided in the context.",
"iconUrl": "https://cdn-icons-png.flaticon.com/512/3161/3161158.png",
"type": "task",
"createdDateTime": "2023-11-15T13:49:00Z",
@ -18,7 +18,7 @@
"field": "sql_statement",
"required": true,
"field_type": "string",
"description": "SQL statement"
"description": "SQL statement provided in the context"
}
]
}