Packages
nous
0.12.16
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.17
0.12.16
0.12.15
0.12.14
0.12.13
0.12.12
0.12.11
0.12.9
0.12.7
0.12.6
0.12.5
0.12.3
0.12.2
0.12.0
0.11.3
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.5.0
AI agent framework for Elixir with multi-provider LLM support
Current section
Files
Jump to
Current section
Files
lib/nous/eval/evaluator.ex
defmodule Nous.Eval.Evaluator do
@moduledoc """
Behaviour for evaluating agent outputs against expected results.
Evaluators determine whether an agent's output matches expectations and
provide a score indicating the quality of the match.
## Built-in Evaluators
- `Nous.Eval.Evaluators.ExactMatch` - Exact string match
- `Nous.Eval.Evaluators.FuzzyMatch` - Similarity-based match
- `Nous.Eval.Evaluators.Contains` - Check for substrings
- `Nous.Eval.Evaluators.ToolUsage` - Verify tool calls
- `Nous.Eval.Evaluators.Schema` - Validate structured output
- `Nous.Eval.Evaluators.LLMJudge` - LLM-based evaluation
## Custom Evaluators
defmodule MyEvaluator do
@behaviour Nous.Eval.Evaluator
@impl true
def evaluate(actual, expected, config) do
# Your evaluation logic
if my_check(actual, expected) do
%{score: 1.0, passed: true, reason: nil, details: %{}}
else
%{score: 0.0, passed: false, reason: "Did not match", details: %{}}
end
end
end
## Result Format
Evaluators must return a map with:
* `:score` - Float from 0.0 to 1.0
* `:passed` - Boolean indicating pass/fail
* `:reason` - String explaining failure (or nil)
* `:details` - Map with additional details
"""
@type score :: float()
@type result :: %{
score: score(),
passed: boolean(),
reason: String.t() | nil,
details: map()
}
@doc """
Evaluate an actual output against expected output.
## Parameters
* `actual` - The actual output from the agent
* `expected` - The expected output
* `config` - Configuration map for the evaluator
## Returns
A result map with score, passed status, and details.
"""
@callback evaluate(actual :: term(), expected :: term(), config :: map()) :: result()
@doc """
Optional: Name of the evaluator for display purposes.
"""
@callback name() :: String.t()
@optional_callbacks [name: 0]
@doc """
Get the evaluator module for an eval_type.
"""
@spec get_evaluator(atom()) :: module() | nil
def get_evaluator(:exact_match), do: Nous.Eval.Evaluators.ExactMatch
def get_evaluator(:fuzzy_match), do: Nous.Eval.Evaluators.FuzzyMatch
def get_evaluator(:contains), do: Nous.Eval.Evaluators.Contains
def get_evaluator(:tool_usage), do: Nous.Eval.Evaluators.ToolUsage
def get_evaluator(:schema), do: Nous.Eval.Evaluators.Schema
def get_evaluator(:llm_judge), do: Nous.Eval.Evaluators.LLMJudge
def get_evaluator(:custom), do: nil
def get_evaluator(_), do: nil
@doc """
Run evaluation using the appropriate evaluator.
"""
@spec run(atom(), term(), term(), map()) :: result()
def run(:custom, actual, expected, config) do
case Map.get(config, :evaluator) do
nil ->
%{
score: 0.0,
passed: false,
reason: "Custom evaluator not specified in config",
details: %{}
}
evaluator when is_atom(evaluator) ->
evaluator.evaluate(actual, expected, config)
end
end
def run(eval_type, actual, expected, config) do
case get_evaluator(eval_type) do
nil ->
%{
score: 0.0,
passed: false,
reason: "Unknown eval_type: #{inspect(eval_type)}",
details: %{}
}
evaluator when is_atom(evaluator) and not is_nil(evaluator) ->
apply(evaluator, :evaluate, [actual, expected, config])
end
end
@doc """
Create a passing result helper.
"""
@spec pass(map()) :: result()
def pass(details \\ %{}) do
%{score: 1.0, passed: true, reason: nil, details: details}
end
@doc """
Create a failing result helper.
"""
@spec fail(String.t(), map()) :: result()
def fail(reason, details \\ %{}) do
%{score: 0.0, passed: false, reason: reason, details: details}
end
@doc """
Create a partial match result helper.
"""
@spec partial(float(), String.t() | nil, map()) :: result()
def partial(score, reason \\ nil, details \\ %{}) do
%{score: score, passed: score >= 0.5, reason: reason, details: details}
end
end