diff --git a/src/Infrastructure/BotSharp.Abstraction/Rules/IRuleCriteriaEvaluator.cs b/src/Infrastructure/BotSharp.Abstraction/Rules/IRuleCriteriaEvaluator.cs
index 4f14ee622..e5f39aeb1 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Rules/IRuleCriteriaEvaluator.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Rules/IRuleCriteriaEvaluator.cs
@@ -18,6 +18,10 @@ public interface IRuleCriteriaEvaluator
/// Evaluate the criteria for a single agent's rule.
///
/// The agent whose rule is being considered
+ ///
+ /// The rule being considered. An agent can carry more than one rule on the same trigger, so the
+ /// evaluator is handed the one under evaluation rather than resolving it from the agent itself.
+ ///
/// The rule trigger
/// The per-request criteria context
///
@@ -25,5 +29,5 @@ public interface IRuleCriteriaEvaluator
/// or null when the evaluator could not produce an answer (missing script/template,
/// failed execution, error).
///
- Task EvaluateAsync(Agent agent, IRuleTrigger trigger, RuleCriteriaContext context);
+ Task EvaluateAsync(Agent agent, AgentRule agentRule, IRuleTrigger trigger, RuleCriteriaContext context);
}
diff --git a/src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs b/src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs
index 31a712944..618c2140e 100644
--- a/src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs
+++ b/src/Infrastructure/BotSharp.Core.Rules/Criteria/Code/CodeCriteriaEvaluator.cs
@@ -25,7 +25,7 @@ public CodeCriteriaEvaluator(
public string Type => BuiltInRuleCriteria.PythonScript;
- public async Task EvaluateAsync(Agent agent, IRuleTrigger trigger, RuleCriteriaContext context)
+ public async Task EvaluateAsync(Agent agent, AgentRule agentRule, IRuleTrigger trigger, RuleCriteriaContext context)
{
var settings = context.Options.GetData() ?? new();
var provider = settings.CodeProcessor ?? BuiltInCodeProcessor.PyInterpreter;
diff --git a/src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs b/src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs
index 839c740fa..6aaab0844 100644
--- a/src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs
+++ b/src/Infrastructure/BotSharp.Core.Rules/Criteria/Llm/LlmCriteriaEvaluator.cs
@@ -30,17 +30,16 @@ public LlmCriteriaEvaluator(
public string Type => BuiltInRuleCriteria.Llm;
- public async Task EvaluateAsync(Agent agent, IRuleTrigger trigger, RuleCriteriaContext context)
+ public async Task EvaluateAsync(Agent agent, AgentRule agentRule, IRuleTrigger trigger, RuleCriteriaContext context)
{
var settings = context.Options.GetData() ?? new();
- var rule = agent.Rules.FirstOrDefault(x => x.TriggerName.IsEqualTo(trigger.Name));
// The Rules agent hosts the criteria-check template by default.
var agentId = !string.IsNullOrWhiteSpace(settings.AgentId) ? settings.AgentId! : BuiltInAgentId.RulesInterpreter;
var templateName = !string.IsNullOrWhiteSpace(settings.TemplateName)
? settings.TemplateName! : (agentId == BuiltInAgentId.RulesInterpreter ? DefaultTemplateName : $"{trigger.Name}_criteria");
- var input = BuildInput(rule?.CriteriaConfig, settings);
+ var input = BuildInput(agentRule.CriteriaConfig, settings);
var msg = $"rule trigger ({trigger.Name}) llm criteria (agent {agentId}, template {templateName}).";
try
diff --git a/src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs b/src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs
index 697fdaeab..f6b906bc8 100644
--- a/src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs
+++ b/src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs
@@ -44,33 +44,41 @@ public async Task> Triggered(IRuleTrigger trigger, string te
var filteredAgents = agents.Items.Where(x => x.Rules.Exists(r => r.TriggerName.IsEqualTo(trigger.Name) && !x.Disabled)).ToList();
foreach (var agent in filteredAgents)
{
- var rule = agent.Rules.FirstOrDefault(x => x.TriggerName.IsEqualTo(trigger.Name) && !x.Disabled);
- if (rule == null)
+ var rules = agent.Rules.Where(x => x.TriggerName.IsEqualTo(trigger.Name) && !x.Disabled).ToList();
+ if (rules.IsNullOrEmpty())
{
continue;
}
- // The rule's own mode wins over the mode carried on the trigger options, so an agent can
- // pick how its criteria is judged without the caller knowing.
- var evaluator = ResolveCriteriaEvaluator(rule.CriteriaConfig?.Mode) ?? criteriaEvaluator;
- if (evaluator != null && options?.Criteria != null)
+ foreach (var rule in rules)
{
- var criteriaContext = new RuleCriteriaContext
+ if (rule == null)
{
- Options = options.Criteria,
- States = states
- };
+ continue;
+ }
- var isTriggered = await EvaluateCriteria(evaluator, agent, trigger, criteriaContext);
- if (!isTriggered)
+ // The rule's own mode wins over the mode carried on the trigger options, so an agent can
+ // pick how its criteria is judged without the caller knowing.
+ var evaluator = ResolveCriteriaEvaluator(rule.CriteriaConfig?.Mode) ?? criteriaEvaluator;
+ if (evaluator != null && options?.Criteria != null)
{
- continue;
+ var criteriaContext = new RuleCriteriaContext
+ {
+ Options = options.Criteria,
+ States = states
+ };
+
+ var isTriggered = await EvaluateCriteria(evaluator, agent, rule, trigger, criteriaContext);
+ if (!isTriggered)
+ {
+ continue;
+ }
}
- }
- var msg = !string.IsNullOrWhiteSpace(rule.Message) ? rule.Message : text;
- var convId = await SendMessageToAgent(agent, trigger, text, msg, states);
- newConversationIds.Add(convId);
+ var msg = !string.IsNullOrWhiteSpace(rule.Message) ? rule.Message : text;
+ var convId = await SendMessageToAgent(agent, trigger, text, msg, states);
+ newConversationIds.Add(convId);
+ }
}
return newConversationIds;
@@ -96,10 +104,11 @@ public async Task> Triggered(IRuleTrigger trigger, string te
private async Task EvaluateCriteria(
IRuleCriteriaEvaluator evaluator,
Agent agent,
+ AgentRule agentRule,
IRuleTrigger trigger,
RuleCriteriaContext context)
{
- var isTriggered = await evaluator.EvaluateAsync(agent, trigger, context);
+ var isTriggered = await evaluator.EvaluateAsync(agent, agentRule, trigger, context);
if (isTriggered != null)
{
return isTriggered.Value;
@@ -118,7 +127,7 @@ private async Task EvaluateCriteria(
}
_logger.LogInformation($"Rule criteria evaluator ({evaluator.Type}) returned no result, falling back to llm for agent ({agent.Name}) and trigger ({trigger.Name}).");
- return await llmEvaluator.EvaluateAsync(agent, trigger, context) ?? false;
+ return await llmEvaluator.EvaluateAsync(agent, agentRule, trigger, context) ?? false;
}
#endregion