Composing AI Teams
This guide walks you through creating custom AI teams by combining agents, skills, and avatars from different sources. Think of it as assembling your dream team from the best available components.
Overview
MeetLoyd's composable architecture lets you:
- Mix and match agents from the Store with your custom agents
- Override skills to use your preferred integrations
- Configure avatars from multiple providers
- Version lock components for production stability
- Customize behavior without forking components
Creating Your First Custom Team
Step 1: Start with a Manifest
Create a team.manifest.json file:
{
"$schema": "https://meetloyd.com/schemas/team.manifest.json",
"name": "My Custom Team",
"slug": "my-custom-team",
"version": "1.0.0",
"description": "A custom team combining store and local components",
"components": {
"agents": [],
"skills": [],
"avatars": []
},
"orchestration": {
"mode": "hierarchical",
"entryAgentId": "coordinator"
}
}
Step 2: Add Agents
Add agents from different sources:
{
"components": {
"agents": [
{
"ref": "store://agents/sdr-agent",
"version": "^1.0.0",
"localId": "sdr",
"comment": "Sales Development Rep from the Store"
},
{
"ref": "store://agents/support-agent",
"version": "^2.0.0",
"localId": "support",
"comment": "Customer support from the Store"
},
{
"ref": "local://agents/my-coordinator",
"localId": "coordinator",
"comment": "Custom coordinator we built"
}
]
}
}
Reference types:
store://- Components from the MeetLoyd Storelocal://- Your custom componentsexternal://- Third-party integrations (avatars/voices)
Step 3: Add Skills
Skills give agents capabilities. Add shared skills:
{
"components": {
"skills": [
{
"ref": "store://skills/salesforce-crm",
"version": "^3.0.0",
"comment": "CRM integration for all agents"
},
{
"ref": "store://skills/slack-notifications",
"version": "^1.2.0"
},
{
"ref": "local://skills/internal-api",
"comment": "Our internal systems"
}
]
}
}
Step 4: Configure Avatars (Optional)
Add video/voice avatars:
{
"components": {
"avatars": [
{
"ref": "external://heygen/avatar-emma-professional",
"localId": "emma",
"config": {
"voiceProvider": "elevenlabs",
"voiceId": "rachel"
}
},
{
"ref": "internal://avatars/default",
"localId": "default-avatar"
}
]
}
}
Step 5: Define Orchestration
Configure how agents work together:
{
"orchestration": {
"mode": "hierarchical",
"entryAgentId": "coordinator",
"routing": {
"intentRouting": [
{
"intent": "sales_inquiry",
"targetAgentId": "sdr",
"priority": 1
},
{
"intent": "support_request",
"targetAgentId": "support",
"priority": 1
}
],
"fallbackAgentId": "coordinator"
}
}
}
Step 6: Deploy
Deploy via CLI or API:
# Validate first
loyd manifest validate team.manifest.json
# Deploy to your team
loyd manifest deploy team.manifest.json --team my-team
# Or deploy to staging first
loyd manifest deploy team.manifest.json --team staging-team
Mixing Store and Custom Components
The Power of Composition
You don't have to build everything from scratch. Start with proven Store components and add your custom pieces:
{
"components": {
"agents": [
{
"ref": "store://agents/enterprise-sdr",
"version": "^2.0.0",
"localId": "sdr",
"comment": "Battle-tested SDR with 4.9 stars"
},
{
"ref": "local://agents/product-specialist",
"localId": "specialist",
"comment": "Knows our specific product deeply"
}
]
}
}
Overriding Store Agent Skills
Store agents come with default skills. Override them with your preferred alternatives:
{
"agents": [
{
"ref": "store://agents/sdr-agent",
"version": "^1.0.0",
"localId": "sdr",
"skillOverrides": [
{
"ref": "local://skills/our-crm",
"replaces": "store://skills/salesforce-crm",
"comment": "Use our HubSpot integration instead"
},
{
"ref": "store://skills/linkedin-sales-nav",
"version": "^2.0.0",
"comment": "Add LinkedIn prospecting"
}
]
}
]
}
Customizing Agent Behavior
Adjust agent behavior without forking:
{
"agents": [
{
"ref": "store://agents/support-agent",
"version": "^2.0.0",
"localId": "support",
"customizations": {
"contextPromptTemplate": "You work for {{company_name}}. Our product is {{product_description}}. Always be helpful and professional.",
"memoryConfig": {
"maxContextTokens": 16000,
"summaryThreshold": 10000
},
"responseStyle": {
"maxLength": 500,
"tone": "professional"
}
}
}
]
}
Sharing Skills Across Agents
Define skills once, use across multiple agents:
{
"components": {
"skills": [
{
"ref": "store://skills/calendar-booking",
"version": "^1.0.0",
"sharedBy": ["sdr", "support", "coordinator"]
}
],
"agents": [
{ "ref": "store://agents/sdr-agent", "localId": "sdr" },
{ "ref": "store://agents/support-agent", "localId": "support" },
{ "ref": "local://agents/coordinator", "localId": "coordinator" }
]
}
}
Version Locking Strategies
Version management is critical for production stability. Here are battle-tested strategies:
Strategy 1: Development (Flexible)
Allow minor and patch updates automatically:
{
"agents": [
{
"ref": "store://agents/sdr-agent",
"version": "^1.0.0",
"comment": "Accept any 1.x.x version"
}
]
}
Pros: Always get latest features and fixes Cons: Updates might change behavior unexpectedly
Strategy 2: Staging (Moderate)
Allow only patch updates:
{
"agents": [
{
"ref": "store://agents/sdr-agent",
"version": "~1.2.0",
"comment": "Accept 1.2.x only (patches)"
}
]
}
Pros: Get bug fixes, no feature changes Cons: Miss out on new features until manual upgrade
Strategy 3: Production (Strict)
Lock to exact versions:
{
"agents": [
{
"ref": "store://agents/sdr-agent",
"version": "1.2.3",
"locked": true,
"comment": "Exact version, no auto-updates"
}
]
}
Pros: Complete predictability Cons: Manual updates required for everything
Recommended Approach
Use different strategies per environment:
# Development - flexible versions
loyd manifest deploy dev.manifest.json --team dev
# Staging - patch updates only
loyd manifest deploy staging.manifest.json --team staging
# Production - locked versions
loyd manifest deploy prod.manifest.json --team production
Example production manifest:
{
"name": "Sales Team",
"slug": "sales-team",
"version": "2.1.0",
"environment": "production",
"components": {
"agents": [
{
"ref": "store://agents/sdr-agent",
"version": "1.2.3",
"locked": true,
"localId": "sdr"
},
{
"ref": "store://agents/ae-agent",
"version": "2.0.1",
"locked": true,
"localId": "ae"
}
],
"skills": [
{
"ref": "store://skills/salesforce-crm",
"version": "3.1.2",
"locked": true
}
]
}
}
Lock Files
When you deploy, MeetLoyd generates a team.lock.json with exact resolved versions:
{
"manifestVersion": "2.1.0",
"resolved": "2026-01-15T10:30:00Z",
"checksum": "sha256:abc123...",
"agents": {
"store://agents/sdr-agent": {
"resolvedVersion": "1.2.3",
"resolvedRef": "store://agents/sdr-agent@1.2.3",
"checksum": "sha256:def456..."
}
}
}
Best practices for lock files:
- Commit lock files to version control
- Review lock file changes in PRs
- Use lock files for rollback reference
Checking for Updates
# Check all components for updates
loyd manifest check-updates --team production
# Output:
# store://agents/sdr-agent: 1.2.3 -> 1.3.0 available (minor)
# store://skills/salesforce-crm: 3.1.2 -> 3.1.5 available (patch)
Upgrading Components
# Upgrade specific component
loyd manifest upgrade store://agents/sdr-agent --team staging
# Upgrade all with patch updates only
loyd manifest upgrade --patch-only --team staging
# Dry run to see what would change
loyd manifest upgrade --dry-run --team production
Rolling Back
If an update causes issues:
# Rollback to previous version
loyd manifest rollback store://agents/sdr-agent --to-version 2 --team production
# Or redeploy from a known-good lock file
loyd manifest deploy --lock-file team.lock.2026-01-10.json --team production
Real-World Examples
Example 1: E-commerce Support Team
{
"name": "E-commerce Support",
"slug": "ecom-support",
"version": "1.0.0",
"components": {
"agents": [
{
"ref": "store://agents/support-triage",
"version": "^2.0.0",
"localId": "triage",
"customizations": {
"contextPromptTemplate": "You handle support for {{store_name}}, an e-commerce store selling {{product_category}}."
}
},
{
"ref": "store://agents/returns-specialist",
"version": "^1.0.0",
"localId": "returns"
},
{
"ref": "local://agents/order-tracker",
"localId": "orders",
"comment": "Custom agent with access to our order system"
}
],
"skills": [
{ "ref": "store://skills/shopify", "version": "^2.0.0" },
{ "ref": "store://skills/zendesk", "version": "^1.5.0" },
{ "ref": "local://skills/warehouse-api" }
]
},
"orchestration": {
"mode": "hierarchical",
"entryAgentId": "triage",
"routing": {
"intentRouting": [
{ "intent": "return_request", "targetAgentId": "returns" },
{ "intent": "order_status", "targetAgentId": "orders" },
{ "intent": "shipping_inquiry", "targetAgentId": "orders" }
],
"fallbackAgentId": "triage"
}
}
}
Example 2: Enterprise Sales Team
{
"name": "Enterprise Sales",
"slug": "enterprise-sales",
"version": "2.0.0",
"components": {
"agents": [
{
"ref": "store://agents/enterprise-sdr",
"version": "2.1.0",
"locked": true,
"localId": "sdr",
"skillOverrides": [
{
"ref": "store://skills/linkedin-sales-nav",
"version": "^2.0.0"
}
]
},
{
"ref": "store://agents/enterprise-ae",
"version": "1.5.2",
"locked": true,
"localId": "ae"
},
{
"ref": "store://agents/demo-specialist",
"version": "^1.0.0",
"localId": "demo",
"customizations": {
"contextPromptTemplate": "Focus on {{industry}} use cases. Key differentiators: {{differentiators}}"
}
}
],
"skills": [
{ "ref": "store://skills/salesforce-crm", "version": "3.1.2", "locked": true },
{ "ref": "store://skills/gong-calls", "version": "^1.0.0" },
{ "ref": "store://skills/docusign", "version": "^2.0.0" }
],
"avatars": [
{
"ref": "external://heygen/professional-sarah",
"localId": "sarah",
"assignTo": "sdr"
}
]
},
"orchestration": {
"mode": "hierarchical",
"entryAgentId": "sdr",
"routing": {
"intentRouting": [
{ "intent": "pricing_discussion", "targetAgentId": "ae", "priority": 1 },
{ "intent": "demo_request", "targetAgentId": "demo", "priority": 1 },
{ "intent": "contract_question", "targetAgentId": "ae", "priority": 2 }
],
"escalation": {
"conditions": [
{ "when": "deal_size > 100000", "escalateTo": "ae" },
{ "when": "sentiment < 0.3", "escalateTo": "ae" }
]
}
}
}
}
Example 3: Parallel Research Team
{
"name": "Research Team",
"slug": "research-team",
"version": "1.0.0",
"components": {
"agents": [
{
"ref": "store://agents/web-researcher",
"version": "^1.0.0",
"localId": "web"
},
{
"ref": "store://agents/data-analyst",
"version": "^1.0.0",
"localId": "analyst"
},
{
"ref": "store://agents/report-writer",
"version": "^1.0.0",
"localId": "writer"
}
]
},
"orchestration": {
"mode": "parallel",
"agentIds": ["web", "analyst"],
"aggregation": "merge",
"postProcessor": "writer",
"comment": "Web and analyst research in parallel, writer synthesizes"
}
}
Testing Your Team
Local Testing
# Validate manifest syntax
loyd manifest validate team.manifest.json
# Resolve versions without deploying
loyd manifest resolve team.manifest.json
# Deploy to a test team
loyd manifest deploy team.manifest.json --team test-team
Integration Testing
# Run test conversations
loyd test run --team test-team --test-file tests/conversations.yaml
# Check agent responses
loyd test assertions --team test-team
Staging Deployment
# Deploy to staging
loyd manifest deploy team.manifest.json --team staging
# Run smoke tests
loyd test smoke --team staging
# Compare with production
loyd manifest diff --team staging --compare-with production
Troubleshooting
"Component not found"
Error: Component store://agents/unknown-agent not found
Fix: Check the Store for the correct component name and ensure it's published.
"Version constraint not satisfiable"
Error: No version of store://agents/sdr-agent satisfies ^3.0.0
Fix: The requested version doesn't exist. Check available versions:
loyd store versions store://agents/sdr-agent
"Skill conflict"
Error: Skill store://skills/crm conflicts with store://skills/hubspot
Fix: Some skills are mutually exclusive. Use skillOverrides with replaces:
{
"skillOverrides": [
{
"ref": "store://skills/hubspot",
"replaces": "store://skills/crm"
}
]
}
"Circular dependency"
Error: Circular routing detected: sdr -> ae -> sdr
Fix: Review your orchestration routing to remove cycles. Use fallbackAgentId instead of circular routes.
Best Practices Summary
- Start with Store components - Don't reinvent the wheel
- Customize, don't fork - Use
customizationsandskillOverrides - Lock production versions - Use exact versions with
locked: true - Test in staging first - Always deploy to staging before production
- Commit lock files - Track exact versions in version control
- Monitor updates - Regularly check for security patches
- Document customizations - Use
commentfields liberally - Use semantic versioning - For your own manifest versions
Using Model Aliases
Instead of tracking specific model version strings, use -latest aliases that resolve automatically:
agents:
- slug: coordinator
name: Team Coordinator
model: claude-sonnet-latest # Always the latest Sonnet
- slug: researcher
name: Research Agent
model: gemini-flash-latest # Fast + cheap for research
- slug: analyst
name: Data Analyst
model: gpt-latest # GPT-4o
This makes manifests portable — update the alias map centrally instead of editing every manifest when models change.
Securing Agents with Authorization
Declare what resources each agent can access directly in your manifest:
agents:
- slug: crm-agent
name: CRM Agent
model: claude-sonnet-latest
tools: [crm, email]
authorization:
- resource: "crm_object_type:hubspot/contacts"
relation: editor
- resource: "crm_object_type:hubspot/deals"
relation: reader
- resource: "email_domain:acme.com"
relation: sender
Permissions are provisioned as OpenFGA tuples at deploy-time. This enforces least-privilege — agents can only access explicitly granted resources.
Adding Agent Identity
For agents that need to authenticate to external services, declare SPIFFE identity config:
agents:
- slug: integration-agent
name: API Integration Agent
model: claude-sonnet-latest
identity:
spiffe:
audiences:
- "https://api.partner.com"
- "https://webhooks.stripe.com"
svidTTL: 3600
The agent receives a SPIFFE ID automatically at deploy-time and can request JWT-SVIDs scoped to the declared audiences.
Development Workflow with Hot-Reload
When developing catalog manifests locally, changes are picked up automatically:
- Start the dev server:
bun run dev - Edit any YAML in
manifests/catalog/ - The catalog cache invalidates automatically (300ms debounce)
- Your next API call uses the updated manifest
[catalog] Cache invalidated — my-team.yaml changed
No restart needed. Hot-reload is automatic in development and disabled in production.
Next Steps
- Team Manifests Reference - Complete manifest specification
- Avatar Providers - Configure video/voice avatars
- Store Browsing - Find components in the Store
- Publishing to Store - Share your components
- Agent Authorization - OpenFGA authorization details
- Agent Identity - SPIFFE identity system