Policy behaviour for agent governance — tool access control, content filtering, etc.
Policies are checked at key points in the agent pipeline:
authorize_tool/3— before a tool executes, decides allow/denyfilter_input/2— filters user input before it reaches the LLMfilter_output/2— filters agent output events before they're returned
Usage
defmodule MyPolicy do
@behaviour ADK.Policy
@impl true
def authorize_tool(%{name: "dangerous_tool"}, _args, _ctx), do: {:deny, "forbidden"}
def authorize_tool(_tool, _args, _ctx), do: :allow
@impl true
def filter_input(content, _ctx), do: {:cont, content}
@impl true
def filter_output(events, _ctx), do: events
end
# Pass policies to Runner.run/5
ADK.Runner.run(runner, user_id, session_id, message, policies: [MyPolicy])Composition
Multiple policies are composed as a chain of responsibility:
authorize_tool— first:denywins; all must allowfilter_input— chained sequentially; first{:halt, events}short-circuitsfilter_output— chained sequentially, each transforms the events
Summary
Callbacks
Authorize a tool call. Return :allow or {:deny, reason}.
Filter user input before the LLM sees it. Return {:cont, content} or {:halt, [ADK.Event.t()]}.
Filter output events before they're returned. Returns transformed events.
Functions
Run authorize_tool across a list of policies. First deny wins.
Run filter_input across a list of policies. First halt wins; otherwise content is threaded.
Run filter_output across a list of policies, threading events through each.
Types
@type tool_decision() :: :allow | {:deny, String.t()}
Callbacks
@callback authorize_tool(tool :: map(), args :: map(), ctx :: ADK.Context.t()) :: tool_decision()
Authorize a tool call. Return :allow or {:deny, reason}.
@callback filter_input(content :: map(), ctx :: ADK.Context.t()) :: {:cont, map()} | {:halt, [ADK.Event.t()]}
Filter user input before the LLM sees it. Return {:cont, content} or {:halt, [ADK.Event.t()]}.
@callback filter_output([ADK.Event.t()], ADK.Context.t()) :: [ADK.Event.t()]
Filter output events before they're returned. Returns transformed events.
Functions
@spec check_tool_authorization([module() | struct()], map(), map(), ADK.Context.t()) :: tool_decision()
Run authorize_tool across a list of policies. First deny wins.
Supports both module-based policies (atoms implementing the ADK.Policy behaviour)
and struct-based policies (e.g., %ADK.Policy.HumanApproval{}). Struct policies
must implement a check/4 function for per-instance configuration.
@spec run_input_filters([module()], map(), ADK.Context.t()) :: {:cont, map()} | {:halt, [ADK.Event.t()]}
Run filter_input across a list of policies. First halt wins; otherwise content is threaded.
@spec run_output_filters([module()], [ADK.Event.t()], ADK.Context.t()) :: [ ADK.Event.t() ]
Run filter_output across a list of policies, threading events through each.