117 lines
8.9 KiB
Markdown
117 lines
8.9 KiB
Markdown
# Prompt Template System
|
|
|
|
## Template language: Inja, NOT Jinja2
|
|
|
|
`[verified]` from `prompts/agent_prompt_helper.prompt:138-310`, which contains the canonical syntax reference embedded into the in-game prompt editor.
|
|
|
|
Inja is a C++ Jinja2-inspired template engine. It looks similar but is **strictly more limited**.
|
|
|
|
### Key differences from Jinja2 worth knowing
|
|
|
|
- **Limited filter set.** Standard Jinja filters like `default`, `upper`, `length`, `join`, `sort` are present; less common ones may not be.
|
|
- **`{% set %}` only affects render context** — does NOT mutate input data.
|
|
- **Array manipulation functions return new arrays** — `append`, `extend`, etc. don't modify originals.
|
|
- **Template inheritance is limited:** `{% extends "..." %}` + `{% block name %}...{% endblock %}` works; multiple inheritance does not.
|
|
- **No macros** in the Jinja2 sense.
|
|
- **Backslashed paths in includes:** `render_template("components\\event_history_compact")` — the `\\` is the path separator in include calls.
|
|
|
|
### SkyrimNet-specific Inja extensions
|
|
|
|
**Section markers** split a single `.prompt` file into multiple chat messages:
|
|
```
|
|
[ system ]
|
|
... system message content ...
|
|
[ end system ]
|
|
|
|
[ user ]
|
|
... user message content ...
|
|
[ end user ]
|
|
|
|
[ assistant ]
|
|
... assistant message content (rare) ...
|
|
[ end assistant ]
|
|
```
|
|
|
|
**Special blocks:**
|
|
- `[ raw ] ... [ end raw ]` — prevents Inja parsing of `{{...}}` etc. Used for embedded examples that contain template syntax literally.
|
|
- `[ cache ] ... [ end cache ]` — marks a block as cacheable for prompt caching across requests.
|
|
|
|
**Include functions:**
|
|
- `render_template("path\\with\\backslashes")` — include another `.prompt` from `prompts/`.
|
|
- `render_subcomponent("subdir", render_mode)` — render numbered submodules from a subdir in load order.
|
|
- `render_character_profile("mode", uuid)` — render an NPC's bio with a render mode (`full`, `target`, `transform`, `thoughts`, `short_inline`, `interject_inline`).
|
|
|
|
**Decorators** (DLL-registered functions callable from templates):
|
|
- `decnpc(uuid)` — universal NPC info accessor; returns object with `name`, `firstName`, `lastName`, `race`, `gender`, pronouns (`subjectivePronoun`, `objectivePronoun`, `possessivePronoun`, `reflexivePronoun`), `level`, `health`, `magicka`, `stamina`, `faction[]`, `isInCombat`, `isHostile`, `isDead`, `isBusy`, all skill values.
|
|
- 30+ other decorators observed in trace dump: `is_in_faction`, `is_player`, `get_arousal_state`, `format_event`, `short_time`, `get_name`, `get_nearby_npc_list`, `get_recent_events`, `get_quest_stage`, `is_narration_enabled`, `get_relevant_memories`, `get_scene_context`, `get_world_knowledge`, `papyrus_util`, `outfit_context`, etc.
|
|
|
|
`[unknown]` Full decorator list with signatures — dynamically generated by the DLL via `prompts/documentation/main.prompt`. Would require dumping the in-game web UI's `/api/documentation` endpoint to enumerate.
|
|
|
|
---
|
|
|
|
## The three-layer override architecture `[verified]`
|
|
|
|
Load priority **high → low** (later layers shadow earlier ones for the same path):
|
|
|
|
| # | Path | Purpose | Notes |
|
|
|---|---|---|---|
|
|
| 1 | `overwrite/SKSE/Plugins/SkyrimNet/prompts/` | **Runtime overrides.** Edits via the in-game prompt editor land here. | Selectively populated. Contains epoch-stamped backups (`agent_chat.prompt.backup.1776372078`). Also hosts runtime-generated content: `dynamic_character_bio.prompt` (~173KB regenerated per session), `_saves/`, `characters/` (per-actor profiles). |
|
|
| 2 | `mods/SkyrimNet/SKSE/Plugins/SkyrimNet/prompts/` | **Shipped baseline + dev/user edits.** | Where our `gamemaster_action_selector.prompt` fix lives. The `.backup` from the fix is here too: `gamemaster_action_selector.prompt.backup`. |
|
|
| 3 | `mods/SkyrimNet/SKSE/Plugins/SkyrimNet/original_prompts/` | **Pristine upstream baseline.** Useful for diffing to detect upstream changes. | `[hypothesis]` Only `characters/` and `submodules/system_head/` are populated; the rest of upstream prompts may be assumed copied to `prompts/` at install time, or this directory only holds pristine *overrideable* defaults. |
|
|
|
|
**Practical implication:** before editing any prompt, check if a runtime override exists in `overwrite/prompts/`. If it does, *that* is the active version — editing in `mods/prompts/` will be silently shadowed.
|
|
|
|
`[verified]` overlay pattern is the same shape as Linux `/etc/foo.conf` + `/etc/foo.conf.d/` + package defaults.
|
|
|
|
---
|
|
|
|
## Submodule numbering convention `[verified]`
|
|
|
|
Submodules in `prompts/submodules/<category>/` are loaded in numerical filename order (e.g. `0010_*.prompt` before `0750_*.prompt`). The numbering carves out conventional ranges:
|
|
|
|
| Range | Conventional purpose | Examples |
|
|
|---|---|---|
|
|
| `0010_…0099_` | Meta/header content (instructions, scene context bootstrap) | `0010_instructions.prompt`, `0250_omnisight.prompt` |
|
|
| `0100_…0499_` | Content sections (summary, background, personality, appearance, equipment) | character bio sections |
|
|
| `0500_…0799_` | Guidelines and behavioral rules | `0750_embedded_actions.prompt` (the ACTION: format spec) |
|
|
| `0800_…0999_` | Late instructions and special toggles | direct narration, recent state changes |
|
|
| `7000_…7999_` | Memories and progression | NPC memory blocks |
|
|
| `9990_` | Speech style — rendered last to override earlier voice instructions | speech style submodules |
|
|
|
|
Earlier numbers establish baseline; higher numbers customize/override. Fits the late-binding-wins pattern.
|
|
|
|
---
|
|
|
|
## Subdirectory map of `prompts/` `[verified]`
|
|
|
|
| Dir | Purpose | Sample contents |
|
|
|---|---|---|
|
|
| `prompts/` (root) | Top-level entry-point prompts (one per agent type) | `dialogue_response.prompt`, `gamemaster_action_selector.prompt`, `native_action_selector.prompt`, `agent_chat.prompt`, `gamemaster_scene_planner.prompt`, `player_dialogue.prompt`, `player_thoughts.prompt` |
|
|
| `prompts/components/` | Reusable building blocks for `render_template("components\\X")` | `event_history.prompt`, `event_history_compact.prompt`, `event_history_verbose.prompt`, `agent_tools_base.prompt`, `memory_access.prompt`, character_bio variants |
|
|
| `prompts/components/context/` | Context-specific component variants | `scene_context.prompt`, `component_npc_state_summary.prompt` |
|
|
| `prompts/submodules/system_head/` | System-prompt prelude pieces | `0010_instructions.prompt`, `0250_omnisight.prompt`, … |
|
|
| `prompts/submodules/user_final_instructions/` | User-message tail pieces | `0750_embedded_actions.prompt` (ACTION: format), audio tag rules, recent state changes |
|
|
| `prompts/submodules/guidelines/` | Cross-cutting style rules | `0900_response_format.prompt` (asterisk narration rules, length limits) |
|
|
| `prompts/submodules/character_bio/` | Per-actor bio submodules merged into `dynamic_character_bio.prompt` | 17 numbered files: header, summary, personality, equipment, etc. |
|
|
| `prompts/submodules/omnisight_*/` | Vision-model description templates (one dir per target type: actor, scene, item, location, furniture, default) | matched 1:1 by `prompts/omnisight/describe_*.prompt` |
|
|
| `prompts/submodules/test_decorators/` | Test-data harness for the in-game template debugger | enabled by MCM toggle |
|
|
| `prompts/target_selectors/` | Meta classifier prompts for "who speaks next" | `dialogue_speaker_selector.prompt`, `player_dialogue_target_selector.prompt` |
|
|
| `prompts/transformers/` | Text→text transformations | `native_dialogue_transformer.prompt`, `universal_translator.prompt` |
|
|
| `prompts/memory/` | Memory generation, ranking, mood evaluation | `generate_memory.prompt`, `memory_ranker.prompt`, `mood_evaluator.prompt` |
|
|
| `prompts/helpers/` | Standalone classifier/profile generation prompts | `evaluate_mood.prompt`, `generate_profile.prompt`, `generate_search_query.prompt` |
|
|
| `prompts/omnisight/` | Vision-model prompts (one per target type) | `describe_actor.prompt`, `describe_scene.prompt`, `describe_item.prompt`, `describe_location.prompt`, `describe_furniture.prompt` |
|
|
| `prompts/dev/` | Developer test prompts | `mcm_test.prompt` |
|
|
| `prompts/documentation/` | Auto-generated decorator documentation rendered into the in-game web UI | `main.prompt`, `category.prompt` |
|
|
| `prompts/web/` | Web-UI bundled base templates | `bundled_base.prompt` |
|
|
| `prompts/translation/{generic,unique}/` | Localization CSVs | `00_SkyrimNet_generic.csv`, `00_SkyrimNet_unique.csv` |
|
|
|
|
---
|
|
|
|
## Editing prompts safely — checklist
|
|
|
|
1. **Is there an override in `overwrite/prompts/`?** If yes, that's the active version. Edit it (the in-game UI does this) or delete it to fall back to `mods/prompts/`.
|
|
2. **Diff against `original_prompts/`** to see what the upstream baseline says.
|
|
3. **Make a `.backup` next to the file** before significant edits — SkyrimNet itself does this convention with epoch-stamped backups; we use the simpler `.backup` suffix.
|
|
4. **Hot-reload picks up changes** — no need to restart Skyrim for prompt edits, but model behavior may take effect on the next agent firing.
|
|
5. **Test against logs** — watch `openrouter_input.log` to see what prompt text the model actually receives after your edit.
|