Skip to content

Context Isolation with Summaries Strategy for pyworkflow_agents #176

@yasha-dev1

Description

@yasha-dev1

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:

  1. Isolation: Sub-agent starts with minimal context (task only)
  2. Containment: Sub-agent's verbose work stays isolated
  3. Summary: Sub-agent returns distilled key findings
  4. Injection: Parent adds only summary to its context

Reference Implementations

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:

  1. Separate event logs: Parent and child have different run_ids
  2. Natural isolation: Child events don't pollute parent
  3. Debuggable: Can inspect child's full event log separately
  4. 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

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    agentsAI Agent module (pyworkflow_agents)featureFeature to be implementedmemoryAgent memory and context management

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions