Skip to main content

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 Store
  • local:// - Your custom components
  • external:// - 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

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

  1. Start with Store components - Don't reinvent the wheel
  2. Customize, don't fork - Use customizations and skillOverrides
  3. Lock production versions - Use exact versions with locked: true
  4. Test in staging first - Always deploy to staging before production
  5. Commit lock files - Track exact versions in version control
  6. Monitor updates - Regularly check for security patches
  7. Document customizations - Use comment fields liberally
  8. 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:

  1. Start the dev server: bun run dev
  2. Edit any YAML in manifests/catalog/
  3. The catalog cache invalidates automatically (300ms debounce)
  4. 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