Merge branch 'SciSharp:master' into master

This commit is contained in:
Haiping 2024-09-30 16:38:07 -05:00 committed by GitHub
commit 15fbf9891c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 225 additions and 54 deletions

View file

@ -16,6 +16,7 @@
<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_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\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\instructions\instruction.liquid" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\database.dictionary.sql.liquid" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\database.summarize.MySql.liquid" /> <None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\database.summarize.MySql.liquid" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\database.summarize.SqlServer.liquid" /> <None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\database.summarize.SqlServer.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.2nd.plan.liquid" />
@ -49,6 +50,9 @@
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\database.summarize.sqlserver.liquid"> <Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\database.summarize.sqlserver.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\database.dictionary.sql.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.next.liquid"> <Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.next.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>

View file

@ -21,34 +21,26 @@ public class SecondaryStagePlanFn : IFunctionCallback
var agentService = _services.GetRequiredService<IAgentService>(); var agentService = _services.GetRequiredService<IAgentService>();
var knowledgeService = _services.GetRequiredService<IKnowledgeService>(); var knowledgeService = _services.GetRequiredService<IKnowledgeService>();
var knowledgeSettings = _services.GetRequiredService<KnowledgeBaseSettings>(); var knowledgeSettings = _services.GetRequiredService<KnowledgeBaseSettings>();
var states = _services.GetRequiredService<IConversationStateService>();
var msgSecondary = RoleDialogModel.From(message);
var taskPrimary = JsonSerializer.Deserialize<PrimaryRequirementRequest>(message.FunctionArgs);
var collectionName = knowledgeSettings.Default.CollectionName ?? KnowledgeCollectionName.BotSharp;
msgSecondary.FunctionArgs = JsonSerializer.Serialize(new SecondaryBreakdownTask var msgSecondary = RoleDialogModel.From(message);
{ var collectionName = knowledgeSettings.Default.CollectionName ?? KnowledgeCollectionName.BotSharp;
TaskDescription = taskPrimary.Requirements var planPrimary = states.GetState("planning_result");
}); var taskPrimary = states.GetState("requirement_detail");
var taskSecondary = JsonSerializer.Deserialize<SecondaryBreakdownTask>(msgSecondary.FunctionArgs); var taskSecondary = JsonSerializer.Deserialize<SecondaryBreakdownTask>(msgSecondary.FunctionArgs);
var items = msgSecondary.Content.JsonArrayContent<FirstStagePlan>();
// Search knowledgebase // Search knowledgebase
foreach (var item in items) var knowledges = await knowledgeService.SearchVectorKnowledge(taskSecondary.SolutionQuestion, collectionName, new VectorSearchOptions
{ {
if (!item.NeedAdditionalInformation) continue; Confidence = 0.6f
});
var knowledges = await knowledgeService.SearchVectorKnowledge(item.Task, collectionName, new VectorSearchOptions var knowledgeResults = "";
{ knowledgeResults = string.Join("\r\n\r\n=====\r\n", knowledges.Select(x => x.ToQuestionAnswer()));
Confidence = 0.6f
});
message.Content += string.Join("\r\n\r\n=====\r\n", knowledges.Select(x => x.ToQuestionAnswer()));
}
// Get second stage planning prompt // Get second stage planning prompt
var currentAgent = await agentService.LoadAgent(message.CurrentAgentId); var currentAgent = await agentService.LoadAgent(message.CurrentAgentId);
var secondPlanningPrompt = await GetSecondStagePlanPrompt(taskSecondary.TaskDescription, message); var secondPlanningPrompt = await GetSecondStagePlanPrompt(taskSecondary.TaskDescription, planPrimary, knowledgeResults, message);
_logger.LogInformation(secondPlanningPrompt); _logger.LogInformation(secondPlanningPrompt);
var plannerAgent = new Agent var plannerAgent = new Agent
@ -64,12 +56,11 @@ public class SecondaryStagePlanFn : IFunctionCallback
message.Content = response.Content; message.Content = response.Content;
_logger.LogInformation(response.Content); _logger.LogInformation(response.Content);
var states = _services.GetRequiredService<IConversationStateService>();
states.SetState("planning_result", response.Content); states.SetState("planning_result", response.Content);
return true; return true;
} }
private async Task<string> GetSecondStagePlanPrompt(string taskDescription, RoleDialogModel message) private async Task<string> GetSecondStagePlanPrompt(string taskDescription, string planPrimary, string knowledgeResults, RoleDialogModel message)
{ {
var agentService = _services.GetRequiredService<IAgentService>(); var agentService = _services.GetRequiredService<IAgentService>();
var render = _services.GetRequiredService<ITemplateRender>(); var render = _services.GetRequiredService<ITemplateRender>();
@ -85,7 +76,8 @@ public class SecondaryStagePlanFn : IFunctionCallback
return render.Render(template, new Dictionary<string, object> return render.Render(template, new Dictionary<string, object>
{ {
{ "task_description", taskDescription }, { "task_description", taskDescription },
{ "primary_plan", new[]{ message.Content } }, { "primary_plan", planPrimary },
{ "additional_knowledge", knowledgeResults },
{ "response_format", responseFormat } { "response_format", responseFormat }
}); });
} }

View file

@ -35,7 +35,7 @@ public class SummaryPlanFn : IFunctionCallback
var allTables = new List<string>(); var allTables = new List<string>();
var ddlStatements = ""; var ddlStatements = "";
var relevantKnowledge = states.GetState("planning_result"); var relevantKnowledge = states.GetState("planning_result");
relevantKnowledge += states.GetState("dictionary_items"); var dictionaryItems = states.GetState("dictionary_items");
foreach (var step in steps) foreach (var step in steps)
{ {
@ -49,12 +49,12 @@ public class SummaryPlanFn : IFunctionCallback
{ {
table = table, table = table,
}); });
await fn.InvokeFunction("get_table_definition", msgCopy); await fn.InvokeFunction("sql_table_definition", msgCopy);
ddlStatements += "\r\n" + msgCopy.Content; ddlStatements += "\r\n" + msgCopy.Content;
} }
// Summarize and generate query // Summarize and generate query
var summaryPlanPrompt = await GetSummaryPlanPrompt(taskRequirement, relevantKnowledge, ddlStatements); var summaryPlanPrompt = await GetSummaryPlanPrompt(taskRequirement, relevantKnowledge, dictionaryItems, ddlStatements);
_logger.LogInformation($"Summary plan prompt:\r\n{summaryPlanPrompt}"); _logger.LogInformation($"Summary plan prompt:\r\n{summaryPlanPrompt}");
var plannerAgent = new Agent var plannerAgent = new Agent
@ -74,7 +74,7 @@ public class SummaryPlanFn : IFunctionCallback
return true; return true;
} }
private async Task<string> GetSummaryPlanPrompt(string taskDescription, string relevantKnowledge, string ddlStatement) private async Task<string> GetSummaryPlanPrompt(string taskDescription, string relevantKnowledge, string dictionaryItems, string ddlStatement)
{ {
var agentService = _services.GetRequiredService<IAgentService>(); var agentService = _services.GetRequiredService<IAgentService>();
var render = _services.GetRequiredService<ITemplateRender>(); var render = _services.GetRequiredService<ITemplateRender>();
@ -94,6 +94,7 @@ public class SummaryPlanFn : IFunctionCallback
{ "task_description", taskDescription }, { "task_description", taskDescription },
{ "summary_requirements", string.Join("\r\n",additionalRequirements) }, { "summary_requirements", string.Join("\r\n",additionalRequirements) },
{ "relevant_knowledges", relevantKnowledge }, { "relevant_knowledges", relevantKnowledge },
{ "dictionary_items", dictionaryItems },
{ "table_structure", ddlStatement }, { "table_structure", ddlStatement },
}); });
} }

View file

@ -13,7 +13,7 @@
"description": "User requirements in detail, don't miss any information especially for those line items, values and numbers.", "description": "User requirements in detail, don't miss any information especially for those line items, values and numbers.",
"items": { "items": {
"type": "string", "type": "string",
"description": "Question converted from requirement in different ways to search in the knowledge base, be short" "description": "Question converted from requirement in different ways to search in the knowledge base, be short and you can refer to the global knowledge."
} }
} }
}, },

View file

@ -0,0 +1,10 @@
You are a sql statement corrector. You will need to refer to the table structure and rewrite the original sql statement so it's using the correct information, e.g. column name.
Output the sql statement only without comments, in JSON format: {{ response_format }}
=====
Original Sql:
{{ original_sql }}
=====
Table Structure:
{{ table_structure }}

View file

@ -18,3 +18,4 @@ For example, you should use SET @id = select max(id) from table;
*** the generated sql query MUST be basedd on the provided table structure. *** *** the generated sql query MUST be basedd on the provided table structure. ***
*** All queries return a maximum of 20 records. *** *** All queries return a maximum of 20 records. ***
*** Only select user friendly columns. *** *** Only select user friendly columns. ***
*** Try to use id instead of string in where clause if you have the dictionary. ***

View file

@ -8,7 +8,7 @@ Thinking process:
- If there is extra knowledge or relationship needed between steps, set the need_additional_information to true for both steps. - If there is extra knowledge or relationship needed between steps, set the need_additional_information to true for both steps.
- If the solution mentioned "related solutions" is needed, set the need_additional_information to true. - If the solution mentioned "related solutions" is needed, set the need_additional_information to true.
- You should find the relationships between data structure based on the task knowledge strictly. If lack of information, set the need_additional_information to true. - You should find the relationships between data structure based on the task knowledge strictly. If lack of information, set the need_additional_information to true.
- If you need to verify or get the enum/term/dictionary value, set the need_additional_information to true. - If you need to lookup the dictionary to verify or get the enum/term/dictionary value, set the need_additional_information to true.
3. Input argument must reference to corresponding variable name that retrieved by previous steps, variable name must start with '@'; 3. Input argument must reference to corresponding variable name that retrieved by previous steps, variable name must start with '@';
4. Output all the subtasks as much detail as possible in JSON: [{{ response_format }}] 4. Output all the subtasks as much detail as possible in JSON: [{{ response_format }}]
5. You can NOT generate the final query before calling function plan_summary. 5. You can NOT generate the final query before calling function plan_summary.

View file

@ -11,8 +11,13 @@ Additional Requirements:
* "output_results" is variable name that needed to be used in the next step. * "output_results" is variable name that needed to be used in the next step.
===== =====
TASK: {{ task_description }} Sub Task Description:
{{ task_description }}
===== =====
Primary Planning: Primary Planning:
{{ primary_plan }} {{ primary_plan }}
=====
Additional Knowledge:
{{ additional_knowledge }}

View file

@ -11,6 +11,10 @@ Task description:
Relevant Knowledges: Relevant Knowledges:
{{ relevant_knowledges }} {{ relevant_knowledges }}
=====
Dictionary Items:
{{ dictionary_items }}
===== =====
Table Structure: Table Structure:
{{ table_structure }} {{ table_structure }}

View file

@ -17,32 +17,34 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <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_dictionary_lookup.json" /> <None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\sql_dictionary_lookup.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\sql_select.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\functions\sql_table_definition.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_dictionary_lookup.fn.liquid" /> <None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_dictionary_lookup.fn.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_executor.fn.liquid" /> <None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_executor.fn.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_table_definition.fn.liquid" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\agent.json" /> <None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\agent.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\execute_sql.json" /> <None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\execute_sql.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\lookup_dictionary.json" /> <None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\lookup_dictionary.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_insert.json" /> <None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_insert.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_select.json" /> <None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_select.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\instructions\instruction.liquid" /> <None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\instructions\instruction.liquid" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\templates\sql_dictionary_lookup.liquid" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\sql_table_definition.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\sql_dictionary_lookup.json"> <Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\sql_dictionary_lookup.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_dictionary_lookup.fn.liquid"> <Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_dictionary_lookup.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\get_table_definition.json"> <Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_table_definition.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\get_table_definition.fn.liquid"> <Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_table_definition.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\agent.json"> <Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\agent.json">
@ -54,9 +56,6 @@
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\instructions\instruction.liquid"> <Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\instructions\instruction.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\templates\sql_dictionary_lookup.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_insert.json"> <Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_insert.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@ -81,4 +80,8 @@
<ProjectReference Include="..\..\Infrastructure\BotSharp.Core\BotSharp.Core.csproj" /> <ProjectReference Include="..\..\Infrastructure\BotSharp.Core\BotSharp.Core.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\templates\" />
</ItemGroup>
</Project> </Project>

View file

@ -4,4 +4,5 @@ public class Utility
{ {
public const string SqlExecutor = "sql-executor"; public const string SqlExecutor = "sql-executor";
public const string SqlDictionaryLookup = "sql-dictionary-lookup"; public const string SqlDictionaryLookup = "sql-dictionary-lookup";
public const string SqlTableDefinition = "sql-table-definition";
} }

View file

@ -8,7 +8,7 @@ namespace BotSharp.Plugin.SqlDriver.Functions;
public class GetTableDefinitionFn : IFunctionCallback public class GetTableDefinitionFn : IFunctionCallback
{ {
public string Name => "get_table_definition"; public string Name => "sql_table_definition";
public string Indication => "Obtain the relevant data structure definitions."; public string Indication => "Obtain the relevant data structure definitions.";
private readonly IServiceProvider _services; private readonly IServiceProvider _services;
private readonly ILogger<GetTableDefinitionFn> _logger; private readonly ILogger<GetTableDefinitionFn> _logger;
@ -38,6 +38,9 @@ public class GetTableDefinitionFn : IFunctionCallback
message.Content = string.Join("\r\n\r\n", tableDdls); message.Content = string.Join("\r\n\r\n", tableDdls);
//var states = _services.GetRequiredService<IConversationStateService>();
//states.SetState($"table_definition_{args.Table}", message.Content);
return true; return true;
} }

View file

@ -1,10 +1,14 @@
using Azure; using Azure;
using BotSharp.Abstraction.Agents.Enums; using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Routing;
using BotSharp.Core.Agents.Services;
using BotSharp.Core.Infrastructures; using BotSharp.Core.Infrastructures;
using BotSharp.Plugin.SqlDriver.Models; using BotSharp.Plugin.SqlDriver.Models;
using MySqlConnector; using MySqlConnector;
using System.Text.RegularExpressions;
using static Dapper.SqlMapper; using static Dapper.SqlMapper;
using static System.Net.Mime.MediaTypeNames;
namespace BotSharp.Plugin.SqlDriver.Functions; namespace BotSharp.Plugin.SqlDriver.Functions;
@ -22,6 +26,26 @@ public class LookupDictionaryFn : IFunctionCallback
{ {
var args = JsonSerializer.Deserialize<LookupDictionary>(message.FunctionArgs); var args = JsonSerializer.Deserialize<LookupDictionary>(message.FunctionArgs);
// get table DDL
var fn = _services.GetRequiredService<IRoutingService>();
var msgCopy = RoleDialogModel.From(message);
await fn.InvokeFunction("sql_table_definition", msgCopy);
// refine SQL
var agentService = _services.GetRequiredService<IAgentService>();
var currentAgent = await agentService.LoadAgent(message.CurrentAgentId);
var dictionarySqlPrompt = await GetDictionarySQLPrompt(args.SqlStatement, msgCopy.Content);
var plannerAgent = new Agent
{
Id = string.Empty,
Name = "sqlDriver_DictionarySearch",
Instruction = dictionarySqlPrompt,
TemplateDict = new Dictionary<string, object>(),
LlmConfig = currentAgent.LlmConfig
};
var response = await GetAiResponse(plannerAgent);
args = JsonSerializer.Deserialize<LookupDictionary>(response.Content);
// check if need to instantely // check if need to instantely
var settings = _services.GetRequiredService<SqlDriverSetting>(); var settings = _services.GetRequiredService<SqlDriverSetting>();
using var connection = new MySqlConnection(settings.MySqlExecutionConnectionString); using var connection = new MySqlConnection(settings.MySqlExecutionConnectionString);
@ -37,9 +61,37 @@ public class LookupDictionaryFn : IFunctionCallback
} }
var states = _services.GetRequiredService<IConversationStateService>(); var states = _services.GetRequiredService<IConversationStateService>();
var dictionaryItems = states.GetState("dictionary_items", ""); var dictionaryItems = states.GetState("dictionary_items", "");
dictionaryItems += "\r\n\r\n" + args.Reason + ":\r\n" + message.Content + "\r\n"; dictionaryItems += "\r\n\r\n" + args.Table + ":\r\n" + args.Reason + ":\r\n" + message.Content + "\r\n";
states.SetState("dictionary_items", dictionaryItems); states.SetState("dictionary_items", dictionaryItems);
return true; return true;
} }
private async Task<string> GetDictionarySQLPrompt(string originalSql, string tableStructure)
{
var agentService = _services.GetRequiredService<IAgentService>();
var render = _services.GetRequiredService<ITemplateRender>();
var knowledgeHooks = _services.GetServices<IKnowledgeHook>();
var agent = await agentService.GetAgent(BuiltInAgentId.Planner);
var template = agent.Templates.FirstOrDefault(x => x.Name == "database.dictionary.sql")?.Content ?? string.Empty;
var responseFormat = JsonSerializer.Serialize(new LookupDictionary{ });
return render.Render(template, new Dictionary<string, object>
{
{ "original_sql", originalSql },
{ "table_structure", tableStructure },
{ "response_format", responseFormat }
});
}
private async Task<RoleDialogModel> GetAiResponse(Agent plannerAgent)
{
var text = "Check and correct the SQL statement.";
var message = new RoleDialogModel(AgentRole.User, text);
var completion = CompletionProvider.GetChatCompletion(_services,
provider: plannerAgent.LlmConfig.Provider,
model: plannerAgent.LlmConfig.Model);
return await completion.GetChatCompletions(plannerAgent, new List<RoleDialogModel> { message });
}
} }

View file

@ -0,0 +1,84 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Settings;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Repositories;
namespace BotSharp.Plugin.SqlDriver.Hooks;
public class GetTableDefinitionHook : AgentHookBase, IAgentHook
{
private const string SQL_EXECUTOR_TEMPLATE = "sql_table_definition.fn";
private IEnumerable<string> _targetSqlExecutorFunctions = new List<string>
{
"sql_table_definition",
};
public override string SelfId => BuiltInAgentId.Planner;
public GetTableDefinitionHook(IServiceProvider services, AgentSettings settings) : base(services, settings)
{
}
public override void OnAgentLoaded(Agent agent)
{
var conv = _services.GetRequiredService<IConversationService>();
var isConvMode = conv.IsConversationMode();
var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(Utility.SqlTableDefinition);
if (isConvMode && isEnabled)
{
var (prompt, fns) = GetPromptAndFunctions();
if (!fns.IsNullOrEmpty())
{
if (!string.IsNullOrWhiteSpace(prompt))
{
agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n";
}
if (agent.Functions == null)
{
agent.Functions = fns;
}
else
{
agent.Functions.AddRange(fns);
}
}
}
base.OnAgentLoaded(agent);
}
private (string, List<FunctionDef>?) GetPromptAndFunctions()
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant);
var fns = agent?.Functions?.Where(x => _targetSqlExecutorFunctions.Contains(x.Name))?.ToList();
var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(SQL_EXECUTOR_TEMPLATE))?.Content ?? string.Empty;
var dbType = GetDatabaseType();
var render = _services.GetRequiredService<ITemplateRender>();
prompt = render.Render(prompt, new Dictionary<string, object>
{
{ "db_type", dbType }
});
return (prompt, fns);
}
private string GetDatabaseType()
{
var settings = _services.GetRequiredService<SqlDriverSetting>();
var dbType = "MySQL";
if (!string.IsNullOrWhiteSpace(settings?.SqlServerConnectionString))
{
dbType = "SQL Server";
}
else if (!string.IsNullOrWhiteSpace(settings?.SqlLiteConnectionString))
{
dbType = "SQL Lite";
}
return dbType;
}
}

View file

@ -11,7 +11,7 @@ public class SqlExecutorHook : AgentHookBase, IAgentHook
private IEnumerable<string> _targetSqlExecutorFunctions = new List<string> private IEnumerable<string> _targetSqlExecutorFunctions = new List<string>
{ {
"sql_select", "sql_select",
"get_table_definition", "sql_table_definition",
}; };
public override string SelfId => string.Empty; public override string SelfId => string.Empty;

View file

@ -6,5 +6,6 @@ public class SqlUtilityHook : IAgentUtilityHook
{ {
utilities.Add(Utility.SqlExecutor); utilities.Add(Utility.SqlExecutor);
utilities.Add(Utility.SqlDictionaryLookup); utilities.Add(Utility.SqlDictionaryLookup);
utilities.Add(Utility.SqlTableDefinition);
} }
} }

View file

@ -24,5 +24,6 @@ public class SqlDriverPlugin : IBotSharpPlugin
services.AddScoped<IAgentUtilityHook, SqlUtilityHook>(); services.AddScoped<IAgentUtilityHook, SqlUtilityHook>();
services.AddScoped<IPlanningHook, SqlDriverPlanningHook>(); services.AddScoped<IPlanningHook, SqlDriverPlanningHook>();
services.AddScoped<IAgentHook, SqlDictionaryLookupHook>(); services.AddScoped<IAgentHook, SqlDictionaryLookupHook>();
services.AddScoped<IAgentHook, GetTableDefinitionHook>();
} }
} }

View file

@ -0,0 +1,18 @@
{
"name": "sql_table_definition",
"description": "Get table structure from database by table name",
"parameters": {
"type": "object",
"properties": {
"table": {
"type": "string",
"description": "table name"
},
"reason": {
"type": "string",
"description": "the reason why you need to call sql_table_definition"
}
},
"required": [ "table", "reason" ]
}
}

View file

@ -1 +0,0 @@
Call get_table_definition to get the table definition of the table you want to query.

View file

@ -1,7 +1,7 @@
Dictionary Lookup Rules: Dictionary Lookup Rules:
===== =====
Please call function sql_dictionary_lookup if user wants to get or retrieve dictionary/enum/term from data tables. Please call function sql_dictionary_lookup if user wants to get or retrieve dictionary/enum/term from data tables.
You must return the id and name/code. You must return the id and name/code. The table name must come from the planning in conversation.
You are connecting to {{ db_type }} database. You can run provided SQL statements by following {{ db_type }} rules. You are connecting to {{ db_type }} database. You can run provided SQL statements by following {{ db_type }} rules.
Dictionary table pattern is table name starting with "data_". You can only query the dictionary table without join other non-dictionary tables. Dictionary table pattern is table name starting with "data_". You can only query the dictionary table without join other non-dictionary tables.

View file

@ -0,0 +1 @@
Call sql_table_definition to get the table definition of the table.

View file

@ -1,5 +1,5 @@
{ {
"name": "get_table_definition", "name": "sql_table_definition",
"description": "Get the DDL, including data structure, data field and relationship for table", "description": "Get the DDL, including data structure, data field and relationship for table",
"parameters": { "parameters": {
"type": "object", "type": "object",

View file

@ -1,9 +0,0 @@
DICTIONARY:
{% for item in items %}
* {{ item }}
{% endfor %}
=====
Which item is the best matching with "{{ keyword }}"?
You must return Id and Name field.