BeamAgent

BeamAgent is a domain-neutral Elixir/OTP runtime for building tool-using AI agents. It provides the model/tool loop, configurable guardrails, deterministic verification, isolated runs, optional PostgreSQL history, and a local dashboard. Your application supplies the model client, tools, and business rules.

BeamAgent dashboard with one active run and two completed runs

The screenshot shows real demo executions: one active run, one successful run, and one verification failure. Token counts and prices are demo values. See how to reproduce it.

Elixir was chosen for the BEAM properties:

Installation

Add BeamAgent to your application's dependencies:

{:beam_agent, "~> 0.3.0"}

Run mix deps.get. PostgreSQL persistence additionally requires an explicit Postgrex dependency, connection setup, and schema migrations as described below. See the changelog for compatibility changes when upgrading.

Quick start

After adding BeamAgent to your application's dependencies, start iex -S mix. This example runs locally without provider credentials:

{:ok, run} =
  BeamAgent.API.run("Hello from my agent",
    llm: {BeamAgent.LLM.Mock, []},
    tools: %{echo: BeamAgent.Tools.Echo},
    guardrails: [max_iterations: 8, max_execution_time_ms: 30_000],
    verification: [required_tools: [:echo]]
  )

run.answer #=> "Done!"
run.run_key #=> opaque lookup key

Follow Build an agent for runnable synchronous and asynchronous examples, a checked tool, an application admission guard, and custom verifiers. The mock client is for local demonstrations; replace it with your implementation of BeamAgent.LLM.Client for a provider-backed agent.

{:ok, run} only when the run finished and verification passed; everything else — a guardrail tripped, an unknown tool, a hard timeout, a runner crash, or verification failing a plausible-looking answer — comes back as {:error, run} with the reason on run. Normal runner outcomes retain their trace; startup failures, crashes, and hard timeouts cannot recover the runner's accumulated trace.

To start without blocking for completion:

{:ok, handle} = BeamAgent.API.start("Summarize the report", llm: {MyApp.LLMClient, []})
status = BeamAgent.API.lookup_active(handle.run_key)
result = BeamAgent.API.await(handle)

start/2 returns after the runner is registered. The supervised run keeps its deadline even if await/1 is delayed. The handle must be awaited once by the process that started it. lookup_active/1 is local to this node and returns only a snapshot; a completed run returns :not_found. Every returned BeamAgent.Run has the same opaque run_key. With an optional trace store, lookup_completed/1 and list_completed/1 return sanitized completed-run evidence.

For a browser view of live and optionally retained completed runs, enable the local dashboard. It is disabled by default and shows no execution content.

Optional runtime controls are documented separately: retry policies, per-node rate limiting, and per-node circuit breakers. An opt-in structured Logger bridge reports only bounded operational fields. Remote placement runs on trusted BEAM nodes without automatically replaying lost work.

To plug in a real model, implement BeamAgent.LLM.Client. Its chat/2 callback can return a reply or tool call, optionally with provider-reported input/output token usage. Configure application prices to calculate per-run cost; see persistent run evidence. To add a tool, implement BeamAgent.Tools.Behaviour and pass it in the :tools map — tools aren't hardcoded into the harness, each run supplies its own.

Dashboard in a Phoenix application

After adding BeamAgent as a dependency, enable its dashboard in the host application's config/dev.exs:

config :beam_agent, :dashboard, port: 4080

Start or restart the Phoenix application with mix phx.server, then open http://127.0.0.1:4080/. BeamAgent starts the dashboard under its own supervisor. It runs on a separate HTTP port; it does not add a Phoenix router route or mount inside Phoenix LiveDashboard. Agent runs must execute in that application's BEAM node to appear in the active table.

Active runs need no database. For completed history, explicitly set up the optional PostgreSQL trace store:

  1. Apply the packaged SQL files priv/sql/001_run_evidence.sql and priv/sql/002_model_usage.sql, in order, to the application's database through its normal migration process. They create beam_agent_runs and beam_agent_trace_steps, then add usage and cost fields. Installed files can be located with Application.app_dir(:beam_agent, "priv/sql").
  2. Supervise a named Postgrex connection or pool in the host application.
  3. Configure the adapter, connection, and explicit retention period:
config :beam_agent, :trace_store,
  adapter: BeamAgent.Trace.Store.Postgres,
  retention_ms: :timer.hours(24),
  options: [connection: MyApp.BeamAgentPostgres]

The connection example and complete storage contract are in persistent run evidence. A Phoenix Ecto Repo alone does not configure this adapter; it expects a Postgrex connection or pool. Installing the package and starting the application do not run migrations. There is currently no BeamAgent migration generator. Applications own schema rollout, credentials, and retention; stored history contains sanitized fields.

The dashboard binds only to loopback and has no authentication. On a remote server, access it through a secured tunnel to that server's loopback address. The current implementation does not provide a public Phoenix-mounted dashboard. See dashboard details for refresh behavior and limitations.

Lifecycle and supervision

Local BeamAgent.API.run/2 starts one BeamAgent.Runner under BeamAgent.RunSupervisor and blocks the caller until a result is ready. BeamAgent.API.start/2 uses a supervised task to own the same lifecycle while the caller continues. Runners register by run_key in a local OTP Registry for active lookup. Runs are restart: :temporary — a finished, failed, or crashed run is a terminal result to hand back to the caller. Optional operation-level retries do not restart the whole run.

See the telemetry reference for the supported events and their measurement, metadata, and privacy contract.

Two independent time limits can race, by design:

Notable features

Development

mix deps.get    # fetch dependencies
mix compile      # compile
mix test          # run the full test suite
mix format          # format per .formatter.exs

See run configuration and multiple verifiers for application defaults, per-run overrides, validation, and ordered verification.