Normandy

Build production-ready AI agents in Elixir β€” structured schemas, tool calling, multi-agent coordination, streaming, and distributed sessions, with first-class Anthropic Claude support.

Hex.pm Documentation CI License: MIT

Normandy is an Elixir framework for building reliable LLM agents on the BEAM. It gives you type-safe input/output schemas with automatic JSON Schema generation, tool/function calling, conversational memory, and OTP-native primitives for multi-agent coordination and distributed, fault-tolerant sessions. It ships with a built-in adapter for Anthropic Claude and a clean protocol for any other LLM provider.

Features

Installation

Add normandy to your dependencies in mix.exs:

def deps do
  [
    {:normandy, "~> 2.0"}
  ]
end

Upgrading from 1.x? See MIGRATING-2.0.md.

Then fetch dependencies:

mix deps.get

Quick Start

Define structured data, then run an agent against Anthropic Claude:

# 1. Configure an LLM client (built-in Anthropic Claude adapter; prompt caching is on
#    by default)
client = %Normandy.LLM.ClaudioAdapter{api_key: System.get_env("ANTHROPIC_API_KEY")}

# 2. Initialize an agent
agent =
  Normandy.Agents.BaseAgent.init(%{
    client: client,
    model: "claude-opus-5-5"
  })

# 3. Run a turn: {:ok, agent, response} with the updated agent (it holds the
#    conversation), or {:error, exception}, e.g. a %Normandy.LLMError{}
input = %{chat_message: "Explain Elixir's actor model in one sentence."}

case Normandy.Agents.BaseAgent.run(agent, input) do
  {:ok, _agent, response} -> IO.puts(response.chat_message)
  {:error, error} -> IO.puts("Turn failed: " <> Exception.message(error))
end

Defining a typed schema is just as direct:

defmodule User do
  use Normandy.Schema

  io_schema "User profile" do
    field(:name, :string, description: "Full name", required: true)
    field(:age, :integer, description: "Age", minimum: 0, maximum: 150)
  end
end

# Export as JSON Schema for LLM prompts / structured output
schema = User.get_json_schema()

Bring your own LLM by implementing the Normandy.Agents.Model protocol β€” Claude is supported out of the box, but nothing is hard-wired to it.

Documentation

Full API reference and guides are published on HexDocs:

Generate the docs locally with mix docs and open doc/index.html.

Development

mix precommit       # compile (warnings as errors), unused deps, format, credo, dialyzer, test
mix test            # run the default suite (integration tests skipped)
mix test --cover    # run with coverage
mix credo --strict  # lint
mix dialyzer        # static type analysis
mix format          # format code

Run the live-API integration suite with an Anthropic key:

export ANTHROPIC_API_KEY="your-api-key"
mix test --include integration --include normandy_integration

Contributing

Contributions are welcome β€” please open an issue or submit a pull request.

License

Released under the MIT License.

Acknowledgments

The schema system is inspired by Ecto's approach to data definition, validation, and changesets.


Made with Elixir πŸ’œ