logo

Are you need IT Support Engineer? Free Consultant

Business AI in ABAP Cloud – Embedding AI with the ABAP AI SDK

  • By sujay
  • 19/10/2026
  • 18 Views

Business AI in ABAP Cloud – Embedding AI with the ABAP AI SDK

Cheers ABAP Folks!

In my last blog post, we discussed the high-level strategy for implementing business AI capabilities in ABAP Cloud. We looked at the two pillars, Embedded AI in ABAP and Agentic AI via Joule, and when to use which approach. If you haven't read it yet, I'd recommend starting there for the bigger picture.

Today, we're going hands-on. This post dives into Embedded AI, the capabilities that run directly inside your ABAP application, powered by the ABAP AI SDK. We'll walk through the fundamentals, introduce the SDK, and then look at two concrete use cases using everyone's favorite business scenario: Trip & Expense Management.

Yes, trip & expense management. We all love it, don't we? Nothing brings more joy to a business traveler than filling out expense reports after a long trip. So, let's see how AI can take some of that joy… I mean burden… away.

 

 

The Fundamentals: Connecting ABAP to AI

Before you write a single line of AI-related ABAP code, you need to get the plumbing right. All embedded AI scenarios in ABAP Cloud require a connection to SAP AI Core, specifically to the Generative AI Hub that provides access to Large Language Models (LLMs) hosted by hyperscalers.

This connection is established via Intelligent Scenario Lifecycle Management (ISLM), a framework that allows you to manage and operate AI scenarios in a standardized, transportable way. Think of ISLM as the lifecycle backbone: it handles connectivity, model deployment, prompt management, and the runtime configuration that ties everything together.

Setting Up the Connection

The setup follows a straightforward pattern (SAP Help, Set Up ABAP AI SDK powered by ISLM

  1. Subscribe to SAP AI Core in your SAP BTP subaccount and enable the required service plan.
  2. Create a service instance and service key for SAP AI Core in the BTP cockpit.
  3. Establish connectivity between SAP AI Core and your ABAP system by creating a communication arrangement based on communication scenario SAP_COM_0A69.

This is a customer-managed outbound integration, which means you own the connection setup. Once established, ISLM can create and activate model deployments, and the ABAP AI SDK can execute prompts against the deployed models. For a detailed step-by-step walkthrough, I recommend the tutorial Set Up SAP BTP and Integrate SAP AI Core with ISLM on SAP Developers.

The ABAP AI SDK, Your AI Toolbox

With the connection established, let's talk about the tool you'll actually use in your ABAP code: the ABAP AI SDK. It's a typed, ABAP-native reuse library that provides access to generative AI functionality via ISLM. It is available across the full ABAP Cloud landscape, covering:

  • SAP S/4HANA Cloud Private Edition & On-Premise
  • SAP S/4HANA Cloud Public Edition
  • SAP Business AI Platform, ABAP environment (Steampunk)

This means the same SDK, the same APIs, the same patterns, regardless of where your ABAP system runs.

What's in for me?

The ABAP AI SDK offers a set of APIs built around common LLM usage patterns:

  • Completion API – Send a prompt to an LLM and receive a generated text response. This is the core building block for most embedded AI scenarios.
  • Prompt Library API – Work with prompt templates predefined in ISLM, including parameterized prompts with runtime variable substitution. Very useful when you want to keep your prompts maintainable and out of your ABAP code.
  • Connectivity management – The SDK handles the connection to the Generative AI Hub transparently. You reference an intelligent scenario by name, and the SDK resolves the rest.
  • Exception handling – Typed ABAP exceptions, so you can integrate AI calls into your existing error handling patterns without reinventing the wheel.

The APIs are vendor-neutral, meaning the same ABAP code works regardless of which underlying LLM provider is configured in SAP AI Core. If your organization switches from one model to another, you change the configuration in ISLM, not your ABAP code. Your CTO changes their mind about the AI vendor? No problem, no code change required – ok, to be honest with you: The prompting might be polished most probably a little.

The Simplest Possible Call

To give you a feel for the developer experience, here's the simplest possible AI call using the Completion API (SAP ABAP Cheat Sheets, Generative AI😞

” Get a Completion API instance for your intelligent scenario
DATA(ai_api) = cl_aic_islm_compl_api_factory=>get( )->create_instance( ‘Z_MY_SCENARIO' ).

” Execute a prompt and get the result
DATA(result) = ai_api->execute_for_string( `Classify this trip as customer-facing or internal.` ).

” Retrieve the generated text
DATA(completion) = result->get_completion( ).

Three lines of ABAP. That's it. The factory creates an instance tied to your intelligent scenario (which contains all the connection and model details), you execute a prompt, and you get back the LLM's response as a string. No HTTP client setup, no JSON parsing, no authentication handling. The SDK takes care of all of that. If you've ever tried calling a REST API from ABAP the “old-fashioned way”, you'll know exactly how much boilerplate this saves you.

Of course, real-world scenarios are a bit more involved. You'll use prompt templates with parameters, handle exceptions, and integrate the result into your business logic. But the fundamental pattern stays the same.

Orchestration Capabilities

For enterprise-grade AI scenarios, the ABAP AI SDK also supports orchestration through the SAP AI Core Orchestration Service. This adds capabilities that are critical for production use:

  • Data Masking – Automatically redact sensitive information (personal data, financial details) before it reaches the LLM. Essential for data protection and GDPR compliance, and something your compliance team will thank you for.
  • Document Grounding – Enrich your prompts with business-relevant context from documents, reducing hallucinations and improving response accuracy. Because an LLM that confidently makes things up is entertaining at dinner parties, but not in your ERP system.
  • Content Filtering – Ensure that LLM responses are safe, appropriate, and compliant with your organisation's policies.

These capabilities are configured at the orchestration level, so you don't need to implement masking or filtering logic yourself in ABAP. It's handled for you.

 

Let’s get our hands dirty: Trip & Expense Management

For the rest of this blog post, we'll use a Trip & Expense Management application as our running example. Picture a RAP-based Fiori application where employees create travel requests, and after the trip, submit expense reports.

We'll tackle two concrete use cases:

  1. Data Prediction with Fiori Input Assistance: Automatically categorizing a trip as “customer-facing” or “internal” based on the trip description.
  2. Data Extraction from files:  Extracting structured data (payment amounts, categories, etc.) from uploaded receipt documents.

Both are real-world scenarios that demonstrate different facets of the ABAP AI SDK. And both address the same fundamental truth: nobody enjoys filling out forms manually when a machine could do it better and faster. Let's start.

 

Use Case 1: Trip Categorization with RAP Recommendations

The Problem

When an employee creates a travel request, they need to fill out several fields: destination, purpose, cost center, and importantly, trip category (e.g. “customer-facing” or “internal”). This categorization drives downstream processes like approval workflows, budget allocation, and reporting. Today, users pick the category manually. They might get it wrong, or they pick whatever option is closest to the mouse cursor. What if the system could propose the right category automatically, based on the trip description?

The Solution: Fiori Input Assistance

This is exactly what Fiori Input Assistance implemented with RAP recommendations is designed for. RAP recommendations are a built-in input assistance feature in the ABAP RESTful Application Programming Model (RAP) that proposes field values for input fields on an SAP Fiori UI (SAP Help, Defining RAP Recommendations). Available since SAP BTP ABAP Environment 2602 and SAP S/4HANA Cloud 2602, RAP Recommendations can be implemented using either deterministic rules or generative AI, and you can even mix both approaches in the same application. The key principles of RAP recommendations:

  • They are exposed in SAP Fiori as a recommendation pattern of type input assistance, meaning the user sees suggested values directly in the input field.
  • They are built on top of a value help, so you need an existing value help for the target field.
  • They are triggered by side effects: when the user changes a source field (e.g. trip description), the recommendation function runs and proposes values for the target field (e.g. trip category).
  • They integrate seamlessly with the RAP programming model. No custom UI development required. Zero. Nada.

Figure 1: Fiori Input Assistance implemented with RAP recommendations

How It Works: The Architecture

The implementation involves three layers:

  1. Behavior Definition – Declare which fields receive recommendations and define the recommendation function.
  2. Behavior Implementation – Implement the recommendation logic in the behavior pool, using the ABAP AI SDK to call an LLM for intelligent categorization.
  3. Projection Behavior Definition – Expose the recommendations in the projection layer for the Fiori UI.

If you've built a RAP Business Object before, this will feel very familiar. It's the same layered pattern, just with AI sprinkled on top.

Step 1: Define Recommendations in the Behavior Definition

In your behavior definition, you mark the target field as relevant for recommendations, declare a recommendation function and define a side effect on which the recommendation function should be triggered. This tells the RAP framework: whenever the user changes the “title” field, execute function GetRecommendedValuesFunction and propose values for attribute “type”. The user types “Customer workshop in Berlin” and the system immediately suggests “Customer-facing”. It almost feels like the system is reading the user's mind, except it's reading their input field.

define behavior for ZR_AITRIP alias Trip
{
//Define for which fields an recommendation output is generated
field ( recommendations ) Type;

//Define the recommendation function itself
recommendation function GetRecommendedValuesFunction;

//Define the trigger (e.g. other context field)
side effects
{
recommendation function GetRecommendedValuesFunction
executed on field Title;
}
}

Step 2: Implement the Recommendation Logic

In the behavior pool, you implement the recommendation function. This is where the ABAP AI SDK comes in. You use the completion API to ask an LLM to categorize the trip based on the available context:

  1. Read the current entity data (description, destination, etc.)
  2. Build a prompt with the trip context, based on your system's master data
  3. Call the ABAP AI SDK Completion API
  4. Parse the LLM response
  5. Return the recommended value(s) with scores

The recommendation function returns a list of proposed values, each with a score and a flag indicating whether it's the suggested (top-ranked) value. The Fiori UI renders these as input assistance. The user can accept, modify, or override the suggestion. The AI proposes, the human decides. As it should be.

The full implementation:

class lhc_zr_aitrip definition inheriting from cl_abap_behavior_handler.

private section.

methods get_global_authorizations for global authorization
importing
request requested_authorizations for Trip
result result.

methods GetRecommendedValuesFunction for read
keys for function Trip~GetRecommendedValuesFunction result result.

methods build_recommendation_prompt
importing title type string
returning value(result) type string.

methods get_type_recommendation
importing prompt type string
returning value(result) type string.

endclass.

class lhc_zr_aitrip implementation.

method get_global_authorizations.
” demo only: open for all users – add real authorization checks before productive use
result-%create = if_abap_behv=>auth-allowed.
endmethod.

method GetRecommendedValuesFunction.

result = corresponding #( keys ).

read entities of zr_aitrip in local mode
entity Trip
fields ( Title ) with corresponding #( keys )
result data(trips)
failed failed.

loop at result assigning field-symbol(<result>).

read table trips with table key id components %tky = <result>-%tky into data(trip).
check sy-subrc = 0 and trip-Title is not initial.

data(type_prompt) = build_recommendation_prompt( conv #( trip-Title ) ).
data(recommended_type) = get_type_recommendation( type_prompt ).

check recommended_type is not initial.

<result>-%param = value #(
trip-type = value #( (
score=”1.0″
value = recommended_type
suggested = ‘X' ) ) ).

endloop.

endmethod.

method build_recommendation_prompt.

result = |Derive a trip category based on the given trip title: | &&
|{ title }| && cl_abap_char_utilities=>cr_lf &&
|The following trip categories exist: C (Customer facing), I (Internal).| && cl_abap_char_utilities=>cr_lf &&
|Please only answer with ‘C' or ‘I' as a single character.|.

endmethod.

method get_type_recommendation.

try.
data(api) = cl_aic_islm_compl_api_factory=>get( )->create_instance( ‘ZINTS_TRAVELAPP' ).
data(message_container) = api->create_message_container( ).
message_container->add_user_message( prompt ).

result = api->execute_for_messages( message_container )->get_completion( ).

catch cx_aic_api_factory cx_aic_prompt_template cx_aic_completion_api.
” recommendations are optional – on failure the field simply gets no suggested value
endtry.

endmethod.

endclass.

Step 3: Expose in the Projection

Make sure to expose the functionality in your UI projection by using “use recommendations;”. And that's it. The Fiori UI picks up the recommendation pattern automatically. No custom UI code, no frontend development, no begging a UI5 developer for help.

The Beauty of It

The beauty of RAP recommendations is the zero-effort UX integration. You implement the intelligence once in the behavior pool, and every Fiori Elements UI that consumes your RAP Business Object automatically benefits from the recommendations. It's the RAP philosophy at its finest: define the behavior, and the framework handles the presentation. And because the recommendation logic uses the ABAP AI SDK, you can switch from a deterministic rule-based approach to a generative AI approach (or vice versa) without changing the behavior definition or the UI. The abstraction is clean, the separation of concerns is real, and your future self will thank your present self for the architecture.

 

Use Case 2: Receipt Data Extraction

The Problem

After a business trip, the employee needs to submit expense reports with receipts attached. Hotel invoices, restaurant bills, taxi receipts. Today, the employee manually types in every field: vendor name, amount, currency, date, tax, category. It's tedious, error-prone, and, let's be honest, the reason most people procrastinate on their expense reports for weeks. Some of us have receipts from trips that happened so long ago that the ink has faded. Not speaking from personal experience, of course.

What if the employee could simply upload a receipt image and the system would extract the relevant data automatically?

Figure 2: Ai-Generated InvoiceFigure 2: AI-generated invoice

The Solution: Document Extraction with the ABAP AI SDK

This use case leverages the ABAP AI SDK's ability to send content, including file-based content, to an LLM and receive structured data back. The flow is:

  1. The user uploads a receipt (image) through the Fiori application.
  2. The ABAP application receives the file content.
  3. The ABAP AI SDK sends the file content along with a structured prompt to the LLM (via the Generative AI Hub).
  4. The LLM extracts key fields: vendor name, amount, currency, date, tax amount, expense category.
  5. The extracted data is returned to the ABAP application and pre-fills the expense line item.

The user uploads a crumpled taxi receipt, and five seconds later all the fields are filled in correctly. That's the kind of user experience that makes people actually submit their expense reports on time.

Figure 3: Extracted Data From FileFigure 3: Extracted data from file

Step 1: Enhance the CDS Data Model for File Upload

Before we can extract anything, we need to give users a way to upload their receipts. In RAP, file uploads are modelled as Large Objects (LOBs) using the @Semantics.largeObject annotation in your CDS view. This tells the framework to treat the field as a binary stream and render it as a file upload control in the Fiori UI (SAP Help, Working with Large Objects). First, your database table needs three fields: the binary content itself, a MIME type field, and a file name field. Then, in your CDS entity, you annotate them accordingly:

@Semantics.largeObject: {
acceptableMimeTypes: [ ‘image/*', ‘application/*' ],
cacheControl.maxAge: #MEDIUM,
contentDispositionPreference: #INLINE,
fileName: ‘UploadedReceiptFileName',
mimeType: ‘UploadedReceiptMimeType'
}
UploadedReceipt,

@Semantics.mimeType: true
UploadedReceiptMimeType,

UploadedReceiptFileName,

Let's break down what's happening here:

  • UploadedReceipt is the actual LOB field that stores the binary content of the uploaded file. The @Semantics.largeObject annotation binds it to its MIME type and file name fields, and defines which file types are accepted.
  • acceptableMimeTypes controls what the user can upload. In our case, image/* covers photos of receipts (JPEG, PNG, etc.) and application/* covers PDFs and other document formats. If you want to be more restrictive, you can specify individual MIME types like ‘image/jpeg', ‘application/pdf'.
  • UploadedReceiptMimeType stores the MIME type of the uploaded file and must carry the @Semantics.mimeType: true annotation. This is what the framework uses to know how to handle the file.
  • UploadedReceiptFileName stores the original file name. Technically optional, but very much recommended, because your users will appreciate seeing “taxi_receipt_berlin.pdf” instead of a cryptic binary blob identifier.
  • contentDispositionPreference: #INLINE tells the browser to display the file directly when possible (e.g. images and PDFs), rather than forcing a download.

Once these annotations are in place, the Fiori Elements automatically renders an upload control on the Object Page. No UI5 coding, no custom frontend logic. The user clicks upload, picks a file, and the binary content flows into your RAP Business Object. If you've ever implemented file upload the “classic” way in SAP, you'll appreciate how clean this is.

Step 2: Extract Data with the ABAP AI SDK

With the receipt uploaded and stored in the LOB field, the extraction can begin. The implementation combines the file content from the RAP entity with the ABAP AI SDK Completion API. You read the binary content from the LOB, and send it along with a structured extraction prompt through the SDK.

An execution flow could look like this:

  1. Read the uploaded receipt content from the entity (UploadedReceipt field)
  2. Convert to base64 encoding
  3. Build a structured extraction prompt:    
    ‘Extract the following fields from this receipt: vendor_name, amount, currency, date, tax_amount, category. Return the result as JSON'.
  4. Call the ABAP AI SDK Completion API with the prompt and the file content
  5. Parse the JSON response
  6. Map extracted fields to the expense line item entity

The full implementation:

class lhc_expense definition inheriting from cl_abap_behavior_handler.

private section.

types: begin of ty_reference_lists,
transaction_types type string,
categories type string,
end of ty_reference_lists.

methods extractData for determine on modify
keys for Expense~extractData.

methods get_reference_data_lists
returning value(result) type ty_reference_lists.

methods build_extraction_prompt
importing reference_lists type ty_reference_lists
returning value(result) type string.

methods call_ai_extraction
importing receipt type xstring
prompt type string
returning value(result) type string
raising cx_aic_completion_api
cx_aic_api_factory.

methods parse_extraction_response
importing json type string
changing expense type any
raising cx_root.

endclass.

class lhc_expense implementation.

method extractData.

read entities of zr_aitrip in local mode
entity Expense all fields with corresponding #( keys )
result data(expenses).

check expenses is not initial.

data(reference_lists) = get_reference_data_lists( ).
data(prompt) = build_extraction_prompt( reference_lists ).

data updates type table for update zr_aitrip\\Expense.

loop at expenses assigning field-symbol(<expense>).

if <expense>-UploadedReceipt is initial.
append value #( %tky = <expense>-%tky
%msg = new_message_with_text( severity = if_abap_behv_message=>severity-error
text=”No receipt uploaded – nothing to extract” ) )
to reported-expense.
continue.
endif.

try.
data(answer) = call_ai_extraction( receipt = <expense>-UploadedReceipt
prompt = prompt ).

parse_extraction_response( exporting json = answer
changing expense = <expense> ).

append corresponding #( <expense> ) to updates.

catch cx_aic_completion_api cx_aic_api_factory into data(ai_error).
append value #( %tky = <expense>-%tky
%msg = new_message_with_text( severity = if_abap_behv_message=>severity-error
text = |Receipt could not be processed by the AI service: { ai_error->get_text( ) }| ) )
to reported-expense.

catch cx_root into data(parse_error).
append value #( %tky = <expense>-%tky
%msg = new_message_with_text( severity = if_abap_behv_message=>severity-error
text = |Receipt data could not be interpreted: { parse_error->get_text( ) }| ) )
to reported-expense.
endtry.

endloop.

check updates is not initial.

modify entities of zr_aitrip in local mode
entity Expense
update fields ( ExpenseDescription TransactionType Category Amount Currency )
with updates.

endmethod.

method get_reference_data_lists.

select transactiontype, description from zr_transactiontype into table @DATA(transaction_types).
loop at transaction_types reference into data(transaction_type).
result-transaction_types = result-transaction_types &&
|{ transaction_type->transactiontype } ({ transaction_type->description }),|.
endloop.

select category, description from zr_category into table @DATA(categories).
loop at categories reference into data(category).
result-categories = result-categories && |{ category->category } ({ category->description }),|.
endloop.

endmethod.

method build_extraction_prompt.

result = |Analyze the attached receipt and extract the following information.| && cl_abap_char_utilities=>cr_lf
&& |Do NOT wrap the response in markdown. Respond in valid JSON.| && cl_abap_char_utilities=>cr_lf
&& |\{ | && cl_abap_char_utilities=>cr_lf
&& |”TransactionType” :””,| && cl_abap_char_utilities=>cr_lf
&& |”Category” :””,| && cl_abap_char_utilities=>cr_lf
&& |”ExpenseDescription” :””,| && cl_abap_char_utilities=>cr_lf
&& |”Amount” :””,| && cl_abap_char_utilities=>cr_lf
&& |”Currency” :””| && cl_abap_char_utilities=>cr_lf
&& |\}| && cl_abap_char_utilities=>cr_lf
&& |The following TransactionTypes exist in the system: { reference_lists-transaction_types }| && cl_abap_char_utilities=>cr_lf
&& |The following Categories exist in the system: { reference_lists-categories }|.

endmethod.

method call_ai_extraction.

data(api) = cl_aic_islm_compl_api_factory=>get( )->create_instance( ‘ZINTS_TRAVELAPP' ).
data(message_container) = api->create_message_container( ).

message_container->add_user_media_message(
)->add_media( aic_media_type=>image_webp
)->set_media_data( cl_web_http_utility=>encode_x_base64( receipt )
)->add_text( prompt ).

result = api->execute_for_messages( message_container )->get_completion( ).

endmethod.

method parse_extraction_response.

/ui2/cl_json=>deserialize( exporting json = json
pretty_name = /ui2/cl_json=>pretty_mode-none
changing data = expense ).

endmethod.

endclass.

The extraction can be triggered as a RAP determination (automatically when the file is uploaded), as a RAP action (explicitly by the user pressing an “Extract” button), or even through an event-driven side effect that fires after the upload completes. All three options are valid RAP patterns, and the right choice depends on your UX preferences. Personally, I like the determination approach: the user uploads the file, and by the time they look at the form fields, they're already filled in. A small moment of delight in an otherwise tedious process.

Orchestration Adds Enterprise Readiness

This is where the orchestration capabilities mentioned earlier become critical. Receipts may contain sensitive personal data: credit card numbers, home addresses, personal phone numbers. By enabling Data Masking in the orchestration configuration, you ensure that such data is redacted before it reaches the LLM. No code changes required, it's a configuration in the intelligent scenario. Similarly, Document Grounding can be used to provide additional context to the LLM (for example, your company's expense categories and vendor master data), improving extraction accuracy significantly. The LLM doesn't just guess “Restaurant” as a category, it picks the exact category code from your company's chart of accounts.

The Beauty of It

Receipt extraction is a high-volume, low-complexity task that AI handles extremely well. Every minute saved on manual data entry is a minute your employees spend on work that actually matters. And because the extraction runs inside ABAP, not in an external service that your users need to learn, the integration is invisible. The user uploads a file, the fields fill themselves. That's it. That's the entire (very simple) user story.

 

Additional References

You want directly jump start into an own implementation? Go for it! In case any questions arise within this journey, feel free to check out the following additional references:

 

Cheerio ABAP Folks!

Source link

Leave a Reply

Your email address will not be published. Required fields are marked *

Chat with us on WhatsApp!