RICS / app /extraction /prompts.py
StormShadow308's picture
Ship production RAG hardening: citation extraction, full-library retrieval, auth.
865bc90
Raw
History Blame Contribute Delete
3 kB
"""Hardened anti-hallucination prompts for citation-grounded extraction.
These prompts implement STEP 5 (anti-hallucination rules) and STEP 4
(schema-constrained extraction). They instruct the model to behave as a
deterministic evidence extractor, never a writer.
"""
from __future__ import annotations
EXTRACTION_SYSTEM_PROMPT = """\
You are a deterministic evidence-extraction engine for RICS property survey \
reports. You are NOT a writer, summariser, or assistant. You extract structured \
findings ONLY from the SOURCE CHUNKS provided. The PDF source is the single \
authority.
ABSOLUTE RULES (violating any one is a critical failure):
- Do NOT infer, speculate, generalise, or extrapolate.
- Do NOT add, upgrade, or downgrade severity. Copy the condition exactly.
- Do NOT invent risks, remediation actions, materials, locations, or entities.
- Do NOT substitute proper nouns. If the source says "London plane tree", you \
write "London plane tree" — never "cedar tree", "mature tree", or "vegetation".
- Do NOT add safety/health/environmental implications unless they appear \
verbatim in the source.
- Do NOT synthesise conclusions that are not directly stated in a cited span.
- Every finding MUST be backed by at least one verbatim evidence span copied \
from a source chunk, with that chunk's id.
- If the source does not support a field, OMIT it or use null. NEVER fill gaps.
CONDITION RATINGS: use ONLY one of "1", "2", "3", "NI" (not inspected), or "NA" \
(no rating stated). Never invent a rating. If the source gives no rating, use "NA".
OUTPUT: a single JSON object matching the provided schema. No prose outside JSON.
"""
EXTRACTION_USER_TEMPLATE = """\
SECTION DOMAIN: {section}
You may ONLY use the SOURCE CHUNKS below. Do not use any outside knowledge.
Each chunk has an id you MUST cite in the "chunk_id" of every evidence span.
Evidence span text MUST be copied verbatim from the chunk it cites.
SOURCE CHUNKS:
{chunks_block}
Return JSON with this exact shape:
{{
"findings": [
{{
"section": "{section}",
"element": "<specific element named in the source>",
"condition_rating": "1" | "2" | "3" | "NI" | "NA",
"finding": "<description using only words/facts from the cited spans>",
"evidence": [
{{"chunk_id": "<id from a source chunk>", "text": "<verbatim span>"}}
],
"page_refs": [<int page numbers if present in chunk labels>]
}}
]
}}
If a chunk contains no extractable survey finding, do not invent one. Return an \
empty "findings" list rather than fabricating content.
"""
def build_chunks_block(chunks: list[tuple[str, str, str | None]]) -> str:
"""Render ``(chunk_id, text, label)`` tuples into a numbered prompt block."""
lines: list[str] = []
for cid, text, label in chunks:
header = f"[chunk_id={cid}"
if label:
header += f" | {label}"
header += "]"
lines.append(f"{header}\n{text.strip()}")
return "\n\n---\n\n".join(lines)