Skip to content

zeroasterisk/adk-elixir

Repository files navigation

ADK — Agent Development Kit for Elixir

Hex.pm Docs CI License

OTP-native AI agent framework inspired by Google ADK, built for the BEAM.

Why Elixir for AI Agents?

Python's ADK is great, but Elixir's runtime was built for this:

Challenge Python ADK ADK Elixir
Concurrent agents asyncio, threading Lightweight processes — millions of agents per node
Crash isolation try/except per agent Process boundaries — one agent crash can't take down others
Recovery Manual retry logic OTP supervisors — automatic restart with backoff strategies
Streaming Async generators Native Stream + Phoenix PubSub
State Shared dicts, locks Process isolation — no locks, no mutexes, no race conditions
Distribution Custom networking Built-in clustering — agents span nodes transparently

📚 Documentation

Full docs →

Installation

Add adk to your dependencies in mix.exs:

def deps do
  [
    {:adk, "~> 0.1.0"}
  ]
end

Quick Start

# Create an agent
agent = ADK.new("assistant",
  model: "gemini-flash-latest",
  instruction: "You are a helpful assistant."
)

# Chat (blocking — returns text)
ADK.chat(agent, "What is Elixir?")
#=> "Elixir is a dynamic, functional language designed for building scalable..."

# Run (returns events for full control)
events = ADK.run(agent, "Tell me about OTP")

Configure an LLM Backend

By default, ADK uses a mock LLM for testing. Configure a real backend:

# config/config.exs
config :adk, :llm_backend, ADK.LLM.Gemini
config :adk, :gemini_api_key, System.get_env("GEMINI_API_KEY")

Supported backends: ADK.LLM.Gemini, ADK.LLM.OpenAI, ADK.LLM.Anthropic.

Agents

LLM Agent

The core agent — sends messages to an LLM, handles tool calls, returns events:

agent = ADK.new("researcher",
  model: "gemini-flash-latest",
  instruction: "You research topics thoroughly.",
  tools: [&MyTools.search/1, &MyTools.summarize/1]
)

Sequential Agent

Chain agents — output of one feeds into the next:

pipeline = ADK.sequential([
  ADK.new("researcher", instruction: "Find relevant information."),
  ADK.new("writer", instruction: "Write a clear summary from the research.")
])

ADK.chat(pipeline, "Explain BEAM concurrency")

Parallel & Loop Agents

Run agents concurrently or iteratively:

# Run multiple agents at once, merge results
parallel = %ADK.Agent.ParallelAgent{
  name: "multi",
  agents: [researcher, fact_checker, editor]
}

# Loop until a condition is met
loop = %ADK.Agent.LoopAgent{
  name: "refiner",
  agent: editor,
  max_iterations: 3
}

Tools

Any function becomes a tool:

def get_weather(%{"city" => city}) do
  %{temp: 72, condition: "sunny", city: city}
end

agent = ADK.new("assistant", tools: [&get_weather/1])

For richer metadata, use the declarative macro:

defmodule MyTools.Calculator do
  use ADK.Tool.Declarative

  @tool name: "calculate",
        description: "Evaluate a math expression",
        parameters: %{
          "expression" => %{type: "string", description: "Math expression"}
        }
  def run(%{"expression" => expr}, _ctx) do
    {result, _} = Code.eval_string(expr)
    %{result: result}
  end
end

Sessions & Persistence

Sessions are GenServers with pluggable storage:

# In-memory (ETS), JSON files, or Ecto (any database)
{:ok, pid} = ADK.Session.start_link(
  app_name: "my_app",
  user_id: "user1",
  session_id: "sess1",
  store: {ADK.Session.Store.InMemory, []}
)
Store Backend Best for
InMemory ETS Testing, single-node
JsonFile JSON files Development
Ecto Any database Production

Phoenix Integration

Optional Phoenix helpers — no Phoenix dependency required:

# REST API with SSE streaming
plug ADK.Phoenix.Controller

# WebSocket real-time communication
socket "/agent", ADK.Phoenix.Channel

# Drop-in LiveView chat component
live "/chat", ADK.Phoenix.ChatLive

📖 See the Phoenix Integration Guide.

A2A Protocol

Full A2A protocol support for inter-agent communication:

# Expose as A2A server
plug ADK.A2A.Server, agent: my_agent, runner: runner

# Call remote agents
{:ok, result} = ADK.A2A.Client.send_task("http://remote:4000", "Research OTP")

# Use remote agents as tools
researcher = ADK.A2A.RemoteAgentTool.new(name: "researcher", url: "http://remote:4000")

Plugins & Policies

Extend agent behavior with plugins and safety policies:

# Plugin: automatic retry on LLM reflection
ADK.Plugin.Registry.register(ADK.Plugin.ReflectRetry)

# Policy: control what agents can do
config :adk, :policy, MyApp.SafetyPolicy

Observability

Built-in :telemetry events + optional OpenTelemetry:

# All agent/tool/LLM calls emit telemetry events
:telemetry.attach("my-handler", [:adk, :agent, :run, :stop], &MyHandler.handle/4, nil)

Mix Tasks

# Generate a new ADK project
mix adk.new my_agent

# Generate Ecto migrations for session persistence
mix adk.gen.migration

Architecture

ADK.Runner
├── ADK.Session (GenServer per session)
├── ADK.Context (immutable invocation context)
└── ADK.Agent (behaviour)
    ├── LlmAgent (LLM ↔ tool loop)
    ├── SequentialAgent (pipeline)
    ├── ParallelAgent (concurrent)
    └── LoopAgent (iterative)

Guides

License

Apache-2.0 — see LICENSE.

About

ADK (Agent Development Kit) for Elixir — OTP-native agent framework

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors