v0.3.0 — Teams, Streaming & Middleware GitHub stars Hex.pm version

The agent engine
for Elixir.

Just the loop. Any provider. Pure OTP. Four tools, three dependencies, one behaviour per provider. Swap models in one line, run agents as supervised GenServers. Need something it doesn't have? The agent builds it.

iex
{:ok, result} = Alloy.run("Read mix.exs and tell me the version",
  provider: {Alloy.Provider.Anthropic, model: "claude-sonnet-4-6"},
  tools: [Alloy.Tool.Core.Read]
)

result.text
#=> "The version in mix.exs is 0.4.0"
Just the engine
No HTTP. No database. No opinions.
Any provider
Swap models in one line.
Pure OTP
Supervised agents. Real parallelism.
Open source
MIT licensed. No lock-in.

Minimal by design. Extensible by nature.

Three dependencies: jason, req, telemetry. That's it. No framework, no platform, no opinions. Need a new tool? The agent writes one. Need a new provider? It's ~200 lines.

Just the Loop

Send messages. Execute tools. Loop until done. Four core tools — read, write, edit, bash — and a system prompt. What you leave out matters more than what you put in.

OTP-Native

Agents run as supervised GenServers. Supervision trees restart crashed agents. Parallel tool execution is real concurrency, not async callbacks.

Multi-Agent Teams

Delegate, broadcast, handoff. Fault-isolated by default — one agent crashes, the others keep running. Shared context optional.

Streaming

Token-by-token streaming from any provider. SSE support built in. Works with the agent loop, GenServer, and REPL.

Middleware

Telemetry, logging, custom hooks. Middleware can halt the loop to enforce spend caps or security policies. Runs at every hook point.

Context Compaction

Automatic conversation summarization when approaching token limits. Agents keep working across long sessions without losing context.

One API. Any provider.

Same tools, same conversation. Only the provider line changes. Adding a new provider is ~200 lines implementing one behaviour.

my_app.exs
{:ok, result} = Alloy.run("Summarize the README",
  provider: {Alloy.Provider.Anthropic, model: "claude-sonnet-4-6"},
  tools: [Alloy.Tool.Core.Read, Alloy.Tool.Core.Bash],
  max_turns: 10
)
Anthropic · OpenAI · Google Gemini · Ollama · OpenRouter · xAI · DeepSeek · Mistral · and growing
GenServer Agents

Agents that survive crashes.

Every agent is a GenServer. Supervisors restart them on failure. Conversations persist across crashes. State is isolated. This is what OTP was built for.

{:ok, agent} = Alloy.Agent.Server.start_link(
  provider: {Alloy.Provider.Anthropic,
    model: "claude-sonnet-4-6"},
  tools: [Read, Write, Edit, Bash],
  system_prompt: "You are a senior Elixir developer."
)

# Stateful conversation
{:ok, r1} = Alloy.Agent.Server.chat(agent,
  "What does this project do?")
{:ok, r2} = Alloy.Agent.Server.chat(agent,
  "Now refactor the main module")
Multi-Agent Teams

Agents that work together.

Spin up teams of specialized agents under a supervisor. Delegate tasks, broadcast to all, or hand off between agents. Fault isolation means one failure doesn't take down the team.

{:ok, team} = Alloy.Team.start_link(
  agents: [
    researcher: [
      provider: {Google, model: "gemini-2.5-flash"},
      system_prompt: "You are a research assistant."
    ],
    coder: [
      provider: {Anthropic, model: "claude-sonnet-4-6"},
      tools: [Read, Write, Edit],
      system_prompt: "You are a senior developer."
    ]
  ]
)

{:ok, research} = Alloy.Team.delegate(
  team, :researcher, "Find the latest Elixir patterns")
{:ok, code} = Alloy.Team.delegate(
  team, :coder, "Implement those patterns")

Your tools, your rules.

Implement a behaviour. Hand it to the agent. Alloy handles parallel execution, error recovery, and provider-specific wire formatting. Ship with five built-in tools, or bring your own.

lib/my_app/tools/web_search.ex
defmodule MyApp.Tools.WebSearch do
  @behaviour Alloy.Tool

  @impl true
  def definition do
    %{
      name: "web_search",
      description: "Search the web for current information",
      input_schema: %{
        type: "object",
        properties: %{
          query: %{type: "string", description: "Search query"}
        },
        required: ["query"]
      }
    }
  end

  @impl true
  def execute(%{"query" => query}, _context) do
    results = MyApp.Search.run(query)
    {:ok, format_results(results)}
  end
end
read
Read files
write
Write files
edit
Search & replace
bash
Shell commands
scratchpad
Key-value notepad

Layers, not magic.

Every layer does one thing. No hidden state, no implicit wiring, no surprises. Read the source in an afternoon.

Your Application
Alloy.run/2 · Agent.Server · Team
Turn Loop
Call provider → execute tools → loop until done
Providers
Wire format
Tools
Parallel exec
Middleware
Hooks & halt
Context & Session
Compaction · discovery · tokens
Scheduler & PubSub
Cron · heartbeat · events

Is Alloy right for you?

Alloy is deliberately narrow. It does one thing well and stays out of your way. Here's how to know if it fits.

Alloy is for you if…

  • You want an agent loop you can read in an afternoon and understand completely. No magic, no hidden wiring.
  • You're building your own application and need an engine underneath it — not a framework that dictates your architecture.
  • You need to swap providers freely without rewriting tool definitions or conversation logic.
  • You want agents running as supervised OTP processes with real fault isolation and parallel execution.
  • You want to own your stack. You pick the database, the HTTP layer, the deployment. Alloy doesn't care.

You might prefer something else if…

  • You want a batteries-included framework with 14 dependencies, built-in persistence, and prescribed architecture. Try Jido or Sagents.
  • You only need structured output extraction, not a full agent loop. Try Instructor.
  • You need Ecto schema integration baked into every layer. LangChain does this natively.
  • You just need to call an LLM and don't need tools or an agent loop. ReqLLM is lighter.

Alloy is an engine, not a framework. Other agent frameworks ship 14 dependencies and tell you how to structure your application. Alloy ships 3 and gets out of the way. If an agent needs a capability it doesn't have, it writes the code itself. That's the whole idea.

Built in the open.

Alloy is MIT licensed and developed on GitHub. Report bugs, request features, or contribute a provider — every PR makes the engine better for everyone.

Get started in 30 seconds.

Add one line to mix.exs and you're ready.

mix.exs
def deps do
  [
    {:alloy, "~> 0.3.0"}
  ]
end
terminal
$ mix deps.get
$ mix alloy --provider anthropic --tools read,bash

Coming soon

Alloy is the engine. AnvilOS is the workshop.

Persistence, connectors, a dashboard, and a runtime for 24/7 agents. Built on Alloy. Built on Phoenix. Built on OTP.

Sign up for updates — or just star the repo and watch.