340 lines
14 KiB
Markdown
340 lines
14 KiB
Markdown
# Feature Roadmap Proposal: Extraction Reference
|
|
|
|
## Status
|
|
|
|
This document captures proposed design and implementation sequencing for the
|
|
extraction-reference feature in Notarius. It describes planned work, not
|
|
implemented behavior. Go snippets are conceptual sketches; the implementing
|
|
agent should adapt names and shapes to the existing contracts, package
|
|
boundaries, and conventions in this repository.
|
|
|
|
## Goal
|
|
|
|
Extraction quality improves significantly when the LLM receives reference
|
|
material alongside the source input. For the initial D&D spell extractor,
|
|
useful reference material includes a party roster (mapping players to player
|
|
characters), a player list, and a campaign glossary.
|
|
|
|
Notarius should support passing this material to extractors as **named reference
|
|
items** without introducing any domain-specific concepts into core or framework
|
|
packages. The framework should know only that:
|
|
|
|
- extractors declare named reference slots they accept;
|
|
- pipeline config and CLI flags bind content (initially files) to those slots;
|
|
- bound content is rendered into module-owned prompt templates;
|
|
- bound content is digested and recorded as run provenance.
|
|
|
|
Only extract modules should know what a "roster" or "glossary" means. All
|
|
domain semantics live in module-owned slot declarations and prompt templates.
|
|
|
|
## Definitions
|
|
|
|
- **Reference slot**: a named, typed-by-convention input declared by an
|
|
extractor, with a human-readable description and a required/optional flag.
|
|
Example: extractor `dnd/spells` declares an optional slot named `roster`.
|
|
- **Reference item**: resolved content bound to a slot for a given run: name,
|
|
content bytes, media type, content digest, and origin (initially a file
|
|
path).
|
|
- **Reference binding**: the association of a slot name to a content source,
|
|
defined in pipeline config and overridable per run via CLI.
|
|
|
|
## Architectural Principles
|
|
|
|
- Reference is opaque to the framework. Core and framework packages must not
|
|
interpret reference content or recognize domain slot names.
|
|
- Reference is an input. Anything that changes extraction output must be
|
|
digested into the run manifest and participate in any cache key.
|
|
- A Reference is not evidence. `SourceRef` values must only ever reference source
|
|
units. Reference items must not receive unit IDs and must not be addressable
|
|
by source references.
|
|
- Slots are declared, not ad hoc. Binding an undeclared slot name, or omitting
|
|
a required slot, should fail at config-load time, before any LLM call.
|
|
- Optional slots degrade gracefully. Prompt templates should render cleanly
|
|
whether or not an optional slot is bound.
|
|
- Determinism. Identical input, config, prompts, and reference bytes should
|
|
produce byte-identical rendered prompts. Reference slots should render in a
|
|
stable, documented order (declaration order).
|
|
|
|
## Proposed Contracts
|
|
|
|
### Slot declaration (extractor contract extension)
|
|
|
|
Extractors should declare the reference slots they accept:
|
|
|
|
```go
|
|
type ReferenceSlot struct {
|
|
Name string
|
|
Description string
|
|
Required bool
|
|
|
|
// MVP can leave these empty/defaulted, but having the fields now
|
|
// makes validation and future docs easier.
|
|
AcceptedMediaTypes []string
|
|
Multiple bool
|
|
MaxBytes int64
|
|
}
|
|
```
|
|
|
|
The extractor interface should gain a method such as:
|
|
|
|
```go
|
|
ReferenceSlots() []ReferenceSlot
|
|
```
|
|
|
|
Extractors with no reference needs return an empty slice. Existing extractors
|
|
should require no other changes.
|
|
|
|
### Resolved reference item
|
|
|
|
```go
|
|
type ReferenceItem struct {
|
|
SlotName string
|
|
MediaType string
|
|
Content []byte
|
|
Digest string
|
|
Origin ReferenceOrigin
|
|
|
|
SizeBytes int64
|
|
TokenEstimate int
|
|
}
|
|
|
|
type ReferenceOrigin struct {
|
|
Type string // "file" for MVP
|
|
URI string // path or future artifact URI
|
|
}
|
|
|
|
type ReferenceSet struct {
|
|
// Stable declaration order, then stable binding order within a slot.
|
|
Slots []ResolvedReferenceSlot
|
|
}
|
|
|
|
type ResolvedReferenceSlot struct {
|
|
Name string
|
|
Items []ReferenceItem
|
|
}
|
|
```
|
|
|
|
`ReferenceItem` is a resolved-content type, not a file path. The only MVP
|
|
producer is "read this file," but the shape should permit future producers
|
|
(prior-run artifacts, derived summaries, entity registries) without contract
|
|
changes.
|
|
|
|
### Binding resolution
|
|
|
|
A resolver should, at config-load time:
|
|
|
|
1. Collect declared slots from every extractor selected by the active
|
|
pipeline (respecting lane selection, e.g. `--only`).
|
|
2. Collect bindings from pipeline config (pipeline-level and lane-level) and
|
|
CLI overrides, applying the standard layering: config file, then CLI.
|
|
3. Fail with a clear error if a required slot is unbound, or if a binding
|
|
references a slot no extractor within the selected pipeline declares.
|
|
Errors should name the pipeline, lane, slot, and the slot description.
|
|
4. Read, digest, and materialize each bound source into a `ReferenceItem`.
|
|
5. Enforce size guardrails (see Validation and Guardrails).
|
|
|
|
## Configuration and CLI
|
|
|
|
### Pipeline config
|
|
|
|
Reference bindings should live in pipeline config, because the initial use cases
|
|
(roster, glossary) are campaign-invariant rather than run-variant. Bindings
|
|
should be supported at two levels:
|
|
|
|
- pipeline level: shared by all artifact lanes;
|
|
- lane level: additions or overrides for a single lane.
|
|
|
|
Illustrative shape (adapt to the existing config format):
|
|
|
|
```yaml
|
|
pipelines:
|
|
dnd-session:
|
|
input: seriatim
|
|
references:
|
|
roster: ./campaign/party_roster.md
|
|
glossary: ./campaign/glossary.md
|
|
artifacts:
|
|
spells:
|
|
extract: dnd/spells
|
|
npcs:
|
|
extract: dnd/npcs
|
|
reference:
|
|
npc_registry: ./campaign/npcs.md
|
|
```
|
|
|
|
### CLI
|
|
|
|
Per-run override flag, repeatable:
|
|
|
|
```text
|
|
notarius run dnd-session --input session-014.json --reference roster=./alt_roster.md
|
|
```
|
|
|
|
CLI bindings override config bindings for the same slot name. The existing
|
|
pipeline-describe/config-validate commands (or their nearest equivalents)
|
|
should surface declared slots, descriptions, required flags, and current
|
|
bindings so users can discover what a pipeline accepts.
|
|
|
|
## Prompt Template Integration
|
|
|
|
Prompt templates are module-owned. Template rendering should expose:
|
|
|
|
- `{{ reference "roster" }}`: renders the content of the bound item;
|
|
- `{{ hasreference "glossary" }}`: predicate for conditional sections, so
|
|
optional slots can be included only when bound.
|
|
|
|
Rules:
|
|
|
|
- Referencing an **undeclared** slot from a template is a module bug and
|
|
should fail at prompt registration/build time (or earliest feasible point),
|
|
not silently at render time.
|
|
- Referencing a declared but unbound **optional** slot should render as
|
|
empty; templates should use `hasreference` to avoid dangling section headers.
|
|
- Rendering must be deterministic and independent of map iteration order.
|
|
- Prompt identity (registry hash) should be computed over the **template**,
|
|
not the rendered prompt. Reference digests are recorded separately in the
|
|
manifest, so a reference edit is visible as a reference change, not a prompt
|
|
change.
|
|
|
|
Note: reference content is repeated in every per-chunk prompt. Diagnostics
|
|
should record per-slot token or byte counts so reference cost is observable.
|
|
Per-slot inclusion policies (e.g., roster in every chunk, glossary on demand)
|
|
are explicitly out of scope until cost data justifies them.
|
|
|
|
## Provenance
|
|
|
|
The run manifest must record, for every bound slot:
|
|
|
|
- slot name;
|
|
- origin (path);
|
|
- content digest;
|
|
- media type;
|
|
- whether the binding came from config or CLI override.
|
|
|
|
Reference digests must participate in any idempotency/cache key alongside source
|
|
digests, prompt hashes, schema versions, model, and parameters. Two runs that
|
|
differ only in reference content must be distinguishable from the manifest
|
|
alone.
|
|
|
|
Diagnostics for a run should include the resolved binding set (with digests,
|
|
not necessarily full content) in the run directory, consistent with the
|
|
existing redacted-effective-config pattern.
|
|
|
|
## Path Resolution
|
|
|
|
- Config-relative paths resolve relative to the pipeline config file.
|
|
- CLI-relative paths resolve relative to the current working directory.
|
|
- Manifest records the normalized absolute path or a redacted/display path
|
|
according to existing diagnostics policy.
|
|
|
|
## Validation and Guardrails
|
|
|
|
### References are not evidence
|
|
|
|
The primary new failure mode: the model extracts facts from references rather
|
|
than from the source input. Example: the roster lists a PC's known spells, and
|
|
the model emits a `SpellCast` for a spell that was never cast in the session,
|
|
with a fabricated or misattributed source reference.
|
|
|
|
Defenses, in priority order:
|
|
|
|
1. **Structural.** `SourceRef` remains the only grounding mechanism and can
|
|
only reference source units. No contract change should make references
|
|
addressable as evidence.
|
|
2. **Prompt discipline.** Module templates should frame references explicitly as
|
|
reference material, e.g. "use the roster to resolve speakers to
|
|
characters; extract only events that occur in the transcript." This
|
|
guidance belongs in the module prompt guidelines, not framework code.
|
|
3. **Validator support.** The source-reference validator (or a sibling
|
|
deterministic validator) should support checking that referenced source
|
|
text plausibly relates to the extracted fact (e.g., spell name or a close
|
|
variant appears in or near the referenced range). Severity should be
|
|
`warn`, not `fail`, given paraphrase and nickname casting.
|
|
4. **Regression fixtures.** Golden-file tests must include a fixture in which
|
|
the bound roster mentions a spell that is never cast in the transcript,
|
|
asserting no artifact record is produced for it. This regression is likely
|
|
to be reintroduced by future prompt edits; the fixture is the guard.
|
|
|
|
### Size and sanity guardrails
|
|
|
|
- Fail fast, before any LLM call, if bound references plus template plus largest
|
|
chunk exceeds the configured model context budget, with an error that names
|
|
the offending slot(s) and sizes.
|
|
- Empty bound files should produce a warning (probable user error).
|
|
- MVP accepts text content only (`utf-8`); other media
|
|
types should be rejected with a clear error.
|
|
|
|
## Out of Scope (MVP)
|
|
|
|
- Non-file reference producers (prior-run artifacts, derived summaries, entity
|
|
registries). The `ReferenceItem` shape should permit them later.
|
|
- Per-chunk or per-slot inclusion policies and context budgeting beyond the
|
|
fail-fast guardrail.
|
|
- Structured/parsed references (e.g., typed roster schemas). References are opaque
|
|
text handed to prompts.
|
|
- Reference caching or preprocessing (summarization, embedding, retrieval).
|
|
- Making reference addressable as evidence, in any form.
|
|
|
|
## Checkpoint Sequencing
|
|
|
|
Each checkpoint should leave the repository compiling, with targeted tests
|
|
covering newly introduced contracts or behavior.
|
|
|
|
1. **Contracts and resolution.** Add `ReferenceSlot`, `ReferenceItem`, and the
|
|
extractor `ReferenceSlots()` method (empty default for existing extractors).
|
|
Implement config parsing for pipeline- and lane-level bindings, CLI
|
|
override flag, layering, and load-time validation (unknown slot, missing
|
|
required slot, unreadable file, empty file warning). Unit tests for
|
|
resolution and error cases.
|
|
2. **Prompt rendering.** Add `reference`/`hasreference` template functions,
|
|
declaration-order rendering, undeclared-slot failure at registration, and
|
|
deterministic-render tests (byte-identical output across runs).
|
|
3. **Provenance.** Record bindings (name, origin, digest, media type,
|
|
binding source) in the run manifest and diagnostics; include reference
|
|
digests in the cache/idempotency key if one exists. Tests: manifest
|
|
round-trip; two runs differing only in reference content produce differing
|
|
manifests.
|
|
4. **Guardrails and validation.** Context-window fail-fast check;
|
|
relatedness `warn` validator (or extension of the source-reference
|
|
validator); media-type rejection.
|
|
5. **First consumer.** Declare `roster` (optional) and `glossary` (optional)
|
|
slots on the D&D spells extractor; update its prompt template with
|
|
conditional reference sections and reference-material framing; add golden
|
|
fixtures with and without references bound, including the
|
|
roster-mentions-uncast-spell fixture. This checkpoint is the acceptance
|
|
test for the feature: spell extraction quality with a roster bound should
|
|
visibly improve speaker-to-character attribution in fixtures.
|
|
|
|
## Open Design Questions
|
|
|
|
The implementing agent should resolve these against existing code and record
|
|
decisions in the implementation plan:
|
|
|
|
- Should slot names be namespaced per lane in config and CLI (e.g.,
|
|
`spells.roster=...`) or flat with lane-level config as the only
|
|
disambiguator? (Recommended default: flat names; lane-level config for
|
|
overrides; revisit if two extractors in one pipeline want the same slot
|
|
name with different content.)
|
|
- Where does binding resolution live relative to the existing config and
|
|
pipeline packages? It must run at load time, alongside existing pipeline
|
|
validation.
|
|
- Does the existing prompt registry hash templates or rendered prompts? If
|
|
rendered, this feature requires moving to template hashing as described in
|
|
Provenance.
|
|
- Should CLI overrides be permitted to bind slots that config leaves unbound
|
|
(yes, presumably), and to *unbind* a config-bound optional slot (e.g.,
|
|
`--reference roster=` to clear)? Decide and test both directions.
|
|
|
|
## Documentation Tasks
|
|
|
|
Once implemented, move contracts out of this roadmap into canonical docs:
|
|
|
|
- `docs/cli.md`: `--reference` flag syntax, layering, and examples;
|
|
- `docs/config.md`: pipeline- and lane-level `references` blocks;
|
|
- `docs/internal/`: slot/item contracts, resolution flow, evidence
|
|
exclusion rule, and template function reference for module authors;
|
|
- module-author guidance: how to declare slots, write conditional reference
|
|
sections, and frame reference material in prompts;
|
|
- `examples/`: a maintained example pipeline with a roster and glossary
|
|
bound, plus matching fixture files.
|
|
``` |