# Run with: mix run examples/multi_agent_session.exs
#
# Demonstrates ExAgent.Session (Roadmap Phase 3): two TestModel agents
# coordinated by a round-robin policy over a shared "world" state. The Session is
# the single writer; each agent's turn proposes a change that the next agent
# sees. Offline — no API key needed.
alias ExAgent.{Event, Models.Test, PubSub, Server, Session}
alias ExAgent.Session.Participant
# Each "bot" is a stateful ExAgent.Server. On its turn, the driver asks it to
# describe an action; the model reply becomes a state change proposed through
# the SharedState handle. (Here the scripted reply is the action text itself.)
{:ok, bot_a} =
Server.start_link(
agent:
ExAgent.new(
model: %Test{script: ~w(attack defend cast)},
instructions: "You are a fighter."
),
agent_id: "bot_a"
)
{:ok, bot_b} =
Server.start_link(
agent:
ExAgent.new(model: %Test{script: ~w(dodge counter heal)}, instructions: "You are a rogue."),
agent_id: "bot_b"
)
{:ok, session} =
Session.start_link(
shared_state: %{round: 1, actions: []},
policy: :round_robin,
participants: [
Participant.new(id: "fighter", kind: :agent, ref: bot_a),
Participant.new(id: "rogue", kind: :agent, ref: bot_b)
],
session_id: "skirmish",
pubsub: :local
)
:ok = PubSub.subscribe({PubSub.Local, []}, Event.session_topic("skirmish"))
IO.puts("=== starting session ===")
{:ok, first} = Session.start(session)
IO.puts("first to act: #{first}")
# A tiny driver: for each participant, run its agent server once and record its
# reply as a shared-state change. Three rounds (so both agents act a few times).
Enum.each(1..4, fn _ ->
current = Session.current(session)
%{ref: server} = Enum.find(Session.participants(session), &(&1.id == current))
{:ok, %{output: action}} = Server.chat(server, "what do you do this turn?")
{:ok, world, next} =
Session.take_turn(session, current, fn s ->
{:ok, %{s | actions: [{current, action} | s.actions]}}
end)
IO.puts("[#{current}] #{action} → next: #{next}")
end)
IO.puts("\n=== final shared state ===")
final = Session.read_state(session)
IO.inspect(final, label: "world")
IO.puts("\n=== closing ===")
:ok = Session.close(session)
IO.puts("status: #{Session.status(session)}")