Künstliche Intelligenz wird ein zunehmend wichtiger Bestandteil moderner Enterprise-Anwendungen. SAP bietet mit dem Joule SDK und den AI Foundation Services auf der Business Technology Platform (BTP) Möglichkeiten, KI-Funktionen direkt in ABAP Cloud Anwendungen zu integrieren. Dieser Artikel zeigt, wie du generative KI programmatisch nutzen kannst.
Übersicht: KI-Optionen für ABAP Cloud
| Option | Beschreibung | Verfügbarkeit |
|---|---|---|
| Joule in ADT | KI-Assistent für Code-Generierung im Editor | Seit 2024 |
| Joule SDK | Programmatische AI-Integration für Anwendungen | Seit 2025 |
| AI Foundation | BTP-basierte ML/AI Services | Seit 2023 |
| Generative AI Hub | Zentrale LLM-Integration auf BTP | Seit 2024 |
| SAP AI Core | Custom ML Model Deployment | Seit 2022 |
┌────────────────────────────────────────────────────────────────────┐│ SAP Business Technology Platform ││ ┌─────────────────────────────────────────────────────────────┐ ││ │ AI Foundation Layer │ ││ │ ┌─────────────┐ ┌─────────────┐ ┌────────────────────────┐ │ ││ │ │ Generative │ │ SAP AI │ │ Document │ │ ││ │ │ AI Hub │ │ Core │ │ Information │ │ ││ │ │ (LLM Access)│ │ (Custom ML) │ │ Extraction │ │ ││ │ └──────┬──────┘ └──────┬──────┘ └──────────┬─────────────┘ │ ││ │ │ │ │ │ ││ │ └───────────────┼────────────────────┘ │ ││ │ │ │ ││ │ ┌─────┴─────┐ │ ││ │ │ Joule SDK │ │ ││ │ └─────┬─────┘ │ ││ └─────────────────────────│───────────────────────────────────┘ ││ │ ││ ┌─────────────────────────┼───────────────────────────────────┐ ││ │ ABAP Cloud │ ││ │ ┌──────────────┐ ┌─────┴─────┐ ┌─────────────────────────┐ │ ││ │ │ S/4HANA Cloud│ │ BTP ABAP │ │ Communication │ │ ││ │ │ (Embedded) │ │Environment│ │ Arrangements │ │ ││ │ └──────────────┘ └───────────┘ └─────────────────────────┘ │ ││ └─────────────────────────────────────────────────────────────┘ │└────────────────────────────────────────────────────────────────────┘Joule SDK: Generative KI für ABAP
Das Joule SDK ermöglicht den programmatischen Zugriff auf SAPs generative KI-Modelle direkt aus ABAP Cloud Anwendungen.
Voraussetzungen
- SAP BTP Subaccount mit AI Foundation Berechtigung
- Generative AI Hub Service aktiviert
- ABAP Cloud System (BTP oder S/4HANA Cloud)
- Communication Arrangement für AI Services
Joule SDK Klassen
| Klasse | Beschreibung |
|---|---|
CL_JOULE_AI_CLIENT | Hauptklasse für AI-Aufrufe |
CL_JOULE_PROMPT_BUILDER | Prompt-Konstruktion |
CL_JOULE_RESPONSE_HANDLER | Response-Verarbeitung |
IF_JOULE_COMPLETION | Interface für Completions |
CX_JOULE_ERROR | Exception-Klasse |
Communication Scenario einrichten
- Communication Scenario erstellen:
<?xml version="1.0" encoding="utf-8"?><scn:scenario xmlns:scn="http://sap.com/xi/BASIS/Communication" scn:id="Z_JOULE_AI_SCENARIO" scn:version="1"> <scn:label>Joule AI Integration</scn:label> <scn:description>Communication Scenario für Joule SDK</scn:description> <scn:allowedInstances>MULTIPLE</scn:allowedInstances> <scn:communicationType>OUTBOUND</scn:communicationType> <scn:outboundServices> <scn:service scn:id="Z_JOULE_AI_SERVICE" scn:authMethod="OAUTH2"/> </scn:outboundServices></scn:scenario>- Communication Arrangement im Fiori Launchpad:
| Feld | Wert |
|---|---|
| Scenario | Z_JOULE_AI_SCENARIO |
| Arrangement Name | Z_JOULE_PROD |
| Communication System | SAP_AI_CORE |
| Auth Method | OAuth 2.0 Client Credentials |
| Token Endpoint | https://[ai-core-url]/oauth/token |
Einfacher AI-Aufruf mit Joule SDK
Text-Generierung
CLASS zcl_joule_example DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_ai_response, text TYPE string, tokens TYPE i, model TYPE string, finish_reason TYPE string, END OF ty_ai_response.
METHODS generate_text IMPORTING iv_prompt TYPE string RETURNING VALUE(rs_result) TYPE ty_ai_response RAISING cx_joule_error.
PRIVATE SECTION. CONSTANTS: c_comm_scenario TYPE if_com_scenario_factory=>ty_cscn_id VALUE 'Z_JOULE_AI_SCENARIO'.ENDCLASS.
CLASS zcl_joule_example IMPLEMENTATION. METHOD generate_text. " Joule AI Client erstellen DATA(lo_client) = cl_joule_ai_client=>create( iv_comm_scenario = c_comm_scenario ).
" Prompt konfigurieren DATA(lo_prompt) = cl_joule_prompt_builder=>create( ). lo_prompt->set_system_message( 'Du bist ein hilfreicher Assistent für SAP-Entwickler. ' && 'Antworte präzise und technisch korrekt auf Deutsch.' ). lo_prompt->add_user_message( iv_prompt ).
" Completion-Parameter DATA(ls_params) = VALUE cl_joule_ai_client=>ty_completion_params( max_tokens = 1000 temperature = '0.7' model = 'gpt-4' " oder 'gemini-pro', 'claude-3' ).
" AI-Aufruf ausführen DATA(lo_response) = lo_client->create_completion( io_prompt = lo_prompt is_parameters = ls_params ).
" Response verarbeiten rs_result = VALUE #( text = lo_response->get_text( ) tokens = lo_response->get_token_count( ) model = lo_response->get_model( ) finish_reason = lo_response->get_finish_reason( ) ). ENDMETHOD.ENDCLASS.Verwendung
" Beispiel: Produktbeschreibung generierenDATA(lo_ai) = NEW zcl_joule_example( ).
TRY. DATA(ls_response) = lo_ai->generate_text( |Erstelle eine kurze Produktbeschreibung für: | && |SAP S/4HANA Cloud, Public Edition. | && |Zielgruppe: IT-Entscheider. Max 100 Wörter.| ).
DATA(lv_description) = ls_response-text.
CATCH cx_joule_error INTO DATA(lx_error). " Fehlerbehandlung DATA(lv_error_text) = lx_error->get_text( ).ENDTRY.Praktische Use Cases
Use Case 1: Automatische E-Mail-Antworten
CLASS zcl_email_ai_responder DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_email, subject TYPE string, body TYPE string, sender TYPE string, END OF ty_email,
BEGIN OF ty_response, suggested_reply TYPE string, sentiment TYPE string, priority TYPE string, category TYPE string, END OF ty_response.
METHODS analyze_and_respond IMPORTING is_email TYPE ty_email RETURNING VALUE(rs_result) TYPE ty_response RAISING cx_joule_error.
PRIVATE SECTION. METHODS build_analysis_prompt IMPORTING is_email TYPE ty_email RETURNING VALUE(rv_prompt) TYPE string.
METHODS parse_ai_response IMPORTING iv_json TYPE string RETURNING VALUE(rs_result) TYPE ty_response.ENDCLASS.
CLASS zcl_email_ai_responder IMPLEMENTATION. METHOD analyze_and_respond. DATA(lo_client) = cl_joule_ai_client=>create( iv_comm_scenario = 'Z_JOULE_AI_SCENARIO' ).
" Strukturierter Prompt für Analyse DATA(lo_prompt) = cl_joule_prompt_builder=>create( ). lo_prompt->set_system_message( |Du bist ein E-Mail-Analyse-Assistent. | && |Analysiere E-Mails und schlage professionelle Antworten vor. | && |Antworte immer im JSON-Format mit den Feldern: | && |suggested_reply, sentiment (positiv/neutral/negativ), | && |priority (hoch/mittel/niedrig), category (Anfrage/Beschwerde/Info/Auftrag).| ).
lo_prompt->add_user_message( build_analysis_prompt( is_email ) ).
" AI-Aufruf DATA(lo_response) = lo_client->create_completion( io_prompt = lo_prompt is_parameters = VALUE #( max_tokens = 1500 temperature = '0.3' " Niedrig für konsistente Ausgabe ) ).
" JSON-Response parsen rs_result = parse_ai_response( lo_response->get_text( ) ). ENDMETHOD.
METHOD build_analysis_prompt. rv_prompt = |Analysiere diese E-Mail und erstelle eine Antwort:\n\n| && |Von: { is_email-sender }\n| && |Betreff: { is_email-subject }\n\n| && |Inhalt:\n{ is_email-body }\n\n| && |Erstelle eine professionelle Antwort auf Deutsch.|. ENDMETHOD.
METHOD parse_ai_response. " JSON parsen /ui2/cl_json=>deserialize( EXPORTING json = iv_json pretty_name = /ui2/cl_json=>pretty_mode-camel_case CHANGING data = rs_result ). ENDMETHOD.ENDCLASS.Use Case 2: Dokumentenklassifizierung
CLASS zcl_document_classifier DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_classification, category TYPE string, confidence TYPE decfloat16, keywords TYPE string_table, summary TYPE string, END OF ty_classification,
BEGIN OF ty_category, name TYPE string, description TYPE string, END OF ty_category, tt_categories TYPE STANDARD TABLE OF ty_category WITH EMPTY KEY.
METHODS classify_document IMPORTING iv_document_text TYPE string it_categories TYPE tt_categories RETURNING VALUE(rs_result) TYPE ty_classification RAISING cx_joule_error.ENDCLASS.
CLASS zcl_document_classifier IMPLEMENTATION. METHOD classify_document. DATA(lo_client) = cl_joule_ai_client=>create( iv_comm_scenario = 'Z_JOULE_AI_SCENARIO' ).
" Kategorien als Kontext aufbereiten DATA(lv_categories) = REDUCE string( INIT result = || FOR cat IN it_categories NEXT result = result && |{ cat-name }: { cat-description }\n| ).
" Prompt für Klassifizierung DATA(lo_prompt) = cl_joule_prompt_builder=>create( ). lo_prompt->set_system_message( |Du bist ein Dokumentenklassifizierer. | && |Klassifiziere Dokumente in vorgegebene Kategorien. | && |Antworte im JSON-Format: | && |category (exakte Kategorie), confidence (0.0-1.0), | && |keywords (Array wichtiger Begriffe), summary (1-2 Sätze).| ).
lo_prompt->add_user_message( |Klassifiziere dieses Dokument:\n\n| && |{ iv_document_text }\n\n| && |Verfügbare Kategorien:\n{ lv_categories }| ).
DATA(lo_response) = lo_client->create_completion( io_prompt = lo_prompt is_parameters = VALUE #( max_tokens = 500 temperature = '0.1' " Sehr niedrig für konsistente Klassifizierung ) ).
" Response parsen /ui2/cl_json=>deserialize( EXPORTING json = lo_response->get_text( ) pretty_name = /ui2/cl_json=>pretty_mode-camel_case CHANGING data = rs_result ). ENDMETHOD.ENDCLASS.Use Case 3: RAP-Integration mit AI-Enrichment
" Behavior Definitionmanaged implementation in class zbp_i_ticket unique;strict ( 2 );
define behavior for ZI_Ticket alias Ticket{ // AI-basierte Determination determination enrichWithAI on modify { field Description; }
// AI Action action suggestSolution result [1] $self;}CLASS lhc_ticket DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS: enrichWithAI FOR DETERMINE ON MODIFY IMPORTING keys FOR Ticket~enrichWithAI,
suggestSolution FOR MODIFY IMPORTING keys FOR ACTION Ticket~suggestSolution RESULT result.ENDCLASS.
CLASS lhc_ticket IMPLEMENTATION. METHOD enrichWithAI. " Tickets lesen READ ENTITIES OF zi_ticket IN LOCAL MODE ENTITY Ticket FIELDS ( Description ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_tickets).
DATA(lo_ai) = cl_joule_ai_client=>create( iv_comm_scenario = 'Z_JOULE_AI_SCENARIO' ).
LOOP AT lt_tickets INTO DATA(ls_ticket). TRY. " AI-Analyse der Beschreibung DATA(lo_prompt) = cl_joule_prompt_builder=>create( ). lo_prompt->set_system_message( |Du analysierst Support-Tickets. | && |Extrahiere: Kategorie, Priorität (1-5), Schlagworte. | && |JSON-Format: category, priority, keywords (Array).| ). lo_prompt->add_user_message( ls_ticket-Description ).
DATA(lo_response) = lo_ai->create_completion( io_prompt = lo_prompt is_parameters = VALUE #( max_tokens = 200 temperature = '0.2' ) ).
" Response parsen DATA: BEGIN OF ls_analysis, category TYPE string, priority TYPE i, keywords TYPE string_table, END OF ls_analysis.
/ui2/cl_json=>deserialize( EXPORTING json = lo_response->get_text( ) CHANGING data = ls_analysis ).
" Ticket aktualisieren MODIFY ENTITIES OF zi_ticket IN LOCAL MODE ENTITY Ticket UPDATE FIELDS ( Category Priority Keywords ) WITH VALUE #( ( %tky = ls_ticket-%tky Category = ls_analysis-category Priority = ls_analysis-priority Keywords = concat_lines_of( table = ls_analysis-keywords sep = ',' ) ) ).
CATCH cx_joule_error INTO DATA(lx_error). APPEND VALUE #( %tky = ls_ticket-%tky %msg = new_message_with_text( text = lx_error->get_text( ) ) ) TO reported-ticket. ENDTRY. ENDLOOP. ENDMETHOD.
METHOD suggestSolution. " Ticket mit Historie lesen READ ENTITIES OF zi_ticket IN LOCAL MODE ENTITY Ticket ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_tickets).
DATA(lo_ai) = cl_joule_ai_client=>create( iv_comm_scenario = 'Z_JOULE_AI_SCENARIO' ).
LOOP AT lt_tickets INTO DATA(ls_ticket). TRY. " Ähnliche gelöste Tickets als Kontext laden DATA(lt_similar) = get_similar_resolved_tickets( ls_ticket-Category ).
DATA(lv_context) = REDUCE string( INIT ctx = || FOR sim IN lt_similar NEXT ctx = ctx && |Problem: { sim-Description }\nLösung: { sim-Solution }\n\n| ).
" AI-Lösungsvorschlag DATA(lo_prompt) = cl_joule_prompt_builder=>create( ). lo_prompt->set_system_message( |Du bist ein Support-Experte. | && |Basierend auf ähnlichen gelösten Tickets, schlage eine Lösung vor. | && |Antworte strukturiert mit: Diagnose, Lösungsschritte, Prävention.| ).
lo_prompt->add_user_message( |Aktuelles Ticket:\n{ ls_ticket-Description }\n\n| && |Ähnliche gelöste Tickets:\n{ lv_context }| ).
DATA(lo_response) = lo_ai->create_completion( io_prompt = lo_prompt is_parameters = VALUE #( max_tokens = 1000 temperature = '0.5' ) ).
" Lösungsvorschlag speichern MODIFY ENTITIES OF zi_ticket IN LOCAL MODE ENTITY Ticket UPDATE FIELDS ( SuggestedSolution ) WITH VALUE #( ( %tky = ls_ticket-%tky SuggestedSolution = lo_response->get_text( ) ) ).
CATCH cx_joule_error. ENDTRY. ENDLOOP.
" Ergebnis zurückgeben READ ENTITIES OF zi_ticket IN LOCAL MODE ENTITY Ticket ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_result).
result = VALUE #( FOR t IN lt_result ( %tky = t-%tky %param = t ) ). ENDMETHOD.ENDCLASS.SAP AI Foundation Services
Document Information Extraction
Der Document Information Extraction Service extrahiert strukturierte Daten aus Dokumenten.
CLASS zcl_document_extraction DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_invoice_data, vendor_name TYPE string, invoice_number TYPE string, invoice_date TYPE d, total_amount TYPE decfloat16, currency TYPE waers, line_items TYPE string_table, END OF ty_invoice_data.
METHODS extract_invoice IMPORTING iv_document_base64 TYPE string RETURNING VALUE(rs_result) TYPE ty_invoice_data RAISING cx_ai_service_error.
PRIVATE SECTION. CONSTANTS c_comm_scenario TYPE string VALUE 'Z_AI_DOC_EXTRACTION'.ENDCLASS.
CLASS zcl_document_extraction IMPLEMENTATION. METHOD extract_invoice. " HTTP Client für AI Service DATA(lo_destination) = cl_http_destination_provider=>create_by_comm_arrangement( comm_scenario = c_comm_scenario service_id = 'Z_DOC_EXTRACTION_SERVICE' ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( i_destination = lo_destination ).
TRY. " Request Body erstellen DATA: BEGIN OF ls_request, document_type TYPE string VALUE 'invoice', file_content TYPE string, options TYPE string VALUE '{"language":"de"}', END OF ls_request.
ls_request-file_content = iv_document_base64.
DATA(lv_json) = /ui2/cl_json=>serialize( data = ls_request compress = abap_true pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).
" Request konfigurieren DATA(lo_request) = lo_client->get_http_request( ). lo_request->set_uri_path( '/document/extraction' ). lo_request->set_header_field( i_name = 'Content-Type' i_value = 'application/json' ). lo_request->set_text( lv_json ).
" Ausführen DATA(lo_response) = lo_client->execute( if_web_http_client=>post ).
IF lo_response->get_status( )-code = 200. " Response parsen DATA: BEGIN OF ls_response, extraction TYPE ty_invoice_data, confidence TYPE decfloat16, END OF ls_response.
/ui2/cl_json=>deserialize( EXPORTING json = lo_response->get_text( ) CHANGING data = ls_response ).
rs_result = ls_response-extraction. ENDIF.
CLEANUP. lo_client->close( ). ENDTRY. ENDMETHOD.ENDCLASS.Text Embedding für Semantic Search
CLASS zcl_embedding_service DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: tt_embedding TYPE STANDARD TABLE OF decfloat16 WITH EMPTY KEY,
BEGIN OF ty_document_embedding, document_id TYPE string, text TYPE string, embedding TYPE tt_embedding, END OF ty_document_embedding, tt_document_embeddings TYPE STANDARD TABLE OF ty_document_embedding WITH EMPTY KEY.
METHODS create_embedding IMPORTING iv_text TYPE string RETURNING VALUE(rt_embedding) TYPE tt_embedding RAISING cx_ai_service_error.
METHODS find_similar_documents IMPORTING iv_query_text TYPE string it_documents TYPE tt_document_embeddings iv_top_k TYPE i DEFAULT 5 RETURNING VALUE(rt_results) TYPE string_table RAISING cx_ai_service_error.
PRIVATE SECTION. METHODS calculate_cosine_similarity IMPORTING it_vec1 TYPE tt_embedding it_vec2 TYPE tt_embedding RETURNING VALUE(rv_similarity) TYPE decfloat16.ENDCLASS.
CLASS zcl_embedding_service IMPLEMENTATION. METHOD create_embedding. DATA(lo_client) = cl_joule_ai_client=>create( iv_comm_scenario = 'Z_JOULE_AI_SCENARIO' ).
" Embedding-Modell aufrufen rt_embedding = lo_client->create_embedding( iv_text = iv_text iv_model = 'text-embedding-ada-002' )->get_vector( ). ENDMETHOD.
METHOD find_similar_documents. " Query-Embedding erstellen DATA(lt_query_embedding) = create_embedding( iv_query_text ).
" Ähnlichkeit zu allen Dokumenten berechnen DATA: BEGIN OF ls_scored, document_id TYPE string, similarity TYPE decfloat16, END OF ls_scored, lt_scored LIKE STANDARD TABLE OF ls_scored.
LOOP AT it_documents INTO DATA(ls_doc). DATA(lv_similarity) = calculate_cosine_similarity( it_vec1 = lt_query_embedding it_vec2 = ls_doc-embedding ).
APPEND VALUE #( document_id = ls_doc-document_id similarity = lv_similarity ) TO lt_scored. ENDLOOP.
" Nach Ähnlichkeit sortieren SORT lt_scored BY similarity DESCENDING.
" Top-K zurückgeben LOOP AT lt_scored INTO ls_scored FROM 1 TO iv_top_k. APPEND ls_scored-document_id TO rt_results. ENDLOOP. ENDMETHOD.
METHOD calculate_cosine_similarity. DATA lv_dot_product TYPE decfloat16 VALUE 0. DATA lv_norm1 TYPE decfloat16 VALUE 0. DATA lv_norm2 TYPE decfloat16 VALUE 0.
DATA(lv_count) = lines( it_vec1 ).
DO lv_count TIMES. DATA(lv_idx) = sy-index. DATA(lv_v1) = it_vec1[ lv_idx ]. DATA(lv_v2) = it_vec2[ lv_idx ].
lv_dot_product = lv_dot_product + ( lv_v1 * lv_v2 ). lv_norm1 = lv_norm1 + ( lv_v1 * lv_v1 ). lv_norm2 = lv_norm2 + ( lv_v2 * lv_v2 ). ENDDO.
IF lv_norm1 > 0 AND lv_norm2 > 0. rv_similarity = lv_dot_product / ( sqrt( lv_norm1 ) * sqrt( lv_norm2 ) ). ENDIF. ENDMETHOD.ENDCLASS.Vollständiges Beispiel: AI-gestützter Helpdesk
1. CDS View für Tickets
@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'AI Support Ticket'
define root view entity ZI_AITicket as select from zai_ticket{ key ticket_id as TicketId, title as Title, description as Description, category as Category, priority as Priority, status as Status, ai_category as AICategory, ai_sentiment as AISentiment, ai_suggested_solution as AISuggestedSolution, ai_confidence as AIConfidence,
@Semantics.user.createdBy: true created_by as CreatedBy,
@Semantics.systemDateTime.createdAt: true created_at as CreatedAt,
@Semantics.user.lastChangedBy: true last_changed_by as LastChangedBy,
@Semantics.systemDateTime.lastChangedAt: true last_changed_at as LastChangedAt}2. AI Service Klasse
CLASS zcl_ticket_ai_service DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. TYPES: BEGIN OF ty_ticket_analysis, category TYPE string, sentiment TYPE string, priority TYPE i, confidence TYPE decfloat16, keywords TYPE string_table, suggested_solution TYPE string, END OF ty_ticket_analysis.
METHODS analyze_ticket IMPORTING iv_title TYPE string iv_description TYPE string RETURNING VALUE(rs_result) TYPE ty_ticket_analysis RAISING cx_joule_error.
METHODS generate_solution IMPORTING iv_description TYPE string iv_category TYPE string RETURNING VALUE(rv_solution) TYPE string RAISING cx_joule_error.
METHODS summarize_ticket IMPORTING iv_description TYPE string RETURNING VALUE(rv_summary) TYPE string RAISING cx_joule_error.
PRIVATE SECTION. DATA mo_client TYPE REF TO cl_joule_ai_client.
METHODS get_client RETURNING VALUE(ro_client) TYPE REF TO cl_joule_ai_client.ENDCLASS.
CLASS zcl_ticket_ai_service IMPLEMENTATION. METHOD get_client. IF mo_client IS NOT BOUND. mo_client = cl_joule_ai_client=>create( iv_comm_scenario = 'Z_JOULE_AI_SCENARIO' ). ENDIF. ro_client = mo_client. ENDMETHOD.
METHOD analyze_ticket. DATA(lo_prompt) = cl_joule_prompt_builder=>create( ).
lo_prompt->set_system_message( |Du analysierst Support-Tickets für ein IT-Helpdesk-System.\n| && |Analysiere das Ticket und extrahiere:\n| && |- category: Die Hauptkategorie (Hardware, Software, Netzwerk, Zugang, Sonstiges)\n| && |- sentiment: Stimmung des Kunden (positiv, neutral, frustriert, verärgert)\n| && |- priority: Dringlichkeit 1-5 (1=kritisch, 5=niedrig)\n| && |- confidence: Dein Vertrauen in die Analyse 0.0-1.0\n| && |- keywords: Wichtige technische Begriffe als Array\n| && |- suggested_solution: Kurzer Lösungsvorschlag (2-3 Sätze)\n| && |Antworte nur im JSON-Format, keine zusätzlichen Erklärungen.| ).
lo_prompt->add_user_message( |Titel: { iv_title }\n\nBeschreibung:\n{ iv_description }| ).
DATA(lo_response) = get_client( )->create_completion( io_prompt = lo_prompt is_parameters = VALUE #( max_tokens = 800 temperature = '0.2' ) ).
" JSON parsen DATA(lv_json) = lo_response->get_text( ).
" Mögliche Markdown-Formatierung entfernen REPLACE ALL OCCURRENCES OF '```json' IN lv_json WITH ''. REPLACE ALL OCCURRENCES OF '```' IN lv_json WITH ''. CONDENSE lv_json.
/ui2/cl_json=>deserialize( EXPORTING json = lv_json pretty_name = /ui2/cl_json=>pretty_mode-camel_case CHANGING data = rs_result ). ENDMETHOD.
METHOD generate_solution. DATA(lo_prompt) = cl_joule_prompt_builder=>create( ).
lo_prompt->set_system_message( |Du bist ein erfahrener IT-Support-Mitarbeiter.\n| && |Erstelle eine detaillierte Lösungsanleitung für das folgende Problem.\n| && |Strukturiere die Antwort mit:\n| && |1. Problemdiagnose (2-3 Sätze)\n| && |2. Lösungsschritte (nummerierte Liste)\n| && |3. Präventionshinweise (falls zutreffend)\n| && |Schreibe präzise und verständlich.| ).
lo_prompt->add_user_message( |Kategorie: { iv_category }\n\nProblem:\n{ iv_description }| ).
DATA(lo_response) = get_client( )->create_completion( io_prompt = lo_prompt is_parameters = VALUE #( max_tokens = 1500 temperature = '0.5' ) ).
rv_solution = lo_response->get_text( ). ENDMETHOD.
METHOD summarize_ticket. DATA(lo_prompt) = cl_joule_prompt_builder=>create( ).
lo_prompt->set_system_message( |Fasse das folgende Support-Ticket in maximal 2 Sätzen zusammen. | && |Konzentriere dich auf das Kernproblem und die gewünschte Lösung.| ).
lo_prompt->add_user_message( iv_description ).
DATA(lo_response) = get_client( )->create_completion( io_prompt = lo_prompt is_parameters = VALUE #( max_tokens = 200 temperature = '0.3' ) ).
rv_summary = lo_response->get_text( ). ENDMETHOD.ENDCLASS.3. RAP Behavior Implementation
CLASS lhc_aiticket DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. DATA mo_ai_service TYPE REF TO zcl_ticket_ai_service.
METHODS get_ai_service RETURNING VALUE(ro_service) TYPE REF TO zcl_ticket_ai_service.
METHODS: analyzeWithAI FOR DETERMINE ON MODIFY IMPORTING keys FOR AITicket~analyzeWithAI,
generateSolution FOR MODIFY IMPORTING keys FOR ACTION AITicket~generateSolution RESULT result,
refreshAnalysis FOR MODIFY IMPORTING keys FOR ACTION AITicket~refreshAnalysis RESULT result.ENDCLASS.
CLASS lhc_aiticket IMPLEMENTATION. METHOD get_ai_service. IF mo_ai_service IS NOT BOUND. mo_ai_service = NEW zcl_ticket_ai_service( ). ENDIF. ro_service = mo_ai_service. ENDMETHOD.
METHOD analyzeWithAI. READ ENTITIES OF zi_aiticket IN LOCAL MODE ENTITY AITicket FIELDS ( Title Description ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_tickets).
DATA(lo_ai) = get_ai_service( ).
LOOP AT lt_tickets INTO DATA(ls_ticket). TRY. " AI-Analyse durchführen DATA(ls_analysis) = lo_ai->analyze_ticket( iv_title = ls_ticket-Title iv_description = ls_ticket-Description ).
" Ergebnisse speichern MODIFY ENTITIES OF zi_aiticket IN LOCAL MODE ENTITY AITicket UPDATE FIELDS ( AICategory AISentiment Priority AIConfidence ) WITH VALUE #( ( %tky = ls_ticket-%tky AICategory = ls_analysis-category AISentiment = ls_analysis-sentiment Priority = ls_analysis-priority AIConfidence = ls_analysis-confidence ) ).
CATCH cx_joule_error INTO DATA(lx_error). APPEND VALUE #( %tky = ls_ticket-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-warning text = |AI-Analyse fehlgeschlagen: { lx_error->get_text( ) }| ) ) TO reported-aiticket. ENDTRY. ENDLOOP. ENDMETHOD.
METHOD generateSolution. READ ENTITIES OF zi_aiticket IN LOCAL MODE ENTITY AITicket FIELDS ( Description AICategory ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_tickets).
DATA(lo_ai) = get_ai_service( ).
LOOP AT lt_tickets INTO DATA(ls_ticket). TRY. " Lösung generieren DATA(lv_solution) = lo_ai->generate_solution( iv_description = ls_ticket-Description iv_category = ls_ticket-AICategory ).
" Speichern MODIFY ENTITIES OF zi_aiticket IN LOCAL MODE ENTITY AITicket UPDATE FIELDS ( AISuggestedSolution ) WITH VALUE #( ( %tky = ls_ticket-%tky AISuggestedSolution = lv_solution ) ).
CATCH cx_joule_error INTO DATA(lx_error). APPEND VALUE #( %tky = ls_ticket-%tky ) TO failed-aiticket.
APPEND VALUE #( %tky = ls_ticket-%tky %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = lx_error->get_text( ) ) ) TO reported-aiticket. ENDTRY. ENDLOOP.
" Ergebnis zurückgeben READ ENTITIES OF zi_aiticket IN LOCAL MODE ENTITY AITicket ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_result).
result = VALUE #( FOR t IN lt_result ( %tky = t-%tky %param = t ) ). ENDMETHOD.
METHOD refreshAnalysis. " Erneute AI-Analyse erzwingen analyzeWithAI( CORRESPONDING #( keys ) ).
READ ENTITIES OF zi_aiticket IN LOCAL MODE ENTITY AITicket ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_result).
result = VALUE #( FOR t IN lt_result ( %tky = t-%tky %param = t ) ). ENDMETHOD.ENDCLASS.Limitierungen und Best Practices
Limitierungen
| Limitation | Beschreibung | Workaround |
|---|---|---|
| Latenz | AI-Aufrufe dauern 1-10 Sekunden | Asynchrone Verarbeitung, Caching |
| Kosten | Tokens werden berechnet | Prompt-Optimierung, Caching |
| Rate Limits | API-Aufruflimits | Retry-Logik, Queuing |
| Halluzination | LLMs können falsche Infos generieren | Validierung, Human Review |
| Kontextlimit | Max Token-Anzahl pro Aufruf | Text-Chunking, Summarization |
| Offline | Cloud-basiert, keine Offline-Nutzung | Fallback-Logik |
| Datenschutz | Daten gehen an externe Services | Anonymisierung, On-Premise ML |
Best Practices
✅ DO:
1. Strukturierte Prompts verwenden - System-Message für Rolle und Format - User-Message für konkreten Input - Beispiele im Prompt (Few-Shot Learning)
2. Temperatur anpassen - 0.1-0.3: Konsistente, faktische Ausgaben - 0.5-0.7: Kreativere Antworten - 0.9+: Sehr kreativ, weniger vorhersagbar
3. Fehlerbehandlung implementieren - Retry bei transienten Fehlern - Graceful Degradation - Logging für Debugging
4. Ergebnisse validieren - JSON-Schema-Validierung - Plausibilitätsprüfungen - Human-in-the-Loop für kritische Entscheidungen
5. Caching einsetzen - Identische Prompts cachen - Embeddings vorberechnen - TTL basierend auf Datenaktualität
❌ DON'T:
1. Sensible Daten unverschlüsselt senden - Personendaten anonymisieren - Keine Passwörter/Secrets im Prompt
2. AI-Ergebnisse blind vertrauen - Immer validieren - Bei Unsicherheit: User fragen
3. Zu lange Prompts - Kontext auf das Wesentliche beschränken - Bei Bedarf: Summarization vorschalten
4. Synchrone Aufrufe in UI-kritischen Pfaden - Background Jobs verwenden - Asynchrone Patterns nutzenKostenoptimierung
" Prompt-Komprimierung für KostenreduktionMETHOD optimize_prompt. " Vor Optimierung: 500 Tokens " Nach Optimierung: 150 Tokens (70% Ersparnis)
DATA(lv_optimized) = |Klassifiziere: { iv_category }\n| && |Text: { substring( val = iv_text len = 500 ) }|. " Maximal 500 Zeichen
" Anstatt: " "Du bist ein hilfreicher Assistent der Dokumente klassifiziert. " Bitte analysiere den folgenden Text sorgfältig und ordne ihn " einer der folgenden Kategorien zu: ..."ENDMETHOD.
" Caching für häufige AnfragenCLASS-DATA: gt_cache TYPE HASHED TABLE OF ty_cache_entry WITH UNIQUE KEY prompt_hash.
METHOD get_cached_or_call. DATA(lv_hash) = calculate_hash( iv_prompt ).
" Cache prüfen READ TABLE gt_cache WITH KEY prompt_hash = lv_hash INTO DATA(ls_cached).
IF sy-subrc = 0 AND ls_cached-timestamp > ( utclong_current( ) - 3600 ). rv_result = ls_cached-response. RETURN. ENDIF.
" AI aufrufen und cachen rv_result = call_ai( iv_prompt ).
INSERT VALUE #( prompt_hash = lv_hash response = rv_result timestamp = utclong_current( ) ) INTO TABLE gt_cache.ENDMETHOD.Ausblick und Roadmap
SAP entwickelt die KI-Integration kontinuierlich weiter:
| Zeitraum | Erwartete Features |
|---|---|
| 2025 | Joule SDK GA, erweiterte RAP-Integration |
| 2025-2026 | Fine-Tuning auf SAP-Daten, Multimodale Modelle |
| 2026+ | On-Premise LLM Support, Edge AI für S/4HANA |
Trends:
- Agentic AI: Autonome Agenten für komplexe Workflows
- RAG (Retrieval Augmented Generation): Unternehmens-Wissensbasen einbinden
- Multimodal: Bilder, Dokumente, Sprache verarbeiten
- Fine-Tuning: Modelle auf SAP-spezifische Domänen anpassen
Fazit
Die Integration von KI in ABAP Cloud eröffnet neue Möglichkeiten für intelligente Anwendungen. Mit dem Joule SDK und den AI Foundation Services können Entwickler generative KI-Funktionen direkt in RAP-Anwendungen einbetten - von automatischer Klassifizierung über Textgenerierung bis zu semantischer Suche.
Wichtig: KI ist ein mächtiges Werkzeug, aber kein Ersatz für fachliche Validierung. Ergebnisse sollten immer geprüft und kritische Entscheidungen nicht vollautomatisch getroffen werden.
Weiterführende Themen
- Joule für ABAP - KI-Assistent für Code-Generierung in ADT
- RAP External API Consumption - REST APIs in RAP integrieren
- RAP Basics - Grundlagen des RESTful Application Programming Model
- Clean ABAP - Wartbarer ABAP-Code