Add PaddleOcrConverter
This commit is contained in:
commit
780bb4ed79
|
|
@ -1,3 +1,3 @@
|
|||
# Prompt Engineering
|
||||
|
||||
LLM uses prompt as input, and the model produces different outputs according to the input.
|
||||
LLM uses prompt as input, and the model produces different outputs according to the input.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
# Template
|
||||
|
||||
We can define the prompt as a template, and the template can be changed according to variables, so that a instruction file can be used to generate a dynamic prompt.
|
||||
We can define the prompt as a template, and the template can be changed according to variables, so that a instruction file can be used to generate a dynamic prompt.
|
||||
`BotSharp` uses [liquid](https://shopify.github.io/liquid/) templates to support various complex dynamic prompt engineering.
|
||||
|
||||
`ITemplateRender`
|
||||
```csharp
|
||||
bool Render(Agent agent, Dictionary<string, object> dict)
|
||||
```
|
||||
|
|
@ -4,4 +4,5 @@ public interface IAgentRouting
|
|||
{
|
||||
Task<Agent> LoadRouter();
|
||||
Task<Agent> LoadCurrentAgent();
|
||||
RoutingRecord[] GetRoutingRecords();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,19 +2,25 @@ using System.Text.Json.Serialization;
|
|||
|
||||
namespace BotSharp.Abstraction.Agents.Models;
|
||||
|
||||
public class RoutingTable
|
||||
public class RoutingRecord
|
||||
{
|
||||
[JsonPropertyName("agent_id")]
|
||||
public string AgentId { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string AgentName { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[JsonPropertyName("required")]
|
||||
public List<string> RequiredFields { get; set; }
|
||||
|
||||
[JsonPropertyName("redirect_to")]
|
||||
public string RedirectTo { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return AgentName;
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,4 +8,5 @@ public class AgentSettings
|
|||
public string RouterId { get; set; }
|
||||
public string DataDir { get; set; }
|
||||
public string TemplateFormat { get; set; }
|
||||
public int MaxRecursiveDepth { get; set; } = 3;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<VersionPrefix>0.9.0</VersionPrefix>
|
||||
<VersionPrefix>0.9.4</VersionPrefix>
|
||||
<PackageIcon>Icon.png</PackageIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BotSharp.Abstraction.Functions.Models;
|
||||
|
||||
public class FunctionExecutionValidationResult
|
||||
{
|
||||
public FunctionExecutionValidationResult()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public FunctionExecutionValidationResult(string validationStatus, string? validationMessage = null)
|
||||
{
|
||||
ValidationStatus = validationStatus;
|
||||
ValidationMessage = validationMessage;
|
||||
}
|
||||
|
||||
[JsonPropertyName("validation_status")]
|
||||
public string ValidationStatus { get; set; }
|
||||
|
||||
[JsonPropertyName("validation_message")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string ValidationMessage { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
namespace BotSharp.Abstraction.Templating;
|
||||
|
||||
public interface ITemplateRender
|
||||
{
|
||||
bool Render(Agent agent, Dictionary<string, object> dict);
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using Fluid;
|
||||
|
||||
namespace BotSharp.Core.Agents.Services;
|
||||
|
||||
|
|
@ -7,13 +6,14 @@ public abstract class AgentHookBase : IAgentHook
|
|||
{
|
||||
protected Agent _agent;
|
||||
public Agent Agent => _agent;
|
||||
private static readonly FluidParser _parser = new FluidParser();
|
||||
|
||||
private readonly IServiceProvider _services;
|
||||
protected readonly IServiceProvider _services;
|
||||
protected readonly AgentSettings _settings;
|
||||
|
||||
public AgentHookBase(IServiceProvider services)
|
||||
public AgentHookBase(IServiceProvider services, AgentSettings settings)
|
||||
{
|
||||
_services = services;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public void SetAget(Agent agent)
|
||||
|
|
@ -28,27 +28,7 @@ public abstract class AgentHookBase : IAgentHook
|
|||
|
||||
public virtual bool OnInstructionLoaded(string template, Dictionary<string, object> dict)
|
||||
{
|
||||
if (_parser.TryParse(template, out var t, out var error))
|
||||
{
|
||||
PopulateStateTokens(dict);
|
||||
var context = new TemplateContext(dict);
|
||||
_agent.Instruction = t.Render(context);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void PopulateStateTokens(Dictionary<string, object> dict)
|
||||
{
|
||||
var stateService = _services.GetRequiredService<IConversationStateService>();
|
||||
var state = stateService.Load();
|
||||
foreach (var t in state)
|
||||
{
|
||||
dict[t.Key] = t.Value;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool OnFunctionsLoaded(ref string functions)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using BotSharp.Abstraction.Agents;
|
||||
using BotSharp.Abstraction.Agents.Models;
|
||||
using System.IO;
|
||||
|
||||
namespace BotSharp.Core.Agents.Services;
|
||||
|
||||
|
|
@ -42,4 +42,12 @@ public class AgentRouter : IAgentRouting
|
|||
|
||||
return agent;
|
||||
}
|
||||
|
||||
public RoutingRecord[] GetRoutingRecords()
|
||||
{
|
||||
var agentSettings = _services.GetRequiredService<AgentSettings>();
|
||||
var dbSettings = _services.GetRequiredService<MyDatabaseSettings>();
|
||||
var filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir, agentSettings.RouterId, "route.json");
|
||||
return JsonSerializer.Deserialize<RoutingRecord[]>(File.ReadAllText(filePath));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Core.Templating;
|
||||
|
||||
namespace BotSharp.Core.Agents.Services;
|
||||
|
||||
|
|
@ -15,6 +16,8 @@ public partial class AgentService
|
|||
}
|
||||
|
||||
var agent = await GetAgent(id);
|
||||
var templateDict = new Dictionary<string, object>();
|
||||
PopulateState(templateDict);
|
||||
|
||||
// After agent is loaded
|
||||
foreach (var hook in hooks)
|
||||
|
|
@ -23,7 +26,7 @@ public partial class AgentService
|
|||
|
||||
if (!string.IsNullOrEmpty(agent.Instruction))
|
||||
{
|
||||
hook.OnInstructionLoaded(agent.Instruction, new Dictionary<string, object>());
|
||||
hook.OnInstructionLoaded(agent.Instruction, templateDict);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(agent.Functions))
|
||||
|
|
@ -41,8 +44,22 @@ public partial class AgentService
|
|||
hook.OnAgentLoaded(agent);
|
||||
}
|
||||
|
||||
// render liquid template
|
||||
var render = _services.GetRequiredService<TemplateRender>();
|
||||
render.Render(agent, templateDict);
|
||||
|
||||
_logger.LogInformation($"Loaded agent {agent}.");
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
private void PopulateState(Dictionary<string, object> dict)
|
||||
{
|
||||
var stateService = _services.GetRequiredService<IConversationStateService>();
|
||||
var state = stateService.Load();
|
||||
foreach (var t in state)
|
||||
{
|
||||
dict[t.Key] = t.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
<VersionPrefix>0.9.0</VersionPrefix>
|
||||
<VersionPrefix>0.9.4</VersionPrefix>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Globals">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using BotSharp.Abstraction.Functions;
|
||||
using BotSharp.Core.Functions;
|
||||
using BotSharp.Core.Hooks;
|
||||
using BotSharp.Core.Templating;
|
||||
using BotSharp.Core.Plugins.Knowledges.Services;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
|
@ -36,10 +38,18 @@ public static class BotSharpServiceCollectionExtensions
|
|||
|
||||
RegisterPlugins(services, config);
|
||||
|
||||
// Register template render
|
||||
services.AddSingleton<TemplateRender>();
|
||||
|
||||
// Register router
|
||||
services.AddScoped<IAgentRouting, AgentRouter>();
|
||||
|
||||
// Register function callback
|
||||
services.AddScoped<IFunctionCallback, RouteToAgentFn>();
|
||||
|
||||
// Register Hooks
|
||||
services.AddScoped<IAgentHook, AgentHook>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,9 +29,17 @@ public partial class ConversationService
|
|||
await hook.OnFunctionExecuting(msg);
|
||||
}
|
||||
|
||||
// Execute function
|
||||
await fn.Execute(msg);
|
||||
|
||||
try
|
||||
{
|
||||
// Execute function
|
||||
await fn.Execute(msg);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
msg.ExecutionResult = ex.Message;
|
||||
_logger.LogError(msg.ExecutionResult);
|
||||
}
|
||||
|
||||
// After functions have been executed
|
||||
foreach (var hook in hooks)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ namespace BotSharp.Core.Conversations.Services;
|
|||
|
||||
public partial class ConversationService
|
||||
{
|
||||
const int maxRecursiveDepth = 3;
|
||||
int currentRecursiveDepth = 0;
|
||||
|
||||
private async Task<bool> GetChatCompletionsAsyncRecursively(IChatCompletion chatCompletion,
|
||||
string conversationId,
|
||||
Agent agent,
|
||||
List<RoleDialogModel> wholeDialogs,
|
||||
int maxRecursiveDepth,
|
||||
Func<RoleDialogModel, Task> onMessageReceived,
|
||||
Func<RoleDialogModel, Task> onFunctionExecuting,
|
||||
Func<RoleDialogModel, Task> onFunctionExecuted)
|
||||
|
|
@ -83,6 +83,7 @@ public partial class ConversationService
|
|||
conversationId,
|
||||
agent,
|
||||
wholeDialogs,
|
||||
maxRecursiveDepth,
|
||||
onMessageReceived,
|
||||
onFunctionExecuting,
|
||||
onFunctionExecuted);
|
||||
|
|
|
|||
|
|
@ -65,11 +65,14 @@ public partial class ConversationService
|
|||
await hook.BeforeCompletion();
|
||||
}
|
||||
|
||||
var agentSettings = _services.GetRequiredService<AgentSettings>();
|
||||
|
||||
var chatCompletion = GetChatCompletion();
|
||||
var result = await GetChatCompletionsAsyncRecursively(chatCompletion,
|
||||
conversationId,
|
||||
agent,
|
||||
wholeDialogs,
|
||||
agentSettings.MaxRecursiveDepth,
|
||||
onMessageReceived,
|
||||
onFunctionExecuting,
|
||||
onFunctionExecuted);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,12 @@ public class RouteToAgentFn : IFunctionCallback
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!HasMissingRequiredField(message, out var agentId))
|
||||
var missingfield = HasMissingRequiredField(message, out var agentId);
|
||||
if (missingfield && message.CurrentAgentId != agentId)
|
||||
{
|
||||
message.CurrentAgentId = agentId;
|
||||
}
|
||||
else
|
||||
{
|
||||
message.CurrentAgentId = agentId;
|
||||
message.ExecutionResult = $"Routed to {args.AgentName}";
|
||||
|
|
@ -45,23 +50,23 @@ public class RouteToAgentFn : IFunctionCallback
|
|||
private bool HasMissingRequiredField(RoleDialogModel message, out string agentId)
|
||||
{
|
||||
var args = JsonSerializer.Deserialize<RoutingArgs>(message.FunctionArgs);
|
||||
var router = _services.GetRequiredService<IAgentRouting>();
|
||||
var records = router.GetRoutingRecords();
|
||||
var routingRule = records.FirstOrDefault(x => x.Name.ToLower() == args.AgentName.ToLower());
|
||||
|
||||
var routes = GetRoutingTable();
|
||||
var agent = routes.FirstOrDefault(x => x.AgentName.ToLower() == args.AgentName.ToLower());
|
||||
|
||||
if (agent == null)
|
||||
if (routingRule == null)
|
||||
{
|
||||
agentId = message.CurrentAgentId;
|
||||
message.ExecutionResult = $"Can't find agent {args.AgentName}";
|
||||
return true;
|
||||
}
|
||||
|
||||
agentId = agent.AgentId;
|
||||
agentId = routingRule.AgentId;
|
||||
|
||||
// Check required fields
|
||||
var jo = JsonSerializer.Deserialize<object>(message.FunctionArgs);
|
||||
bool hasMissingField = false;
|
||||
foreach (var field in agent.RequiredFields)
|
||||
foreach (var field in routingRule.RequiredFields)
|
||||
{
|
||||
if (jo is JsonElement root)
|
||||
{
|
||||
|
|
@ -71,17 +76,21 @@ public class RouteToAgentFn : IFunctionCallback
|
|||
hasMissingField = true;
|
||||
break;
|
||||
}
|
||||
else if (root.EnumerateObject().Any(x => x.Name == field) &&
|
||||
string.IsNullOrEmpty(root.EnumerateObject().FirstOrDefault(x => x.Name == field).Value.ToString()))
|
||||
{
|
||||
message.ExecutionResult = $"missing {field}.";
|
||||
hasMissingField = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasMissingField && !string.IsNullOrEmpty(routingRule.RedirectTo))
|
||||
{
|
||||
agentId = routingRule.RedirectTo;
|
||||
}
|
||||
|
||||
return hasMissingField;
|
||||
}
|
||||
|
||||
private RoutingTable[] GetRoutingTable()
|
||||
{
|
||||
var agentSettings = _services.GetRequiredService<AgentSettings>();
|
||||
var dbSettings = _services.GetRequiredService<MyDatabaseSettings>();
|
||||
var filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir, agentSettings.RouterId, "route.json");
|
||||
return JsonSerializer.Deserialize<RoutingTable[]>(File.ReadAllText(filePath));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
src/Infrastructure/BotSharp.Core/Hooks/AgentHook.cs
Normal file
16
src/Infrastructure/BotSharp.Core/Hooks/AgentHook.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
namespace BotSharp.Core.Hooks;
|
||||
|
||||
public class AgentHook : AgentHookBase
|
||||
{
|
||||
public AgentHook(IServiceProvider services, AgentSettings settings)
|
||||
: base(services, settings)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool OnInstructionLoaded(string template, Dictionary<string, object> dict)
|
||||
{
|
||||
var router = _services.GetRequiredService<IAgentRouting>();
|
||||
dict["routing_records"] = router.GetRoutingRecords();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Templating;
|
||||
using Fluid;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace BotSharp.Core.Templating;
|
||||
|
||||
public class TemplateRender : ITemplateRender
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger _logger;
|
||||
private static readonly FluidParser _parser = new FluidParser();
|
||||
private TemplateOptions _options;
|
||||
|
||||
public TemplateRender(IServiceProvider services, ILogger<TemplateRender> logger)
|
||||
{
|
||||
_services = services;
|
||||
_logger = logger;
|
||||
_options = new TemplateOptions();
|
||||
_options.MemberAccessStrategy.MemberNameStrategy = MemberNameStrategies.CamelCase;
|
||||
_options.MemberAccessStrategy.Register<RoutingRecord>();
|
||||
}
|
||||
|
||||
public bool Render(Agent agent, Dictionary<string, object> dict)
|
||||
{
|
||||
var template = agent.Instruction;
|
||||
if (_parser.TryParse(template, out var t, out var error))
|
||||
{
|
||||
var context = new TemplateContext(dict, _options);
|
||||
agent.Instruction = t.Render(context);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue