Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public static async Task<HookEmittedResult> Emit<T>(IServiceProvider services, A
{
try
{
logger.LogInformation($"Emit hook action on {action.Method.Name}({hook.GetType().Name})");
action(hook);
}
catch (Exception ex)
Expand Down
24 changes: 14 additions & 10 deletions src/Plugins/BotSharp.Plugin.Planner/BotSharp.Plugin.Planner.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@

<ItemGroup>
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\agent.json" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\functions\plan_primary_stage.json" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\functions\plan_secondary_stage.json" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\functions\plan_summary.json" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\instructions\instruction.liquid" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.1st.next.liquid" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.2nd.plan.liquid" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.summarize.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\plan_primary_stage.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\plan_secondary_stage.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\plan_summary.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\planner_prompt.two_stage.1st.plan.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\plan_primary_stage.fn.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\plan_secondary_stage.fn.liquid" />
Expand All @@ -28,25 +29,28 @@
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\agent.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\instructions\instruction.liquid">
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\functions\plan_primary_stage.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.2nd.plan.liquid">
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\functions\plan_secondary_stage.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.summarize.liquid">
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\functions\plan_summary.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\plan_secondary_stage.json">
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\instructions\instruction.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\plan_primary_stage.json">
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.1st.next.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.1st.plan.liquid">
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.2nd.plan.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\plan_summary.json">
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.summarize.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.1st.plan.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\plan_secondary_stage.fn.liquid">
Expand Down
178 changes: 29 additions & 149 deletions src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Core.Routing.Planning;
using Microsoft.EntityFrameworkCore;

namespace BotSharp.Plugin.Planner.TwoStaging;

Expand All @@ -10,12 +9,6 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner
private readonly IServiceProvider _services;
private readonly ILogger _logger;
public int MaxLoopCount => 10;
private bool _isTaskCompleted;

private Queue<FirstStagePlan> _plan1st = new Queue<FirstStagePlan>();
private Queue<SecondStagePlan> _plan2nd = new Queue<SecondStagePlan>();

private List<string> _executionContext = new List<string>();

public TwoStageTaskPlanner(IServiceProvider services, ILogger<TwoStageTaskPlanner> logger)
{
Expand All @@ -25,78 +18,36 @@ public TwoStageTaskPlanner(IServiceProvider services, ILogger<TwoStageTaskPlanne

public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId, List<RoleDialogModel> dialogs)
{
// push agent to routing context
var routing = _services.GetRequiredService<IRoutingService>();
routing.Context.Push(BuiltInAgentId.Planner, "Make plan in TwoStage planner");
return new FunctionCallFromLlm
{
AgentName = router.Name,
Response = dialogs.Last().Content,
Function = "route_to_agent"
};


/*FirstStagePlan[] items = await GetFirstStagePlanAsync(router, messageId, dialogs);
var nextStepPrompt = await GetNextStepPrompt(router);
var inst = new FunctionCallFromLlm();

foreach (var item in items)
{
_plan1st.Enqueue(item);
};
// chat completion
var completion = CompletionProvider.GetChatCompletion(_services,
provider: router?.LlmConfig?.Provider,
model: router?.LlmConfig?.Model);

// Get Second Stage Plan
if (_plan2nd.IsNullOrEmpty())
// text completion
dialogs = new List<RoleDialogModel>
{
var plan1 = _plan1st.Dequeue();

if (plan1.ContainMultipleSteps)
{
SecondStagePlan[] items = await GetSecondStagePlanAsync(router, messageId, plan1, dialogs);

foreach (var item in items)
{
_plan2nd.Enqueue(item);
}
}
else
new RoleDialogModel(AgentRole.User, nextStepPrompt)
{
_plan2nd.Enqueue(new SecondStagePlan
{
Description = plan1.Task,
Tables = plan1.Tables,
Parameters = plan1.Parameters,
Results = plan1.Results,
});
FunctionName = nameof(TwoStageTaskPlanner),
MessageId = messageId
}
}

var plan2 = _plan2nd.Dequeue();

var secondStagePrompt = GetSecondStageTaskPrompt(router, plan2);
var inst = new FunctionCallFromLlm
{
AgentName = "SQL Driver",
Response = secondStagePrompt,
Function = "route_to_agent"
};
var response = await completion.GetChatCompletions(router, dialogs);

inst.HandleDialogsByPlanner = true;
_isTaskCompleted = _plan1st.IsNullOrEmpty() && _plan2nd.IsNullOrEmpty();
inst = response.Content.JsonContent<FunctionCallFromLlm>();

return inst;*/
// Fix LLM malformed response
PlannerHelper.FixMalformedResponse(_services, inst);

return inst;
}

public List<RoleDialogModel> BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
{
var question = inst.Response;
if (_executionContext.Count > 0)
{
var content = GetContext();
question = $"CONTEXT:\r\n{content}\r\n" + inst.Response;
}
else
{
question = $"CONTEXT:\r\n{question}";
}

var taskAgentDialogs = new List<RoleDialogModel>
{
Expand All @@ -113,22 +64,22 @@ public bool AfterHandleContext(List<RoleDialogModel> dialogs, List<RoleDialogMod
{
dialogs.AddRange(taskAgentDialogs.Skip(1));

// Keep execution context
_executionContext.Add(taskAgentDialogs.Last().Content);

return true;
}

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 (message.StopCompletion || _isTaskCompleted)
if (message.StopCompletion)
{
context.Empty(reason: $"Agent queue is cleared by {nameof(TwoStageTaskPlanner)}");
return false;
Expand All @@ -145,16 +96,6 @@ public async Task<bool> AgentExecuted(Agent router, FunctionCallFromLlm inst, Ro
return true;
}

public string GetContext()
{
var content = "";
foreach (var c in _executionContext)
{
content += $"* {c}\r\n";
}
return content;
}

private async Task<string> GetFirstStagePlanPrompt(Agent router)
{
var template = router.Templates.First(x => x.Name == "two_stage.1st.plan").Content;
Expand All @@ -180,61 +121,18 @@ private async Task<string> GetFirstStagePlanPrompt(Agent router)
});
}

private string GetFirstStageNextPrompt(Agent router)
private async Task<string> GetNextStepPrompt(Agent router)
{
var template = router.Templates.First(x => x.Name == "first_stage.next").Content;
var responseFormat = JsonSerializer.Serialize(new FirstStagePlan
{
});
var agentService = _services.GetRequiredService<IAgentService>();
var planner = await agentService.LoadAgent(BuiltInAgentId.Planner);
var template = planner.Templates.First(x => x.Name == "two_stage.1st.next").Content;
var states = _services.GetRequiredService<IConversationStateService>();
var render = _services.GetRequiredService<ITemplateRender>();
return render.Render(template, new Dictionary<string, object>
{
{ "response_format", responseFormat },
});
}

private async Task<SecondStagePlan[]> GetSecondStagePlanAsync(Agent router, string messageId, FirstStagePlan plan1st, List<RoleDialogModel> dialogs)
{
var secondStagePrompt = GetSecondStagePlanPrompt(router, plan1st);
var firstStageSystemPrompt = await GetFirstStagePlanPrompt(router);

var plan = new SecondStagePlan[0];

var llmProviderService = _services.GetRequiredService<ILlmProviderService>();
var model = llmProviderService.GetProviderModel("azure-openai", "gpt-4");

// chat completion
var completion = CompletionProvider.GetChatCompletion(_services,
provider: "azure-openai",
model: model.Name);

string text = string.Empty;

var conversations = dialogs.Where(x => x.Role != AgentRole.Function).ToList();
conversations.Add(new RoleDialogModel(AgentRole.User, secondStagePrompt)
{
CurrentAgentId = router.Id,
MessageId = messageId,
{ StateConst.EXPECTED_ACTION_AGENT, states.GetState(StateConst.EXPECTED_ACTION_AGENT) },
{ StateConst.EXPECTED_GOAL_AGENT, states.GetState(StateConst.EXPECTED_GOAL_AGENT) }
});

try
{
var response = await completion.GetChatCompletions(new Agent
{
Id = router.Id,
Name = nameof(TwoStageTaskPlanner),
Instruction = firstStageSystemPrompt
}, conversations);

text = response.Content;
plan = response.Content.JsonArrayContent<SecondStagePlan>();
}
catch (Exception ex)
{
_logger.LogError($"{ex.Message}: {text}");
}

return plan;
}

private string GetSecondStageTaskPrompt(Agent router, SecondStagePlan plan)
Expand All @@ -249,22 +147,4 @@ private string GetSecondStageTaskPrompt(Agent router, SecondStagePlan plan)
{ "output_results", JsonSerializer.Serialize(plan.Results) },
});
}

private string GetSecondStagePlanPrompt(Agent router, FirstStagePlan plan)
{
var template = router.Templates.First(x => x.Name == "planner_prompt.two_stage.2nd.plan").Content;
var responseFormat = JsonSerializer.Serialize(new SecondStagePlan
{
Tool = "tool name if task solution provided",
Parameters = new JsonDocument[] { JsonDocument.Parse("{}") },
Results = new string[] { "" }
});
var context = GetContext();
var render = _services.GetRequiredService<ITemplateRender>();
return render.Render(template, new Dictionary<string, object>
{
{ "task_description", plan.Task },
{ "response_format", responseFormat }
});
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "282a7128-69a1-44b0-878c-a9159b88f3b9",
"name": "Planner",
"description": "Plan feasible implementation steps for complex problems",
"description": "Plan feasible implementation steps for user task request",
"type": "task",
"createdDateTime": "2023-08-27T10:39:00Z",
"updatedDateTime": "2023-08-27T14:39:00Z",
Expand All @@ -11,7 +11,8 @@
"profiles": [ "planning" ],
"utilities": [ "two-stage-planner" ],
"llmConfig": {
"provider": "openai",
"model": "gpt-4o-mini"
"provider": "anthropic",
"model": "claude-3-5-sonnet-20240620",
"max_recursion_depth": 10
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Use the TwoStagePlanner approach to plan the overall implementation steps, call plan_primary_stage.
If need_additional_information is true, call plan_secondary_stage for the specific primary stage.
You must Call plan_summary as the last step to summarize the final planning steps.
You must call plan_summary as the last planning step to summarize the final query.
You must generate the final sql statement from function of plan_summary.

{% if global_knowledges != empty -%}
=====
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
What is the next step based on the CONVERSATION?
Route to the last handling agent in priority.
{% if expected_next_action_agent != empty -%}
Expected next action agent is {{ expected_next_action_agent }}.
{%- else -%}
Next action agent is inferred based on user lastest response.
{%- endif %}
{% if expected_user_goal_agent != empty -%}
Expected user goal agent is {{ expected_user_goal_agent }}.
{%- else -%}
User goal agent is inferred based on user initial request.
{%- endif %}
Always route to planner first.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
</PropertyGroup>

<ItemGroup>
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\get_table_definition.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\sql_select.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\get_table_definition.fn.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_executor.fn.liquid" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\agent.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\get_table_columns.json" />
Expand All @@ -23,6 +25,12 @@
</ItemGroup>

<ItemGroup>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\get_table_definition.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\get_table_definition.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\agent.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Expand Down
Loading