Files
nimmersky/skyrimnet/prompt-templates.md

8.9 KiB

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 arraysappend, 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.