Add EvaluatingService.
This commit is contained in:
parent
a69cb49044
commit
2f3e71ef09
|
|
@ -10,13 +10,19 @@ public interface IAgentService
|
||||||
Task<List<Agent>> GetAgents();
|
Task<List<Agent>> GetAgents();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Load agent configurations and triggher hooks
|
/// Load agent configurations and trigghe hooks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id"></param>
|
/// <param name="id"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<Agent> LoadAgent(string id);
|
Task<Agent> LoadAgent(string id);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get agent detail without trigger any hook.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id"></param>
|
||||||
|
/// <returns>Original agent information</returns>
|
||||||
Task<Agent> GetAgent(string id);
|
Task<Agent> GetAgent(string id);
|
||||||
|
|
||||||
Task<bool> DeleteAgent(string id);
|
Task<bool> DeleteAgent(string id);
|
||||||
Task UpdateAgent(Agent agent, AgentField updateField);
|
Task UpdateAgent(Agent agent, AgentField updateField);
|
||||||
Task UpdateAgentFromFile(string id);
|
Task UpdateAgentFromFile(string id);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
using BotSharp.Abstraction.Routing.Models;
|
|
||||||
|
|
||||||
namespace BotSharp.Abstraction.Conversations.Models;
|
namespace BotSharp.Abstraction.Conversations.Models;
|
||||||
|
|
||||||
public class RoleDialogModel
|
public class RoleDialogModel
|
||||||
|
|
@ -10,29 +8,36 @@ public class RoleDialogModel
|
||||||
public string Role { get; set; }
|
public string Role { get; set; }
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
public string Content { get; set; }
|
public string Content { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
public string CurrentAgentId { get; set; }
|
public string CurrentAgentId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Function name if LLM response function call
|
/// Function name if LLM response function call
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
public string? FunctionName { get; set; }
|
public string? FunctionName { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
public string? FunctionArgs { get; set; }
|
public string? FunctionArgs { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Function execution result, this result will be seen by LLM.
|
/// Function execution result, this result will be seen by LLM.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
public string? ExecutionResult { get; set; }
|
public string? ExecutionResult { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Function execution structured data, this data won't pass to LLM.
|
/// Function execution structured data, this data won't pass to LLM.
|
||||||
/// It's ideal to render in rich content in UI.
|
/// It's ideal to render in rich content in UI.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
public object ExecutionData { get; set; }
|
public object ExecutionData { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stop conversation completion
|
/// Stop conversation completion
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.Always)]
|
||||||
public bool StopCompletion { get; set; }
|
public bool StopCompletion { get; set; }
|
||||||
|
|
||||||
public RoleDialogModel(string role, string text)
|
public RoleDialogModel(string role, string text)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
using BotSharp.Abstraction.Evaluations.Models;
|
||||||
|
|
||||||
|
namespace BotSharp.Abstraction.Evaluations;
|
||||||
|
|
||||||
|
public interface IEvaluatingService
|
||||||
|
{
|
||||||
|
Task<EvaluationResult> Evaluate(EvaluationRequest request);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace BotSharp.Abstraction.Evaluations.Models;
|
||||||
|
|
||||||
|
public class EvaluationRequest
|
||||||
|
{
|
||||||
|
public string AgentId { get; set; }
|
||||||
|
public string Task { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace BotSharp.Abstraction.Evaluations.Models;
|
||||||
|
|
||||||
|
public class EvaluationResult
|
||||||
|
{
|
||||||
|
public List<RoleDialogModel> Dialogs { get; set; }
|
||||||
|
public string TaskInstruction { get; set; }
|
||||||
|
public string SystemPrompt { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace BotSharp.Abstraction.Evaluations.Settings;
|
||||||
|
|
||||||
|
public class EvaluatorSetting
|
||||||
|
{
|
||||||
|
public string EvaluatorId { get; set; }
|
||||||
|
public string Provider { get; set; }
|
||||||
|
public string Model { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,9 @@ using BotSharp.Abstraction.Routing;
|
||||||
using BotSharp.Core.Routing.Hooks;
|
using BotSharp.Core.Routing.Hooks;
|
||||||
using BotSharp.Abstraction.Routing.Models;
|
using BotSharp.Abstraction.Routing.Models;
|
||||||
using BotSharp.Core.Plugins;
|
using BotSharp.Core.Plugins;
|
||||||
|
using BotSharp.Abstraction.Evaluations.Settings;
|
||||||
|
using BotSharp.Abstraction.Evaluations;
|
||||||
|
using BotSharp.Core.Evaluatings;
|
||||||
|
|
||||||
namespace BotSharp.Core;
|
namespace BotSharp.Core;
|
||||||
|
|
||||||
|
|
@ -68,6 +71,13 @@ public static class BotSharpServiceCollectionExtensions
|
||||||
|
|
||||||
services.AddScoped<IAgentHook, RoutingAgentHook>();
|
services.AddScoped<IAgentHook, RoutingAgentHook>();
|
||||||
|
|
||||||
|
// Evaluation
|
||||||
|
var evalSetting = new EvaluatorSetting();
|
||||||
|
config.Bind("Evaluator", evalSetting);
|
||||||
|
services.AddSingleton((IServiceProvider x) => evalSetting);
|
||||||
|
|
||||||
|
services.AddScoped<IEvaluatingService, EvaluatingService>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace BotSharp.Core.Evaluatings;
|
|
||||||
|
|
||||||
public class Evaluater
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
using BotSharp.Abstraction.Conversations.Models;
|
||||||
|
using BotSharp.Abstraction.Evaluations;
|
||||||
|
using BotSharp.Abstraction.Evaluations.Models;
|
||||||
|
using BotSharp.Abstraction.Evaluations.Settings;
|
||||||
|
using BotSharp.Abstraction.Templating;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace BotSharp.Core.Evaluatings;
|
||||||
|
|
||||||
|
public class EvaluatingService : IEvaluatingService
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _services;
|
||||||
|
private readonly EvaluatorSetting _settings;
|
||||||
|
public EvaluatingService(IServiceProvider services, EvaluatorSetting settings)
|
||||||
|
{
|
||||||
|
_services = services;
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<EvaluationResult> Evaluate(EvaluationRequest request)
|
||||||
|
{
|
||||||
|
var agentService = _services.GetRequiredService<IAgentService>();
|
||||||
|
var evaluator = await agentService.GetAgent(_settings.EvaluatorId);
|
||||||
|
var taskPrompt = evaluator.Templates.First(x => x.Name == $"task.{request.Task}").Content;
|
||||||
|
|
||||||
|
var render = _services.GetRequiredService<ITemplateRender>();
|
||||||
|
var prompt = render.Render(evaluator.Instruction, new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "task_prompt", taskPrompt}
|
||||||
|
});
|
||||||
|
|
||||||
|
var service = _services.GetRequiredService<IConversationService>();
|
||||||
|
var conv = await service.NewConversation(new Conversation
|
||||||
|
{
|
||||||
|
AgentId = request.AgentId
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = new EvaluationResult
|
||||||
|
{
|
||||||
|
TaskInstruction = taskPrompt,
|
||||||
|
SystemPrompt = evaluator.Instruction
|
||||||
|
};
|
||||||
|
|
||||||
|
var textCompletion = CompletionProvider.GetTextCompletion(_services);
|
||||||
|
RoleDialogModel response = default;
|
||||||
|
var dialogs = new List<RoleDialogModel>();
|
||||||
|
int roundCount = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// var text = string.Join("\r\n", dialogs.Select(x => $"{x.Role}: {x.Content}"));
|
||||||
|
// text = instruction + $"\r\n###\r\n{text}\r\n{AgentRole.User}: ";
|
||||||
|
var question = await textCompletion.GetCompletion(prompt);
|
||||||
|
dialogs.Add(new RoleDialogModel(AgentRole.User, question));
|
||||||
|
prompt += question.Trim();
|
||||||
|
|
||||||
|
response = await SendMessage(request.AgentId, conv.Id, question);
|
||||||
|
dialogs.Add(new RoleDialogModel(AgentRole.Assistant, response.Content));
|
||||||
|
prompt += $"\r\n{AgentRole.Assistant}: {response.Content.Trim()}";
|
||||||
|
prompt += $"\r\n{AgentRole.User}: ";
|
||||||
|
|
||||||
|
roundCount++;
|
||||||
|
|
||||||
|
if (response.FunctionName == "conversation_end" ||
|
||||||
|
response.FunctionName == "human_intervention_needed" ||
|
||||||
|
roundCount > 5)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Conversation ended by function {response.FunctionName}", Color.Green);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Dialogs = dialogs;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<RoleDialogModel> SendMessage(string agentId, string conversationId, string text)
|
||||||
|
{
|
||||||
|
var conv = _services.GetRequiredService<IConversationService>();
|
||||||
|
conv.SetConversationId(conversationId, new List<string>());
|
||||||
|
|
||||||
|
RoleDialogModel response = default;
|
||||||
|
|
||||||
|
await conv.SendMessage(agentId,
|
||||||
|
new RoleDialogModel("user", text),
|
||||||
|
async msg => response = msg,
|
||||||
|
fnExecuting => Task.CompletedTask,
|
||||||
|
fnExecuted => Task.CompletedTask);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -489,6 +489,9 @@ public class FileRepository : IBotSharpRepository
|
||||||
return responses;
|
return responses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !DEBUG
|
||||||
|
[MemoryCache(10 * 60)]
|
||||||
|
#endif
|
||||||
public Agent? GetAgent(string agentId)
|
public Agent? GetAgent(string agentId)
|
||||||
{
|
{
|
||||||
var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir);
|
var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir);
|
||||||
|
|
@ -766,8 +769,8 @@ public class FileRepository : IBotSharpRepository
|
||||||
{
|
{
|
||||||
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
|
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
|
||||||
var splits = fileName.ToLower().Split('.');
|
var splits = fileName.ToLower().Split('.');
|
||||||
var name = splits[0];
|
var name = string.Join('.', splits.Take(splits.Length - 1));
|
||||||
var extension = splits[1];
|
var extension = splits.Last();
|
||||||
if (extension.Equals(_agentSettings.TemplateFormat, StringComparison.OrdinalIgnoreCase))
|
if (extension.Equals(_agentSettings.TemplateFormat, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var content = File.ReadAllText(file);
|
var content = File.ReadAllText(file);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
using BotSharp.Abstraction.ApiAdapters;
|
||||||
|
using BotSharp.Abstraction.Evaluations;
|
||||||
|
using BotSharp.Abstraction.Evaluations.Models;
|
||||||
|
|
||||||
|
namespace BotSharp.OpenAPI.Controllers;
|
||||||
|
|
||||||
|
[Authorize]
|
||||||
|
[ApiController]
|
||||||
|
public class EvaluationController : ControllerBase, IApiAdapter
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _services;
|
||||||
|
public EvaluationController(IServiceProvider services)
|
||||||
|
{
|
||||||
|
_services = services;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("/evaluation")]
|
||||||
|
public async Task<EvaluationResult> RunTask([FromBody] EvaluationRequest request)
|
||||||
|
{
|
||||||
|
var eval = _services.GetRequiredService<IEvaluatingService>();
|
||||||
|
return await eval.Evaluate(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,14 +15,18 @@
|
||||||
|
|
||||||
"Router": {
|
"Router": {
|
||||||
"RouterId": "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a",
|
"RouterId": "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a",
|
||||||
"RouterName": "PizzaBot",
|
|
||||||
"Description": "Pizza restaurant AI Bot",
|
|
||||||
"UseTextCompletion": false,
|
"UseTextCompletion": false,
|
||||||
"EnableReasoning": false,
|
"EnableReasoning": false,
|
||||||
"Provider": "azure-openai",
|
"Provider": "azure-openai",
|
||||||
"Model": "gpt-3.5-turbo"
|
"Model": "gpt-3.5-turbo"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"Evaluator": {
|
||||||
|
"EvaluatorId": "dfd9b46d-d00c-40af-8a75-3fbdc2b89869",
|
||||||
|
"Provider": "azure-openai",
|
||||||
|
"Model": "gpt-3.5-turbo"
|
||||||
|
},
|
||||||
|
|
||||||
"Agent": {
|
"Agent": {
|
||||||
"DataDir": "agents",
|
"DataDir": "agents",
|
||||||
"TemplateFormat": "liquid",
|
"TemplateFormat": "liquid",
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
What is the next step based on the CONVERSATION? Or you can handle without asking specific agent.
|
What is the next step based on the CONVERSATION? Or you can handle without asking specific agent.
|
||||||
|
|
||||||
Response must be in JSON format
|
Response must be in JSON format
|
||||||
{% if enabled_reasoning -%}
|
{% if enabled_reasoning %}
|
||||||
{
|
{
|
||||||
"function":"route_to_agent"
|
"function":"route_to_agent"
|
||||||
}
|
}
|
||||||
{%- else -%}
|
{% else %}
|
||||||
{
|
{
|
||||||
"function":"route_to_agent",
|
"function":"route_to_agent",
|
||||||
"reason":"the reason why you select this function or agent",
|
"reason":"the reason why you select this function or agent",
|
||||||
|
|
@ -14,7 +14,7 @@ Response must be in JSON format
|
||||||
"user_goal_agent":"agent who can achieve user original goal",
|
"user_goal_agent":"agent who can achieve user original goal",
|
||||||
"args": {}
|
"args": {}
|
||||||
}
|
}
|
||||||
{%- endif %}
|
{% endif %}
|
||||||
|
|
||||||
If the user has no other tasks need help with, set function as conversation_end with reason and reply user courteously.
|
If the user has no other tasks need help with, set function as conversation_end with reason and reply user courteously.
|
||||||
If the user wants to reach out to real human being, set function as human_intervention_needed with reason and reply user courteously.
|
If the user wants to reach out to real human being, set function as human_intervention_needed with reason and reply user courteously.
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "EvaluationAgent",
|
||||||
|
"description": "Evaluate the performance of the LLM agents",
|
||||||
|
"createdDateTime": "2023-08-18T00:00:00Z",
|
||||||
|
"updatedDateTime": "2023-08-18T00:00:00Z",
|
||||||
|
"id": "dfd9b46d-d00c-40af-8a75-3fbdc2b89869"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
This is a model evaluation program, which interactive with model to complete a certain task based on the background information given to you.
|
||||||
|
|
||||||
|
{{ task_prompt }}
|
||||||
|
|
||||||
|
user: Hi!
|
||||||
|
assistant: Hello, How can I help you?
|
||||||
|
user:
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
Role: You're a customer who is going to buy a pizza.
|
||||||
|
* You like pepperoni flavor.
|
||||||
|
* You will pay the order in cash.
|
||||||
|
* Your address is 347 S Gladstone Ave, Aurora, IL 60506.
|
||||||
|
* Your phone number is +16308926431
|
||||||
|
|
||||||
|
Requirments:
|
||||||
|
* You want to know what kind of pizza do they have.
|
||||||
|
* You want to buy three piece of pizza.
|
||||||
|
* Say Bye if the order is placed and payment is completed.
|
||||||
Loading…
Reference in a new issue