> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ensemble.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Agents

> Reusable workers that compose operations into cohesive functionality. Version, test, and deploy independently.

Think of agents as the workers in your system - each one has a specific job and does it well.

## Agent Hierarchy

```
Components (prompts, configs)  -> Versioned artifacts
    |
Agents (workers)               -> Reusable logic
    |
Ensembles (orchestration)      -> Workflows
```

## Anatomy of an Agent

An agent has:

* **Inputs**: Parameters it accepts
* **Operation**: The type of work it performs (code, think, http, etc.)
* **Handler** (for code operations): TypeScript file that implements the logic
* **Outputs**: Data it returns

### The Handler Pattern

For custom logic, agents use the **handler pattern** which separates YAML contracts from TypeScript implementation:

**YAML (Contract)** - Declares WHAT the agent does:

```yaml theme={null}
# agents/my-agent/my-agent.yaml
name: my-agent
operation: code
handler: ./my-agent.ts
description: What this agent does

schema:
  input:
    query: string
  output:
    result: string
```

**TypeScript (Implementation)** - Defines HOW it works:

```typescript theme={null}
// agents/my-agent/my-agent.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default async function handler(ctx: AgentExecutionContext) {
  const { query } = ctx.input
  return { result: `Processed: ${query}` }
}
```

This pattern keeps agents:

* **Testable** - Pure TypeScript functions
* **Type-safe** - Full TypeScript type checking
* **Debuggable** - Standard debugging tools work
* **Maintainable** - Clear separation of concerns

### Multi-Operation Agent Example

For agents with multiple operations, use YAML flow:

```yaml theme={null}
# agents/company-enricher/agent.yaml
agent: company-enricher
description: Enriches company data from multiple sources

inputs:
  company_name:
    type: string
    required: true
  include_news:
    type: boolean
    default: false

operations:
  # Search for company
  - name: search
    operation: http
    config:
      url: https://api.duckduckgo.com/?q=${input.company_name}&format=json

  # Scrape website
  - name: scrape
    operation: http
    config:
      url: ${search.output.AbstractURL}

  # Extract with AI
  - name: extract
    operation: think
    config:
      provider: openai
      model: gpt-4o-mini
      prompt: |
        Extract company info from: ${scrape.output.body}
        Return JSON: {name, description, industry, founded}

  # Optional news
  - name: fetch-news
    operation: http
    condition: ${input.include_news}
    config:
      url: https://news-api.com?q=${input.company_name}

outputs:
  company_data: ${extract.output}
  news: ${fetch-news.output}
  source: ${search.output.AbstractURL}
```

## Agent Types

Conductor provides three categories of agents:

### 1. System Agents

Built-in agents for core infrastructure functionality.

**Location**: `agents/system/`

**Available**:

* `redirect` - URL redirect service (permanent, expiring, single-use links)
* `docs` - Documentation generation and serving
* `fetch` - HTTP fetching with caching
* `validate` - Data validation with multiple strategies
* `scrape` - Web scraping with bot detection
* `slug` - Generate URL-safe slugs
* `tools` - MCP tools integration
* `queries` - SQL query execution

### 2. Debug Agents

Development utilities for testing and debugging.

**Location**: `agents/debug/`

**Available**:

* `echo` - Returns input unchanged (inspect data)
* `delay` - Add artificial delay (simulate slow operations)
* `inspect-context` - View execution context

### 3. Custom Agents

Agents you build for your specific needs.

**Location**: `agents/user/my-agent/`

**Example**: Company enricher, data processor, report generator

### 4. Pre-built Feature Agents

Ready-made agents for common workflows.

**Location**: Built-in, reference by name

**Available**:

* `scraper` - Web scraping
* `validator` - Data validation
* `rag` - Retrieval-augmented generation
* `hitl` - Human-in-the-loop approval
* `fetcher` - Smart HTTP fetching
* `transformer` - Data transformation
* `scheduler` - Task scheduling

See [Starter Kit](/conductor/starter-kit/overview) for details.

## Using Agents

### In Ensembles

```yaml theme={null}
# ensembles/enrich-company.yaml
ensemble: enrich-company

agents:
  # Use custom agent
  - name: enricher
    agent: company-enricher
    inputs:
      company_name: ${input.company}
      include_news: true

  # Use pre-built agent
  - name: validate
    agent: validator
    inputs:
      data: ${enricher.output.company_data}
      schema: company-schema

output:
  data: ${enricher.output.company_data}
  valid: ${validate.output.valid}
```

### Directly via API

```typescript theme={null}
const result = await conductor.executeAgent('company-enricher', {
  company_name: 'Anthropic',
  include_news: true
});
```

## Agent Composition

Agents can use other agents:

```yaml theme={null}
agent: full-company-profile

inputs:
  company_name:
    type: string
    required: true

operations:
  # Use company-enricher agent
  - name: enrich
    agent: company-enricher
    inputs:
      company_name: ${input.company_name}

  # Use scraper agent
  - name: scrape-social
    agent: scraper
    inputs:
      url: https://linkedin.com/company/${input.company_name}

  # Merge results
  - name: merge
    operation: code
    config:
      script: scripts/merge-company-profiles
    input:
      company_data: ${enrich.output.company_data}
      social_data: ${scrape-social.output}

outputs:
  profile: ${merge.output}
```

```typescript theme={null}
// scripts/merge-company-profiles.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default function mergeCompanyProfiles(context: AgentExecutionContext) {
  const { company_data, social_data } = context.input

  return {
    ...company_data,
    social: social_data
  }
}
```

## Versioning Agents

Version agents with Edgit:

```bash theme={null}
# Register agent
edgit components add company-enricher agents/company-enricher/ agent

# Create version
edgit tag create company-enricher v1.0.0

# Tag for production
edgit tag set company-enricher prod v1.0.0
edgit push --tags --force
```

Lock to specific versions in ensembles:

```yaml theme={null}
agents:
  - name: enricher
    agent: company-enricher@v1.0.0  # Pinned to v1.0.0
    inputs:
      company_name: ${input.company}
```

## Agent Features

### State Management

Share state across operations:

```yaml theme={null}
agent: stateful-processor

state:
  schema:
    processed_count: number
    items: array

operations:
  - name: process
    operation: code
    config:
      script: scripts/process-item
    input:
      item: ${input.item}
      current_items: ${state.items || []}
    state:
      use: [items]
      set:
        items: ${process.output.items}
        processed_count: ${process.output.count}

outputs:
  count: ${state.processed_count}
```

```typescript theme={null}
// scripts/process-item.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default function processItem(context: AgentExecutionContext) {
  const { item, current_items } = context.input

  return {
    items: [...current_items, item],
    count: current_items.length + 1
  }
}
```

### Caching

Cache entire agent execution:

```yaml theme={null}
agent: expensive-analysis

cache:
  ttl: 3600
  key: analysis-${input.document_id}

operations:
  # ...operations...
```

Or cache individual operations:

```yaml theme={null}
operations:
  - name: scrape
    operation: http
    config:
      url: ${input.url}
    cache:
      ttl: 86400  # 24 hours
```

### Error Handling

Graceful failure handling:

```yaml theme={null}
operations:
  - name: try-primary
    operation: http
    config:
      url: https://primary-api.com
    retry:
      maxAttempts: 2

  - name: fallback
    operation: http
    condition: ${try-primary.failed}
    config:
      url: https://backup-api.com

outputs:
  data: ${try-primary.output || fallback.output}
  source: ${try-primary.executed ? 'primary' : 'fallback'}
```

## Agent Patterns

### Pattern 1: Data Pipeline

```yaml theme={null}
agent: data-pipeline

inputs:
  raw_data: object

operations:
  - name: validate
    agent: validator
    inputs:
      data: ${input.raw_data}

  - name: transform
    agent: transformer
    condition: ${validate.output.valid}
    inputs:
      data: ${input.raw_data}

  - name: store
    operation: storage
    condition: ${validate.output.valid}
    config:
      type: d1
      query: INSERT INTO data (json) VALUES (?)
      params: [${transform.output}]

outputs:
  success: ${validate.output.valid}
  stored: ${store.executed}
```

### Pattern 2: AI Chain

```yaml theme={null}
agent: content-analyzer

inputs:
  text: string

operations:
  - name: extract-entities
    operation: think
    config:
      provider: openai
      model: gpt-4o-mini
      prompt: Extract entities from: ${input.text}

  - name: analyze-sentiment
    operation: think
    config:
      provider: openai
      model: gpt-4o-mini
      prompt: Analyze sentiment: ${input.text}

  - name: summarize
    operation: think
    config:
      provider: openai
      model: gpt-4o-mini
      prompt: Summarize: ${input.text}

outputs:
  entities: ${extract-entities.output}
  sentiment: ${analyze-sentiment.output}
  summary: ${summarize.output}
```

### Pattern 3: API Orchestration

```yaml theme={null}
agent: multi-source-aggregator

inputs:
  user_id: string

operations:
  # Parallel API calls
  - name: fetch-profile
    operation: http
    config:
      url: https://api1.com/users/${input.user_id}

  - name: fetch-activity
    operation: http
    config:
      url: https://api2.com/activity?user=${input.user_id}

  - name: fetch-preferences
    operation: http
    config:
      url: https://api3.com/prefs/${input.user_id}

  # Merge results
  - name: merge
    operation: code
    config:
      script: scripts/merge-user-data
    input:
      profile: ${fetch-profile.output.body}
      activity: ${fetch-activity.output.body}
      preferences: ${fetch-preferences.output.body}

outputs:
  user_data: ${merge.output}
```

```typescript theme={null}
// scripts/merge-user-data.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default function mergeUserData(context: AgentExecutionContext) {
  const { profile, activity, preferences } = context.input

  return {
    profile,
    activity,
    preferences
  }
}
```

## Testing Agents

```typescript theme={null}
// agents/company-enricher/agent.test.ts
import { describe, it, expect } from 'vitest';
import { TestConductor } from '@ensemble-edge/conductor/testing';

describe('company-enricher', () => {
  it('should enrich company data', async () => {
    const conductor = await TestConductor.create();
    await conductor.loadProject('./');

    const result = await conductor.executeAgent('company-enricher', {
      company_name: 'Anthropic',
      include_news: false
    });

    expect(result).toBeSuccessful();
    expect(result.output.company_data).toHaveProperty('name');
    expect(result.output.company_data).toHaveProperty('industry');
  });

  it('should include news when requested', async () => {
    const conductor = await TestConductor.create();
    await conductor.loadProject('./');

    const result = await conductor.executeAgent('company-enricher', {
      company_name: 'Anthropic',
      include_news: true
    });

    expect(result.output.news).toBeDefined();
  });
});
```

## Using Agents in TypeScript Ensembles

Agents defined in YAML can be used seamlessly in TypeScript ensembles:

```typescript theme={null}
import { createEnsemble, step, parallel } from '@anthropic/conductor'

const enrichAndAnalyze = createEnsemble('enrich-and-analyze')
  // Use custom YAML agent
  .addStep(
    step('enricher')
      .agent('company-enricher')  // References agents/company-enricher/agent.yaml
      .input({
        company_name: '${input.company}',
        include_news: true
      })
  )
  // Use pre-built agent
  .addStep(
    step('validate')
      .agent('validator')
      .input({
        data: '${enricher.output.company_data}',
        schema: 'company-schema'
      })
  )
  // Parallel agent execution
  .addStep(
    parallel('multi-scrape')
      .steps(
        step('scrape-linkedin').agent('scraper').input({ url: '${input.linkedinUrl}' }),
        step('scrape-twitter').agent('scraper').input({ url: '${input.twitterUrl}' })
      )
  )
  .build()

export default enrichAndAnalyze
```

### Agent Composition Patterns

Compose multiple agents into reusable patterns:

```typescript theme={null}
import { createEnsemble, step, tryStep } from '@anthropic/conductor'

// Resilient data fetching pattern
const resilientFetch = createEnsemble('resilient-fetch')
  .addStep(
    tryStep('fetch-with-fallback')
      .try(
        step('primary')
          .agent('fetcher')
          .input({ url: '${input.primaryUrl}' })
          .retry({ maxAttempts: 2, backoff: 'exponential' })
      )
      .catch(
        step('fallback')
          .agent('fetcher')
          .input({ url: '${input.fallbackUrl}' })
      )
  )
  .build()

export default resilientFetch
```

<Note>
  For complete TypeScript API documentation, see the [TypeScript API Reference](/conductor/reference/ts-schema).
</Note>

***

## Best Practices

1. **Single Responsibility** - Each agent does one thing well
2. **Clear Inputs/Outputs** - Document what goes in and comes out
3. **Version Agents** - Use Edgit for version control
4. **Test Thoroughly** - Unit test each agent
5. **Cache Aggressively** - Cache expensive operations
6. **Handle Failures** - Always have fallbacks
7. **Keep It Declarative** - Let Conductor handle orchestration
8. **Monitor Performance** - Track execution times

## Next Steps

<CardGroup cols={2}>
  <Card title="TypeScript API" icon="code" href="/conductor/reference/ts-schema">
    TypeScript API reference
  </Card>

  <Card title="Ensembles" icon="diagram-project" href="/conductor/core-concepts/ensembles">
    Orchestrate agents
  </Card>

  <Card title="Starter Kit" icon="box" href="/conductor/starter-kit/overview">
    Use ready-made agents
  </Card>

  <Card title="Creating Agents" icon="hammer" href="/conductor/building/creating-agents">
    Build custom agents
  </Card>
</CardGroup>
