Composable middleware that observes/transforms an agent run at well-defined
points. This is ExAgent's "capabilities" spine, modelled the Elixir way: a
behaviour whose callbacks default to no-ops via use ExAgent.Capability, so
a capability overrides only the hooks it cares about.
A capability is any module. It is passed as the first argument to its own callbacks (so it can carry config as module state or read its own attributes).
Hooks (all optional)
before_model_request(cap, state)— return (possibly modified)state. Setstate.request_messagesto alter what's sent to the model without touching the canonical history (e.g. keep a sliding window, redact PII).after_model_request(cap, state)— observe/modify state after the model responded (state already includes the new response + merged usage).before_tool_execute(cap, ctx, tool_call)— observe/replace a tool call.after_tool_execute(cap, ctx, tool_call, result)— observe a tool result.
Example
defmodule MyApp.RedactPII do
use ExAgent.Capability
@impl true
def before_model_request(_cap, state) do
redacted = redact(state.messages)
%{state | request_messages: redacted}
end
end
ExAgent.new(model: "openai:gpt-4o", capabilities: [MyApp.RedactPII])
Summary
Types
@type state() :: map()