Most multi-agent failures I've seen don't happen inside an agent. They happen between two of them.
When the handoff between two agents is treated as loosely structured JSON or implicit shared state, the interface is underspecified — and underspecified interfaces fail quietly.
A production handoff should be treated like any other API contract:
- an explicit input schema;
- an explicit output schema;
- required and optional fields;
- semantic constraints, not only valid JSON;
- a clear rejection and fallback path.
A concrete example
A research agent should not hand an unstructured object to a scoring agent. It should emit something the scorer can actually validate:
class ResearchResult(BaseModel):
subject_id: str
evidence: list[EvidenceItem] = Field(min_length=1)
source_count: int = Field(ge=1)
confidence: float = Field(ge=0, le=1)
The scorer runs only after that result passes validation. That gate is the important part.
Probabilistic output, consequential action
An LLM output is probabilistic. A database write, an external message, or a business decision is an action. Those two steps should not be directly coupled.
Between them, put a deterministic gate:
- Parse the model output.
- Validate its schema and business rules.
- Reject, retry, route to review, or stop the branch when validation fails.
- Execute the action only on an accepted contract.
- Record the handoff version and validation result.
Why it changes failure behavior
Without a contract, a missing field becomes a null value, the null becomes a misleading score, and the score becomes a bad CRM update. The symptom shows up several steps away from the cause, and by then the trail is cold.
With contracts, the same failure is localized: ResearchAgent -> ScoringAgent, field source_count, validation rule failed.
That is more than defensive programming. It is architecture for software systems whose internal steps are non-deterministic.
It gives staff engineers independently testable boundaries, helps principal engineers reduce coupling between teams and agents, and gives architects and CTOs a clearer way to reason about compatibility, ownership, and recovery.
The rule
Every probabilistic step that feeds a consequential action needs an explicit validation gate. Typed contracts are one strong way to define that boundary.
The model can be creative inside the boundary. The boundary should not be.
Where have you found the most valuable contract in an agent workflow — between agents, between an agent and a tool, or before the final action?