The system prompt defines your agent's role, tone, and constraints. Yao supports a default prompt plus multiple named presets that can be selected at runtime.
## The Default Prompt
`prompts.yml` at the root of your assistant directory is the default system prompt. It's sent to the LLM at the start of every conversation unless overridden.
```yaml
# assistants/my-assistant/prompts.yml
- role: system
content: |
You are a helpful writing assistant.
Focus on clarity and brevity.
Ask for clarification when the request is ambiguous.
```
Typically only `system` role is used here, though the engine does not enforce a role restriction.
### Context Variables
Use built-in variables inside prompt content:
```yaml
- role: system
content: |
Today is $SYS.DATE ($SYS.WEEKDAY). Current time: $SYS.TIME.
User locale: $CTX.LOCALE. User ID: $CTX.USER_ID.
```
| Variable | Value |
|----------|-------|
| `$SYS.DATE` | Current date, e.g. `2026-04-20` |
| `$SYS.TIME` | Current time, e.g. `14:30:00` |
| `$SYS.DATETIME` | Date + time |
| `$SYS.TIMEZONE` | System timezone |
| `$SYS.WEEKDAY` | Day of week, e.g. `Monday` |
| `$CTX.LOCALE` | User locale, e.g. `en-us` |
| `$CTX.USER_ID` | Current user ID |
| `$CTX.TEAM_ID` | Current team ID |
| `$CTX.CHAT_ID` | Current chat session ID |
| `$ENV.KEY` | Any environment variable |
### Embedding External Files
Reference an external file from the `assets/` directory. Supported types: `.md`, `.txt`, `.yml`, `.yaml`, `.json`.
```yaml
- role: system
content: |
@assets/guidelines.md
@assets/examples.txt
@assets/config.json
```
The file content is inlined at load time.
## Multiple Presets
Create a `prompts/` directory for additional named presets. Each file is a preset identified by its filename (without extension).
```
assistants/my-assistant/
├── prompts.yml ← default prompt (always loaded unless overridden)
└── prompts/
├── edit.yml ← preset name: "edit"
└── review.yml ← preset name: "review"
```
Each preset file has the same format as `prompts.yml`:
```yaml
# assistants/my-assistant/prompts/edit.yml
- role: system
content: |
You are now in edit mode. The user is revising existing content.
Focus on improving clarity and fixing errors.
Preserve the original intent.
```
## Switching Presets in a Create Hook
Return `prompt_preset` from your `Create` Hook to select a preset for the current request:
```typescript
// assistants/my-assistant/src/index.ts
export function Create(
ctx: agent.Context,
messages: agent.Message[]
): agent.Create {
// Detect edit mode from URL parameter
const isEdit = ctx.route?.includes("?mode=edit");
if (isEdit) {
return { messages, prompt_preset: "edit" };
}
return { messages }; // use default prompt
}
```
The `prompt_preset` value maps to the filename in `prompts/`. If the preset doesn't exist, the default `prompts.yml` is used.
**Real example** — `yao-mark` switches between `create` (default) and `edit` prompts based on whether the URL contains a canvas ID:
```typescript
// From yaobots/assistants/yao/mark/src/index.ts
const existing = detectAndLoadExisting(ctx);
if (existing) {
return { messages, prompt_preset: "edit" };
}
return { messages };
```
## Nested Preset Keys
The `prompts/` directory supports subdirectories. The preset key uses `.` as separator:
```
prompts/
├── chat.yml ← key: "chat"
├── task.yml ← key: "task"
└── analysis/
└── deep.yml ← key: "analysis.deep"
```
```typescript
return { messages, prompt_preset: "analysis.deep" };
```
## Disabling Global Prompts
If your Yao instance has global system prompts configured, you can opt out:
**Per-assistant** (in `package.yao`):
```json
{
"name": "My Assistant",
"connector": "$ENV.DEFAULT_CONNECTOR",
"disable_global_prompts": true
}
```
**Per-request** (in Create Hook):
```typescript
return {
messages,
disable_global_prompts: true,
};
```
## What's Next
Your agent has a defined role. Now give it tools to work with.
→ **[Add MCP Tools](./add-tools)**