YAML configuration
Experiments are described in a single YAML file, which aigora reads to set up the market, agents, and their instructions. This page documents the complete configuration schema, including required fields, defaults, and validation rules. This page is necessarily long, but you don't need to read it all at once. Use it as a reference when you want to understand a specific field or feature, and follow the first experiment tutorial to see how the fields work together in practice.
| Field | Required | Default | Description |
|---|---|---|---|
variables |
No | Not declared; interpolation disabled | Named inputs to reuse within the YAML file or override for a run. |
experiment |
Yes | — | The experiment's identity, timing, assets, markets, and agent groups. |
A full example configuration
The following example is a complete YAML configuration for a simple market with one buyer and one seller. Use it as a template for your own experiments, and see the first experiment tutorial for a walkthrough of its features.
variables:
model: gpt-5.4-mini
reasoning_effort: none
seed: 42
buyer_value: 100
seller_cost: 40
experiment:
name: first_experiment
seed: ${variables.seed}
output_dir: results
protocol:
period_duration_seconds: 30
assets:
- id: asset
name: Good
markets:
- id: asset
price_min: '0'
price_max: '150'
agents:
- id: buyer
lm:
provider: openai
model: ${variables.model}
temperature: null
reasoning_effort: ${variables.reasoning_effort}
max_tokens: 4096
prompt:
instructions:
content: |
You are a buyer. You can buy one unit in this round.
Your private value is ${variables.buyer_value}.
Your earnings are your private value minus the purchase price.
If you do not trade, your earnings are zero.
Maximise your earnings. Do not buy above your value. Do not sell.
context_providers:
- type: snapshot_period_timing
- type: snapshot_order_book
- type: snapshot_own_orders
- type: snapshot_own_holdings
- type: snapshot_last_action_outcome
initial_holdings:
cash: '150'
units: {asset: 0}
trading_constraints:
allowed_sides: [bid]
max_units: {asset: 1}
max_order_quantity: 1
max_outstanding_orders: 1
- id: seller
lm:
provider: openai
model: ${variables.model}
temperature: null
reasoning_effort: ${variables.reasoning_effort}
max_tokens: 4096
prompt:
instructions:
content: |
You are a seller. You can sell one unit in this round.
Your private cost is ${variables.seller_cost}.
Your earnings are the sale price minus your private cost.
If you do not trade, your earnings are zero.
Maximise your earnings. Do not sell below your cost. Do not buy.
context_providers:
- type: snapshot_period_timing
- type: snapshot_order_book
- type: snapshot_own_orders
- type: snapshot_own_holdings
- type: snapshot_last_action_outcome
initial_holdings:
cash: '0'
units: {asset: 1}
trading_constraints:
allowed_sides: [ask]
max_order_quantity: 1
max_outstanding_orders: 1
variables
Variables are optional named inputs that can be reused in the YAML file, to avoid repeating the same value in multiple places, or to allow the caller to dynamically override them for a run. The syntax is defined by OmegaConf, which aigora uses to load and validate the YAML file.
Declaring a variable
Declare variables at the top level, alongside experiment.
Reference a declared input with ${variables.name} wherever its value is needed in the YAML document.
Names must be strings; values can be strings, numbers, booleans, null, lists, or mappings.
variables:
reasoning_effort: none
seed: 42
buyer_values: ["100", "80", "60"]
buyer_endowment:
cash: "150"
units: { asset: 0 }
Use variables to avoid repetition
In the example above, ${variables.model} is used for both agents' model names, and ${variables.buyer_value} is inserted into the buyer's instructions.
If you want to change the model or the buyer's value, you can do it in one place instead of editing multiple fields.
Validate before making model requests
Use aigora.load_config("experiment.yaml", variables={"seed": 7}) to resolve inputs and validate the configuration without starting a market.
Setting a variable at runtime
Use ??? instead of a value when the caller must supply an input:
Loading this document fails unless you supply variables={"seed": 7} or another valid seed.
Every ??? must be resolved, even if that variable is not referenced elsewhere in the document.
Use variables to dynamically define inputs
You can pass a mapping to aigora.run to define a variable at runtime.
For example, you can run the same experiment with different seeds or reasoning effort levels without changing the YAML file.
import aigora
run_dir = await aigora.run(
"experiment.yaml",
variables={"reasoning_effort": "medium", "seed": 7},
)
See Parameter sweeps for an example of running multiple combinations of variables in a loop.
Keep literal text
When interpolation is enabled, escape a literal ${ with a backslash inside a single-quoted YAML string:
The resolved text contains ${variables.seed} without the backslash.
Text loaded from a prompt file is always literal; only values inside the YAML document are interpolated.
experiment
| Field | Required | Default | Description |
|---|---|---|---|
name |
Yes | — | Nonblank experiment label written to the output records. |
description |
No | "" |
Researcher metadata describing the experiment. |
seed |
No | 42 |
Integer seed for local random state. |
output_dir |
Yes | — | Parent directory for unique run folders, relative to the YAML file. |
protocol |
Yes | — | Shared trading periods, timing, and lifecycle hooks. |
assets |
Yes | — | Nonempty list of assets that agents can hold. |
markets |
Yes | — | Nonempty list of markets for declared assets. |
agents |
Yes | — | Nonempty list of complete agent-group configurations. |
name
A nonblank string identifying the experiment in its output records. It is a label, not an asset or agent ID, so the identifier pattern described below does not apply.
description
Free-form researcher metadata, empty by default. It is saved with the configuration and is not automatically added to agent prompts.
seed
An integer controlling local random state, including the shared random-number generator available to hooks. Hosted model output and asynchronous action order need not repeat exactly when the same seed is used.
output_dir
A nonblank path to the parent directory where aigora creates a unique folder for each run. Relative paths are resolved from the directory containing the YAML file; absolute paths are also accepted. See Output files for the folder contents.
protocol
| Field | Required | Default | Description |
|---|---|---|---|
periods |
No | 1 |
Number of trading rounds; an integer of at least 1. |
period_duration_seconds |
Yes | — | Positive, finite duration of each trading round, in seconds. |
action_interval_seconds |
No | 0.0 |
Nonnegative, finite delay after each agent decision, in seconds. |
period_interval_seconds |
No | 0.0 |
Nonnegative, finite delay between rounds, in seconds. |
hooks |
No | {period_start: [], period_end: []} |
Ordered callbacks at the start and end of each period. |
A period is a timed trading round. The protocol applies to all agents and markets in the experiment.
periods
The number of rounds to run, defaulting to one. It must be an integer of at least 1.
period_duration_seconds
The wall-clock trading duration of each period, in seconds. It must be positive and finite; preparation, hooks, and between-period compaction do not consume this trading budget.
action_interval_seconds
The delay after each agent decision before it may decide again, while trading time remains. It defaults to zero and must be nonnegative and finite.
period_interval_seconds
The delay between trading periods, outside the trading clock. It defaults to zero and must be nonnegative and finite.
hooks
| Field | Required | Default | Description |
|---|---|---|---|
period_start |
No | [] |
Hooks before each period's opening snapshots and trading clock. |
period_end |
No | [] |
Hooks after successful trading, before closing snapshots and order cleanup. |
Both kinds of hook receive live agents and a shared, seeded random-number generator through context.rng.
Each list runs in the configured order.
If a hook fails, the experiment stops; earlier changes remain in place and partially applied hooks are not retried.
See the Python API for hook interfaces and examples.
period_start
| Field in each list item | Required | Default | Description |
|---|---|---|---|
type |
Yes | — | Dotted Python class path for a hook with on_period_start(context). |
params |
No | {} |
Keyword arguments passed to the hook constructor. |
Start hooks run before every period, including the first. Use them to reset endowments or update information before agents see the opening state.
type
A nonblank, importable class path, such as examples.tutorials.multiple_rounds.hooks.ResetEndowments.
The class must implement on_period_start(context).
params
A mapping of constructor keyword arguments, empty by default. The selected hook defines the accepted keys and their requirements; aigora does not impose a common parameter schema.
period_end
| Field in each list item | Required | Default | Description |
|---|---|---|---|
type |
Yes | — | Dotted Python class path for a hook with on_period_end(context). |
params |
No | {} |
Keyword arguments passed to the hook constructor. |
End hooks run after every successfully completed period, including the last, before closing snapshots and order cleanup. Use them for settlement such as paying dividends on held assets.
type
A nonblank, importable class path whose class implements on_period_end(context).
The hook API example shows how to implement a dividend payment.
params
A mapping of constructor keyword arguments, empty by default. Its accepted keys, required values, and defaults depend on the selected hook.
assets
| Field in each list item | Required | Default | Description |
|---|---|---|---|
id |
Yes | — | Unique identifier for an asset that agents can hold. |
name |
Yes | — | Nonblank display name used by built-in context providers. |
description |
No | "" |
Researcher metadata describing the asset. |
An asset is something an agent can hold; a market is where that asset can be traded. Declare at least one asset. An asset without a matching market can appear in holdings but cannot be traded through orders.
id
A unique asset key matching [a-z][a-z0-9_]*, such as asset or bond_1.
Use the same ID in markets, holdings, and position bounds.
name
The nonblank display label used by built-in context providers when presenting the asset to agents. It need not match the identifier.
description
Free-form researcher metadata, empty by default. It does not enter agent prompts automatically.
markets
| Field in each list item | Required | Default | Description |
|---|---|---|---|
id |
Yes | — | ID of the declared asset traded in this market. |
description |
No | "" |
Researcher metadata describing the market. |
price_min |
Yes | — | Finite decimal lower bound on accepted order prices. |
price_max |
Yes | — | Finite decimal upper bound, strictly greater than price_min. |
short_selling_allowed |
No | false |
Allow sales that create negative holdings, subject to agent position bounds. |
Declare at least one market, with at most one market per asset. The built-in continuous double auction matches crossing orders at the price of the order already resting in the book. For example, an ask at 70 followed by a bid at 80 trades at 70. Orders at the same price have priority in arrival order.
id
The ID of a declared asset, matching [a-z][a-z0-9_]* and unique among markets.
Agents use this ID to identify the market in their actions.
description
Free-form researcher metadata, empty by default. It is saved with the configuration and does not enter prompts automatically.
price_min
The lowest accepted order price, inclusive.
It must be finite and strictly less than price_max.
Prices use decimal arithmetic; quote decimal amounts in YAML, such as '0.10', to preserve the value you enter.
price_max
The highest accepted order price, inclusive.
It must be finite and strictly greater than price_min.
short_selling_allowed
Whether agents may submit asks that would leave them with negative holdings in this asset.
It defaults to false; setting it to true still leaves each agent's configured position bounds in force.
agents
| Field in each list item | Required | Default | Description |
|---|---|---|---|
id |
Yes | — | Unique agent-group identifier, used as the prefix of runtime agent IDs. |
n |
No | 1 |
Number of independent agents created from this entry. |
lm |
Yes | — | Model provider and request settings. |
prompt |
Yes | — | Participant instructions, observations, and optional conversation controls. |
initial_holdings |
Yes | — | Initial cash and asset positions for each agent. |
response |
No | {include_reason: true} |
Whether actions must include a reason. |
error_handling |
No | {request_timeout_seconds: null, max_retries: 3, failure_policy: abort} |
Request timeouts, retries, and terminal decision-failure behaviour. |
trading_constraints |
No | {allowed_sides: [bid, ask], min_units: {}, max_units: {}, min_order_quantity: 1, max_order_quantity: null, max_outstanding_orders: null} |
Limits applied when admitting orders. |
Each list item defines a group of participants with the same configuration. Every participant gets its own conversation and provider instances, including when YAML anchors share authoring inputs.
id
A unique group identifier matching [a-z][a-z0-9_]*.
An entry with id: buyer and n: 3 creates buyer_1, buyer_2, and buyer_3; even a group with n: 1 gets the _1 suffix.
n
The number of agents created from this group, defaulting to one. It must be an integer of at least 1.
lm
| Field | Required | Default | Description |
|---|---|---|---|
provider |
Yes | — | Model service: openai, anthropic, or google. |
model |
Yes | — | Nonblank model identifier for the selected provider. |
temperature |
Yes | — | Finite number, or explicit null to omit the parameter from requests. |
reasoning_effort |
Yes | — | none, low, medium, high, xhigh, or explicit null to omit the parameter. |
max_tokens |
Yes | — | Positive integer limiting model output tokens. |
extra_params |
No | {} |
Additional supported provider parameters. |
These settings belong to each agent group, so different groups may use different providers or models. YAML validation can succeed even when the selected provider later rejects a combination of model settings.
provider
Choose openai, anthropic, or google.
Credentials come from OPENAI_API_KEY, ANTHROPIC_API_KEY, or GEMINI_API_KEY, respectively, in the process running the experiment.
See Installation for setup; API keys do not belong in YAML.
model
A nonblank model identifier for the selected service.
The example uses gpt-5.4-mini, matching the tutorials; choose a model that supports your requested settings.
temperature
A finite number, or null to leave temperature out of the provider request.
This field is required even when its value is null; aigora does not supply a temperature default.
The acceptable numeric range and whether temperature is supported depend on the selected model.
reasoning_effort
One of none, low, medium, high, xhigh, or null.
The field must be present, and the selected model must support the requested level.
none and null differ
reasoning_effort: none explicitly requests that effort level.
reasoning_effort: null omits the parameter so the provider's default can apply.
max_tokens
A positive integer setting the model's output-token limit. It is required and has no aigora default.
extra_params
A mapping of additional parameters supported by the provider, empty by default. Its keys are provider-specific; it cannot replace the model settings, messages, conversation controls, retries, or timeouts that aigora manages.
prompt
| Field | Required | Default | Description |
|---|---|---|---|
instructions |
Yes | — | Prompt source defining the participant's objective and treatment instructions. |
system |
No | null |
Prompt source replacing the generated execution contract in full. |
context_providers |
Yes | — | Nonempty, ordered list of observations supplied before each decision. |
between_period_compaction |
No | null |
Prompt source for summarising active history between successful periods. |
For each decision, aigora sends the active conversation history and a fresh provider update, together with the system message. Once a response is parsed successfully, the exchange enters the conversation even if the market later rejects the action. A failed decision leaves the history unchanged.
instructions
| Field | Required | Default | Description |
|---|---|---|---|
content |
If file is not set |
null |
Nonblank participant instructions written directly in YAML. |
file |
If content is not set |
null |
Nonblank path to a UTF-8 file containing the participant instructions. |
Choose exactly one non-null source; neither an empty mapping nor two populated sources is valid. These instructions enter the agent's conversation history.
content
Inline, nonblank instructions describing the participant's objective and treatment.
Use a YAML block scalar (content: |) for multiple lines; variable references are resolved when variables is declared.
file
A nonblank path, resolved relative to the YAML file, such as prompts/buyer.txt.
The file must contain nonblank UTF-8 text; its contents are not interpolated.
system
| Field, when a source is supplied | Required | Default | Description |
|---|---|---|---|
content |
If file is not set |
null |
Nonblank replacement system message written in YAML. |
file |
If content is not set |
null |
Nonblank path to a UTF-8 replacement system message. |
Omit system, or set it to null, to use aigora's generated action-format and trading instructions.
Supplying a source replaces that entire message; choose exactly one non-null content or file.
The system message is kept separately from conversation history, and the parser and trading constraints still enforce the same rules.
content
Inline, nonblank text replacing the system message in full. Variable references are resolved when interpolation is enabled.
file
A nonblank path to nonblank UTF-8 system text, resolved relative to the YAML file.
The file's contents remain literal, including any ${...} expressions.
context_providers
| Field in each list item | Required | Default | Description |
|---|---|---|---|
type |
Yes | — | Built-in provider name or dotted Python class path. |
params |
No | {} |
Keyword arguments passed to the provider constructor. |
Supply at least one provider. Providers run in list order to build each agent's observation before a decision. See Built-in context providers for names, supported parameters, and examples of their text.
type
A nonblank built-in name, such as snapshot_order_book, or an importable class path implementing get_context(context).
See Customise what agents 'see' for a custom provider.
params
A mapping of constructor keyword arguments, empty by default. The selected provider defines its accepted keys and defaults; see its entry in the provider reference. You can use variables to supply whole lists or mappings here.
between_period_compaction
| Field, when a source is supplied | Required | Default | Description |
|---|---|---|---|
content |
If file is not set |
null |
Nonblank inline instructions for summarising the active history. |
file |
If content is not set |
null |
Nonblank path to a UTF-8 file containing summary instructions. |
Omit this field, or set it to null, to disable compaction.
Otherwise, choose exactly one non-null source to request a summary after each successfully completed period except the last.
The summary replaces active history for the next period, while the complete audit trace remains on disk.
Compaction uses the agent's model, retry settings, and timeout; if it fails, the run stops even when the decision failure policy is do_nothing.
content
Inline, nonblank instructions telling the model what to preserve in its summary. Variable references are resolved when interpolation is enabled.
file
A nonblank path to nonblank UTF-8 summary instructions, resolved relative to the YAML file. The loaded text remains literal.
initial_holdings
| Field | Required | Default | Description |
|---|---|---|---|
cash |
Yes | — | Finite decimal amount of settled cash at the start. |
units |
Yes | — | Mapping of asset IDs to integer positions; {} is valid. |
These are the initial accounts of every participant created from this agent group. Trading constraints govern subsequent order admission; they do not adjust these initial balances or rebalance accounts changed by hooks.
cash
A finite decimal cash amount; zero is valid and there is no default.
Quote decimal amounts such as '150.00' to preserve their exact value in YAML.
units
A required mapping from declared asset IDs to integer quantities, such as {asset: 1}.
An empty mapping is valid, and omitted assets have zero units.
Integer positions may be negative; market and agent constraints determine which subsequent orders are admitted.
response
| Field | Required | Default | Description |
|---|---|---|---|
include_reason |
No | true |
Require a string reason in each action; false forbids it. |
Agents return one JSON action per decision: place_bid, place_ask, cancel_order, or do_nothing.
include_reason
When true, every action must contain a string reason.
When false, the parser forbids that field rather than merely making it optional.
error_handling
| Field | Required | Default | Description |
|---|---|---|---|
request_timeout_seconds |
No | null |
Positive, finite per-request timeout; null sets no aigora request timeout. |
max_retries |
No | 3 |
Nonnegative integer counting additional attempts after the initial request. |
failure_policy |
No | abort |
Stop the run on a terminal decision failure, or record a failed no-op with do_nothing. |
These settings apply to model decisions and are also used for compaction requests, except that a terminal compaction failure always stops the run.
request_timeout_seconds
A positive, finite timeout for each individual model request attempt, or null for no aigora request timeout.
The end of a trading period also cancels outstanding decision requests.
max_retries
The number of additional attempts after the first; the default of 3 permits up to four attempts.
Temporary provider failures, truncated output, or invalid action responses may trigger retries.
Nonretryable failures, such as authentication or configuration errors, stop the attempts earlier.
A parsed order rejected by the market does not trigger a model retry.
failure_policy
Choose abort to stop the run when a decision fails, or do_nothing to record a failed no-op and continue.
With do_nothing, the failed exchange does not advance the agent's conversation or observation baseline.
This differs from a market rejection, such as an order rejected for insufficient cash, which is recorded as a trading outcome.
See Output files for recorded actions and failures.
trading_constraints
| Field | Required | Default | Description |
|---|---|---|---|
allowed_sides |
No | [bid, ask] |
Nonempty set of permitted order sides. |
min_units |
No | {} |
Mapping of asset IDs to integer lower position bounds. |
max_units |
No | {} |
Mapping of asset IDs to nonnegative integer upper position bounds. |
min_order_quantity |
No | 1 |
Positive integer minimum quantity for a submitted order. |
max_order_quantity |
No | null |
Positive integer maximum submitted quantity; null adds no maximum. |
max_outstanding_orders |
No | null |
Positive integer cap on open orders across markets; null adds no cap. |
These rules control order admission. Open bids reserve cash and room under position limits; open asks reserve room to sell. Until a trade settles, a pending sale cannot fund a purchase, and a pending purchase cannot supply units for a sale.
allowed_sides
A nonempty YAML list containing bid, ask, or both, interpreted as a set.
Use [bid] for buyers and [ask] for sellers when the experimental design assigns fixed roles.
min_units
A mapping from declared asset IDs to integer lower position bounds; negative bounds are allowed.
An omitted asset has no additional agent-specific lower bound, but short selling still requires the market's permission.
A bound cannot exceed the corresponding max_units value.
max_units
A mapping from declared asset IDs to nonnegative integer upper position bounds. An omitted asset has no upper position bound; each supplied maximum must be at least the corresponding minimum.
min_order_quantity
The minimum submitted order quantity, defaulting to 1.
It must be a positive integer and cannot exceed a configured max_order_quantity.
The limit is checked on submission, so a partial fill may leave a smaller remaining quantity.
max_order_quantity
A positive integer limiting each submitted order, or null for no upper quantity limit.
When set, it must be at least min_order_quantity.
max_outstanding_orders
A positive integer limiting an agent's total open orders across all markets, or null for no cap.
Other admission checks, including cash and position limits, still apply.
Reuse and validation
You can use YAML anchors to share configuration between agent groups.
YAML merges are shallow
Replacing a nested block such as prompt replaces the whole block, including inherited instructions and providers.
Explicitly merge its contents too if you want to retain those settings while changing one field.
Asset, market, and agent-group IDs must match [a-z][a-z0-9_]* and be unique within their respective lists.
Aigora rejects unknown schema fields, duplicate explicit keys, references to undeclared assets, nonfinite numbers, and invalid ranges.
Counts, quantities, and positions must be integers; booleans are not accepted as integers.
The open mappings documented above accept the keys defined by their author, provider, or hook.
Aigora validates the configuration, loads prompt files, and constructs providers and hooks before reserving a run directory. It saves resolved inputs and defaults in the run metadata. Secret-like parameters are redacted there, so you may need replacement values before rerunning a saved configuration.