Sagents (Sagents v0.12.0)
Copy MarkdownSagents provides hierarchical agent capabilities with composable middleware.
Sagents extends LangChain with powerful features:
- Middleware System: Composable components for agent capabilities
- TODO Management: Task planning and progress tracking
- Virtual Filesystem: File operations for agent workflows
- Task Delegation: Hierarchical sub-agents for complex tasks
- Context Management: Automatic summarization and optimization
- Observability: Custom telemetry and tracing via middleware callbacks (see Observability Guide)
Quick Start
alias Sagents.{Agent, State}
alias LangChain.ChatModels.ChatAnthropic
# Create an agent
{:ok, agent} = Agent.new(%{
model: ChatAnthropic.new!(%{model: "claude-sonnet-4-6"}),
system_prompt: "You are a helpful assistant."
})
# Execute with a State
state = State.new!(%{messages: [%{role: "user", content: "Hello!"}]})
{:ok, result} = Agent.execute(agent, state)Middleware Composition
Sagents uses a middleware pattern for extensibility:
# Use default middleware (TODO, Filesystem, SubAgent, etc.)
{:ok, agent} = Agent.new(%{
model: model,
middleware: [MyCustomMiddleware]
})
# Customize default middleware
{:ok, agent} = Agent.new(%{
model: model,
filesystem_opts: [long_term_memory: true]
})
# Provide complete middleware stack
{:ok, agent} = Agent.new(%{
model: model,
replace_default_middleware: true,
middleware: [MyMiddleware1, MyMiddleware2]
})Creating Custom Middleware
defmodule MyMiddleware do
@behaviour Sagents.Middleware
@impl true
def init(opts) do
{:ok, %{enabled: Keyword.get(opts, :enabled, true)}}
end
@impl true
def system_prompt(_config) do
"Custom instructions for the agent."
end
@impl true
def tools(_config) do
[my_custom_tool()]
end
@impl true
def before_model(state, _config) do
# Preprocess state before LLM
{:ok, state}
end
@impl true
def after_model(state, _config) do
# Postprocess after LLM response
{:ok, state}
end
endState Management
Agent state flows through middleware and execution:
state = Sagents.State.new!(%{
messages: [%{role: "user", content: "Hello"}],
files: %{"/notes.txt" => "content"},
metadata: %{session_id: "123"}
})
{:ok, result_state} = Sagents.Agent.execute(agent, state)See Sagents.Agent for agent creation and execution, and Sagents.State for
state management functions.
Summary
Functions
@spec ready?() :: boolean()
Whether this node can currently host and route agent sessions.
Wire this into your application's readiness check (not its liveness
check). It reports false in exactly two situations, both normal:
- the node has not finished starting
Sagents.Supervisor, and Sagents.Supervisorhas shut down while the BEAM is still draining, which is every rolling deploy.
In that second window the node is still reachable and will still accept HTTP
connections, but every agent lookup on it fails. A readiness check that
ignores this keeps the load balancer routing requests to a node that cannot
serve them. See docs/deployment.md for the full sequence and a Fly.io
example.
Examples
# Phoenix router
get "/ready", MyAppWeb.HealthController, :ready
# Controller
def ready(conn, _params) do
if Sagents.ready?() do
send_resp(conn, 200, "ok")
else
send_resp(conn, 503, "draining")
end
end