Run agents

Copy Markdown

Setup

Paste this into IEx, with your Anthropic key:

Mix.install([{:wardrik_core, "~> 0.1.0-rc.2"}])
System.put_env("ANTHROPIC_API_KEY", "sk-ant-...")

children = [
  {WardrikCore.Backends.MemoryStore, name: :agents},
  {WardrikCore, backend: {WardrikCore.Backends.MemoryStore, name: :agents}}
]

{:ok, _} = Supervisor.start_link(children, strategy: :rest_for_one)

In an app, put the two children in your supervision tree.

Start an agent

{:ok, agent} = WardrikCore.start_agent(agent_id: "support", model: "anthropic:claude-haiku-4-5")
:ok = WardrikCore.ask(agent, "What's on fire?")

WardrikCore.history(agent)
#=> [#Message<:user text>, #Message<:assistant text>]
  • ask/3 returns once the message is stored. The agent answers in its own process.
  • Agent IDs are your app's. Wardrik doesn't read them.

Conversations

A conversation runs on the agent's options, with its own history:

{:ok, thread} =
  WardrikCore.start_agent(
    agent_id: "support",
    conversation: "C1",
    model: "anthropic:claude-haiku-4-5"
  )

WardrikCore.whereis("support")
WardrikCore.whereis({"support", "C1"})

"support" alone means the agent's main history, {"support", nil}.

Where agents are kept

BackendKeeps agentsNeeds
WardrikCore.Backends.MemoryStoreIn memory, until the node stopsNothing
EKVStoreOn disk, on each node{:ekv, "~> 0.4"}
ObjectStoreIn an S3-compatible bucketBucket and credentials
MirrorStoreIn two backends, while you migrateThe two backends

The last three are under DurableServer.Backends.

On disk:

children = [
  {EKV, name: :agents, data_dir: "/var/lib/my_app/agents", cluster_size: 1},
  {WardrikCore, backend: {DurableServer.Backends.EKVStore, name: :agents}}
]

In S3:

children = [
  {WardrikCore,
   backend:
     {DurableServer.Backends.ObjectStore,
      bucket: "my-app-agents",
      access_key_id: System.fetch_env!("AWS_ACCESS_KEY_ID"),
      secret_access_key: System.fetch_env!("AWS_SECRET_ACCESS_KEY"),
      s3_endpoint: "https://s3.amazonaws.com",
      default_region: "us-east-1"}}
]

To keep agents in Postgres, use WardrikStore.Durable from wardrik_store.

After a crash

  • An agent that crashes restarts and picks its message back up.
  • With a backend that keeps agents, so does one whose node died. A sweep finds it every :discovery_interval_ms (60_000 by default).
  • A turn resumes from its last checkpoint, without calling the model again.
  • A lost tool call runs again if its tool is read_only or idempotent. Otherwise it parks as :in_doubt:
{:tool_calls, [{call, {:park, :in_doubt}}]} = WardrikCore.parked(agent)

# Run it again
WardrikCore.resume(agent, %{call.id => :approve})

# Or tell the model it didn't run
WardrikCore.resume(agent, %{call.id => {:deny, "It may have run already."}})

Options

start_agent/2 takes :instructions, :capabilities, :middleware, :principal, :credentials, :keys, :events, :metadata, :timeout, :max_rounds, :retries and :prices. See WardrikCore.start_agent/2.

configure/2 changes them from the next turn:

:ok =
  WardrikCore.configure(agent,
    instructions: "Answer in one sentence.",
    prices: %{"anthropic:claude-haiku-4-5" => [input: 1.0, output: 5.0]}
  )

Control a running agent

FunctionDoes
ask/3Queues a message. A running turn ends at its next step
interrupt/1Stops the running or parked turn
kill/2Stops one running tool call. The model gets an error
resume/3Resumes a parked turn
status/1, history/1, parked/1What the agent is doing, has said, and waits on
stop/1Stops the agent until it's started again
delete/2Stops the agent and deletes it
list/1, whereis/2Every saved agent, and the running process for one
nodes/1, placements/1, move/3Cluster nodes, where each agent runs, and moving one
WardrikCore.status(agent)
#=> :idle

WardrikCore.list()
#=> [{"support", nil}, {"support", "C1"}]

:ok = WardrikCore.delete({"support", "C1"})