-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Overview
The Context Isolation with Summaries Strategy runs each sub-agent in a fresh, isolated context and requires them to return concise summaries to the parent agent. This prevents context bloat in multi-agent systems by ensuring sub-agents cannot pollute the parent's context with verbose output, while maintaining a clear hierarchical communication pattern.
This is the Claude Code Task tool and Claude Agent SDK sub-agents pattern.
How It Works
Traditional Multi-Agent (Context Leak):
Parent Agent Context: [1000 messages]
↓
Sub-Agent A inherits context → Adds 500 messages → Total: 1500
↓
Sub-Agent B inherits context → Adds 500 messages → Total: 2000
↓
Parent Agent now has 2000 messages (context exploded!)
Context Isolation with Summaries:
Parent Agent Context: [100 messages]
↓
Sub-Agent A (ISOLATED) → Fresh context → Returns summary (5 lines)
↓
Parent Agent adds summary → Total: 105 messages
↓
Sub-Agent B (ISOLATED) → Fresh context → Returns summary (5 lines)
↓
Parent Agent adds summary → Total: 110 messages (controlled growth!)
Data Flow:
┌──────────────────────────────────┐
│ Parent Agent Context │
│ - Task description │
│ - High-level plan │
│ - Summary 1 from Sub-Agent A │
│ - Summary 2 from Sub-Agent B │
│ Size: ~100 messages (bounded) │
└──────────────────────────────────┘
│ │
↓ ↓
┌─────────┐ ┌─────────┐
│ Sub- │ │ Sub- │
│ Agent A │ │ Agent B │
│ (FRESH) │ │ (FRESH) │
└─────────┘ └─────────┘
│ │
↓ ↓
[1000 msgs] [1000 msgs]
(isolated) (isolated)
│ │
↓ ↓
Summary (5) Summary (5)
│ │
└────────┬────────┘
↓
Parent receives
summaries only
Key Principles:
- Isolation: Sub-agent starts with minimal context (task only)
- Containment: Sub-agent's verbose work stays isolated
- Summary: Sub-agent returns distilled key findings
- Injection: Parent adds only summary to its context
Reference Implementations
- Claude Code Sub-agents Documentation - Each subagent runs in its own context window, isolated from main conversation
- Claude Code Multiple Agent Systems Guide (2026) - Context isolation patterns
- Agent Design Patterns (2026) - Map-reduce pattern for parallelizable tasks
- Context Management with Subagents - Prevents cross-contamination, verbose output stays isolated
- Claude Agent SDK Subagents - Official SDK pattern for context isolation
- Claude Code Parallel Subagents - Parallel work with isolated contexts
Proposed PyWorkflow Implementation
PyWorkflow's step workers on Celery provide natural context isolation! Each step runs on a separate worker with its own context.
from dataclasses import dataclass
from typing import List, Dict, Any, Optional
from pyworkflow import workflow, step, get_context, start_child_workflow
from pyworkflow.primitives.parallel import parallel
@dataclass
class SubAgentTask:
"""Task specification for sub-agent"""
task_id: str
description: str
context: Dict[str, Any] # Minimal context (not full parent context)
@dataclass
class SubAgentSummary:
"""Summary returned by sub-agent"""
task_id: str
summary: str
key_findings: List[str]
artifacts: Optional[Dict[str, Any]] = None
# Sub-agent workflow (runs in ISOLATED context)
@workflow(durable=True)
async def sub_agent_workflow(task: SubAgentTask) -> SubAgentSummary:
"""
Sub-agent workflow with isolated context.
IMPORTANT: This workflow starts with FRESH context.
Parent agent's conversation history is NOT inherited.
"""
# Sub-agent can do extensive work here
# Example: Process 1000 files, generate verbose logs, etc.
research_results = await research_task(task.description, task.context)
analysis = await analyze_results(research_results)
validation = await validate_analysis(analysis)
# Generate CONCISE summary for parent
summary_prompt = f"""
Summarize these research findings in 3-5 bullet points:
Research: {research_results}
Analysis: {analysis}
Validation: {validation}
Focus on key insights only. Be concise.
"""
summary = await call_llm(summary_prompt)
# Return summary (not full verbose output)
return SubAgentSummary(
task_id=task.task_id,
summary=summary,
key_findings=[
"Finding 1",
"Finding 2",
"Finding 3"
],
artifacts={"detailed_report_path": "/path/to/report.pdf"}
)
@step()
async def research_task(description: str, context: dict) -> str:
"""Sub-agent step 1: Research (isolated context)"""
# This runs on Celery worker with isolated context
# Can generate 10,000 tokens of verbose output
# Parent will never see this
verbose_research = await perform_detailed_research(description)
return verbose_research
@step()
async def analyze_results(research: str) -> str:
"""Sub-agent step 2: Analyze (isolated context)"""
# Another isolated step
# Verbose analysis stays here
verbose_analysis = await perform_deep_analysis(research)
return verbose_analysis
@step()
async def validate_analysis(analysis: str) -> str:
"""Sub-agent step 3: Validate (isolated context)"""
# Final isolated step
validation_report = await validate_with_tests(analysis)
return validation_report
# Parent agent workflow
@workflow(durable=True)
async def parent_agent_workflow(main_task: str, sub_tasks: List[str]):
"""
Parent agent orchestrates sub-agents with context isolation.
Parent's context remains clean - only summaries are added.
"""
ctx = get_context()
# Parent's conversation (stays bounded)
parent_context = {
"main_task": main_task,
"status": "in_progress",
"summaries": []
}
# Dispatch sub-agents in parallel (each isolated)
sub_agent_tasks = [
SubAgentTask(
task_id=f"task_{i}",
description=sub_task,
context={"main_task": main_task} # Minimal context only
)
for i, sub_task in enumerate(sub_tasks)
]
# Run sub-agents in parallel (each in isolated context)
summaries = await parallel([
start_child_workflow(
sub_agent_workflow,
task,
wait_for_completion=True
)
for task in sub_agent_tasks
])
# Parent receives ONLY summaries (not verbose output)
for summary in summaries:
parent_context["summaries"].append(summary.summary)
# Synthesize final result from summaries
final_prompt = f"""
Main task: {main_task}
Sub-agent summaries:
{chr(10).join(f"- {s}" for s in parent_context["summaries"])}
Synthesize a final answer based on these summaries.
"""
final_result = await call_llm(final_prompt)
return {
"result": final_result,
"sub_agent_summaries": summaries,
"parent_context_size": len(str(parent_context)) # Stays small!
}
# Advanced: Map-Reduce pattern with isolation
@workflow(durable=True)
async def map_reduce_with_isolation(items: List[str], operation: str):
"""
Map-reduce pattern where each map task runs in isolated context.
Example use cases:
- Code review: Each file reviewed in isolation
- Migration: Each file migrated in isolation
- Testing: Each test suite run in isolation
"""
# MAP: Distribute tasks to isolated sub-agents
map_tasks = [
SubAgentTask(
task_id=f"map_{i}",
description=f"Process item: {item}",
context={"operation": operation, "item": item}
)
for i, item in enumerate(items)
]
map_results = await parallel([
start_child_workflow(sub_agent_workflow, task, wait_for_completion=True)
for task in map_tasks
])
# REDUCE: Aggregate summaries (in parent context)
reduce_prompt = f"""
Aggregate these results:
{chr(10).join(r.summary for r in map_results)}
Provide final summary.
"""
final_summary = await call_llm(reduce_prompt)
return {
"final_summary": final_summary,
"individual_summaries": [r.summary for r in map_results]
}Integration with Event Sourcing
Context isolation maps to PyWorkflow's child workflow pattern:
Parent Workflow:
# Parent's events (bounded)
Event(type=EventType.WORKFLOW_STARTED, data={"main_task": "..."})
Event(type=EventType.CHILD_WORKFLOW_STARTED, data={"child_run_id": "sub_123"})
Event(type=EventType.CHILD_WORKFLOW_COMPLETED, data={
"child_run_id": "sub_123",
"summary": "Key findings..." # Only summary stored
})Child Workflow (Isolated):
# Child's events (separate run_id, isolated storage)
Event(run_id="sub_123", type=EventType.WORKFLOW_STARTED, data={"task": "..."})
Event(run_id="sub_123", type=EventType.STEP_COMPLETED, data={"verbose": "..."})
Event(run_id="sub_123", type=EventType.STEP_COMPLETED, data={"verbose": "..."})
Event(run_id="sub_123", type=EventType.STEP_COMPLETED, data={"verbose": "..."})
# ... 1000s more events (isolated, not in parent's event log)
Event(run_id="sub_123", type=EventType.WORKFLOW_COMPLETED, data={"summary": "..."})Key Advantages:
- Separate event logs: Parent and child have different run_ids
- Natural isolation: Child events don't pollute parent
- Debuggable: Can inspect child's full event log separately
- Storage efficiency: Parent stores only summary, child's full log available if needed
PyWorkflow Mapping:
| Pattern Element | PyWorkflow Implementation |
|---|---|
| Sub-agent | Child workflow with separate run_id |
| Isolated context | Child workflow's independent event log |
| Summary return | Child workflow's result (structured output) |
| Parent context | Parent workflow's event log (contains only child summary) |
| Parallel sub-agents | parallel() primitive with child workflows |
Example: Claude Code Task tool pattern:
@workflow(durable=True)
async def claude_code_main_agent():
"""Main agent (like Claude Code main conversation)"""
# User asks complex question requiring research
# Delegate to isolated sub-agent (like Task tool)
research_summary = await start_child_workflow(
research_sub_agent,
query="Research topic X",
wait_for_completion=True
)
# Main agent receives only summary
# Sub-agent's verbose work is isolated in separate run_id
return f"Based on research: {research_summary}"Trade-offs
Pros:
- Prevents context bloat: Parent's context stays bounded
- Token efficiency: Only summaries in parent context
- Clear separation: Sub-agent work is contained
- Parallelization: Isolated sub-agents run independently
- Debugging: Sub-agent's full context available separately
Cons:
- Less context for sub-agents: They don't see parent's full history
- Summary loss: Detail is lost in summarization
- Coordination overhead: Parent must aggregate summaries
- Not suitable for tight coupling: If sub-agents need to share context frequently
When to Use:
- Map-reduce patterns: Process many items independently (code review, migration)
- Delegated research: Sub-agent does deep dive, parent orchestrates
- Parallel tasks: Tasks that don't need each other's context
- Context management: Prevent unbounded context growth
When to Avoid:
- Tight collaboration: Agents need to see each other's work in detail
- Sequential dependent tasks: Each task builds on previous (use shared state instead)
- Short simple tasks: Isolation overhead not worth it
Related Issues
- Dual-Layer Context Strategy (Hot + Cold Path) for pyworkflow_agents #170 - Dual-Layer Context Strategy - complementary (hot/cold path)
- State Delta Strategy (Efficient Multi-Agent Context) for pyworkflow_agents #173 - State Delta Strategy - alternative for shared state between agents
- Short-term / Conversation Memory for pyworkflow_agents #155 - Short-term / Conversation Memory - what parent maintains