Sagents (Sagents v0.8.0-rc.3)

Copy Markdown

Sagents 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
alias LangChain.ChatModels.ChatAnthropic

# Create an agent
{:ok, agent} = Sagents.new(
  model: ChatAnthropic.new!(%{model: "claude-sonnet-4-6"}),
  system_prompt: "You are a helpful assistant."
)

# Execute with messages
{:ok, result} = Sagents.execute(agent, [
  %{role: "user", content: "Hello!"}
])

Middleware Composition

Sagents uses a middleware pattern for extensibility:

# Use default middleware (TODO, Filesystem, SubAgent, etc.)
{:ok, agent} = Sagents.new(
  model: model,
  middleware: [MyCustomMiddleware]
)

# Customize default middleware
{:ok, agent} = Sagents.new(
  model: model,
  filesystem_opts: [long_term_memory: true]
)

# Provide complete middleware stack
{:ok, agent} = Sagents.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
end

State Management

Agent state flows through middleware and execution:

state = State.new!(%{
  messages: [%{role: "user", content: "Hello"}],
  files: %{"/notes.txt" => "content"},
  metadata: %{session_id: "123"}
})

{:ok, result_state} = Sagents.execute(agent, state)

See Sagents.State for state management functions.

Summary

Functions

Execute an agent with the given state.

Execute an agent asynchronously.

Create a new Agent with default middleware stack.

Create a new Agent, raising on error.

Functions

execute(agent, state)

Execute an agent with the given state.

Examples

# Execute with State struct
state = State.new!(%{messages: [%{role: "user", content: "Hello"}]})
{:ok, result_state} = Sagents.execute(agent, state)

# Execute with message list (convenience)
{:ok, result_state} = Sagents.execute(agent, [
  %{role: "user", content: "Hello"}
])

execute_async(agent, state_or_messages)

Execute an agent asynchronously.

Returns a Task that can be awaited.

Examples

task = Sagents.execute_async(agent, messages)
{:ok, result_state} = Task.await(task)

new(opts \\ [])

Create a new Agent with default middleware stack.

This is a convenience function that delegates to Sagents.Agent.new/1 with sensible defaults for common use cases.

Options

All options from Sagents.Agent.new/1 are supported. Common options:

  • :model - LangChain ChatModel struct (required)
  • :system_prompt - Base system instructions
  • :tools - Additional tools beyond middleware
  • :middleware - Extra middleware (appended to defaults)
  • :replace_default_middleware - Use only provided middleware (default: false)
  • :name - Agent name for identification

Examples

# Basic agent
{:ok, agent} = Sagents.new(
  model: ChatAnthropic.new!(%{model: "claude-sonnet-4-6"}),
  system_prompt: "You are helpful."
)

# With custom tools
{:ok, agent} = Sagents.new(
  model: model,
  tools: [calculator_tool, search_tool]
)

# With custom middleware
{:ok, agent} = Sagents.new(
  model: model,
  middleware: [LoggingMiddleware, MetricsMiddleware]
)

new!(opts \\ [])

Create a new Agent, raising on error.

See new/1 for options.