multi-agent-lab / docs /schema /scenario-config.md
agharsallah
feat: Implement well-known typed fields for verdicts in output models
ce159dc
|
Raw
History Blame Contribute Delete
4.46 kB

A newer version of the Gradio SDK is available: 6.20.0

Upgrade

Scenario Config Contract

A scenario is declarative data: a goal, a seed, and a cast of agent names. Defined by ScenarioConfig (src/core/config.py), loaded from config/scenarios/<name>.yaml, validated by validate_scenario().

Schema

name: mystery-roots            # unique slug (required)
title: "πŸ” Mystery Roots"      # display name for the UI (optional)
goal: >                        # shared objective, injected into every prompt
  Converge on the most interesting, evidence-supported explanation.
default_seed: "All the clocks stopped at 3:07."   # required
example_seeds:                 # gallery seeds for the UI
  - "All the clocks stopped at 3:07."
  - "The bridge appeared overnight."
cast:                          # agent names, resolved via the agent registry
  - clue-gatherer
  - hypothesis-former
  - devils-advocate
  - mystery-judge
genesis_text: "A mystery settles over the wood: {seed}"   # '{seed}' substituted
competition:                   # optional contest contract (ADR-0029); absent == none
  kind: judged                 # versus | judged | none
governor:                      # optional per-scenario budget (else defaults)
  max_turns: 2000
  max_calls_per_turn: 16
  max_total_calls: 20000

Fields

Field Meaning
name Unique slug; the registry key.
title UI display label; falls back to name.
goal The shared objective. Rendered as a SHARED GOAL block in every agent prompt and carried on the genesis run.started event (projection.goal). This is how a scenario "sets up the goal."
default_seed Seed used when none is supplied.
example_seeds Seed gallery for the UI dropdown.
cast Agent names that participate. Selecting who participates is editing this list. Each must exist in config/agents/.
genesis_text Template for the opening world.observed; {seed} is replaced.
competition Optional CompetitionConfig β€” does this scenario produce a winner, and how? See below.
governor Optional GovernorConfig; omit for engine defaults.

Competition: who can win, and who decides

A scenario declares whether it produces a winner with the optional competition: block (CompetitionConfig, ADR-0029). Absent block == kind: none β€” full sessions and history, but nobody wins.

competition:
  kind: versus | judged | none   # default none
  teams:                         # versus only
    spy: [spy-nil]
    herd: [spy-cara, spy-bex, spy-ovo]

The three kinds split who derives the winner:

kind ground truth? winner derived by shipped example
versus yes β€” the team map code β€” the scenario's handler scores the judge's accusation against teams the-steeped
judged no β€” judgment is the result the model β€” the judge's validated winner field mystery-roots
none n/a nobody β€” judges don't declare winner at all everything else

Validation rules (enforced in CompetitionConfig and WorldConfig, src/core/config.py β€” a bad block fails loudly at load, per ADR-0011):

  • teams is permitted only when kind: versus, and is required (non-empty) there.
  • Member lists must be non-empty and mutually disjoint β€” no double agents.
  • Every team member must appear in the scenario's cast.
  • No team label may equal an agent name. The winner payload key carries either an agent name or a team label; this rule keeps that union unambiguous.

The registry injects the competition context into the cast's agents at build time (the same seam as agent.manifest), which arms verdict validation in the base agent. The machine-readable verdict and run-summary keys this produces are documented in events.md.

Scheduling lives on the agents

A scenario does not declare a scheduling policy. Cadence is per-agent β€” each cast member's manifest carries subscribes_to (reactive) and schedule.tick_every (periodic). The conductor routes accordingly. (The legacy Scenario.schedule() method remains only as the Phase-0/1 fallback for agents without a manifest.)

Building one

from src.core.registry import default_registry
scenario = default_registry().build_scenario("mystery-roots")   # cast -> live agents

See also: agent-manifest.md, world-config.md.