Appearance
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 summariesCustomizing 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:
| Method | Returns | Description |
|---|---|---|
user() | User|null | The authenticated user model, or null if unauthenticated |
message() | string | The user's message text for this request |
images() | array | Array 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:
- Static identity first: Establishes who Iris is and how it should behave
- Memories second: Facts about the user that inform the response
- Summaries third: Historical context from previous conversations
- Integrations fourth: Calendar, weather, or other external data
- 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.