Skip to content

System Prompts

The system prompt tells Iris who it is and what it knows about you. It's assembled dynamically for each request, combining a static persona with contextual information like memories, summaries, and calendar events.

How It Works

Every time you send a message, Iris builds a system prompt by rendering a series of prompt classes in order. Each class is responsible for one section of the prompt:

┌─────────────────────────────┐
│  IrisStaticPrompt           │  ← Core identity and personality
├─────────────────────────────┤
│  MemoryPrompt               │  ← Truths + contextual memories
├─────────────────────────────┤
│  SkillsPrompt               │  ← Available agent skills
├─────────────────────────────┤
│  SummaryPrompt              │  ← Recent conversation summaries
├─────────────────────────────┤
│  CalendarPrompt             │  ← Upcoming calendar events
├─────────────────────────────┤
│  CurrentTimePrompt          │  ← Current date and time
└─────────────────────────────┘

The result is a personalized, context-aware prompt that includes everything Iris needs to respond appropriately.

Architecture

Prompts are self-contained: each prompt injects a RequestContext and any services it needs, then fetches its own context when content() is called. This provides:

  • Uniform pattern: Core and custom prompts work identically
  • Testability: Prompts can be tested in isolation
  • Flexibility: Each prompt injects only what it needs
  • Conditional rendering: Prompts can return empty content if they have nothing to contribute

Prompt Pipeline

Each prompt is a separate class rendered in order. The default pipeline:

Static Prompt (Cached)

Core identity and behavior that rarely changes:

  • Identity and personality
  • Communication style
  • Tool usage guidelines

Cached for 1 hour for performance.

Memory Prompt

Recalls relevant context for the current message:

  • Truths: Stable, core facts ranked by relevance (pinned Truths always included)
  • Memories: Semantically similar memories found via search
  • Only renders when Truths or memories exist

Skills Prompt

Lists available agent skills for Iris to activate:

  • Skill names and descriptions from .agents/skills/
  • Only renders when skills are enabled and available

Summary Prompt

Injects recent conversation summaries for continuity:

  • Narrative arc from previous conversations
  • Emotional context and relationship dynamics
  • Only renders when summaries exist

Calendar Prompt

Retrieves upcoming calendar events for the current user:

  • Upcoming events for the next 7 days
  • Calendar names and default calendar info
  • Only renders when events exist

Current Time Prompt

Provides the current date and time in the configured timezone. Always renders.

Prompt Templates

Templates are Blade files in resources/views/prompts/:

prompts/
├── personas/
│   └── iris-static.blade.php       # Core identity
├── recalled-context.blade.php      # Memory context
├── calendar-context.blade.php      # Calendar events
└── summary-context.blade.php       # Conversation summaries

Customizing Prompts

Create your own prompt classes to customize Iris's behavior. Each prompt is self-contained and injects the dependencies it needs.

The content() method can return either a string or a Blade view:

php
<?php

declare(strict_types=1);

namespace App\Extensions\Prompts;

use App\Prompts\Prompt;
use App\ValueObjects\RequestContext;
use Illuminate\View\View;

class WeatherPrompt extends Prompt
{
    public function __construct(
        protected RequestContext $requestContext,
        protected WeatherService $weather,
    ) {}

    // Return a Blade view for complex templates
    public function content(): View
    {
        return view('prompts.extensions.weather', [
            'forecast' => $this->weather->getForecast(
                $this->requestContext->user()?->id
            ),
        ]);
    }
}

For simpler prompts, return a string directly:

php
<?php

declare(strict_types=1);

namespace App\Extensions\Prompts;

use App\Prompts\Prompt;

class TimezonePrompt extends Prompt
{
    public function content(): string
    {
        return "## User Timezone\n\nThe user is in the Pacific timezone.";
    }
}

The RequestContext API

The RequestContext value object provides access to everything about the current request:

MethodReturnsDescription
user()User|nullThe authenticated user model, or null if unauthenticated
message()stringThe user's message text for this request
images()arrayArray of attached image data (for multi-modal requests)

Using RequestContext

Every prompt receives a RequestContext via constructor injection. Use it to customize prompt content based on the current request:

php
public function __construct(
    protected RequestContext $requestContext,
) {}

public function content(): string
{
    $user = $this->requestContext->user();

    if (! $user) {
        return ''; // No content for unauthenticated requests
    }

    // Customize based on user or message
    $message = $this->requestContext->message();

    if (str_contains(strtolower($message), 'urgent')) {
        return "## Priority Mode\n\nThe user has indicated urgency.";
    }

    return '';
}

Registering Custom Prompts

Add your prompt to config/iris-custom.php. The prompts array replaces the default list, so include the core prompts you want to keep:

php
// config/iris-custom.php
return [
    'prompts' => [
        App\Prompts\IrisStaticPrompt::class,
        App\Prompts\MemoryPrompt::class,
        App\Prompts\SummaryPrompt::class,
        App\Extensions\Prompts\WeatherPrompt::class,  // Your custom prompt
        App\Prompts\CalendarPrompt::class,
        App\Prompts\CurrentTimePrompt::class,
    ],
];

Prompt Ordering Considerations

Prompts are rendered in order, and that order matters. The system prompt flows from general identity to specific context:

  1. Static identity first: Establishes who Iris is and how it should behave
  2. Memories second: Facts about the user that inform the response
  3. Summaries third: Historical context from previous conversations
  4. Integrations fourth: Calendar, weather, or other external data
  5. Temporal context last: Current date/time anchors everything

When adding custom prompts, consider where the information fits logically. User-specific context (like project information) typically goes after memories but before temporal context.

Complete Custom Prompt Example

Here's a full example of creating and registering a custom prompt that injects work project context:

1. Create the Prompt Class

php
<?php

declare(strict_types=1);

namespace App\Extensions\Prompts;

use App\Models\User;
use App\Prompts\Prompt;
use App\Services\ProjectService;
use App\ValueObjects\RequestContext;
use Illuminate\View\View;

class WorkContextPrompt extends Prompt
{
    public function __construct(
        protected RequestContext $requestContext,
        protected ProjectService $projectService,
    ) {}

    public function content(): View|string
    {
        $user = $this->requestContext->user();

        if (! $user) {
            return '';
        }

        $projects = $this->projectService->getActiveProjects($user->id);

        if ($projects->isEmpty()) {
            return '';
        }

        return view('prompts.extensions.work-context', [
            'projects' => $projects,
        ]);
    }
}

2. Create the Blade Template

blade
{{-- resources/views/prompts/extensions/work-context.blade.php --}}
## Current Work Projects

The user is currently working on these projects:

@foreach($projects as $project)
- **{{ $project->name }}**: {{ $project->description }}
  - Status: {{ $project->status }}
  - Deadline: {{ $project->deadline?->format('F j, Y') ?? 'No deadline' }}
@endforeach

Use this context when the user asks about work or projects.

3. Register the Prompt

php
// config/iris-custom.php
return [
    'prompts' => [
        App\Prompts\IrisStaticPrompt::class,
        App\Prompts\MemoryPrompt::class,
        App\Prompts\SummaryPrompt::class,
        App\Extensions\Prompts\WorkContextPrompt::class,  // After summaries
        App\Prompts\CalendarPrompt::class,
        App\Prompts\CurrentTimePrompt::class,
    ],
];

Caching Static Prompts

For prompts with content that rarely changes, you can enable caching to reduce token costs:

php
public function providerOptions(): array
{
    return [
        'cacheType' => 'ephemeral',
    ];
}

The IrisStaticPrompt uses this to cache the core identity prompt for 1 hour. Dynamic prompts (memories, summaries) shouldn't be cached since their content changes with each request.