Deterministic evaluators
Overview
Section titled “Overview”Deterministic evaluators provide fast, code-based evaluation without LLM judges. They perform exact checks on outputs, trajectories, and environment state, making them a good fit for regression testing, CI/CD pipelines, and cases where evaluation criteria are objective and well-defined.
Key features
Section titled “Key features”- No LLM Required: Pure code-based evaluation, fast and free
- Deterministic Results: Same input always produces the same score
- Multiple Check Types: Output matching, tool call verification, and state comparison
- Async Support: All evaluators support both sync and async evaluation
Available evaluators
Section titled “Available evaluators”Output evaluators
Section titled “Output evaluators”Equals
Section titled “Equals”Checks if actual_output equals an expected value.
from strands_evals.evaluators import Equals
# Compare against explicit valueevaluator = Equals(value="Paris")
# Or compare against case's expected_output (when value is None)evaluator = Equals()Parameters:
value(optional): The expected value. IfNone, usesexpected_outputfrom the evaluation case.
Contains
Section titled “Contains”Checks if actual_output contains a substring.
from strands_evals.evaluators import Contains
evaluator = Contains(value="Paris", case_sensitive=False)Parameters:
value(required): The substring to search for.case_sensitive(optional, defaultTrue): Whether the check is case-sensitive.
StartsWith
Section titled “StartsWith”Checks if actual_output starts with a prefix.
from strands_evals.evaluators import StartsWith
evaluator = StartsWith(value="The capital", case_sensitive=False)Parameters:
value(required): The prefix to check.case_sensitive(optional, defaultTrue): Whether the check is case-sensitive.
Trajectory evaluators
Section titled “Trajectory evaluators”ToolCalled
Section titled “ToolCalled”Checks if a specific tool was called in the trajectory. Works with both list-based trajectories and Session objects.
from strands_evals.evaluators import ToolCalled
evaluator = ToolCalled(tool_name="calculator")Parameters:
tool_name(required): Name of the tool to check for.
SkillInvoked
Section titled “SkillInvoked”Checks if a specific skill was invoked in the trajectory. A refused load does not
count as invoked, since the agent never received the skill. Skill signals are
recognized for the Strands AgentSkills plugin and several other harnesses (see
the skill evaluators for the full list).
from strands_evals.evaluators import SkillInvoked
evaluator = SkillInvoked(skill_name="pdf-processing")Parameters:
skill_name(required): Name of the skill to check for.
Scores 1.0 when a successful load of the named skill is found, else 0.0. The
reason distinguishes a skill that was never requested from one that was requested
but whose load failed.
Environment state evaluators
Section titled “Environment state evaluators”StateEquals
Section titled “StateEquals”Checks if a named environment state matches an expected value. Useful for verifying that tool-using agents produce the correct side effects.
from strands_evals.evaluators import StateEquals
# Compare against explicit valueevaluator = StateEquals(name="temperature", value=72.0)
# Or compare against case's expected_environment_stateevaluator = StateEquals(name="temperature")Parameters:
name(required): Name of the environment state to check.value(optional): Expected value. IfNone, usesexpected_environment_statefrom the evaluation case.
Usage example
Section titled “Usage example”import asyncio
from strands import Agentfrom strands_evals import Case, Experimentfrom strands_evals.evaluators import Equals, Contains, ToolCalled
cases = [ Case( name="capital-check", input="What is the capital of France?", expected_output="Paris" )]
# Combine multiple deterministic checksevaluators = [ Contains(value="Paris", case_sensitive=False), Contains(value="France", case_sensitive=False),]
def get_response(case: Case) -> str: agent = Agent(callback_handler=None) return str(agent(case.input))
experiment = Experiment(cases=cases, evaluators=evaluators)
async def main(): report = await experiment.run_evaluations_async(get_response) report.run_display()
asyncio.run(main())Combining with LLM evaluators
Section titled “Combining with LLM evaluators”Deterministic evaluators pair well with LLM-based evaluators, combining fast exact checks with nuanced quality judgment:
from strands_evals.evaluators import Contains, HelpfulnessEvaluator, CorrectnessEvaluator
evaluators = [ Contains(value="Paris"), # Fast deterministic check CorrectnessEvaluator(), # LLM-based correctness HelpfulnessEvaluator(), # LLM-based helpfulness]Best practices
Section titled “Best practices”- Use for regression testing: Deterministic evaluators are ideal for CI/CD since they’re fast and don’t require API calls
- Combine with LLM evaluators: Use deterministic checks as a first pass, then LLM evaluators for nuanced assessment
- Case sensitivity: Use
case_sensitive=Falsewhen exact casing doesn’t matter - State verification: Use
StateEqualswhen your agent modifies external state through tools
Related evaluators
Section titled “Related evaluators”- OutputEvaluator: LLM-based output evaluation with custom rubrics
- TrajectoryEvaluator: LLM-based trajectory evaluation
- CustomEvaluator: Build your own evaluation logic