Architecture Overview¶
Amplifier is built on a modular architecture inspired by the Linux kernel model. This page provides a comprehensive overview of how all the pieces fit together.
The Linux Kernel Analogy¶
Amplifier mirrors Linux kernel concepts:
| Linux Concept | Amplifier Analog | Purpose |
|---|---|---|
| Ring 0 kernel | amplifier-core | Mechanisms only, never policy |
| Syscalls | Session operations | Few, sharp APIs |
| Loadable drivers | Modules | Compete at edges |
| Signals/Netlink | Event bus / hooks | Observe and control |
| /proc & dmesg | JSONL logs | Single canonical stream |
| Capabilities | Approval system | Deny-by-default |
| Scheduler | Orchestrator modules | Swap execution strategies |
| VM/Memory | Context manager | Conversation memory |
System Layers¶
Layer 1: Application¶
The application layer (e.g., amplifier-app-cli) handles:
- User interaction (CLI, REPL)
- Configuration resolution (bundles, profiles, settings)
- Mount Plan creation
- Approval and display systems
# Application defines configuration (mount plan)
config = {
"session": {"orchestrator": "loop-basic", "context": "context-simple"},
"providers": [{"module": "provider-anthropic"}],
"tools": [{"module": "tool-filesystem"}, {"module": "tool-bash"}],
}
# Application creates and runs session
async with AmplifierSession(config) as session:
response = await session.execute(prompt)
Layer 2: Kernel¶
The kernel (amplifier-core, ~2,600 lines) provides:
- Mount Plan validation
- Module discovery and loading
- Session lifecycle management
- Event emission
- Coordinator infrastructure
# Kernel validates config and loads modules
session = AmplifierSession(config) # Validates required fields
await session.initialize() # Discovers and loads all modules
# Modules mount via coordinator
await coordinator.mount("tools", tool_instance)
coordinator.hooks.register("tool:pre", hook_function)
# Events flow through the kernel
await hooks.emit("tool:pre", {...})
Layer 3: Modules¶
Modules implement policies using kernel mechanisms:
# Provider module
class AnthropicProvider:
async def complete(self, request: ChatRequest) -> ChatResponse:
# Decide: which model, what parameters, how to call API
...
# Tool module
class BashTool:
async def execute(self, input: dict) -> ToolResult:
# Decide: safety checks, command execution, result formatting
...
# Hook module
async def logging_hook(event: str, data: dict) -> HookResult:
# Decide: what to log, where to log, how to redact
...
Data Flow¶
1. Session Initialization¶
Application
↓ (Mount Plan)
Kernel validates config
↓ (Discovers modules via entry points / filesystem)
ModuleLoader
↓ (Calls mount() for each module)
Modules register with Coordinator
↓
Session ready
2. Request Execution¶
User prompt
↓
Application → Session.execute(prompt)
↓
Kernel → Orchestrator.execute()
↓
Orchestrator:
├─ Get messages from Context
├─ Call Provider.complete()
├─ Parse tool calls
├─ Execute Tools (via Tool.execute())
├─ Add results to Context
└─ Repeat until final response
↓
Return response to Application
3. Event Flow¶
Module emits event
↓
Kernel → Hooks.emit(event, data)
↓
Hook Registry dispatches to all registered hooks
↓
Each Hook returns HookResult
↓
Kernel processes results (deny, modify, inject context, etc.)
Module Contracts¶
All modules use Python Protocol (structural typing):
# No inheritance required - just implement the interface
class MyTool:
@property
def name(self) -> str: ...
@property
def description(self) -> str: ...
@property
def input_schema(self) -> dict: ...
async def execute(self, input: dict) -> ToolResult: ...
Module Types¶
| Type | Protocol | Required Methods | Purpose |
|---|---|---|---|
| Provider | Provider | name, complete(), parse_tool_calls(), get_info(), list_models() | LLM backends |
| Tool | Tool | name, description, input_schema, execute() | Agent capabilities |
| Orchestrator | Orchestrator | execute() | Execution loops |
| ContextManager | ContextManager | add_message(), get_messages(), compact() | Memory |
| Hook | Callable | __call__(event, data) -> HookResult | Observability |
Coordinator¶
The ModuleCoordinator provides infrastructure to modules:
class ModuleCoordinator:
# Session context
session_id: str
config: dict[str, Any]
# Module registration
async def mount(category: str, module: Any, name: str) -> None
def get(category: str, name: str | None = None) -> Any
# Hook system
hooks: HookRegistry
# Capability checks
def check_capability(name: str) -> bool
def get_capability(name: str) -> Any
Modules receive the coordinator when mounted and use it to: - Register themselves - Access other modules - Emit events via hooks - Check capabilities
Mount Plan Specification¶
Mount Plans are simple dictionaries:
{
"session": {
"orchestrator": "loop-streaming", # Module ID
"context": "context-simple" # Module ID
},
"providers": [
{
"module": "provider-anthropic", # Required
"name": "claude", # Optional
"source": "git+https://...", # Optional
"config": {...} # Optional
}
],
"tools": [...],
"hooks": [...]
}
The kernel validates structure and loads referenced modules.
Event System¶
Canonical events flow through the HookRegistry:
# Kernel emits events
await hooks.emit("tool:pre", {
"tool_name": "bash",
"tool_input": {"command": "ls -la"}
})
# Hooks observe and optionally control
async def approval_hook(event: str, data: dict) -> HookResult:
if requires_approval(data):
if not await get_user_approval(data):
return HookResult(action="deny", reason="User denied")
return HookResult(action="continue")
Events include: - execution:start, execution:end - prompt:submit, prompt:complete - provider:request, provider:response, provider:error - tool:pre, tool:post, tool:error - content_block:start, content_block:end - Custom events from modules
Module Discovery¶
Three discovery methods (priority order):
-
Python Entry Points
-
Explicit Search Paths
-
Environment Variables
Polyglot Module Loading¶
Modules can be written in any language via four transport types:
| Transport | Usage | Integration |
|---|---|---|
python | Direct Python import | Native performance |
rust | Native linking (Rust host) or gRPC sidecar (Python host) | Compiled binary |
wasm | In-process WASM runtime | Sandboxed execution |
grpc | External service | Remote/microservices |
The module declares its transport in amplifier.toml. The host runtime decides how to load it.
Session Lifecycle¶
┌─────────────────────────────────────────────────┐
│ 1. Creation: AmplifierSession(config) │
│ - Validates Mount Plan │
│ - Creates Coordinator │
└────────────┬────────────────────────────────────┘
│
┌────────────▼────────────────────────────────────┐
│ 2. Initialization: await session.initialize() │
│ - Discovers modules │
│ - Loads and mounts modules │
│ - Registers hooks │
└────────────┬────────────────────────────────────┘
│
┌────────────▼────────────────────────────────────┐
│ 3. Execution: await session.execute(prompt) │
│ - Orchestrator drives agent loop │
│ - Modules interact via Coordinator │
│ - Events flow through hooks │
└────────────┬────────────────────────────────────┘
│
┌────────────▼────────────────────────────────────┐
│ 4. Cleanup: await session.cleanup() │
│ - Modules clean up resources │
│ - Final events emitted │
└─────────────────────────────────────────────────┘
Key Design Patterns¶
1. Dependency Injection¶
Modules receive all dependencies via the coordinator:
async def mount(coordinator: ModuleCoordinator, config: dict) -> None:
# No global state or imports of other modules
tool = MyTool(config)
await coordinator.mount("tools", tool, name="my-tool")
2. Event-Driven Communication¶
Modules communicate via events, not direct calls:
# Module emits event
await coordinator.hooks.emit("custom:event", {"data": "..."})
# Other modules observe
coordinator.hooks.register("custom:event", my_handler)
3. Protocol-Based Contracts¶
No inheritance required - just implement the interface:
# This works without extending any base class
class MyOrchestrator:
async def execute(self, prompt, context, providers, tools, hooks):
# Implementation
...
4. Configuration Over Code¶
Behavior changes via configuration, not code changes:
Backward Compatibility¶
The kernel maintains strict backward compatibility:
- Stable APIs: Session, Coordinator, Module contracts
- Additive changes: New optional fields, capabilities
- Deprecation windows: Long sunset periods
- Version detection: Modules can check kernel version
Modules can break their own APIs - they compete at the edges.
Next Steps¶
- Kernel Philosophy - Learn kernel design principles
- Module System - Understand module loading
- Mount Plans - Configuration specification
- Event System - Canonical events reference