GenServer that manages a single LLM agent.
Holds the provider struct in state and dispatches calls via the
ExAgent.Provider behaviour. Contains the tool execution loop
that automatically invokes tools when the LLM requests them.
State
The agent state includes:
provider- any struct implementingExAgent.Providercontext- conversation history (ExAgent.Context)tools- available tools for function-callingskills- loadable skill definitionsactive_skill- currently active skill (if any)
Summary
Functions
Sends a user message to the agent and returns the response.
Sends a user message asynchronously, returning a Task.
Sends a user message and returns a lazy Stream of the assistant's text chunks.
Returns a specification to start this module under a supervisor.
Returns the current conversation context.
Dynamically loads a skill into the agent.
Resets the agent's conversation context.
Starts an agent process linked to the current process.
Types
@type agent_opts() :: [ id: String.t(), provider: struct(), tools: [ExAgent.Tool.t()], skills: [ExAgent.Skill.t()], name: GenServer.name() ]
@type state() :: %{ id: String.t(), provider: struct(), context: ExAgent.Context.t(), tools: [ExAgent.Tool.t()], skills: [ExAgent.Skill.t()], active_skill: ExAgent.Skill.t() | nil, built_in_tools: [atom()], status: :idle | :processing | :handed_off, reply_to: GenServer.from() | nil, task_ref: reference() | nil }
Functions
@spec chat(GenServer.server(), String.t(), keyword()) :: {:ok, ExAgent.Message.t()} | {:handoff, pid() | atom(), ExAgent.Context.t()} | {:error, term()}
Sends a user message to the agent and returns the response.
The agent appends the message to its context, evaluates skills, calls the LLM, and executes any requested tools in a loop until a final response or handoff is returned.
Options
:files- list of file attachments, each a map with:mime_typeand either:data(binary) or:path(file path). Files become part of the conversation context and are sent to the LLM alongside the text message.:built_in_tools- list of provider-specific built-in tools to enable for this message. Overrides the agent-levelbuilt_in_toolsif provided. Examples:[:google_search],[:web_search],[:thinking].
Examples
ExAgent.Agent.chat(agent, "Describe this image",
files: [%{path: "photo.jpg", mime_type: "image/jpeg"}])
ExAgent.Agent.chat(agent, "Search the web for Elixir news",
built_in_tools: [:google_search])
@spec chat_async(GenServer.server(), String.t(), keyword()) :: Task.t()
Sends a user message asynchronously, returning a Task.
Accepts the same options as chat/3.
@spec chat_stream(GenServer.server(), String.t(), keyword()) :: Enumerable.t()
Sends a user message and returns a lazy Stream of the assistant's text chunks.
Tool-call turns (if any tools are configured) are resolved first without streaming; only the final assistant turn is streamed. When no tools are configured the response is streamed directly.
The conversation context is committed when the stream is fully consumed, so
the returned stream must be consumed (e.g. with Enum/Stream functions)
for the agent to return to idle. Raises ExAgent.StreamError if the agent is
busy with another request.
Examples
agent
|> ExAgent.Agent.chat_stream("Explain OTP step by step")
|> Enum.each(&IO.write/1)
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec get_context(GenServer.server()) :: ExAgent.Context.t()
Returns the current conversation context.
@spec load_skill(GenServer.server(), ExAgent.Skill.t()) :: :ok
Dynamically loads a skill into the agent.
@spec reset(GenServer.server()) :: :ok
Resets the agent's conversation context.
@spec start_link(agent_opts()) :: GenServer.on_start()
Starts an agent process linked to the current process.