Robutler

Dynamic Agents

Load agents at runtime using the dynamic_agents parameter and resolver functions.

Overview

Dynamic agents enable runtime agent loading without pre-registration:

  • On-Demand Creation - Agents created when first requested
  • Configuration-Driven - Load from external sources (DB, API, files)
  • Flexible Updates - Change agent behavior without redeployment
  • Memory Efficient - Only create agents that are actually used

Dynamic Agent Resolver

The dynamic_agents parameter accepts a resolver function that creates agents by name:

import { BaseAgent } from 'webagents';
import { createAgentApp } from 'webagents/server';
import { serve } from 'webagents/server/node';

async function resolveAgent(agentName: string): Promise<BaseAgent | null> {
  const config = await loadConfig(agentName);
  if (!config) return null;
  return new BaseAgent({
    name: config.name,
    instructions: config.instructions,
    model: config.model,
  });
}

const app = createAgentApp({
  title: 'Dynamic Server',
  dynamicAgents: resolveAgent,
});
await serve(app, { host: '0.0.0.0', port: 8000 });

Resolver Function Signature

The resolver function must match this signature:

type AgentResolver = (agentName: string) => Promise<BaseAgent | null> | BaseAgent | null;

Parameters:

  • agent_name: The agent name from the URL path
  • Returns: BaseAgent instance or None if not found

Resolution Flow

  1. Request arrives for /agent-name/chat/completions
  2. Static Check - Look for pre-registered agents first
  3. Dynamic Call - Call dynamic_agents(agent_name) if not found
  4. Agent Creation - Resolver creates and returns BaseAgent
  5. Request Processing - Server uses the resolved agent

Configuration Sources

Database Resolver

async function dbResolver(agentName: string): Promise<BaseAgent | null> {
  const row = await db.query(
    'SELECT * FROM agents WHERE name = $1',
    [agentName],
  );
  if (!row) return null;
  return new BaseAgent({
    name: row.name,
    instructions: row.instructions,
    model: row.model,
  });
}

File-Based Resolver

import { readFile } from 'node:fs/promises';
import { existsSync } from 'node:fs';

async function fileResolver(agentName: string): Promise<BaseAgent | null> {
  const path = `agents/${agentName}.json`;
  if (!existsSync(path)) return null;
  const config = JSON.parse(await readFile(path, 'utf8'));
  return new BaseAgent(config);
}

API Resolver

async function apiResolver(agentName: string): Promise<BaseAgent | null> {
  const res = await fetch(`https://api.example.com/agents/${agentName}`);
  if (!res.ok) return null;
  const config = await res.json();
  return new BaseAgent(config);
}

Combined Static and Dynamic

Use both static agents and dynamic resolution:

const staticAgents = [
  new BaseAgent({ name: 'assistant', model: 'openai/gpt-4o' }),
  new BaseAgent({ name: 'support', model: 'openai/gpt-4o' }),
];

async function dynamicResolver(agentName: string): Promise<BaseAgent | null> {
  return loadFromDatabase(agentName);
}

const app = createAgentApp({
  agents: staticAgents,
  dynamicAgents: dynamicResolver,
});

Error Handling

Handle errors gracefully in resolvers:

async function safeResolver(agentName: string): Promise<BaseAgent | null> {
  try {
    const config = await loadConfig(agentName);
    if (!config) {
      console.info(`Agent '${agentName}' not found`);
      return null;
    }
    const agent = new BaseAgent(config);
    console.info(`Created agent '${agentName}'`);
    return agent;
  } catch (err) {
    console.error(`Failed to resolve agent '${agentName}':`, err);
    return null;
  }
}

See Also

On this page