Reorder shared D&D extraction prompt messages

This commit is contained in:
2026-07-21 14:22:46 +00:00
parent 1c9819f08e
commit 3a8a82ebc9
18 changed files with 147 additions and 99 deletions

View File

@@ -98,6 +98,18 @@ D&D prompt content. `internal/framework/promptfs` provides the domain-neutral
filesystem composition helper used to combine module-owned files with shared
domain prompt fragments.
The D&D spell, NPC, and combat-turn extractors each declare an ordered prompt
asset manifest. The manifest lists the package-owned YAML and Markdown files,
then the exact shared fragments rendered by that prompt; the same ordered list
drives both filesystem mounting and the prompt fingerprint. Shared extraction
evidence and in-world identity messages precede the cacheable transcript and
campaign-reference messages. Spell and combat prompts then render immediate
resolution and the cacheable NPC registry before their lane-specific material;
the NPC prompt renders its task and instructions instead. The identity,
transcript, and campaign-reference messages are ephemeral in all three prompts,
and the NPC registry is ephemeral where it is used by spell and combat prompts.
Unused shared assets are neither mounted nor fingerprinted.
Schema helpers load embedded JSON Schema with identity and digest metadata,
return defensive copies, and expose a diagnostics map that omits schema bytes.
The small framework registry contains only generic test schemas; production
@@ -113,8 +125,8 @@ input and an optional `application/json` `npcs` input. The extractor generates
the catalog input from its prepared
effective catalog as `{"spell_names":[...]}` using sorted canonical names only.
The shared D&D prompt assets include a generic NPC grounding fragment directly
after the campaign reference message for both extractors. When an NPC registry
is bound, the
after the campaign reference message for spell and combat prompts. When an NPC
registry is bound, the
domain registry boundary strictly decodes and identity-validates one durable
artifact, re-encodes canonical JSON, and generates a semantic digest over
those bytes. The unbound input is exactly `{"npcs":[]}`. Input digests cover

View File

@@ -47,9 +47,16 @@ LLM-backed extensions own their prompt definitions and response schemas under
package-local embedded assets. Shared filesystem composition belongs in
`internal/framework/promptfs`; reusable D&D prompt fragments, reference
declarations, prompt-input assembly, and source-unit helpers belong in
`internal/modules/dnd/shared`, which also owns bounded D&D diagnostics. Stage
contracts expose only Notarius structured-
completion types, not Scriptorium public types.
`internal/modules/dnd/shared`, which also owns bounded D&D diagnostics. The
spell, NPC, and combat-turn extractors use ordered package-local prompt
manifests for both rendering and prompt fingerprinting, so only the shared
fragments each prompt actually renders participate in either operation. Their
common prompt order is system, extraction evidence, in-world identity,
transcript, and campaign references, with ephemeral cache control on identity,
transcript, and references. Spell and combat prompts append immediate
resolution and an ephemeral NPC registry before lane-specific material; the
NPC prompt appends its task and instructions. Stage contracts expose only
Notarius structured-completion types, not Scriptorium public types.
Reference material may inform a module or prompt but must not become source
evidence. The resolver and materializer behavior is described in
@@ -182,15 +189,14 @@ source evidence.
The prompt limits each cast to its declaration and immediate resolution; it
does not follow summoned creatures, persistent effects, or other downstream
consequences through the scene. Source references must collectively support all
reported facts, using separate narrow ranges when immediate evidence is
non-contiguous. The prompt grounds caster names in in-world identities, using
the player and party references to disambiguate transcript speakers without
treating those references as event evidence. Effects describe the session as
played: model rules knowledge cannot supplement or correct the transcript, and
nonstandard adjudication is attributed to the GM or table rather than stated as
a universal rule. Structural source validation remains deterministic; semantic
claim completeness is enforced through extraction policy and evaluation.
consequences through the scene. Shared extraction-evidence and identity rules
require transcript-supported factual claims and the most specific in-world
caster identity, while campaign references only disambiguate source text.
Effects describe the session as played: model rules knowledge cannot supplement
or correct the transcript, and nonstandard adjudication is attributed to the GM
or table rather than stated as a universal rule. Structural source validation
remains deterministic; semantic claim completeness is enforced through
extraction policy and evaluation.
Both the extractor and deterministic catalog validator expose
the effective base-plus-overlay semantic digest as scoped prepared-component
@@ -223,12 +229,16 @@ The NPC extractor maps private model output to the canonical `dnd.NPCList`,
assigns source identity and deterministic NPC IDs, and preserves source
references for deterministic validation. It uses the shared campaign
references only for disambiguation and does not consume the optional NPC
registry slot. Its prompt and private response schema are package-owned.
registry slot. Its prompt and private response schema are package-owned. The
prompt uses the common evidence, identity, transcript, and campaign-reference
messages, then the NPC-specific task and instructions; only the identity,
transcript, and campaign-reference messages carry ephemeral cache control.
### `internal/modules/dnd/extract/combatturns`
The combat extractor prepares one structured request per supplied chunk using
the shared transcript, campaign-reference, and NPC-grounding prompt inputs. It
the shared extraction-evidence, identity, transcript, campaign-reference,
immediate-resolution, and NPC-grounding prompt inputs. It
maps the private response to `dnd.CombatTurnList`, assigns the current source
identity, removes exact duplicate source ranges, and orders turns by valid
source-document position while preserving malformed candidate fields for
@@ -236,8 +246,11 @@ deterministic validators. Its package-owned private response schema enforces
only the structural JSON envelope; semantic artifact constraints remain with
the validator chain. Its prepared metadata and checkpoint fingerprints contain
only prompt/schema/mapping identities plus an optional NPC registry digest.
The package exposes typed registration and is included in the production D&D
registrar with the default combat extraction chain.
The prompt renders immediate resolution and the NPC registry before the
combat-specific task and instructions; identity, transcript, campaign
references, and the NPC registry use ephemeral cache control. The package
exposes typed registration and is included in the production D&D registrar with
the default combat extraction chain.
The combat normalizer accepts only the optional structured NPC registry.
Campaign references remain extractor-only LLM context and are not materialized

View File

@@ -20,6 +20,12 @@ inputs:
messages:
- role: system
content_file: ./sharedassets/common-dnd-system.md
- role: user
content_file: ./sharedassets/common-dnd-extraction-evidence.md
- role: user
content_file: ./sharedassets/common-dnd-identity.md
cache_control:
type: ephemeral
- role: user
content_file: ./sharedassets/common-dnd-transcript.md
cache_control:
@@ -28,8 +34,12 @@ messages:
content_file: ./sharedassets/common-dnd-references.md
cache_control:
type: ephemeral
- role: user
content_file: ./sharedassets/common-dnd-immediate-resolution.md
- role: user
content_file: ./sharedassets/common-dnd-npcs.md
cache_control:
type: ephemeral
- role: user
content_file: ./task.md
- role: user

View File

@@ -1,12 +1,5 @@
Return exactly one JSON object and no explanatory text.
Return the combat_turns array even when no combat turn is established. Return
one or more actions for every turn. Use one of the supported turn_kind and
action category values. Set round to null when the transcript does not state
an explicit or unambiguous positive round number. Set resolution to null when
the transcript establishes the declaration but not an immediate resolution.
Every source reference must contain start_unit_id and end_unit_id from the
provided transcript. Do not add source_id; the extraction mapper assigns the
current source identity. Do not include fields not defined by the response
schema.
action category values. Set round to null when the transcript does not state an
explicit or unambiguous positive round number. Set resolution to null when the
transcript establishes the declaration but not an immediate resolution.

View File

@@ -5,26 +5,15 @@ participant takes a combat turn or performs a discrete interrupting combat
event. Reactions, legendary actions, lair actions, and other out-of-turn events
belong at the point where they occur in transcript chronology.
Report only the declaration and its immediate observed resolution. Immediate
resolution may include directly associated rolls, damage, healing, movement,
conditions, target outcomes, or an interruption. Do not follow consequences
that occur on later turns or elsewhere in the scene.
Exclude initiative setup without a turn or combat event, tactical planning,
table talk, rules lookup, hypothetical actions, abandoned declarations, recap
of combat outside the current passage, and downstream consequences.
Use only the supplied transcript as evidence. Do not infer a round, target,
roll, amount, condition, outcome, or action classification from D&D rules
knowledge. Preserve the session as played; attribute relevant nonstandard
rulings to the GM or table.
Do not infer a round, target, roll, amount, condition, outcome, or action
classification from D&D rules knowledge. Preserve the session as played;
attribute relevant nonstandard rulings to the GM or table.
The actor must be the in-world character or creature, not a player, transcript
speaker, or GM. Use player, party, glossary, and NPC reference material only
to disambiguate identities. Reference material is context, never combat
evidence. Unmatched actors and targets remain permitted.
Unmatched actors and targets remain permitted.
For every factual detail in a turn, cite all supporting transcript units in the
turn-level source_refs collection. Use narrow ranges when evidence is
non-contiguous. Numeric source-unit IDs identify transcript units; they do not
establish chronology outside the supplied transcript.
turn-level source_refs collection.

View File

@@ -20,8 +20,11 @@ var promptAssetManifest = shared.PromptAssetManifest{
},
SharedFiles: []string{
"common-dnd-system.md",
"common-dnd-extraction-evidence.md",
"common-dnd-identity.md",
"common-dnd-transcript.md",
"common-dnd-references.md",
"common-dnd-immediate-resolution.md",
"common-dnd-npcs.md",
},
}

View File

@@ -17,6 +17,12 @@ inputs:
messages:
- role: system
content_file: ./sharedassets/common-dnd-system.md
- role: user
content_file: ./sharedassets/common-dnd-extraction-evidence.md
- role: user
content_file: ./sharedassets/common-dnd-identity.md
cache_control:
type: ephemeral
- role: user
content_file: ./sharedassets/common-dnd-transcript.md
cache_control:

View File

@@ -1,18 +1,5 @@
Return exactly one JSON object and no explanatory text.
For every NPC record, cite one or more transcript source-unit ranges that
collectively support the canonical name, every alias, the description, and
every relationship. Use integer start_unit_id and end_unit_id values from the
transcript. Do not provide source_id; the extractor assigns it automatically.
Use narrow ranges when evidence is not contiguous and do not bridge unrelated
conversation with a broad range.
A canonical name must be the most specific in-world identity supported by the
transcript. Never use a human player, transcript speaker, or GM name when an
associated in-world character or creature is identified. Player, party, and
glossary references may disambiguate identities, but they are not transcript
evidence and cannot establish that an NPC appeared, acted, or was discussed.
Do not return a person or entity mentioned only in those references.
For every NPC record, cite transcript units that support the canonical name,
every alias, the description, and every relationship.
Descriptions must be short session records, not biographies, statistics,
alignment, motivations, or lore inferred from general D&D knowledge. Do not

View File

@@ -5,12 +5,11 @@ Include an in-world non-PC participant when the transcript establishes that it
appears, acts, speaks, or is materially discussed and gives it a proper name,
a stable alias or title, or an individually useful distinguishing description.
Exclude human players, transcript speakers, the GM as an out-of-world person,
Exclude human players, transcript speakers, and the GM as out-of-world people,
player characters identified by the player or party references, incidental or
hypothetical name drops, corrected transcription mistakes, characters mentioned
only by reference material, indistinguishable crowds or groups, and temporary
summoned creatures or spell effects without a persistent individual identity.
Return canonical in-world names rather than player names or transcript speaker
names. Keep each description concise and limited to facts established by the
Keep each description concise and limited to facts established by the
transcript. Include only explicitly supported aliases and relationships.

View File

@@ -20,6 +20,8 @@ var promptAssetManifest = shared.PromptAssetManifest{
},
SharedFiles: []string{
"common-dnd-system.md",
"common-dnd-extraction-evidence.md",
"common-dnd-identity.md",
"common-dnd-transcript.md",
"common-dnd-references.md",
},

View File

@@ -23,6 +23,12 @@ inputs:
messages:
- role: system
content_file: ./sharedassets/common-dnd-system.md
- role: user
content_file: ./sharedassets/common-dnd-extraction-evidence.md
- role: user
content_file: ./sharedassets/common-dnd-identity.md
cache_control:
type: ephemeral
- role: user
content_file: ./sharedassets/common-dnd-transcript.md
cache_control:
@@ -31,8 +37,12 @@ messages:
content_file: ./sharedassets/common-dnd-references.md
cache_control:
type: ephemeral
- role: user
content_file: ./sharedassets/common-dnd-immediate-resolution.md
- role: user
content_file: ./sharedassets/common-dnd-npcs.md
cache_control:
type: ephemeral
- role: user
content_file: ./catalog.md
- role: user

View File

@@ -1,40 +1,29 @@
Source references must use integer source-unit IDs from the transcript. Provide
start_unit_id and end_unit_id for each source reference; the extractor assigns
source_id automatically.
For each spell cast, source references must collectively support the caster,
spell, effect, and narrative_description. If a detail is not supported by the
cited transcript units, omit that detail or describe only the supported attempt
or declaration.
Report only the casting declaration or action and its immediate resolution.
This may include an immediately resolved target, saving throw, damage, healing,
condition, interruption, or outcome. Do not follow summoned creatures,
persistent effects, or other downstream consequences through the rest of the
scene.
For spells, do not follow summoned creatures, persistent effects, or other
downstream consequences through the rest of the scene.
For every detail you report, cite all supporting transcript units. The source
references for an artifact must collectively support every factual claim in
caster, spell, effect, and narrative_description. When immediate supporting
evidence is non-contiguous, provide multiple narrow source references. Do not
use one broad range merely to bridge unrelated table conversation. If a detail
is not supported by the cited transcript units, omit that detail or describe
only the supported attempt or declaration.
Return only D&D spell-cast artifacts. For each spell cast, identify the
in-world caster, spell name, effect, narrative description, and source
references.
Return only D&D spell-cast artifacts. For each spell cast, identify the in-world
caster, spell name, effect, narrative description, and source references.
The caster must be the canonical in-world character or creature, not the human
player, transcript speaker, or GM. Use the player and party references together
with transcript context to map first-person player speech to the associated
player character and use the canonical character name from the references.
Likewise, attribute a spell narrated by the GM to the in-world creature that
casts it. Never return a player or GM name when the associated in-world caster
can be identified. If the caster cannot be resolved, use only the most specific
in-world identity supported by the transcript; do not invent a name.
Use the player and party references together with transcript context to map
first-person player speech to the associated player character and use the
canonical character name from the references. Likewise, attribute a spell
narrated by the GM to the in-world creature that casts it. If the caster cannot
be resolved, use only the most specific in-world identity supported by the
transcript; do not invent a name.
Use the canonical spell-name catalog to select spell names. Do not return a
spell name absent from that catalog, even when it is suggested by general D&D
knowledge or reference material.
Use player, party, and glossary reference material only to clarify source text;
references are not source evidence for a spell cast. Do not return spells,
casters, or effects that are mentioned only in reference material.
Use player, party, and glossary reference material only to clarify source text.
Do not return spells, casters, or effects that are mentioned only in reference
material.
Effects and narrative descriptions are session records, not rules summaries.
Report only mechanics, explanations, and outcomes established by the cited
@@ -42,5 +31,3 @@ transcript units. Preserve the table's observed resolution without silently
correcting it from general D&D knowledge. If the transcript gives a possibly
nonstandard rationale, use wording such as "the GM rules" or "the table
resolves" rather than asserting that rationale as a universal rule.
Return exactly one JSON object and no explanatory text.

View File

@@ -21,8 +21,11 @@ var promptAssetManifest = shared.PromptAssetManifest{
},
SharedFiles: []string{
"common-dnd-system.md",
"common-dnd-extraction-evidence.md",
"common-dnd-identity.md",
"common-dnd-transcript.md",
"common-dnd-references.md",
"common-dnd-immediate-resolution.md",
"common-dnd-npcs.md",
},
}

View File

@@ -46,9 +46,12 @@ func TestScriptoriumPromptPreparesWithMissingOptionalReferences(t *testing.T) {
transcript := []byte(`{"id":"session-1","segments":[]}`)
prepared := prepareSpellsPrompt(t, transcript, " ", " ", " ")
if !strings.Contains(prepared.Messages[2].Content, " ") {
t.Fatalf("reference message did not include empty optional reference placeholders")
for _, message := range prepared.Messages {
if strings.Contains(message.Content, "Player list reference:") && strings.Contains(message.Content, "Party roster reference:") && strings.Contains(message.Content, "Glossary reference:") {
return
}
}
t.Fatalf("reference message did not render empty optional reference placeholders")
}
func TestScriptoriumPromptDiagnosticsOmitRawMaterials(t *testing.T) {

View File

@@ -23,10 +23,13 @@ type PromptAssetManifest struct {
}
var sharedPromptPaths = map[string]string{
"common-dnd-system.md": "assets/prompts/common-dnd-system.md",
"common-dnd-transcript.md": "assets/prompts/common-dnd-transcript.md",
"common-dnd-references.md": "assets/prompts/common-dnd-references.md",
"common-dnd-npcs.md": "assets/prompts/common-dnd-npcs.md",
"common-dnd-system.md": "assets/prompts/common-dnd-system.md",
"common-dnd-extraction-evidence.md": "assets/prompts/common-dnd-extraction-evidence.md",
"common-dnd-identity.md": "assets/prompts/common-dnd-identity.md",
"common-dnd-transcript.md": "assets/prompts/common-dnd-transcript.md",
"common-dnd-references.md": "assets/prompts/common-dnd-references.md",
"common-dnd-immediate-resolution.md": "assets/prompts/common-dnd-immediate-resolution.md",
"common-dnd-npcs.md": "assets/prompts/common-dnd-npcs.md",
}
func (manifest PromptAssetManifest) PromptFS(moduleFS fs.FS) (fs.FS, error) {

View File

@@ -0,0 +1,14 @@
Transcript units are the only evidence for extracted events and factual claims.
Campaign and registry references may disambiguate names, aliases, speakers, or
other identities, but they do not establish events, participation, effects, or
source evidence.
Every reported factual claim must be supported by cited transcript units. Use
integer `start_unit_id` and `end_unit_id` values from the transcript. Omit
`source_id`; Notarius assigns the current source identity.
When supporting evidence is non-contiguous, use multiple narrow ranges rather
than a broad range that bridges unrelated conversation.
Return exactly one JSON object and no explanatory text. Output only the
configured JSON object and fields defined by its response schema.

View File

@@ -0,0 +1,7 @@
Use the most specific supported in-world character or creature identity for
each actor or participant. Do not identify a human player, transcript speaker,
or the GM as an out-of-world person when an in-world identity is supported.
Player, party, glossary, campaign, and NPC registry references may disambiguate
an identity, but a reference alone cannot establish that the identity
participated in the transcript.

View File

@@ -0,0 +1,7 @@
Report only a declaration or action and its immediate observed resolution.
Immediate resolution may include directly associated rolls, damage, healing,
movement, conditions, target outcomes, interruptions, or other outcomes shown
with that declaration or action.
Do not follow consequences that occur on later turns or elsewhere in the
scene.