Robutler

Agent Prompts

Enhance your agent's system prompt dynamically using the @prompt decorator. Prompt functions execute before each LLM call and contribute contextual information to the system message.

Prompts run in priority order (lower runs first) and support scope-based access control. Use them for dynamic context, user-specific information, or system status updates.

Overview

Prompt functions generate dynamic content that gets appended to the agent's system message before LLM execution. They're perfect for injecting real-time context, user information, or environmental data.

Key features:

  • Dynamic system-prompt enhancement
  • Priority-based execution order
  • Scope-based access control
  • Context injection
  • Automatic string concatenation
  • Sync and async support

Basic Usage

Simple Prompt

import { BaseAgent, Skill, prompt } from 'webagents';
import type { Context } from 'webagents';

class StatusPrompts extends Skill {
  readonly name = 'status-prompts';

  @prompt()
  systemStatus(ctx: Context): string {
    return 'System Status: Online - All services operational';
  }
}

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

The agent's effective system message becomes:

You are a helpful AI assistant.

System Status: Online - All services operational

Your name is assistant, you are an AI agent in the Internet of Agents.
Current time: 2024-01-15T10:30:00

Priority-Based Execution

class ContextPrompts extends Skill {
  readonly name = 'context-prompts';

  @prompt({ priority: 5 })
  timePrompt(ctx: Context): string {
    return `Current Time: ${new Date().toISOString()}`;
  }

  @prompt({ priority: 10 })
  systemStatusPrompt(ctx: Context): string {
    return `System Status: ${getSystemStatus()}`;
  }

  @prompt({ priority: 20 })
  userContextPrompt(ctx: Context): string {
    const userId = ctx.auth?.userId ?? 'anonymous';
    return `Current User: ${userId}`;
  }
}

Prompts execute in ascending priority order (5 → 10 → 20).

Scope-Based Access Control

Control which callers see specific prompt content:

class ScopedPrompts extends Skill {
  readonly name = 'scoped-prompts';

  @prompt({ scope: 'all' })
  publicPrompt(ctx: Context): string {
    return 'Public system information';
  }

  @prompt({ scope: 'owner' })
  ownerPrompt(ctx: Context): string {
    return `Owner Dashboard: ${getOwnerStats()}`;
  }

  @prompt({ scope: 'admin' })
  adminPrompt(ctx: Context): string {
    return `DEBUG MODE: ${getDebugInfo()}`;
  }

  @prompt({ scope: ['premium', 'enterprise'] })
  premiumPrompt(ctx: Context): string {
    return 'Premium features enabled';
  }
}

Context Access

Access request context for dynamic content:

class UserPrompts extends Skill {
  readonly name = 'user-prompts';

  @prompt({ priority: 10 })
  async userContextPrompt(ctx: Context): Promise<string> {
    const userId = ctx.auth?.userId ?? 'anonymous';
    const userData = await getUserData(userId);
    return `User Context:
- Name: ${userData.name}
- Role: ${userData.role}
- Preferences: ${userData.preferences}`;
  }

  @prompt({ priority: 20 })
  async dynamicDataPrompt(ctx: Context): Promise<string> {
    const [market, weather] = await Promise.all([
      fetchMarketData(),
      fetchWeather(),
    ]);
    return `Real-time Context:
- Market: ${market.status}
- Weather: ${weather.condition}`;
  }
}

Skill Integration

Use prompts within skills for modular functionality:

import { Skill, prompt } from 'webagents';
import type { Context } from 'webagents';

class AnalyticsSkill extends Skill {
  readonly name = 'analytics';

  @prompt({ priority: 15, scope: 'owner' })
  async analyticsPrompt(ctx: Context): Promise<string> {
    const stats = await this.getAnalyticsData();
    return `Analytics Summary:
- Active Users: ${stats.activeUsers}
- Revenue Today: $${stats.dailyRevenue}
- System Load: ${stats.cpuUsage}%`;
  }

  @prompt({ priority: 25 })
  async performancePrompt(ctx: Context): Promise<string> {
    const metrics = await this.getPerformanceMetrics();
    return `Performance: ${metrics.responseTime}ms avg`;
  }

  private async getAnalyticsData() {
    return { activeUsers: 1250, dailyRevenue: 5420, cpuUsage: 23 };
  }
  private async getPerformanceMetrics() {
    return { responseTime: 150 };
  }
}

const agent = new BaseAgent({
  name: 'analytics-agent',
  model: 'openai/gpt-4o',
  skills: [new AnalyticsSkill()],
});

Advanced Patterns

Conditional Prompts

@prompt({ priority: 10 })
conditionalPrompt(ctx: Context): string {
  const userRole = (ctx.metadata.user_role as string) ?? 'guest';
  if (userRole === 'admin') return 'ADMIN MODE: Full system access enabled';
  if (userRole === 'premium') return 'PREMIUM MODE: Enhanced features available';
  return 'STANDARD MODE: Basic features';
}

@prompt({ priority: 15 })
timeBasedPrompt(ctx: Context): string {
  const hour = new Date().getHours();
  if (hour >= 6 && hour < 12) return 'Good morning! System ready for daily operations.';
  if (hour >= 12 && hour < 18) return 'Good afternoon! Peak usage period - optimized for performance.';
  return 'Good evening! Running in power-save mode.';
}

Error Handling

@prompt({ priority: 5 })
async safePrompt(ctx: Context): Promise<string> {
  try {
    const externalData = await fetchExternalService();
    return `External Status: ${externalData.status}`;
  } catch (e) {
    console.warn('External service unavailable', e);
    return 'External Status: Offline (using cached data)';
  }
}

@prompt({ priority: 10 })
async resilientAsyncPrompt(ctx: Context): Promise<string> {
  try {
    const data = await Promise.race([
      fetchSlowService(),
      new Promise<never>((_, r) => setTimeout(() => r(new Error('timeout')), 2000)),
    ]);
    return `Live Data: ${data.value}`;
  } catch (e) {
    return (e as Error).message === 'timeout'
      ? 'Live Data: Timeout (using fallback)'
      : 'Live Data: Service unavailable';
  }
}

Best Practices

Keep Prompts Concise

// Good — concise and focused
@prompt()
statusPrompt(ctx: Context): string {
  return `Status: ${getStatus()}`;
}

// Avoid — too verbose, burns tokens on every call

Use Appropriate Priorities

@prompt({ priority: 5 })  // Core system info first
systemPrompt(ctx: Context) { /* ... */ }

@prompt({ priority: 10 }) // User context second
userPrompt(ctx: Context)  { /* ... */ }

@prompt({ priority: 15 }) // Specific features last
featurePrompt(ctx: Context) { /* ... */ }

Handle Failures Gracefully

Wrap external calls; never let a prompt throw — the agent will fall back to its base instructions but you lose the contextual signal.

Integration Examples

With Authentication

@prompt({ priority: 10, scope: 'owner' })
authContextPrompt(ctx: Context): string {
  const user = ctx.auth?.user;
  if (user) return `Authenticated as: ${user.name} (${user.email})`;
  return 'Authentication: Guest user';
}

With Payment Skills

@prompt({ priority: 15, scope: 'owner' })
async billingContextPrompt(ctx: Context): Promise<string> {
  const balance = await getUserBalance(String(ctx.auth?.userId));
  const usage = await getCurrentUsage(String(ctx.auth?.userId));
  return `Billing Status:
- Balance: $${balance.toFixed(2)}
- Usage Today: ${usage} credits`;
}

With Discovery Skills

@prompt({ priority: 20 })
async networkStatusPrompt(ctx: Context): Promise<string> {
  const connected = await countConnectedAgents();
  return `Network: ${connected} agents connected`;
}

See Also

  • Tools — Executable functions for agents
  • Hooks — Event-driven processing
  • Skills — Modular agent capabilities
  • Endpoints — HTTP API routes

On this page