AI Guardrails for SAP: Stop Hallucinations in Prod
SAP

AI Guardrails for SAP: Stop Hallucinations in Prod

Why AI Guardrails for SAP Are Not Optional

You've connected an LLM to your SAP system. Maybe it's generating ABAP code, extracting data from documents, or answering questions about your material master. It works brilliantly in testing. Then one day it confidently posts a goods receipt for a purchase order that doesn't exist, or generates a vendor payment with a hallucinated IBAN. That's the moment you realize that AI guardrails for SAP aren't a nice-to-have — they're the difference between a useful assistant and a production incident.

This article is about building those guardrails. Not the theoretical kind you read about in AI safety papers, but the practical, engineer-level validation layers you put between an LLM's output and your SAP write operations. I've seen what happens when these layers are missing. Let me save you the pain.

Understanding the Hallucination Risk Surface in SAP

Before you can guard against something, you need to understand where the risk actually sits. In SAP integrations, LLM hallucinations tend to cluster around a few specific failure modes:

  • Fabricated key fields: The model invents a material number, cost center, or vendor ID that looks plausible but doesn't exist in your system.
  • Plausible but wrong values: A real material number, but the wrong plant or storage location. The BAPI accepts it. The warehouse team is confused for three days.
  • Structural hallucinations: The model generates a JSON payload with the right shape but missing mandatory fields, or with extra fields it invented from training data about a different SAP version.
  • Confident confabulation: The model answers a question about your system configuration based on generic SAP knowledge rather than your actual customizing. The answer sounds authoritative and is completely wrong for your setup.

The tricky part is that none of these look obviously wrong at first glance. That's what makes them dangerous. A guardrail system has to catch what your eyes won't.

The Four-Layer Guardrail Architecture

I think about SAP AI guardrails in four layers. Each one catches a different class of problem. You want all four in place before anything touches a production system.

Layer 1: Prompt-Level Constraints

The cheapest guardrail is the one built into the prompt itself. Before your LLM even generates an answer, you shape what it's allowed to do.

This means being explicit in your system prompt about what constitutes a valid response. If you're asking the model to extract invoice data, tell it the exact fields you expect, the format for each one, and what to return when a value is absent. Give it a structured output schema and tell it to return null rather than guessing.

System: You extract invoice data from documents. 
Return ONLY a JSON object with these exact fields:
{
  "vendor_number": "10-digit string or null",
  "invoice_date": "YYYYMMDD format or null",
  "gross_amount": "decimal number or null",
  "currency": "3-letter ISO code or null",
  "line_items": []
}
Never invent values. If a field is not clearly present in the document, return null.
Do not add fields that are not listed above.

This sounds basic. But I've reviewed a lot of SAP AI integrations where the prompt just says "extract the invoice data and return it as JSON." That's an invitation to hallucinate.

Layer 2: Schema and Type Validation

Whatever the model returns, validate it against a strict schema before you do anything else with it. This is your first programmatic guardrail and it should be non-negotiable.

In an ABAP context, if you're receiving JSON from an LLM, deserialize it into a typed structure and verify every field:

METHOD validate_llm_invoice_response.
  DATA: lv_json     TYPE string,
        ls_invoice  TYPE zs_llm_invoice_response,
        lv_errors   TYPE string_table.

  " Deserialize into typed structure
  /ui2/cl_json=>deserialize(
    EXPORTING json = lv_json
    CHANGING  data = ls_invoice ).

  " Mandatory field checks
  IF ls_invoice-vendor_number IS INITIAL.
    APPEND 'vendor_number is missing' TO lv_errors.
  ENDIF.

  IF ls_invoice-invoice_date IS INITIAL.
    APPEND 'invoice_date is missing' TO lv_errors.
  ENDIF.

  " Type and range checks
  IF ls_invoice-gross_amount <= 0.
    APPEND 'gross_amount must be positive' TO lv_errors.
  ENDIF.

  IF strlen( ls_invoice-currency ) <> 3.
    APPEND 'currency must be 3-character ISO code' TO lv_errors.
  ENDIF.

  " If any errors, do NOT proceed to SAP write
  IF lines( lv_errors ) > 0.
    " Log errors, alert, or return for human review
    me->handle_validation_failure( lv_errors ).
    RETURN.
  ENDIF.

  " Only here do we proceed to the BAPI call
  me->post_invoice_to_sap( ls_invoice ).
ENDMETHOD.

You're essentially treating the LLM like an untrusted external API. Because that's exactly what it is.

Layer 3: SAP Master Data Cross-Validation

This is the layer most people skip, and it's the one that catches the most dangerous hallucinations. Schema validation tells you the response is structurally valid. Master data cross-validation tells you it's actually true against your SAP system.

Before you write anything, query SAP to confirm the key fields exist:

METHOD validate_against_master_data.
  DATA: ls_vendor  TYPE lfa1,
        lv_exists  TYPE abap_bool.

  " Does this vendor actually exist?
  SELECT SINGLE lifnr
    FROM lfa1
    INTO @DATA(lv_lifnr)
    WHERE lifnr = @is_invoice-vendor_number.

  IF sy-subrc <> 0.
    " Vendor doesn't exist — this is a hallucination
    me->flag_for_human_review(
      reason = |Vendor { is_invoice-vendor_number } not found in LFA1|
      payload = is_invoice ).
    RETURN.
  ENDIF.

  " Is the currency valid in T009B?
  SELECT SINGLE waers
    FROM tcurc
    INTO @DATA(lv_waers)
    WHERE waers = @is_invoice-currency.

  IF sy-subrc <> 0.
    me->flag_for_human_review(
      reason = |Currency { is_invoice-currency } not in TCURC|
      payload = is_invoice ).
    RETURN.
  ENDIF.

  " Passed all master data checks
  ev_valid = abap_true.
ENDMETHOD.

Yes, this adds database reads. Yes, it's worth it. The alternative is a hallucinated vendor number making it into a BAPI call, which depending on your BAPI configuration might silently fail, loudly fail, or — worst case — match a real vendor by coincidence.

If you're building more complex AI pipelines that retrieve SAP documentation context, you'll want to look at how RAG pipelines can ground LLM responses in your actual SAP data rather than training-time knowledge. Grounded responses hallucinate less, but you still need validation layers — grounding reduces the problem, it doesn't eliminate it.

Layer 4: Confidence Scoring and Human-in-the-Loop Routing

Not every LLM response needs the same level of scrutiny. A low-value read-only query carries different risk than a goods movement. Build a confidence-based routing layer that decides what gets auto-processed versus what gets queued for human review.

You can implement this in several ways:

  • Ask the model to self-score: Prompt the LLM to return a confidence score (0-1) alongside its answer, and explain what it was uncertain about. Models are not perfectly calibrated, but low self-reported confidence is a real signal worth acting on.
  • Rule-based threshold routing: Define thresholds per transaction type. Invoice posting under €1,000 with 100% field completeness and all master data validated: auto-process. Invoice over €10,000 or any null fields: human review queue.
  • Anomaly flagging: Compare the extracted values against historical patterns. An invoice amount three standard deviations above normal for that vendor is a flag worth raising, regardless of model confidence. This pairs well with the anomaly detection patterns we cover for SAP transactional data.

Practical Implementation Pattern: The Guard Wrapper Class

The cleanest way to implement this in ABAP is to build a guard wrapper that sits between your LLM integration and any SAP write operation. Every AI-driven posting goes through the wrapper. Nothing bypasses it.

CLASS zcl_ai_sap_guard DEFINITION
  PUBLIC FINAL CREATE PUBLIC.

  PUBLIC SECTION.
    TYPES: BEGIN OF ts_guard_result,
             passed      TYPE abap_bool,
             confidence  TYPE decfloat16,
             violations  TYPE string_table,
             action      TYPE string,  " AUTO_PROCESS / HUMAN_REVIEW / REJECT
           END OF ts_guard_result.

    METHODS:
      evaluate
        IMPORTING
          iv_operation_type TYPE string
          iv_payload        TYPE REF TO data
        RETURNING
          VALUE(rs_result)  TYPE ts_guard_result.

  PRIVATE SECTION.
    METHODS:
      check_schema          IMPORTING iv_payload TYPE REF TO data
                            RETURNING VALUE(rv_ok) TYPE abap_bool,
      check_master_data     IMPORTING iv_payload TYPE REF TO data
                            RETURNING VALUE(rv_ok) TYPE abap_bool,
      score_confidence      IMPORTING iv_payload TYPE REF TO data
                            RETURNING VALUE(rv_score) TYPE decfloat16,
      determine_action      IMPORTING iv_operation_type TYPE string
                                      iv_confidence TYPE decfloat16
                                      iv_violations TYPE string_table
                            RETURNING VALUE(rv_action) TYPE string.
ENDCLASS.

When you're using Claude function calling to drive BAPI operations, the guard wrapper slots in naturally between the function call response and the actual BAPI execution. Same pattern applies to OpenAI API integrations in ABAP — the transport layer changes, but the guard stays consistent.

Logging and Audit Trail for AI Operations

One thing that often gets overlooked until an auditor asks for it: every AI-assisted write operation in SAP needs a complete audit trail. You need to be able to answer:

  • What was the raw LLM output before any processing?
  • Which guardrail checks ran and what were their results?
  • Was this auto-processed or human-reviewed?
  • Who reviewed it, and when?
  • What was posted to SAP as a result?

Build a custom logging table for this from day one. Retrofitting audit trails into an existing AI integration is painful. The data you need is available at processing time and gone afterward.

" Minimal audit log structure
TYPES: BEGIN OF ts_ai_audit_log,
         log_id         TYPE sysuuid_x16,
         timestamp      TYPE timestamp,
         operation_type TYPE string,
         raw_llm_output TYPE string,  " Store the original response
         guard_result   TYPE string,  " JSON of guard evaluation
         action_taken   TYPE string,
         reviewer_id    TYPE syuname,
         sap_doc_number TYPE string,
       END OF ts_ai_audit_log.

Common Mistakes I See in the Wild

Let me be direct about patterns I encounter repeatedly in real SAP AI projects:

Trusting the BAPI return code as your only guard. BAPIs will reject structurally invalid calls, yes. But they won't tell you a vendor number was hallucinated and happened to match a real entry. The BAPI thinks it's a valid posting. It's not.

Testing only with clean, well-formatted inputs. Guardrails need adversarial testing. Feed your pipeline messy documents, documents in unexpected languages, documents with missing fields, documents with misleading content. See what the model does. Then make sure your guards catch it.

Building guardrails only for the happy path. What happens when your guard rejects a response? Where does it go? Who sees it? If the answer is "it just fails silently," you have a worse problem than no guardrails at all. Every rejection needs a handling path.

Treating confidence thresholds as set-and-forget. Review your guard logs monthly. If 80% of human-review-routed items are being approved unchanged, your threshold is too conservative. If auto-processed items are generating downstream corrections, it's too permissive. Tune it like the production system parameter it is.

A Note on Generated ABAP Code Specifically

If you're using AI to generate ABAP code rather than transactional data, your guardrail concerns are different but equally serious. Static analysis, syntax checks, and quality gates matter a lot here. We've covered this separately in the LLM-generated ABAP code quality review guide — the short version is: never transport AI-generated code without a human review step and automated quality scoring.

Building the Habit, Not Just the Code

Technical guardrails are necessary but not sufficient. The humans operating these systems need to understand that AI outputs require verification, not blind trust. Build that expectation into your documentation, your training, and your operational runbooks from the start.

The teams that do this well treat AI like a smart junior consultant: fast, knowledgeable, occasionally very wrong, and always worth a second look before anything goes live. That mental model keeps people appropriately skeptical without abandoning the productivity gains that make AI integration worthwhile in the first place.

Set up your guardrails. Log everything. Review the logs. Tune the thresholds. And never let an LLM write directly to production without something checking its work.

This article is part of the SAP AI integration & architecture hub — patterns, use cases, and guardrails for AI in SAP systems.