A plugin that validates LLM responses and retries with reflection feedback when they don't meet quality criteria.
Configuration
# Basic — retries on error events only (default behaviour)
ADK.Plugin.register({ADK.Plugin.ReflectRetry, max_retries: 3})
# With custom validation — retries when validator returns {:error, reason}
ADK.Plugin.register({ADK.Plugin.ReflectRetry,
max_retries: 3,
validator: fn events ->
text = events |> Enum.map_join("\n", &ADK.Event.text/1)
if String.contains?(text, "I don't know"),
do: {:error, "Response was evasive — provide a concrete answer"},
else: :ok
end
})
# With custom reflection template
ADK.Plugin.register({ADK.Plugin.ReflectRetry,
max_retries: 2,
validator: &MyApp.validate_response/1,
reflection_template: "Attempt {attempt}/{max}: {reason}\n\nPlease revise your response."
})How it works
In after_run/3, this plugin:
- Checks for error events (events with non-nil
:errorfield) - If no errors and a
:validatorfunction is configured, calls it with the events - If validation fails (or errors found), builds a reflection message and re-runs the agent
- Repeats up to
:max_retriestimes - Returns whatever the last attempt produced if retries are exhausted
Validator function
The validator receives the list of events and must return:
:ok— response is acceptable{:error, reason}— response failed validation;reasonis included in reflection
Reflection template
The template string supports these placeholders:
{attempt}— current attempt number (1-based){max}— max retries configured{reason}— the error/validation failure reason
Default: "[Reflect & Retry — Attempt {attempt}/{max}] {reason}\n\nPlease try again, adjusting your approach."
Summary
Functions
Build reflection events from error events (legacy helper).
Check events for errors or validation failures.
Check if an event has an error.
Types
@type config() :: [ max_retries: pos_integer(), validator: validator() | nil, reflection_template: String.t() ]
@type state() :: %{ max_retries: pos_integer(), validator: validator() | nil, reflection_template: String.t(), retry_counts: %{required(String.t()) => non_neg_integer()} }
@type validator() :: ([ADK.Event.t()] -> :ok | {:error, String.t()})
Functions
@spec build_reflection_events([ADK.Event.t()], pos_integer()) :: [ADK.Event.t()]
Build reflection events from error events (legacy helper).
@spec check_events([ADK.Event.t()], state()) :: :ok | {:error, String.t()}
Check events for errors or validation failures.
@spec has_error?(ADK.Event.t()) :: boolean()
Check if an event has an error.