Part 2 of the Grammar of AI Collaboration series.
In Part 1, we introduced the language metaphor: agents are words, composition patterns are grammar, and solutions are sentences. Today we dive deeper into the Agent Factory—the system that spawns customized agents on demand.
The Problem with Hard-Coded Agents
Traditional multi-agent systems define agents at design time:
# The old way - agents defined once, used everywhere
class SecurityAuditor:
def __init__(self):
self.model = "claude-3-opus" # Fixed tier
self.prompt = "You are a security auditor..." # Generic instructions
self.tools = ["code_reader", "vuln_scanner"] # Fixed toolset
# Every security audit uses the same configuration
auditor = SecurityAuditor()
auditor.run(code) # Same agent regardless of task
What's wrong with this?
- Over-engineering: Simple security checks use premium models
- Under-powered: Complex audits don't get enhanced capabilities
- Rigidity: Can't adapt to project-specific requirements
- No specialization: Authentication audit same as API audit
The Solution: Agent Templates + Dynamic Spawning
We separate the archetype (what an agent CAN do) from the instance (what it DOES do for this task):
┌─────────────────────────────────────────────────────────────┐
│ TEMPLATE LIBRARY │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ security_auditor │ │
│ │ ├─ capabilities: [vuln_scan, threat_model, comply] │ │
│ │ ├─ tier_preference: PREMIUM │ │
│ │ ├─ tools: [code_reader, vuln_scanner, cve_db] │ │
│ │ └─ default_instructions: "Audit code for..." │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ SPAWN WITH CONTEXT │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ security_auditor_a7f2 (INSTANCE) │ │
│ │ ├─ tier: CAPABLE (downgraded - simple task) │ │
│ │ ├─ instructions: "Audit auth module for OWASP..." │ │
│ │ ├─ focus: authentication │ │
│ │ └─ quality_gates: {max_high_vulns: 0} │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Anatomy of an Agent Template
from dataclasses import dataclass, field
from typing import Any
@dataclass
class AgentTemplate:
"""Reusable agent archetype that can spawn customized instances."""
# Identity
id: str # "security_auditor"
role: str # "Security Expert"
# Capabilities (what it CAN do)
capabilities: list[str] # ["vuln_scan", "threat_model", "compliance"]
# Resource preferences
tier_preference: str # "CHEAP", "CAPABLE", "PREMIUM"
# Tools available
tools: list[str] # ["code_reader", "vuln_scanner"]
# Default behavior
default_instructions: str # Base prompt template
# Quality requirements
quality_gates: dict[str, Any] # {"min_score": 0.8, "max_vulns": 0}
# Optional metadata
category: str = "security"
cost_weight: float = 1.0
timeout_seconds: int = 300
Example Templates
TEMPLATES = {
"security_auditor": AgentTemplate(
id="security_auditor",
role="Security Expert",
capabilities=["vulnerability_scan", "threat_modeling", "compliance_check"],
tier_preference="PREMIUM",
tools=["code_reader", "vuln_scanner", "cve_database"],
default_instructions="""
Analyze code for security vulnerabilities.
Focus on: {focus_areas}
Severity threshold: {severity}
Compliance requirements: {compliance}
""",
quality_gates={"max_critical": 0, "max_high": 3},
category="security",
),
"test_coverage_analyzer": AgentTemplate(
id="test_coverage_analyzer",
role="Test Coverage Expert",
capabilities=["gap_analysis", "coverage_metrics", "test_suggestions"],
tier_preference="CAPABLE",
tools=["coverage_analyzer", "ast_parser", "test_framework"],
default_instructions="""
Analyze test coverage for the specified code.
Target coverage: {target_coverage}%
Focus modules: {modules}
Identify: untested paths, edge cases, error handling
""",
quality_gates={"min_coverage": 80},
category="testing",
),
"documentation_writer": AgentTemplate(
id="documentation_writer",
role="Documentation Specialist",
capabilities=["api_docs", "readme", "tutorials", "changelogs"],
tier_preference="CHEAP",
tools=["code_reader", "markdown_formatter"],
default_instructions="""
Generate documentation for the specified code.
Style: {style}
Audience: {audience}
Include: {sections}
""",
quality_gates={"completeness": 0.9},
category="documentation",
),
}
The Agent Factory
The factory transforms templates into task-specific agents:
class AgentFactory:
"""Spawns agents from templates with task-specific customization."""
def __init__(self, templates: dict[str, AgentTemplate]):
self.templates = templates
self.spawned_count = 0
def spawn(
self,
template_id: str,
requirements: TaskRequirements,
overrides: dict | None = None
) -> Agent:
"""Create agent instance from template."""
template = self.templates[template_id]
# 1. SELECT APPROPRIATE TIER
tier = self._select_tier(template.tier_preference, requirements)
# 2. CUSTOMIZE INSTRUCTIONS
instructions = self._customize_instructions(
template.default_instructions,
requirements,
overrides or {}
)
# 3. FILTER TOOLS (only what's needed)
tools = self._select_tools(template.tools, requirements)
# 4. CONFIGURE QUALITY GATES
quality_gates = self._configure_gates(
template.quality_gates,
requirements
)
# 5. SPAWN INSTANCE
self.spawned_count += 1
instance_id = f"{template.id}_{self.spawned_count:04d}"
return Agent(
id=instance_id,
template_id=template.id,
role=template.role,
tier=tier,
instructions=instructions,
tools=tools,
quality_gates=quality_gates,
capabilities=template.capabilities,
)
Intelligent Tier Selection
The factory doesn't blindly use the template's preferred tier—it right-sizes based on the task:
def _select_tier(
self,
preference: str,
requirements: TaskRequirements
) -> str:
"""Select appropriate tier based on task requirements."""
# Complexity scores: CHEAP=1, CAPABLE=2, PREMIUM=3
tier_scores = {"CHEAP": 1, "CAPABLE": 2, "PREMIUM": 3}
preference_score = tier_scores[preference]
# Adjust based on requirements
adjustments = 0
# Critical tasks get upgraded
if requirements.is_critical:
adjustments += 1
# Simple tasks get downgraded
if requirements.complexity == "simple":
adjustments -= 1
# User override
if requirements.force_tier:
return requirements.force_tier
# Calculate final tier
final_score = max(1, min(3, preference_score + adjustments))
return {1: "CHEAP", 2: "CAPABLE", 3: "PREMIUM"}[final_score]
Examples of tier selection:
| Template | Task | Selected Tier | Why |
|---|---|---|---|
| security_auditor (PREMIUM) | Production release | PREMIUM | Critical + matches preference |
| security_auditor (PREMIUM) | Dev branch check | CAPABLE | Non-critical, downgraded |
| documentation_writer (CHEAP) | API docs | CHEAP | Matches preference |
| documentation_writer (CHEAP) | Executive summary | CAPABLE | Upgraded for quality |
Dynamic Instruction Customization
Instructions are templates that get filled with task-specific context:
def _customize_instructions(
self,
template_instructions: str,
requirements: TaskRequirements,
overrides: dict
) -> str:
"""Generate task-specific instructions."""
# Build context from requirements
context = {
"focus_areas": requirements.focus or "general",
"severity": requirements.severity_threshold or "medium",
"compliance": ", ".join(requirements.compliance or ["none"]),
"modules": ", ".join(requirements.target_modules or ["all"]),
"target_coverage": requirements.coverage_target or 80,
"style": requirements.doc_style or "technical",
"audience": requirements.audience or "developers",
"sections": ", ".join(requirements.sections or ["overview", "api"]),
}
# Apply overrides
context.update(overrides)
# Generate customized instructions
instructions = template_instructions.format(**context)
# Add task-specific context
if requirements.additional_context:
instructions += f"\n\nAdditional context:\n{requirements.additional_context}"
return instructions
Example transformation:
# Template instructions
"""
Analyze code for security vulnerabilities.
Focus on: {focus_areas}
Severity threshold: {severity}
Compliance requirements: {compliance}
"""
# Requirements
requirements = TaskRequirements(
focus="authentication, session management",
severity_threshold="high",
compliance=["OWASP", "GDPR"],
additional_context="Recent breach attempt on login endpoint"
)
# Spawned agent receives:
"""
Analyze code for security vulnerabilities.
Focus on: authentication, session management
Severity threshold: high
Compliance requirements: OWASP, GDPR
Additional context:
Recent breach attempt on login endpoint
"""
Spawning in Action
Let's trace a complete spawn operation:
# User request
result = orchestrate("security audit on auth module for release")
# Meta-orchestrator analyzes task
requirements = TaskRequirements(
intent="security_audit",
target_modules=["auth"],
is_critical=True, # Release = critical
compliance=["OWASP", "PCI-DSS"],
focus="authentication, authorization, session",
severity_threshold="high",
complexity="moderate"
)
# Factory spawns agent
factory = AgentFactory(TEMPLATES)
agent = factory.spawn(
template_id="security_auditor",
requirements=requirements
)
# Result:
print(agent)
# Agent(
# id="security_auditor_0001",
# template_id="security_auditor",
# role="Security Expert",
# tier="PREMIUM", # Critical + PREMIUM preference
# instructions="Analyze code for security vulnerabilities.
# Focus on: authentication, authorization, session
# Severity threshold: high
# Compliance requirements: OWASP, PCI-DSS",
# tools=["code_reader", "vuln_scanner", "cve_database"],
# quality_gates={"max_critical": 0, "max_high": 0}, # Stricter for release
# capabilities=["vulnerability_scan", "threat_modeling", "compliance_check"]
# )
Template Registry & Discovery
Templates are registered in a central library:
class TemplateRegistry:
"""Central registry for agent templates."""
def __init__(self):
self._templates: dict[str, AgentTemplate] = {}
self._by_capability: dict[str, list[str]] = {}
def register(self, template: AgentTemplate):
"""Register a template."""
self._templates[template.id] = template
# Index by capability
for cap in template.capabilities:
if cap not in self._by_capability:
self._by_capability[cap] = []
self._by_capability[cap].append(template.id)
def find_by_capability(self, capability: str) -> list[AgentTemplate]:
"""Find templates that have a capability."""
template_ids = self._by_capability.get(capability, [])
return [self._templates[tid] for tid in template_ids]
def find_best_match(self, requirements: TaskRequirements) -> AgentTemplate:
"""Find the best template for requirements."""
candidates = []
for cap in requirements.needed_capabilities:
candidates.extend(self.find_by_capability(cap))
# Score by capability coverage
scored = []
for template in set(candidates):
coverage = len(set(template.capabilities) & set(requirements.needed_capabilities))
scored.append((coverage, template))
scored.sort(reverse=True, key=lambda x: x[0])
return scored[0][1] if scored else None
Capability-based discovery:
registry = TemplateRegistry()
registry.register(TEMPLATES["security_auditor"])
registry.register(TEMPLATES["test_coverage_analyzer"])
# Find templates for "vulnerability scanning"
matches = registry.find_by_capability("vulnerability_scan")
# → [security_auditor]
# Find best match for complex requirements
requirements = TaskRequirements(
needed_capabilities=["vulnerability_scan", "compliance_check"]
)
best = registry.find_best_match(requirements)
# → security_auditor (has both capabilities)
Creating Custom Templates
Users can define their own templates:
# Custom template for your organization
custom_template = AgentTemplate(
id="internal_code_reviewer",
role="Internal Code Standards Expert",
capabilities=["style_check", "pattern_enforcement", "team_conventions"],
tier_preference="CHEAP",
tools=["code_reader", "style_checker"],
default_instructions="""
Review code against internal standards:
- Naming conventions: {naming_style}
- Architecture patterns: {patterns}
- Forbidden patterns: {forbidden}
Be strict about: {strict_rules}
Be lenient about: {lenient_rules}
""",
quality_gates={"min_style_score": 0.9},
category="code_review",
)
# Register it
registry.register(custom_template)
# Now available for orchestration
result = orchestrate("review PR for internal standards")
# → Uses internal_code_reviewer template
Performance Characteristics
| Operation | Latency | Notes |
|---|---|---|
| Template lookup | <1ms | Dictionary lookup |
| Tier selection | <1ms | Simple arithmetic |
| Instruction customization | <5ms | String formatting |
| Tool filtering | <1ms | Set intersection |
| Full spawn | <10ms | All operations combined |
Spawning is cheap. The real cost is agent execution, not creation.
Key Takeaways
Templates define capabilities, not behavior — The same security_auditor template spawns different agents for auth audits vs. API audits
Dynamic customization at spawn time — Instructions, tier, tools all adapt to the task
Right-sizing is automatic — Critical tasks get premium resources, simple tasks get efficient execution
Capability-based discovery — System finds appropriate templates by matching capabilities to requirements
Extensible by design — Add custom templates for your organization's needs
What's Next
In Part 3: Building Agent Teams, we'll see how multiple spawned agents combine into teams using composition patterns.
The Agent Factory code is available in Empathy Framework v4.4.0.
*Patrick Roebuck is the creator of the Empathy Framework.
Top comments (0)