This prompt is designed for observability pipelines that need to convert unstructured model refusal responses into structured, queryable data. The primary job-to-be-done is post-hoc analysis: you have a log of raw text where a model refused a user request, and you need to classify why it refused, identify the specific policy invoked, assess the model's confidence in the refusal, and determine if the refusal is potentially recoverable. The ideal user is a safety engineer, policy auditor, or product manager who needs to understand refusal trends at scale without manually reading thousands of responses. Required context includes the raw refusal text and the original user request; optional context includes the system prompt, conversation history, and applicable policy documents.
Prompt
Refusal Reason Extraction and Classification Prompt

When to Use This Prompt
Defines the operational context for the Refusal Reason Extraction and Classification Prompt, including its ideal use cases, prerequisites, and critical limitations.
This is not a real-time intervention tool. Do not use this prompt to make live safety decisions or to gate model responses in a production request path. It is an offline ingestion step for analytics, trend analysis, and policy auditing. The prompt assumes the refusal has already occurred and that you are now extracting structured metadata from it. It is also not a substitute for human policy review when dealing with genuinely ambiguous safety boundaries—the recoverability flag is a signal for downstream triage, not an automated override. For high-risk domains, always route flagged cases to human review before taking action on the classification output.
Before implementing this prompt, ensure your observability pipeline can capture the raw model response and the original user request together. The prompt's value depends on having both sides of the interaction. If your logging only captures the refusal text without the triggering request, the classification will be incomplete and the recoverability assessment unreliable. Once you have the structured output, you can ingest it into your analytics database for trend detection, policy gap analysis, and false-positive rate monitoring. The next step is to pair this extraction prompt with a validation schema that rejects malformed classifications before they enter your analytics store.
Use Case Fit
Where the Refusal Reason Extraction and Classification Prompt delivers value and where it introduces risk.
Good Fit: Production Observability Pipelines
Use when: you need to ingest unstructured refusal text from production logs and convert it into structured, queryable records for dashboards and trend analysis. Guardrail: Validate the output against a strict schema before ingestion to prevent downstream analytics corruption.
Bad Fit: Real-Time User-Facing Decisions
Avoid when: the classification result is used to immediately override a refusal and respond to a user without human review. Risk: Misclassification of a legitimate safety refusal as a false positive could expose users to harmful content. Guardrail: Always route recoverable: true flags to a human review queue before actioning.
Required Inputs: Raw Response and Policy Context
What to watch: The prompt cannot function accurately without the full, unredacted model response and a reference to the active safety policy. Guardrail: If the policy version or raw response is missing, the prompt must return a classification_error status rather than guessing the reason.
Operational Risk: Policy Drift and Stale Mappings
What to watch: The classification prompt relies on internal knowledge of policy articles that may change over time. Guardrail: Version-lock the policy reference in the prompt's system context and trigger a mandatory review of the classification logic whenever the source safety policy is updated.
Bad Fit: Single-Turn Chatbots Without Logging
Avoid when: you are building a simple chatbot and only need to show a generic error message. Risk: The overhead of structured extraction adds latency and complexity without a consumer for the structured data. Guardrail: Only deploy this prompt when the output is directly consumed by a monitoring or analytics system.
Good Fit: Safety Policy Gap Analysis
Use when: safety engineers need to aggregate refusal reasons across thousands of interactions to identify over-refusal patterns or missing policy coverage. Guardrail: Combine the structured reason_code output with user feedback signals to prioritize which policy gaps to fix first.
Copy-Ready Prompt Template
A copy-ready prompt template for extracting and classifying refusal reasons from model responses into structured JSON for observability pipelines.
The prompt template below is designed to be dropped directly into your observability or safety logging pipeline. It takes an unstructured model refusal response and a reference safety policy, then outputs a structured JSON object suitable for analytics ingestion. The template uses square-bracket placeholders for all dynamic inputs, making it easy to parameterize in code before sending to the model.
textYou are a safety observability classifier. Your task is to analyze a model's refusal response against a provided safety policy and extract structured refusal data. ## INPUT Model Refusal Response: """ [REFUSAL_RESPONSE_TEXT] """ Safety Policy Reference: """ [SAFETY_POLICY_TEXT] """ ## OUTPUT SCHEMA Return ONLY a valid JSON object with the following fields. Do not include any text outside the JSON. { "refusal_detected": boolean, "refusal_reason": string (direct quote or tight paraphrase from the response explaining why the request was refused), "reason_code": string (classify into one of: "POLICY_VIOLATION", "CAPABILITY_GAP", "LEGAL_RESTRICTION", "SAFETY_BOUNDARY", "AMBIGUOUS_REQUEST", "OTHER"), "policy_reference": string (the specific policy clause, section, or principle cited, or null if none cited), "confidence": number (0.0 to 1.0 indicating how clearly the refusal maps to a policy reason), "recoverable": boolean (true if the request could likely be fulfilled with rephrasing or additional context, false if the refusal appears definitive), "recovery_suggestion": string (if recoverable, a brief suggestion for how the user could rephrase; otherwise null), "analysis_notes": string (brief analyst note on any ambiguity, policy interpretation edge cases, or notable patterns) } ## CONSTRAINTS - If the response does not contain a refusal, set refusal_detected to false and leave other fields null. - Do not invent policy citations. Only reference policy text actually provided. - For ambiguous cases, set confidence below 0.7 and explain the ambiguity in analysis_notes. - Never classify a legitimate safety refusal as recoverable if recovery would circumvent the policy.
To adapt this template, replace [REFUSAL_RESPONSE_TEXT] with the raw model output you want to classify, and [SAFETY_POLICY_TEXT] with the relevant policy document or excerpt. For production use, validate the output JSON against the schema before ingestion. If the model returns malformed JSON, implement a retry with stricter format instructions or fall back to a repair prompt. For high-stakes safety observability, always sample and manually review classifications to calibrate confidence thresholds and catch systematic misclassifications.
Prompt Variables
Required inputs for the Refusal Reason Extraction and Classification Prompt. Each placeholder must be populated before the prompt is sent. Validation notes describe how to verify the input is well-formed before execution.
| Placeholder | Purpose | Example | Validation Notes |
|---|---|---|---|
[MODEL_RESPONSE] | The full unstructured text response from the model that may contain a refusal | I'm sorry, I can't help with that request because it violates our safety policy regarding harmful content. | Non-empty string required. Must be the raw model output, not a pre-processed or truncated version. Minimum 1 character, maximum 8192 tokens. |
[POLICY_DOCUMENT] | The safety policy text against which the refusal should be classified | Safety Policy v2.3: Do not generate content that promotes violence, illegal activities, or hate speech. Do not provide instructions for creating weapons or harmful substances. | Non-empty string required. Must contain explicit policy categories or rules. If policy is versioned, include version identifier in the text. |
[POLICY_VERSION] | Version identifier for the policy document used, for audit trail and downstream analytics | v2.3 | String required. Must match a known policy version in the policy registry. Null not allowed. Format: v[MAJOR].[MINOR] or equivalent versioning scheme. |
[REQUEST_CONTEXT] | The original user request that triggered the model response, for intent comparison | Can you tell me how to make a smoke bomb? | Non-empty string required. Must be the exact user input, not a paraphrase. If multi-turn conversation, include the specific turn that triggered the refusal. |
[CONVERSATION_HISTORY] | Prior turns in the conversation for context when refusal may depend on earlier exchanges | [{"role": "user", "content": "I'm working on a chemistry project"}, {"role": "assistant", "content": "I'd be happy to help with your chemistry project. What specific topic are you studying?"}] | JSON array of message objects with role and content fields. Null allowed if single-turn interaction. Max 10 prior turns to avoid context pollution. |
[CLASSIFICATION_TAXONOMY] | The set of valid refusal reason codes and their definitions for structured output | ["SAFETY_VIOLATION", "POLICY_AMBIGUITY", "CAPABILITY_GAP", "OVER_REFUSAL", "LEGITIMATE_REFUSAL"] | Non-empty array of strings required. Each code must have a definition in accompanying documentation. Codes must be mutually exclusive. Validate against taxonomy registry before use. |
[CONFIDENCE_THRESHOLD] | Minimum confidence score required for automatic classification before escalation to human review | 0.85 | Float between 0.0 and 1.0. Default 0.85 if not specified. Values below 0.7 risk high false-positive rates in classification. Must be configurable per deployment environment. |
[OUTPUT_SCHEMA_VERSION] | Schema version for the structured output format, enabling downstream parser compatibility | 1.2.0 | String required. Must match a known schema version in the output schema registry. Used by downstream ingestion to select the correct parser. Format: semantic versioning. |
Implementation Harness Notes
How to wire the Refusal Reason Extraction and Classification Prompt into an observability pipeline with validation, retries, and structured logging.
This prompt is designed to be a post-processing step in an observability pipeline, not a real-time user-facing component. After a model generates a refusal response, that raw text is routed to this prompt for structured extraction. The output is a machine-readable classification record that downstream analytics systems can ingest. The primary integration point is a logging middleware or a message queue consumer that receives model responses, detects refusals (either via a lightweight classifier or a keyword heuristic), and then invokes this prompt to produce the structured record. Do not use this prompt on non-refusal responses; it will hallucinate refusal reasons where none exist.
The implementation harness should enforce a strict schema contract before the classification record enters your analytics store. Wrap the LLM call in a validation layer that checks: (1) the reason_code matches your internal taxonomy enum, (2) the confidence field is a float between 0.0 and 1.0, (3) the recoverable field is a boolean, and (4) the policy_reference field is not null when the refusal is policy-based. If validation fails, implement a single retry with the validation error message appended to the prompt as additional context. If the retry also fails, log the raw refusal text and the failed parse attempt to a dead-letter queue for manual review. For high-volume production systems, consider using a cheaper, faster model (like a fine-tuned small classifier) for the initial refusal detection step, reserving this prompt for the detailed extraction on detected refusals only.
Logging is critical for this workflow. Every invocation should produce an audit record containing: the original model response, the extracted classification, the model used for extraction, the latency, and the validation status. This audit trail enables offline analysis of refusal patterns, false-positive rate monitoring, and policy gap detection. When deploying this prompt, start with a shadow mode that logs classifications without blocking or altering the user experience. After validating accuracy against a human-labeled sample of at least 200 refusal responses, promote the pipeline to active use. Avoid wiring this prompt directly into a synchronous user-facing code path; the extraction latency will degrade the user experience, and a failed extraction should never block a response from being delivered.
Expected Output Contract
Defines the exact fields, types, and validation rules for the structured refusal record. Use this contract to build downstream ingestion, analytics, and alerting pipelines that consume the prompt's output.
| Field or Element | Type or Format | Required | Validation Rule |
|---|---|---|---|
refusal_id | string (UUID v4) | Must be a valid UUID v4 generated by the system, not the model. Reject if missing or malformed. | |
original_request | string | Must be a non-empty string. Truncate to 4096 characters. Reject if null or empty. | |
refusal_reason_code | string (enum) | Must match one of the predefined reason codes: [POLICY_VIOLATION], [CAPABILITY_GAP], [SAFETY_BOUNDARY], [AMBIGUOUS_INTENT], [LEGAL_RESTRICTION], [OTHER]. Reject on mismatch. | |
policy_reference | string | null | If provided, must match the pattern 'POL-[0-9]{3}' (e.g., POL-042). Null allowed when reason_code is [CAPABILITY_GAP] or [OTHER]. | |
confidence_score | number (float 0.0-1.0) | Must be a float between 0.0 and 1.0 inclusive. Reject if out of range, non-numeric, or missing. | |
is_recoverable | boolean | Must be true or false. Reject if string, null, or integer. | |
recovery_strategy | string (enum) | null | Required if is_recoverable is true. Must be one of: [CONTEXT_REFRAME], [PARTIAL_FULFILLMENT], [EXPLICIT_PERMISSION], [CLARIFICATION_QUESTION], [ESCALATE_TO_HUMAN]. Null allowed if is_recoverable is false. | |
extracted_at | string (ISO 8601 datetime UTC) | Must be a valid ISO 8601 datetime string ending in 'Z'. Reject if unparseable or missing timezone. |
Common Failure Modes
What breaks first when extracting and classifying refusal reasons, and how to guard against it in production observability pipelines.
Model Classifies a Legitimate Refusal as a False Positive
What to watch: The extraction prompt misinterprets a correct safety refusal as an error, flagging it for recovery when the model was right to refuse. This inflates false-positive metrics and can lead to unsafe policy relaxation. Guardrail: Include few-shot examples of correct refusals with their policy citations. Add a validation step that cross-references the extracted reason code against the original request's risk profile before flagging for recovery.
Unstructured Refusal Text Produces Unparseable JSON
What to watch: The model's refusal response is a long, empathetic paragraph with no clear structure. The extraction prompt fails to map this to the required schema, returning null fields or malformed JSON that breaks the analytics pipeline. Guardrail: Implement a two-pass approach. First, classify whether the response is a refusal. Second, extract structured fields. Add a schema validator in the harness that rejects records with missing required fields and triggers a retry with stricter formatting instructions.
Recoverability Flag Is Overly Optimistic
What to watch: The prompt marks refusals as 'recoverable' when the underlying policy is absolute (e.g., illegal content, self-harm). This creates false work for recovery pipelines and can lead to dangerous retry attempts. Guardrail: Maintain a hardcoded list of non-recoverable reason codes in the application layer. Override the model's recoverability flag if the extracted reason code matches a non-recoverable category, regardless of the model's confidence.
Policy Reference Is Hallucinated or Vague
What to watch: The model cites a policy section like 'Section 4.2' that doesn't exist in your actual safety policy document, or returns a generic label like 'content policy' with no actionable specificity. Guardrail: Constrain the output to an enum of known policy IDs from your actual policy registry. Use a post-extraction check that validates the returned policy reference against the registry and flags mismatches for human review.
Confidence Score Doesn't Correlate with Accuracy
What to watch: The model returns high confidence for incorrect classifications, especially on ambiguous or edge-case refusals. Teams relying on the confidence score to auto-route tickets will send misclassified items to the wrong queue. Guardrail: Calibrate the confidence score by running a golden dataset of 200+ labeled refusals through the prompt. Set a threshold based on observed precision/recall, not the raw score. Route low-confidence and ambiguous cases to a human review queue.
Prompt Drift After Safety Policy Update
What to watch: Your safety team updates the policy document, adding new refusal categories and deprecating old ones. The extraction prompt, still referencing the old taxonomy, begins misclassifying new refusal types or using deprecated reason codes. Guardrail: Version-lock the prompt to a specific policy document version. Implement a regression test suite that runs after every policy update, checking that new refusal categories are correctly extracted and old codes are no longer produced.
Evaluation Rubric
Use this rubric to test the quality of the refusal reason extraction and classification prompt before shipping. Each criterion targets a specific failure mode in production observability pipelines.
| Criterion | Pass Standard | Failure Signal | Test Method |
|---|---|---|---|
Schema Compliance | Output is valid JSON matching the [OUTPUT_SCHEMA] with all required fields present | JSON parse error or missing required field such as refusal_code or confidence_score | Schema validator: parse output and validate against the defined JSON Schema |
Refusal Code Accuracy | refusal_code matches the actual refusal category from a pre-labeled test set with >= 90% accuracy | refusal_code is 'other' for a known category or misclassifies a policy violation as a capability refusal | Run against 50 pre-labeled refusal responses and measure exact-match accuracy per code |
Confidence Score Calibration | confidence_score is >= 0.8 for unambiguous refusals and <= 0.5 for ambiguous or borderline cases | confidence_score is 0.95 for a vague refusal or 0.3 for a clear policy citation | Compare confidence_score distribution against human-labeled certainty ratings on 30 samples |
Policy Reference Grounding | policy_reference field contains a specific policy ID or section when the refusal cites a policy, else null | policy_reference is populated with a hallucinated policy ID or is null when the model explicitly cited a policy | Cross-check policy_reference against the original refusal text for citation presence and accuracy |
Recoverability Flag Correctness | recoverable is true when the refusal suggests an alternative or asks for clarification, false when it is a hard block | recoverable is false for a refusal that offered a safe alternative or true for a permanent policy block | Run against 20 refusals with known recoverability labels and measure binary classification accuracy |
Null Handling for Missing Fields | Optional fields such as policy_reference or suggested_alternative are null when no evidence exists in the refusal text | Optional fields contain empty strings, 'N/A', or hallucinated values when the source refusal had no such content | Inspect outputs for 10 refusals with known missing information and verify null vs. fabricated content |
Original Text Fidelity | original_refusal_text field is an exact substring match from the model response with no modification | original_refusal_text is paraphrased, truncated, or contains added text not present in the source | String-exact match check between the extracted field and the source refusal response |
Classification Consistency | Same refusal text produces the same refusal_code and recoverable value across 5 repeated runs | refusal_code flips between 'policy_violation' and 'capability_gap' across runs for the same input | Run the same 10 refusal samples 5 times each and measure output stability with temperature=0 |
Enabling Efficiency, Speed & Accuracy
Intelligent Analysis, Decision & Execution
We build AI systems for teams that need search across company data, workflow automation across tools, or AI features inside products and internal software.
Talk to Us
Search across company data
Give teams answers from docs, tickets, runbooks, and product data with sources and permissions.
Useful when people spend too long searching or get different answers from different systems.

Automate internal workflows
Use AI to route work, draft outputs, trigger actions, and keep approvals and logs in place.
Useful when repetitive work moves across multiple tools and teams.

Add AI to products and internal tools
Build assistants, guided actions, or decision support into the software your team or customers already use.
Useful when AI needs to be part of the product, not a separate tool.
Adapt This Prompt
How to adapt
Use the base prompt with a single model call and minimal post-processing. Replace the strict JSON schema enforcement with a looser structured output request. Log raw outputs for manual review.
Prompt modification:
- Remove
[OUTPUT_SCHEMA]validation block - Replace with:
Return your analysis as JSON with these keys: refusal_detected, reason_code, policy_reference, confidence, recoverable - Add:
If you cannot determine a field, use null.
Watch for
- Inconsistent reason codes across runs
- Missing
policy_referencewhen refusal is implicit - Confidence scores that are always 0.9 without calibration
- Free-text fields that break downstream ingestion

About the author
Prasad Kumkar
CEO & MD, Inference Systems
Prasad Kumkar is the CEO & MD of Inference Systems and writes about AI systems architecture, LLM infrastructure, model serving, evaluation, and production deployment. Over 5+ years, he has worked across computer vision models, L5 autonomous vehicle systems, and LLM research, with a focus on taking complex AI ideas into real-world engineering systems.
His work and writing cover AI systems, large language models, AI agents, multimodal systems, autonomous systems, inference optimization, RAG, evaluation, and production AI engineering.
Partnered with leading AI, data, and software stack.
How We Work
Custom AI workflows for your Business
One-fit-all AI don't work for modern businesses. At Inferensys, we aim to understand your business & custom requirements; which we use to define most efficient agentic workflows, the data, and the tools for your business.
01
Review the use case
We understand the task, the users, and where AI can actually help.
Read more02
Pick the right approach
We define what needs search, automation, or product integration.
Read more03
Build the first useful version
We implement the part that proves the value first.
Read more04
Improve from there
We add the checks and visibility needed to keep it useful.
Read moreThe first call is a practical review of your use case and the right next step.
Talk to Us