Skip to content

Common Patterns

Practical patterns for using Amplifier Foundation.

Bundle Organization

Single-File Bundle

For simple configurations:

---
bundle:
  name: my-app
  version: 1.0.0

session:
  orchestrator: {module: loop-streaming}
  context: {module: context-simple}

providers:
  - module: provider-anthropic
    source: git+https://github.com/microsoft/amplifier-module-provider-anthropic@main
---

You are a helpful assistant.

Multi-File Bundle

For bundles with agents and context:

my-bundle/
├── bundle.md          # Main configuration
├── agents/
│   ├── bug-hunter.md
│   └── code-reviewer.md
└── context/
    ├── guidelines.md
    └── style.md

The bundle can reference these:

agents:
  include:
    - bug-hunter
    - code-reviewer

context:
  include:
    - guidelines
    - style

Composition Patterns

Base + Environment

Layer environment-specific config on a base:

# base.py
base = Bundle(
    name="base",
    session={"orchestrator": "loop-streaming", "context": "context-simple"},
    providers=[{"module": "provider-anthropic", "source": "...", "config": {"default_model": "claude-sonnet-4-5"}}],
)

# dev.py
dev = Bundle(
    name="dev",
    providers=[{"module": "provider-anthropic", "config": {"debug": True}}],
)

# prod.py
prod = Bundle(
    name="prod",
    providers=[{"module": "provider-anthropic", "config": {"timeout": 60}}],
)

# Usage
env = os.getenv("ENV", "dev")
overlay = dev if env == "dev" else prod
bundle = base.compose(overlay)

Includes Chain

Use includes: for declarative layering:

# base.md
bundle:
  name: base
  version: 1.0.0
session:
  orchestrator: {module: loop-streaming}
providers:
  - module: provider-anthropic
    source: git+...
# dev.md
bundle:
  name: dev
  version: 1.0.0
includes:
  - ./base.md
providers:
  - module: provider-anthropic
    config: {debug: true}

Feature Bundles

Compose features additively:

# Features as partial bundles
filesystem_tools = Bundle(
    name="filesystem",
    tools=[{"module": "tool-filesystem", "source": "..."}],
)

web_tools = Bundle(
    name="web",
    tools=[{"module": "tool-web", "source": "..."}],
)

# Compose what you need
full = base.compose(filesystem_tools, web_tools)

Provider Patterns

Multi-Provider Setup

Configure multiple providers for failover:

providers:
  - module: provider-anthropic
    source: git+...
    config:
      default_model: claude-sonnet-4-5
      priority: 1

  - module: provider-openai
    source: git+...
    config:
      default_model: gpt-5.4
      priority: 2

Mock Provider for Testing

Use mock provider in tests:

test_bundle = Bundle(
    name="test",
    providers=[{
        "module": "provider-mock",
        "source": "git+https://github.com/microsoft/amplifier-module-provider-mock@main",
        "config": {"responses": ["Hello!", "How can I help?"]},
    }],
)

Session Patterns

Basic Session

Load, prepare, execute:

bundle = await load_bundle("/path/to/bundle.md")
prepared = await bundle.prepare()

async with prepared.create_session() as session:
    response = await session.execute("Hello!")
    print(response)

Multi-Turn Conversation

Session maintains context:

async with prepared.create_session() as session:
    await session.execute("My name is Alice")
    response = await session.execute("What's my name?")
    # Response knows about Alice

Resuming Sessions

Resume from session ID:

# First session
async with prepared.create_session() as session:
    await session.execute("Remember: X=42")
    session_id = session.session_id

# Later: resume
async with prepared.create_session(session_id=session_id) as session:
    response = await session.execute("What is X?")
    # Knows X=42

Agent Patterns

Defining Agents

In agents/bug-hunter.md:

---
agent:
  name: bug-hunter
  description: Finds and fixes bugs

providers:
  - module: provider-anthropic
    config:
      default_model: claude-sonnet-4-5
      temperature: 0.3  # More deterministic
---

You are an expert bug hunter. Find bugs systematically.

Spawning Agents

# Load agent as bundle
agent_bundle = await load_bundle("./agents/bug-hunter.md")

# Spawn sub-session
result = await prepared.spawn(
    child_bundle=agent_bundle,
    instruction="Find the bug in auth.py",
    compose=True,            # Compose with parent bundle (default: True)
    parent_session=session,  # Inherit UX from parent
    session_id=None,         # Or provide ID to resume existing sub-session
)

# Returns: {"output": response, "session_id": child_id}
print(result["output"])

Spawning with Provider Preferences

Use provider_preferences for ordered fallback chains when spawning agents:

from amplifier_foundation import ProviderPreference

# Spawn with provider preference chain (tries in order)
result = await prepared.spawn(
    child_bundle=agent_bundle,
    instruction="Quick analysis task",
    provider_preferences=[
        ProviderPreference(provider="anthropic", model="claude-haiku-*"),
        ProviderPreference(provider="openai", model="gpt-5-mini"),
    ],
)

# Model patterns use glob matching (fnmatch)
# "claude-haiku-*" resolves to latest matching model (e.g., "claude-haiku-20250110")

Controlling Agent Tool Inheritance

By default, spawned agents inherit all tools from their parent. Configure tool inheritance in the task tool's config section:

# In your bundle.md
tools:
  - module: tool-task
    source: git+https://github.com/microsoft/amplifier-module-tool-task@main
    config:
      exclude_tools: [tool-task]  # Agents inherit all EXCEPT these

Or specify an explicit allowlist:

tools:
  - module: tool-task
    config:
      inherit_tools: [tool-filesystem, tool-bash]  # Agents get ONLY these

Common pattern: Prevent agents from delegating further:

# Coordinator has task tool for orchestration
tools:
  - module: tool-filesystem
  - module: tool-bash
  - module: tool-task
    config:
      exclude_tools: [tool-task]  # Spawned agents can't delegate

This ensures agents do the work themselves rather than trying to spawn sub-agents.

Design rationale: Tool inheritance config belongs in tool-task's config section because: - The task tool is the module that consumes this config - Follows the pattern of all other tool configs - Without tool-task mounted, this config is meaningless - Standard module-config merge rules apply during bundle composition

Agent Resolution Pattern

Apps typically resolve agent names to bundles:

async def resolve_agent(name: str, agent_configs: dict) -> Bundle:
    """Resolve agent name to Bundle."""
    if name in agent_configs:
        # Inline agent definition
        return Bundle.from_dict(agent_configs[name])

    # Look for agent file
    agent_path = find_agent_file(name)
    if agent_path:
        return await load_bundle(agent_path)

    raise ValueError(f"Unknown agent: {name}")

# Usage
agent_bundle = await resolve_agent("bug-hunter", bundle.agents)
result = await prepared.spawn(agent_bundle, instruction)

Validation Patterns

Validate Before Use

from amplifier_foundation import load_bundle, validate_bundle_or_raise

bundle = await load_bundle(path)
validate_bundle_or_raise(bundle)  # Raises if invalid
prepared = await bundle.prepare()

Validate Completeness

For bundles that should be directly mountable:

from amplifier_foundation import BundleValidator

validator = BundleValidator()
result = validator.validate_completeness(bundle)

if not result.valid:
    print("Missing required sections:")
    for error in result.errors:
        print(f"  - {error}")

Custom Validation

Extend validator for app-specific rules:

class AppValidator(BundleValidator):
    def validate(self, bundle):
        result = super().validate(bundle)

        # Custom rule: must have filesystem tool
        has_filesystem = any(
            t.get("module") == "tool-filesystem"
            for t in bundle.tools
        )
        if not has_filesystem:
            result.add_warning("Missing filesystem tool")

        return result

Registry Patterns

Named Bundles

Register commonly-used bundles:

registry = BundleRegistry()
registry.register("foundation", "git+https://github.com/microsoft/amplifier-foundation-bundle@main")
registry.register("dev", "./bundles/dev.md")

# Load by name
bundle = await registry.load("foundation")

Bundle Discovery

Auto-register bundles from directory:

from pathlib import Path

def register_bundles(registry: BundleRegistry, directory: Path):
    for path in directory.glob("*.md"):
        name = path.stem  # filename without extension
        registry.register(name, str(path))

# Register all bundles in directory
register_bundles(registry, Path("./bundles"))

Error Handling

Graceful Loading

from amplifier_foundation import (
    load_bundle,
    BundleNotFoundError,
    BundleLoadError,
    BundleValidationError,
)

try:
    bundle = await load_bundle(path)
except BundleNotFoundError:
    print(f"Bundle not found: {path}")
except BundleLoadError as e:
    print(f"Failed to load bundle: {e}")
except BundleValidationError as e:
    print(f"Invalid bundle: {e}")

Fallback Bundles

async def load_with_fallback(primary: str, fallback: str) -> Bundle:
    try:
        return await load_bundle(primary)
    except BundleError:
        return await load_bundle(fallback)

Performance Patterns

Cache Resolved Bundles

from amplifier_foundation import DiskCache, BundleRegistry
from pathlib import Path

# DiskCache: Apps decide the cache location
cache_dir = Path("~/.myapp/cache/bundles").expanduser()
cache = DiskCache(cache_dir)

# BundleRegistry uses ~/.amplifier by default, or specify custom home
registry = BundleRegistry(home=Path("~/.myapp").expanduser())

# First load: downloads from git
bundle = await registry.load("git+https://...")

# Subsequent loads: uses cache
bundle = await registry.load("git+https://...")

Prepare Once, Execute Many

# Prepare once (downloads modules)
bundle = await load_bundle(path)
prepared = await bundle.prepare()

# Execute many times
for prompt in prompts:
    async with prepared.create_session() as session:
        response = await session.execute(prompt)

Next Steps