Skip to content

Tutorial 2 — Creating a Multi-Agent System

Packages: mas-ctl (run-mas, validate), mas-lab (telemetry/plots) Deployment: deployments/local-inproc.yaml with runtime_id: mas-runtime-py Time: ~30 min

Goal

You compose a multi-agent system (MAS) from a kind: MAS manifest and run it. The example is a trip planner: a moderator delegates to three specialists in the fictional "Arborian Network".

What you need

  • Tutorial 0 (config and infra) and Tutorial 1 (agent manifests, overlays).
  • A terminal in docs/tutorials/02-creating-a-mas.
  • An LLM API key for Steps 5 and 6. Validate, compile, telemetry, and plots run offline.

mas-ctl run-mas uses the same default_infra / infra_refs as mas-ctl chat.

user ──▶ moderator ──delegates_to──▶ schedule_agent   (schedules + attractions)
                                 ├─▶ itinerary_agent  (route plan)
                                 └─▶ concierge_agent  (fares + costs)

Step 1 — Read the specialists

Each specialist is a kind: Agent manifest (Tutorial 1) with its own tools and skills. The files in agents/ also pin the model (gpt-4o), temperature, and token limits.

# agents/schedule-agent/agent.yaml (abridged)
metadata:
  name: schedule_agent
spec:
  description: "Transport schedule and attractions specialist."
  context:
    role: |
      You can look up train and airplane schedules and attractions inside cities. ...
  tools:
    - ref: ../../tools/lookup_schedule.tool.yaml
  skills:
    - transport-schedule-lookup
Agent Tools Skill
schedule_agent lookup_schedule transport-schedule-lookup
itinerary_agent query_graph_database route-planning
concierge_agent get_fares, calc fare-and-itinerary-assembly

Full manifests: schedule, itinerary, concierge. Tools live in tools/ (Python plus a *.tool.yaml descriptor). Skills are SKILL.md files in skills/ that the agent loads on demand (Tutorial 6).

Step 2 — Read the moderator

The moderator is the entry point. It has no domain tools. The runtime injects delegation tools instead.

# agents/moderator/agent.yaml (abridged)
spec:
  context:
    role: |
      You are a moderator coordinating a team of specialized agents. ...
      Call only one agent at a time. Make sure you use ALL agents. ...
  skills:
    - trip-orchestration

Full manifest: agents/moderator/agent.yaml.

Step 3 — Read the MAS manifest

spec.agency.agents lists agent identities (id plus ref). spec.workflow holds the topology. Each agent keeps its own tools and skills, so it can be validated and reused without the MAS.

# mas.yaml (abridged)
kind: MAS
metadata: {name: trip-planner, version: "0.1.0"}
spec:
  agency:
    agents:
      - {id: moderator,       ref: agents/moderator/agent.yaml}
      - {id: schedule_agent,  ref: agents/schedule-agent/agent.yaml}
      # itinerary_agent, concierge_agent likewise
  workflow:
    entry: moderator
    nodes:
      - id: moderator
        delegates_to: [ schedule_agent, itinerary_agent, concierge_agent ]
      - id: schedule_agent
      # ...

Full manifest: mas.yaml. Overlays: single-agent, linear.

The schema has no workflow.type. The moderator's LLM chooses which allowed specialist to call, and in what order. Each delegation is a tool call, delegate_to_<agent_id>, with a task argument, sent over the in-process bus (CommBus).

Topology overlays

An overlay changes participants and topology without touching agent files.

Topology Configuration Who decides order?
LLM-directed Entry node lists delegates_to The entry agent's LLM
Single-agent One participant, one workflow node No delegation
Deterministic linear Entry design_pattern: deterministic_linear plus ordered delegates_to The design-pattern plugin
# overlays/single-agent.yaml (patch only; see overlays/linear.yaml for the linear one)
spec:
  target: {kind: MAS}
  patch:
    agents:
      $op:
        replace:
          - {id: generalist, ref: agents/generalist/agent.yaml}
    workflow: {entry: generalist, nodes: [{id: generalist}]}

overlays/linear.yaml keeps the roster and adds this to the patch:

    agents:
      moderator:
        design_pattern: {type: deterministic_linear}

An experiment scenario selects an overlay by name (file name without .yaml, equal to its metadata.name). You write one in Tutorial 3:

scenarios:
  - id: linear
    overlays: {logic: [linear], control: [], infra: []}

Step 4 — Check the flavour

A flavour is a named runtime environment (Tutorial 1). This tutorial uses the bundled local flavour from mas-library-standard (mas-ctl flavour list). It runs all agents in-process (agent_comm.protocol: agent-local).

config.yaml here selects it (mas_ctl.flavour: local) and sets mas_ctl.trace: summary, so run-mas prints each delegation on stderr. The LLM endpoint comes from your Tutorial 0 infra.

Step 5 — Run the MAS

Use a city from datasets/arborian-network.yaml (Celestia, Verdantia, and thirteen others). An unknown name such as Thornhaven returns found: false with error: unknown_city rather than a zero-hop route. Repeat runs can return immediately from the LLM response cache; see the note after the commands.

# Default topology (LLM-directed): the moderator delegates dynamically
mas-ctl run-mas mas.yaml -q "Plan a 3-day trip from Celestia to Verdantia, budget $500"

# Interactive multi-turn
mas-ctl run-mas mas.yaml -i

# Single-agent topology: one generalist, no delegation
mas-ctl run-mas mas.yaml -o overlays/single-agent.yaml -q "Plan a 3-day trip from Celestia to Verdantia, budget $500"

# Deterministic-linear: specialists run in the declared order
mas-ctl run-mas mas.yaml -o overlays/linear.yaml -q "Plan a 3-day trip from Celestia to Verdantia, budget $500"

To inspect the resolved MAS, compile it (use --layout bundle for one file):

mas-ctl compile mas.yaml -o overlays/linear.yaml -O ./compiled-linear/

In the default topology the moderator typically calls schedule_agent, itinerary_agent, concierge_agent, then assembles the itinerary with prices. Order and wording are the LLM's choice and vary between runs. If a specialist reports no path or an unknown city, that failure belongs in the final answer.

Repeat runs can return from the LLM response cache. Pass -v to log a cache hit. Disable with --no-cache-read or MAS_LLM_CACHE_READ=false. See Tutorial 0 and llm-cache.md.

Step 6 — Read the event stream

mas-ctl run-mas records events.jsonl, one JSON event per line, at traces/events.jsonl by default. --events --events-file PATH chooses another path:

mas-ctl run-mas mas.yaml --events --events-file logs/events.jsonl \
  -q "Plan a 3-day trip from Celestia to Verdantia, budget $500"

Delegation shows up as tool_call_* events on delegate_to_<agent_id>, with the specialist's own tool calls in between. Real lines carry more fields, such as call_id, session_id, and timestamp:

{"kind": "tool_call_start", "agent_id": "moderator", "tool_name": "delegate_to_schedule_agent", "arguments": {"task": "…"}, …}
{"kind": "tool_call_start", "agent_id": "schedule_agent", "tool_name": "lookup_schedule", …}
{"kind": "tool_call_end", "agent_id": "schedule_agent", "tool_name": "lookup_schedule", …}
{"kind": "tool_call_end", "agent_id": "moderator", "tool_name": "delegate_to_schedule_agent", …}

Inspect the trace:

mas-lab telemetry show logs/events.jsonl     # event count, agents, kinds
mas-lab plot multilevel-trajectory logs/events.jsonl --format html -o output/trajectory.html

telemetry show prints the event count, the agents, and a breakdown by event kind. plot writes an HTML trajectory with delegation arrows. Without an API key, run both on fixtures/events.jsonl, a pre-recorded single-agent trace (15 events, no delegation). Steps 5 and 6 need OPENAI_API_KEY (or a cache replay from Tutorial 0). Without it, run-mas stops with a "Live LLM configured but API key unset" error.

Going further

  • A2A: point an agent id at a remote peer with --bind. The MAS manifest does not change. Tutorial 5 covers serving and mixed topologies.

To expose a specialist as a server instead, run mas-ctl serve docs/tutorials/02-creating-a-mas/agents/schedule-agent/agent.yaml --infra-ref library-samples/infra/mixed-agents.infra.yaml from the repo root.

mas-ctl run-mas mas.yaml --bind schedule_agent=a2a://127.0.0.1:9006/ -q "Plan a 3-day trip from Celestia to Verdantia, budget $500"
  • Design-space lab: labs/design-space.lab/ extends the trip planner with more patterns, topologies, and datasets.
  • CI: demo/scenario.yaml lists each step's commands and expected exit codes. Run pytest tests/tutorials/test_scenario_commands.py -v -k tuto-02. Live steps need TUTORIAL_ONLINE=1 and credentials.

What you learned

  • A kind: MAS manifest separates agent identity (agency) from topology (workflow). Each agent keeps its own tools and skills.
  • Overlays change participants and delegation without touching agent files. mas-ctl compile shows the result.
  • events.jsonl records delegation as delegate_to_<id> tool calls.

See also the glossary and the user guide.

Next

Tutorial 3 — Experiments, Analysis and Evaluation runs benchmarks and compares topologies.