guardrails — Guardrail, GuardrailLibrary, GuardrailEngine

On this page

Injects explicit safety, grounding, and output-quality instructions directly into the prompt. The module's design principle, stated in its docstring: guardrails are prompt instructions that steer generation before it happens, not post-hoc output filters.

Table of contents


Guardrail

Python · 5 lines
@dataclass
class Guardrail:
    name:        str
    instruction: str
    priority:    int = 50   # higher = injected first

A single named, prioritized instruction string. priority determines render order (descending) when multiple guardrails are combined.


GuardrailLibrary

A namespace class (not instantiated) holding every built-in Guardrail as a class attribute.

AttributenamepriorityCategoryPurpose
GROUNDINGgrounding100Anti-hallucinationAnswer only from provided context
UNCERTAINTYuncertainty90Anti-hallucinationAdmit when context is insufficient
NO_FABRICATIONno_fabrication95Anti-hallucinationNever invent facts/citations
CITE_SOURCEScite_sources80CitationCite bracketed source numbers
STAY_ON_TOPICstay_on_topic70ScopeDon't volunteer unrelated info
NO_PERSONAL_OPINIONSno_personal_opinions60ScopeReport, don't editorialize
SAFE_OUTPUTsafe_output110SafetyDecline harmful/unethical requests
PII_PROTECTIONpii_protection105SafetyDon't expose PII from context
CONCISEconcise40FormatNo filler/preamble
NO_MARKDOWN_LEAKAGEno_markdown_leakage30FormatNo markdown unless format requires it
SHOW_REASONINGshow_reasoning50Reasoning qualityThink step by step
SELF_CHECKself_check45Reasoning qualitySelf-verify before finalizing

SAFE_OUTPUT (110) is the highest-priority guardrail; NO_MARKDOWN_LEAKAGE (30) is the lowest.


GuardrailEngine

Python
GuardrailEngine(extra_guardrails: Optional[List[Guardrail]] = None) -> None

build()

Python
def build(self, request: PromptRequest) -> tuple[str, List[str]]

Selects applicable guardrails, sorts them, deduplicates by name, renders the final instruction block, and returns (block, applied_names).

Internal workflow

  1. _select(request) → candidate Guardrail list (see Selection logic).
  2. Append self._extra (constructor-supplied guardrails) — these are always appended regardless of request.enable_guardrails.
  3. Sort descending by .priority.
  4. Deduplicate by .name, keeping the first occurrence encountered after sorting (i.e. if a name appears twice with different priorities, the higher-priority instance's position is kept but ties/order among equal-name duplicates follow Python's stable-sort behavior on the pre-sort list order).
  5. _render(unique, request) → the final block string.
  6. Return (block, [g.name for g in unique]).

_select (internal)

Python
def _select(self, req: PromptRequest) -> List[Guardrail]

Selection rules, in order:

  1. Always-on (regardless of enable_guardrails): SAFE_OUTPUT, CONCISE.
  2. If req.enable_guardrails:
    • req.documents non-empty → add GROUNDING, NO_FABRICATION
    • req.enable_uncertainty → add UNCERTAINTY
    • req.enable_citations and req.documents non-empty → add CITE_SOURCES
    • req.prompt_type not in {AGENT, TOOL_USE} → add STAY_ON_TOPIC
    • always (within this branch) → add PII_PROTECTION
  3. Strategy-driven (independent of enable_guardrails): req.strategy in {CHAIN_OF_THOUGHT, MULTI_HOP, LEAST_TO_MOST} → add SHOW_REASONING.
  4. Complexity-driven (independent of enable_guardrails): req.complexity in {COMPLEX, EXPERT} → add SELF_CHECK.

[!NOTE] Strategy/complexity guardrails apply even with enable_guardrails=False SHOW_REASONING and SELF_CHECK are added outside the if req.enable_guardrails: block, so setting enable_guardrails=False does not suppress them — only GROUNDING, NO_FABRICATION, UNCERTAINTY, CITE_SOURCES, STAY_ON_TOPIC, and PII_PROTECTION are gated by that flag. SAFE_OUTPUT and CONCISE are unconditionally always-on.

_render (internal)

Python
def _render(self, guardrails: List[Guardrail], req: PromptRequest) -> str
  1. Resolves lang = resolve_lang(req.language) (see templates).
  2. If any guardrails are selected: appends the localized header (guardrail_header(lang)) then a numbered list, where each guardrail's text is looked up via guardrail_text(g.name, lang, fallback=g.instruction) — i.e. it prefers a natively-authored bilingual instruction from templates and falls back to the guardrail's own .instruction string if no localized version exists for that name/language.
  3. Appends the output-format instruction: first tries format_instruction_text(req.output_format, lang) (bilingual, from templates.py); if that returns None, falls back to self._FORMAT_INSTRUCTIONS.get(req.output_format) (English-only, defined on this class).
  4. If req.output_schema is set and req.output_format == OutputFormat.STRUCTURED, appends a JSON-formatted schema block to the format instruction.

_FORMAT_INSTRUCTIONS (English fallback, class attribute):

OutputFormatInstruction summary
JSONReturn a JSON object matching {"answer", "sources", "confidence", "reasoning"}, no preamble/fences
BULLET_LISTConcise bulleted list, self-contained factual bullets
MARKDOWNUse headers/bullets/bold
CITATIONInline [1]/[2] citations + trailing Sources section
STRUCTUREDJSON matching the provided schema
TEXT(not in this dict — no format instruction is appended for TEXT, and format_instruction_text also has no TEXT entry, so plain-text output gets no format guardrail line at all)

Parameters

NameType
requestPromptRequest

Returns

(instruction_block: str, applied_guardrail_names: List[str]).

Raises

No exceptions raised under normal use.

Side effects

One logger.debug call listing applied guardrail names.

Thread safety

Stateless with respect to self beyond the constructor-fixed _extra list; safe for concurrent read-only use across threads (not explicitly tested).

Examples

1. Basic

Python · 6 lines
from zerda.prompt.guardrails import GuardrailEngine
from zerda.prompt.types import PromptRequest

engine = GuardrailEngine()
block, applied = engine.build(PromptRequest(query="What is RAG?"))
print(applied)  # ['safe_output', 'concise']  (no docs -> no grounding-related guardrails)

2. Intermediate — with documents and citations

Python · 3 lines
req = PromptRequest(query="...", documents=[doc1, doc2], enable_citations=True)
block, applied = engine.build(req)
# applied includes 'grounding', 'no_fabrication', 'cite_sources', 'pii_protection', 'stay_on_topic'

3. Advanced — custom extra guardrail

Python · 8 lines
from zerda.prompt.guardrails import Guardrail, GuardrailEngine

no_legal_advice = Guardrail(
    name="no_legal_advice",
    instruction="Do not provide legal advice; recommend consulting a licensed attorney.",
    priority=85,
)
engine = GuardrailEngine(extra_guardrails=[no_legal_advice])

4. Error handling — there is little to fail here

Python · 2 lines
# GuardrailEngine.build() does not raise for missing/None fields on a well-formed PromptRequest;
# malformed enum values would already have raised earlier, in PromptEngine.build().

5. Production — structured output with schema

Python · 8 lines
req = PromptRequest(
    query="Extract line items from this invoice.",
    documents=[invoice_doc],
    output_format=OutputFormat.STRUCTURED,
    output_schema={"type": "object", "properties": {"line_items": {"type": "array"}}},
)
block, applied = engine.build(req)
assert "Schema:" in block

Best practices

  • If you need a guardrail suppressed entirely regardless of settings, you cannot do so through PromptRequest flags for SAFE_OUTPUT/CONCISE (always-on) or for SHOW_REASONING/SELF_CHECK (strategy/complexity-driven) — you would need to subclass or bypass GuardrailEngine.
  • Prefer extra_guardrails over monkey-patching GuardrailLibrary for custom policies.

Common mistakes

  • Assuming enable_guardrails=False produces a guardrail-free prompt. It doesn't — SAFE_OUTPUT, CONCISE, and (conditionally) SHOW_REASONING/SELF_CHECK still apply.
  • Expecting a format instruction line for OutputFormat.TEXT — none exists in either the bilingual table or the English fallback dict.
  • templates — source of localized guardrail/format text
  • types — OutputFormat, PromptType, QueryComplexity
  • builder — the only internal caller of GuardrailEngine.build()

Related pages