Normandy

View Source

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

  • 🧠 Agent system β€” conversational agents with memory, state, and turn-based history.
  • πŸ“‹ Schema DSL β€” typed, validated structs with JSON Schema generation (nested schemas, anyOf/oneOf/allOf, conditional if/then/else, virtual fields, introspection).
  • πŸ”§ Tool calling β€” LLM tool/function calling with automatic execution loops.
  • 🀝 Multi-agent coordination β€” reactive patterns (race/all/some), agent pools, and supervised agent processes.
  • 🌐 Distributed sessions β€” single-node to multi-node, fault-tolerant sessions across Tiers 0/1/2 (in-memory/ETS, Postgres, Mnesia, Redis) with eager resume on node loss.
  • πŸ›‘οΈ Guardrails β€” input/output admission control with fail-open/closed policies and semantic scope checks.
  • 🌊 Streaming β€” real-time response streaming with callback-based event processing.
  • πŸ’° Prompt caching β€” up to 90% cost reduction via automatic Anthropic prompt caching.
  • πŸ”„ Resilience β€” built-in retry (exponential backoff + jitter) and circuit breaker patterns.
  • πŸ“¦ Batch processing β€” concurrent processing of many inputs with progress tracking.
  • πŸ“ Context management β€” token counting, automatic truncation, and LLM-based summarization.
  • πŸ”Œ Protocol support β€” interoperate via Model Context Protocol (MCP) and Agent-to-Agent (A2A).
  • πŸ“Š Observability β€” Telemetry events, structured lifecycle logging, and OpenTelemetry-compatible spans.
  • 🎯 Type safety β€” comprehensive type system with Dialyzer support, backed by 900+ tests including property-based testing.

Installation

Add normandy to your dependencies in mix.exs:

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

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)
client = %Normandy.LLM.ClaudioAdapter{
  api_key: System.get_env("ANTHROPIC_API_KEY"),
  options: %{enable_caching: true}
}

# 2. Initialize an agent
agent =
  Normandy.Agents.BaseAgent.init(%{
    client: client,
    model: "claude-sonnet-4-6",
    temperature: 0.7
  })

# 3. Run a turn
{agent, response} =
  Normandy.Agents.BaseAgent.run(agent, %{
    chat_message: "Explain Elixir's actor model in one sentence."
  })

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:

  • πŸ“š API documentation β€” every module, grouped by Core, Agents, DSL, Coordination, Tools, Context Management, Resilience, Guardrails, MCP, A2A, and LLM Adapters.
  • 🌐 Distributed sessions guide β€” Tiers 0/1/2, durable stores, and multi-node setup.
  • πŸ“ CHANGELOG β€” release notes and migration guidance.
  • πŸ—ΊοΈ ROADMAP β€” phased development history and direction.

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

Development

mix test            # run the default suite (integration tests skipped)
mix test --cover    # run with coverage
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 πŸ’œ