Skip to main content

03.26Agent orchestration

Interface contracts between AI agents: who may write what

How AR Labs lets AI agents share state safely: interface contracts drawn between C4 containers, one writer per file, direction down, status up.

On this page
  1. A multi-agent system with shared state is a distributed system
  2. Where the contract sits in a C4 diagram
  3. The contract for each role
  4. Direction down, status up
  5. A lower level never changes a higher one
  6. Where code can enforce the contract
  7. Questions to ask of your own design

When several AI agents share state, each one needs a contract: what it reads, what it alone may write, what it may only propose, and who decides a proposal. AR Labs runs a strategy agent that reviews plans from the year down to the day, department sessions that do and report the work, and a marketing-manager agent on a server, and they coordinate mostly through shared files. An AI agent is a language model run in a loop with tools, so it can act, not only reply. This page describes how we draw the boundaries between such agents and the rules that keep one from overwriting another. It is for anyone building a multi-agent system in which agents read and write the same documents, records or tables.

A multi-agent system with shared state is a distributed system

Agents that read and write shared stores, each with a partial view of the others, fail the way distributed systems do: stale reads, two writers on one record, actions outside a component's authority. Agents make the failures quieter. A service that calls the wrong endpoint gets an error; an agent that opens the wrong file reads it successfully and produces output that looks right. So the boundaries have to be written where the agent will read them, and checked by something other than the agent.

Where the contract sits in a C4 diagram

Simon Brown's C4 model describes software at four zoom levels: system context, containers (the separately running parts and how they talk), components inside a container, and code. Our strategy agent's design had a clear context view (a person governs, the strategy agent orchestrates, departments execute) and detailed component views (each review's phases, inputs and outputs). What it lacked when it first ran was the specification of each arrow between containers. An arrow labelled "sends monthly goals to" gives a direction. It does not say which file is the source, how fresh it must be, or who may write it. That specification is the interface contract, and it belongs on the boundary between containers.

FIG. 01
flowchart TD
  P["Person: sets direction, approves"]
  SA["Strategy agent: reviews, year to day"]
  SS["Strategic state document"]
  OS["Operational state document"]
  D["Department sessions"]
  M["Marketing-manager agent"]
  DB["Dashboard (read-only view)"]
  P -->|"approves at each gate"| SA
  SA -->|"writes; direction flows down"| SS
  SS -->|"read"| D
  SS -->|"read"| M
  D -->|"write; status flows up"| OS
  OS -->|"read"| SA
  SA -->|"data for"| DB

The contract for each role

We apply Bertrand Meyer's design by contract, from his work on the Eiffel language, to agent roles. A precondition is what must be true before a role runs, and the caller's responsibility. A postcondition is what the role guarantees to have produced when it finishes. An invariant is what stays true whichever role runs. Between components in code, a type system or a runtime check enforces these. Between agents sharing files, nothing enforces them unless the contract is written and checked.

Our strategy agent works in nested reviews, called cadences: yearly, quarterly, monthly, weekly and daily. Five rules form their contract, and the review procedures carry them:

Rule What it says What it prevents
Read from your parent's output Each review reads the main output of the review directly above it, not the one above that. The weekly review reads the monthly goals file, not the quarterly objectives Reasoning from a source that the level in between has already refined
One level up A review may propose changes only to its immediate parent A weekly finding rewriting quarterly objectives
Write your own outputs Each review writes its own documents; its parent's are read-only to it Two writers on one file
Goals are append-only A met goal is marked done with its date and stays; a new goal is appended below Losing the record of what was achieved
Restricted scope for ad hoc sessions A strategy session called between reviews may write narrative and decisions, but not goal tables; it raises a flag for the next review instead A side conversation quietly changing targets

The ownership becomes a table each review can check before it writes:

Review Writes Reads only
Yearly Vision, roadmap, activity map, yearly objectives None
Quarterly The quarter's objectives and key results The yearly outputs
Monthly The monthly goals file, and the month's theme in the strategic state The quarter's targets
Weekly The weekly log, the week's focus, and the week's priorities in the strategic state The monthly goals file
Daily The daily log The weekly outputs

A change that crosses a boundary is a proposal, never a write. When a weekly review finds that a monthly goal no longer fits, it proposes the change with its reason, a person approves it explicitly, and only then is the monthly goals file changed, with a record of what triggered the change and who approved it.

Direction down, status up

Two documents carry information between the strategy agent and everyone else, and each has one writer. The strategic state document holds the strategy agent's assessment and flows down: the strategy agent writes it and the organisation reads it. The operational state document holds each department's focus, blockers and results and flows up: departments write it and the strategy agent reads it. A department session that has something the strategy agent should know writes it there, or raises it through the escalation workflow if it needs a decision; it never edits the strategy agent's own memory.

Two further boundaries keep views from becoming sources: the planning dashboard never writes company state, and the strategy agent never reads the dashboard as input, which would mean reasoning from a summary of its own summary.

Robert C. Martin's interface segregation principle, the "I" in SOLID, adds one more rule: give each role a narrow read surface, the specific sections it consumes, because an agent shown more than it needs can act on data outside its scope. The first rule above is that rule for our reviews.

A lower level never changes a higher one

The marketing-manager agent's design, recorded in its architecture decisions, generalises the same idea into levels of work, after Gregory Bateson's logical levels. Each level states three things for the level below it: what its children may be, what counts as a child failing, and how a child is created, revised or retired, and by whom. A lower level never edits a higher one. When work meets a contradiction with a level above, such as evidence against that level's assumption or two instructions that conflict, the agent raises it as an item on the higher level, citing the evidence, and that level's own change process settles it. The agent cannot edit the company context or its own mandate; it can only propose changes. And no level grades its own success. This design is accepted; its implementation is deferred.

Between agents that do not share a workspace, the same rule holds through the file-based inbox: changes to another agent's instructions or working records are requested through the mailbox and made by that agent, never made directly.

Where code can enforce the contract

Our position is that a rule that matters is enforced by a check, not by asking an agent nicely. Between components of a program this is straightforward. In Salesbot, our browser voice agent, the web page and the voice server share a list of conversation stages and a set of input limits, each defined once in a contract file, and a contract test fails if the two sides drift apart.

Between our agents, the contracts are written into each role's procedure, and the check is a person: every phase of a strategy review ends at a gate (a point work cannot pass until a condition is met) where a person reviews the output before the agent continues. No check in code yet stops a review writing a strategy file it does not own. Where we have enforced ownership in code is around the coding pipeline: a hook (a script run automatically around an agent's action that can block it) refuses an unattended agent's edits to the scripts and settings that implement the pipeline's gates, and logs any such edit made while a person is watching, because an agent that can rewrite a gate can pass any gate.

Questions to ask of your own design

  • Does every shared file, table or queue have exactly one writer?
  • Does each agent have a defined read surface, or does it load everything and reason about all of it?
  • Is authority written per data source, not only per agent? An agent that knows its own level but not which writes are in scope will eventually overstep.
  • Before an agent runs, is it checked that its inputs exist and are fresh, and afterwards that it produced what it owed?
  • When an agent disagrees with a level above it, does it have a route to say so that is not an edit?