Sagents.Factory behaviour (Sagents v0.16.2)
Copy MarkdownBehaviour for modules that build %Sagents.Agent{} instances from
validated configuration.
Contract
A factory implements one callback:
@callback create_agent(agent_id :: String.t(), config :: term()) ::
{:ok, Sagents.Agent.t(), session_opts :: keyword()}
| {:error, term()}agent_idis generated bySagents.Sessionfrom the conversation ID via the configuredagent_id_fun. It is system-supplied, opaque to the factory's domain logic.configis whatever the configuredSagents.FactoryRouterreturned as the third element of{:ok, factory_module, config}. The library treats it as opaque and forwards it verbatim. By convention,configis a typed struct (commonly anEcto.Schema embedded_schema) defined by a paired*Configmodule — see the host's generatedFactoryConfigfor the canonical shape.
Returns
{:ok, agent, session_opts}— successful build.session_optsis a keyword list with library-recognized keys::fresh_state_attrs— map seeded intoSagents.State.new!/1only on fresh state (ignored on resume).:supervisor_opts— keyword list merged into the supervisor child config (e.g.:message_preprocessor).- All other keys are app-internal; the library forwards but does not inspect them.
{:error, reason}— propagated up the session-start path.
Where it runs
Sagents.Session calls the factory in the process that starts the session.
Under the :horde distribution it also calls it again on the node that ends
up hosting the agent when that is a different node, or a later run of the
same one, so a moved agent is built by the code that runs it. That call
happens inside a supervisor, so a factory must build from its arguments alone
and not from the calling process: its process dictionary, self(), or
context the caller set. The same applies to the Sagents.FactoryRouter.
Example
defmodule MyApp.Agents.Factory do
@behaviour Sagents.Factory
alias Sagents.Agent
alias MyApp.Agents.FactoryConfig
@impl true
def create_agent(agent_id, %FactoryConfig{} = c) do
agent =
Agent.new!(%{
agent_id: agent_id,
scope: c.scope,
model: build_model(c),
base_system_prompt: base_system_prompt(c),
middleware: build_middleware(c),
tools: build_tools(c),
tool_context: c.tool_context
})
{:ok, agent, []}
end
end
Summary
Callbacks
@callback create_agent(agent_id :: String.t(), config :: term()) :: {:ok, Sagents.Agent.t(), session_opts :: keyword()} | {:error, term()}