-
Notifications
You must be signed in to change notification settings - Fork 161
[FEAT]: Department-Aware LLM Prompt Templating — Dynamic System Prompts per Agency #410
Description
Description
FireForm currently uses a single hardcoded system prompt for all LLM extractions,
regardless of which agency's form is being filled. This means the LLM has no knowledge
of which fields are relevant for a specific department — a county sheriff form needs
suspect descriptions, an EMS form needs patient vitals, a CAL FIRE form needs acres
burned and suppression method. A generic prompt forces the LLM to guess, resulting in
missed fields and inconsistent extraction quality across agencies.
This feature proposes a prompt templating engine that dynamically constructs the
Ollama system prompt at runtime based on the target agency's profile.
Rationale
Without agency-aware prompts, FireForm cannot reliably scale beyond a single department.
Every new agency onboarded with a new PDF template also needs a tailored extraction
strategy — otherwise the LLM extracts generic fields and misses agency-specific ones.
This is the missing architectural link between the PDF template registry (what fields
need filling) and the LLM extraction layer (what fields to look for in the transcript).
Currently these two layers are disconnected. This proposal connects them.
Real example: CAL FIRE requires fire_cause, acres_burned, suppression_method,
and structures_threatened. None of these will be reliably extracted by a generic prompt
designed for a master incident schema.
Proposed Solution
- Logic change in
src/— newPromptTemplateEngineclass that loads agency config
and builds a targeted system prompt before each Ollama call - New prompt for Mistral/Ollama — per-agency
.txtprompt templates stored in
src/prompts/ - Update to
requirements.txt— no new dependencies needed (PyYAML already present)
Agency config extension (YAML):
# agency_templates/cal_fire.yaml
agency_id: cal_fire
name: CAL FIRE
prompt_template: prompts/cal_fire.txt
required_fields:
- incident_type
- fire_cause
- suppression_method
- acres_burned
- structures_threatened
extraction_hints:
fire_cause: "Look for phrases like 'started by', 'ignited', 'caused by'"
acres_burned: "Extract numeric values near words like 'acres', 'hectares'"Prompt engine sketch:
class PromptTemplateEngine:
def build_system_prompt(self, agency_id: str) -> str:
agency_config = self._load_agency_config(agency_id)
base_prompt = self._load_prompt_template(
agency_config["prompt_template"]
)
fields_block = self._format_required_fields(
agency_config["required_fields"],
agency_config.get("extraction_hints", {})
)
return f"{base_prompt}\n\nRequired fields:\n{fields_block}"Backward compatible — falls back to default prompt if no agency config exists.
Acceptance Criteria
- Feature works in Docker container
- Documentation updated in
docs/ - JSON output validates against the schema
-
PromptTemplateEngineloads correct prompt for a givenagency_id - Falls back gracefully to default prompt when no agency config is found
- At least 2 sample agency prompt templates included (e.g. CAL FIRE, EMS)
- Unit tests cover prompt construction and fallback behavior
Additional Context
Related issues:
- [FEAT]: Externalize LLM System Prompt to a Text File #259 — Externalize LLM System Prompt (this extends that concept per-agency)
- [Enhancement]: PDF Field-to-Data Mapping is Positional-Only — Fragile, Unscalable, and Agency-Incompatible #42 — PDF Field Mapping is positional-only (this is the LLM-side complement)
- [FEAT]: Department Profile System for Pre-Mapped PDF Templates #206 — Department Profile System (this adds the prompt layer to that profile)
- [BUG]: Mistral AI Outputs are Written Directly to Legal PDFs Without Confidence Checks #222 — Unvalidated Mistral output (agency-specific prompts reduce hallucination risk)