Multi-Agent Context Management: A Production Playbook¶

Multi-agent systems fail when every agent receives everything. Prompts fill with stale tool output. Facts get copied across handoffs. Two agents make incompatible decisions from different snapshots.
The fix is not a larger context window. You need a context pipeline. Keep durable state outside the prompt. Route only relevant data. Compress deterministically. Persist decisions with provenance. This playbook gives you an implementation pattern you can adapt to LangGraph, LangChain, CrewAI, or custom Python.
Context architecture: separate durable state from working prompts¶
Treat the prompt as a compiled view, not as your database.
Google’s context-aware multi-agent architecture separates context into four layers:
- Working context: The ephemeral input assembled for one model invocation.
- Session: Structured events from the current interaction.
- Memory: Persistent, searchable knowledge.
- Artifacts: Large text or binary objects referenced by name and version.
The working context can include system instructions, agent identity, selected history, tool results, optional memory results, and artifact references. It is rebuilt for each invocation from underlying state. That makes it configurable and independent of the model.
Do not append the entire conversation and every tool result to one prompt. Google identifies three consequences: higher cost and latency, weaker signal because irrelevant or stale information remains present, and eventual context-window overflow.
A useful production boundary looks like this:
Durable state
├── session events
├── searchable memories
├── approved decisions
└── versioned artifacts
│
▼
Context compiler
├── permission filter
├── relevance selector
├── deduplicator
├── compactor
└── budget enforcer
│
▼
Working prompt for one agent invocation
The compiler should be a named, ordered pipeline. This makes context behavior testable. It also prevents individual agents from inventing their own loading rules.
Framework choice affects where these components live. The comparison of Deep Agents, LangChain, and LangGraph covers their different approaches to orchestration and state.
Route each task with an explicit context policy¶
A router should select both the agent and the context policy. Routing only by task name is too weak.
Evaluate:
- Intent and domain.
- Required capabilities and tools.
- Router confidence.
- Estimated context size.
- Latency budget.
- Relative agent cost.
- Data permissions.
- Available fallback behavior.
Return a structured route instead of an agent name:
from dataclasses import dataclass
from typing import FrozenSet
@dataclass(frozen=True)
class Route:
agent_id: str
allowed_tools: FrozenSet[str]
context_budget: int
fallback_agent_id: str
required_labels: FrozenSet[str]
def route_task(intent: str, estimated_tokens: int) -> Route:
if intent == "competitor_change_analysis":
return Route(
agent_id="research_agent",
allowed_tools=frozenset({"artifact_read", "memory_search"}),
context_budget=estimated_tokens,
fallback_agent_id="supervisor",
required_labels=frozenset({"public"}),
)
return Route(
agent_id="supervisor",
allowed_tools=frozenset(),
context_budget=estimated_tokens,
fallback_agent_id="human_review",
required_labels=frozenset(),
)
In a competitor-monitoring workflow, the fetcher stores page snapshots as artifacts. The research agent receives approved URLs, relevant diffs, and prior decisions. The report agent receives the research summary, citations, and output constraints. It does not need raw fetch logs.
Two orchestration patterns are common:
- A supervisor delegates work to specialized agents.
- A hierarchical pipeline passes one agent’s output to the next.
Vellum describes both patterns and recommends proving the value of a single-agent design before introducing multiple agents. Specialization or parallel execution should solve an identified problem. Multi-agent systems otherwise add coordination risk, complexity, and materially greater token consumption.
Use a versioned context envelope for every handoff¶
Never pass an untyped summary string between services. Use a versioned envelope.
from dataclasses import dataclass, field
from typing import Any
@dataclass
class ContextEnvelope:
context_id: str
task_id: str
parent_task_id: str | None
agent_id: str
schema_version: str
intent: str
constraints: list[str] = field(default_factory=list)
selected_facts: list[dict[str, Any]] = field(default_factory=list)
decisions: list[dict[str, Any]] = field(default_factory=list)
summary: str = ""
artifact_refs: list[dict[str, str]] = field(default_factory=list)
provenance_links: list[str] = field(default_factory=list)
sensitivity_labels: list[str] = field(default_factory=list)
token_budget: int | None = None
expires_at: str | None = None
Keep facts and decisions separate. A fact records evidence. A decision records what the system chose, who approved it, and which state version it used.
Microsoft’s A2A context model uses a contextId to group related tasks within a conversation. The same context can contain multiple task identifiers, with a task identifier assigned per turn. An A2A message contains a parts array. Each part contains exactly one of text, file, or data, plus optional metadata identifying the data type and version.
That maps cleanly to an envelope:
{
"context_id": "monitoring-run",
"task_id": "write-report",
"parent_task_id": "analyze-changes",
"agent_id": "report_agent",
"schema_version": "report-envelope",
"intent": "produce_evidence_backed_summary",
"constraints": ["Use approved sources", "Do not infer missing changes"],
"artifact_refs": [
{
"name": "change-set",
"version": "current"
}
]
}
Persist the envelope before dispatch and after completion. If execution stops, the coordinator can resume from a known state instead of reconstructing it from logs. For framework-specific recovery, see these LangGraph checkpoint and production job patterns.
Compress and persist context with deterministic rules¶
Compaction should be policy-driven. Do not ask an agent to “shorten this” and discard the source.
Use this pipeline:
- Deduplicate events by a stable event ID.
- Remove events the target agent cannot access.
- Rank facts by task relevance and recency.
- Preserve user constraints and approved decisions.
- Summarize completed work.
- Move large outputs into versioned artifacts.
- Keep provenance links in the summary.
- Reject the handoff if required evidence still exceeds its budget.
Use configurable soft and hard limits based on your selected model, reserved output capacity, instructions, and tool protocol. A soft limit triggers compaction. A hard limit blocks dispatch. There is no universal percentage that fits every model and workflow.
Reserve separate budgets for:
- System instructions and agent identity.
- User constraints.
- Evidence and provenance.
- Session history.
- Tool results.
- Expected output.
A deterministic selector can run before model-based summarization:
def select_events(events, allowed_labels, max_items):
visible = [
event for event in events
if set(event["labels"]).issubset(allowed_labels)
]
unique = {event["event_id"]: event for event in visible}
ranked = sorted(
unique.values(),
key=lambda event: (
event["is_approved_decision"],
event["task_relevance"],
event["created_at"],
),
reverse=True,
)
return ranked[:max_items]
Store interaction events in the session. Promote reusable facts into searchable memory only after validation. Put large reports, snapshots, and exports in artifact storage. The prompt should contain references and selected excerpts, not duplicated files.
Choose the transport and resolve contradictions safely¶
Microsoft describes three context-sharing strategies: shared storage, coordinator message payloads, and a separate context store owned by each domain agent.
Choose with a simple decision tree:
- Small and security-cleared: Embed context in the handoff payload.
- Long history: Send a
context_idand load selected state from shared storage. - Strong domain ownership: Let the domain agent maintain its own context store.
- Sensitive or independently deployed services: Use a hybrid. Send a minimal envelope and resolve authorized references locally.
Shared storage reduces payload size and supports long histories without message-size constraints. It also creates a runtime dependency and expands the security surface between services. Separately deployed agents may also require conversational continuity, independent versioning, no shared infrastructure, and security isolation.
Contradictions need explicit rules:
- Compare state versions and provenance.
- Prefer a newer approved decision over an older draft.
- Never silently overwrite incompatible approved decisions.
- Quarantine the conflicting output.
- Ask a verifier agent to compare evidence.
- Require human approval before an irreversible action.
Use optimistic concurrency when saving decisions:
class VersionConflict(Exception):
pass
def save_decision(store, context_id, expected_version, decision):
current = store.load(context_id)
if current["version"] != expected_version:
raise VersionConflict(context_id)
return store.update(
context_id=context_id,
decision=decision,
expected_version=expected_version,
)
A retry must reload current state and reevaluate the decision. It must not force an overwrite.
Operate, measure, and roll out the context system¶
Track context as an operational subsystem. Useful metrics include:
- Context-loss rate.
- Duplicate-token rate.
- Compaction frequency.
- Handoff success rate.
- Contradiction rate.
- Context assembly latency.
- Cost per completed task.
- Recovery time.
- Grounded-decision quality.
Log context hashes, schema versions, trace IDs, permission decisions, artifact versions, and redaction events. Do not put secrets or raw sensitive values in logs.
Apply least-privilege access to memories and artifacts. Isolate tenant data. Define retention limits. Keep an audit trail showing which context each agent received. These controls should run before context compilation, not after model output.
Use idempotent writes, bounded retries, persistent checkpoints, and replay tests. A replay test should rebuild a prompt from recorded durable state and verify routing, permissions, selected evidence, and conflict behavior. The broader operational issues are covered in this guide to scaling AI agents and production bottlenecks.
Roll out in stages:
- Establish a single-agent baseline.
- Run the router in shadow mode without changing execution.
- Enable deterministic selection and compaction.
- Add persistent memory and versioned artifacts.
- Introduce specialized agents.
- Add parallel execution only where it helps.
- Automate low-risk conflict resolution after replay testing.
Conclusion¶
Reliable multi-agent context management starts with separation. Durable state belongs in sessions, memory, decisions, and artifacts. Working prompts should be rebuilt for each invocation.
Route context with policy. Use typed, versioned envelopes. Compact with deterministic rules. Preserve provenance. Reject conflicting writes instead of hiding them. Then measure whether handoffs remain grounded, efficient, and recoverable.
Once those boundaries are explicit, you can deploy each agent as an isolated job while keeping coordination state durable. A managed agent runtime such as HollowHost can handle execution, secrets, scheduling, and operational isolation, while your application retains control of the context model.