L’intelligence artificielle devient un composant de plus en plus important des applications d’entreprise modernes. SAP propose avec le SDK Joule et les services AI Foundation sur la Business Technology Platform (BTP) des possibilités d’intégrer directement des fonctionnalités IA dans les applications ABAP Cloud. Cet article montre comment utiliser l’IA générative de manière programmatique.
Vue d’ensemble : Options IA pour ABAP Cloud
| Option | Description | Disponibilité |
|---|---|---|
| Joule dans ADT | Assistant IA pour la génération de code dans l’éditeur | Depuis 2024 |
| SDK Joule | Intégration IA programmatique pour les applications | Depuis 2025 |
| AI Foundation | Services ML/AI basés sur BTP | Depuis 2023 |
| Generative AI Hub | Intégration LLM centralisée sur BTP | Depuis 2024 |
| SAP AI Core | Déploiement de modèles ML personnalisés | Depuis 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 │ │ ││ │ └──────────────┘ └───────────┘ └─────────────────────────┘ │ ││ └─────────────────────────────────────────────────────────────┘ │└────────────────────────────────────────────────────────────────────┘SDK Joule : IA générative pour ABAP
Le SDK Joule permet l’accès programmatique aux modèles IA génératifs de SAP directement depuis les applications ABAP Cloud.
Prérequis
- SAP BTP Subaccount avec autorisation AI Foundation
- Service Generative AI Hub activé
- Système ABAP Cloud (BTP ou S/4HANA Cloud)
- Communication Arrangement pour les services IA
Classes du SDK Joule
| Classe | Description |
|---|---|
CL_JOULE_AI_CLIENT | Classe principale pour les appels IA |
CL_JOULE_PROMPT_BUILDER | Construction de prompts |
CL_JOULE_RESPONSE_HANDLER | Traitement des réponses |
IF_JOULE_COMPLETION | Interface pour les complétions |
CX_JOULE_ERROR | Classe d’exception |
Configurer le Communication Scenario
- Créer un Communication Scenario :
<?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 dans le Fiori Launchpad :
| Champ | Valeur |
|---|---|
| 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 |
Appel IA simple avec le SDK Joule
Génération de texte
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. " Créer le client Joule AI DATA(lo_client) = cl_joule_ai_client=>create( iv_comm_scenario = c_comm_scenario ).
" Configurer le prompt DATA(lo_prompt) = cl_joule_prompt_builder=>create( ). lo_prompt->set_system_message( 'Tu es un assistant utile pour les développeurs SAP. ' && 'Réponds de manière précise et techniquement correcte en français." ). lo_prompt->add_user_message( iv_prompt ).
" Paramètres de complétion DATA(ls_params) = VALUE cl_joule_ai_client=>ty_completion_params( max_tokens = 1000 temperature = '0.7" model = 'gpt-4' " ou 'gemini-pro', 'claude-3" ).
" Exécuter l'appel IA DATA(lo_response) = lo_client->create_completion( io_prompt = lo_prompt is_parameters = ls_params ).
" Traiter la réponse 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.Utilisation
" Exemple : générer une description de produitDATA(lo_ai) = NEW zcl_joule_example( ).
TRY. DATA(ls_response) = lo_ai->generate_text( |Crée une brève description de produit pour : | && |SAP S/4HANA Cloud, Public Edition. | && |Public cible : décideurs IT. Max 100 mots.| ).
DATA(lv_description) = ls_response-text.
CATCH cx_joule_error INTO DATA(lx_error). " Gestion des erreurs DATA(lv_error_text) = lx_error->get_text( ).ENDTRY.Cas d’usage pratiques
Cas d’usage 1 : Réponses automatiques aux e-mails
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" ).
" Prompt structuré pour l'analyse DATA(lo_prompt) = cl_joule_prompt_builder=>create( ). lo_prompt->set_system_message( |Tu es un assistant d'analyse d'e-mails. | && |Analyse les e-mails et propose des réponses professionnelles. | && |Réponds toujours au format JSON avec les champs : | && |suggested_reply, sentiment (positif/neutre/négatif), | && |priority (élevée/moyenne/faible), category (demande/réclamation/info/commande).| ).
lo_prompt->add_user_message( build_analysis_prompt( is_email ) ).
" Appel IA DATA(lo_response) = lo_client->create_completion( io_prompt = lo_prompt is_parameters = VALUE #( max_tokens = 1500 temperature = '0.3' " Faible pour une sortie cohérente ) ).
" Parser la réponse JSON rs_result = parse_ai_response( lo_response->get_text( ) ). ENDMETHOD.
METHOD build_analysis_prompt. rv_prompt = |Analyse cet e-mail et crée une réponse :\n\n| && |De : { is_email-sender }\n| && |Objet : { is_email-subject }\n\n| && |Contenu :\n{ is_email-body }\n\n| && |Crée une réponse professionnelle en français.|. ENDMETHOD.
METHOD parse_ai_response. " Parser le JSON /ui2/cl_json=>deserialize( EXPORTING json = iv_json pretty_name = /ui2/cl_json=>pretty_mode-camel_case CHANGING data = rs_result ). ENDMETHOD.ENDCLASS.Cas d’usage 2 : Classification de documents
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" ).
" Préparer les catégories comme contexte DATA(lv_categories) = REDUCE string( INIT result = || FOR cat IN it_categories NEXT result = result && |{ cat-name }: { cat-description }\n| ).
" Prompt pour la classification DATA(lo_prompt) = cl_joule_prompt_builder=>create( ). lo_prompt->set_system_message( |Tu es un classificateur de documents. | && |Classe les documents dans des catégories prédéfinies. | && |Réponds au format JSON : | && |category (catégorie exacte), confidence (0.0-1.0), | && |keywords (tableau de termes importants), summary (1-2 phrases).| ).
lo_prompt->add_user_message( |Classe ce document :\n\n| && |{ iv_document_text }\n\n| && |Catégories disponibles :\n{ lv_categories }| ).
DATA(lo_response) = lo_client->create_completion( io_prompt = lo_prompt is_parameters = VALUE #( max_tokens = 500 temperature = '0.1' " Très faible pour une classification cohérente ) ).
" Parser la réponse /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.Cas d’usage 3 : Intégration RAP avec enrichissement IA
" Behavior Definitionmanaged implementation in class zbp_i_ticket unique;strict ( 2 );
define behavior for ZI_Ticket alias Ticket{ // Détermination basée sur l'IA determination enrichWithAI on modify { field Description; }
// Action IA 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. " Lire les tickets 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. " Analyse IA de la description DATA(lo_prompt) = cl_joule_prompt_builder=>create( ). lo_prompt->set_system_message( |Tu analyses les tickets de support. | && |Extrais : catégorie, priorité (1-5), mots-clés. | && |Format JSON : category, priority, keywords (tableau).| ). 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' ) ).
" Parser la réponse 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 ).
" Mettre à jour le ticket 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. " Lire le ticket avec l'historique 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. " Charger des tickets résolus similaires comme contexte 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 && |Problème : { sim-Description }\nSolution : { sim-Solution }\n\n| ).
" Proposition de solution IA DATA(lo_prompt) = cl_joule_prompt_builder=>create( ). lo_prompt->set_system_message( |Tu es un expert du support. | && |Sur la base de tickets résolus similaires, propose une solution. | && |Réponds de manière structurée avec : diagnostic, étapes de solution, prévention.| ).
lo_prompt->add_user_message( |Ticket actuel :\n{ ls_ticket-Description }\n\n| && |Tickets résolus similaires :\n{ lv_context }| ).
DATA(lo_response) = lo_ai->create_completion( io_prompt = lo_prompt is_parameters = VALUE #( max_tokens = 1000 temperature = '0.5' ) ).
" Enregistrer la proposition de solution 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.
" Retourner le résultat 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.Services SAP AI Foundation
Document Information Extraction
Le service Document Information Extraction extrait des données structurées de documents.
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. " Client HTTP pour le service IA 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. " Créer le corps de la requête DATA: BEGIN OF ls_request, document_type TYPE string VALUE 'invoice', file_content TYPE string, options TYPE string VALUE '{"language":"fr"}', 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 ).
" Configurer la requête 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 ).
" Exécuter DATA(lo_response) = lo_client->execute( if_web_http_client=>post ).
IF lo_response->get_status( )-code = 200. " Parser la réponse 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 pour la recherche sémantique
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" ).
" Appeler le modèle d'embedding rt_embedding = lo_client->create_embedding( iv_text = iv_text iv_model = 'text-embedding-ada-002" )->get_vector( ). ENDMETHOD.
METHOD find_similar_documents. " Créer l'embedding de la requête DATA(lt_query_embedding) = create_embedding( iv_query_text ).
" Calculer la similarité avec tous les documents 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.
" Trier par similarité SORT lt_scored BY similarity DESCENDING.
" Retourner les Top-K 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.Exemple complet : Service d’assistance basé sur l’IA
1. Vue CDS pour les 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. Classe de service IA
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( |Tu analyses les tickets de support pour un système de service d'assistance IT.\n| && |Analyse le ticket et extrais :\n| && |- category : la catégorie principale (Hardware, Software, Network, Access, Other)\n| && |- sentiment : l'humeur du client (positive, neutral, frustrated, angry)\n| && |- priority : l'urgence 1-5 (1=critique, 5=faible)\n| && |- confidence : ta confiance dans l'analyse 0.0-1.0\n| && |- keywords : termes techniques importants sous forme de tableau\n| && |- suggested_solution : brève proposition de solution (2-3 phrases)\n| && |Réponds uniquement au format JSON, sans explications supplémentaires.| ).
lo_prompt->add_user_message( |Titre : { iv_title }\n\nDescription :\n{ iv_description }| ).
DATA(lo_response) = get_client( )->create_completion( io_prompt = lo_prompt is_parameters = VALUE #( max_tokens = 800 temperature = '0.2" ) ).
" Parser le JSON DATA(lv_json) = lo_response->get_text( ).
" Supprimer le formatage Markdown éventuel 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( |Tu es un technicien de support IT expérimenté.\n| && |Crée un guide de solution détaillé pour le problème suivant.\n| && |Structure la réponse avec :\n| && |1. Diagnostic du problème (2-3 phrases)\n| && |2. Étapes de solution (liste numérotée)\n| && |3. Conseils de prévention (si applicable)\n| && |Écris de manière précise et compréhensible.| ).
lo_prompt->add_user_message( |Catégorie : { iv_category }\n\nProblème :\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( |Résume le ticket de support suivant en 2 phrases maximum. | && |Concentre-toi sur le problème principal et la solution souhaitée.| ).
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. Implémentation du comportement RAP
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. " Effectuer l'analyse IA DATA(ls_analysis) = lo_ai->analyze_ticket( iv_title = ls_ticket-Title iv_description = ls_ticket-Description ).
" Enregistrer les résultats 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 = |Analyse IA échouée : { 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. " Générer la solution DATA(lv_solution) = lo_ai->generate_solution( iv_description = ls_ticket-Description iv_category = ls_ticket-AICategory ).
" Enregistrer 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.
" Retourner le résultat 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. " Forcer une nouvelle analyse IA 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.Limitations et meilleures pratiques
Limitations
| Limitation | Description | Solution de contournement |
|---|---|---|
| Latence | Les appels IA durent 1-10 secondes | Traitement asynchrone, mise en cache |
| Coûts | Les tokens sont facturés | Optimisation du prompt, mise en cache |
| Limites de débit | Limites d’appels API | Logique de retry, mise en file d’attente |
| Hallucination | Les LLM peuvent générer de fausses informations | Validation, revue humaine |
| Limite de contexte | Nombre max de tokens par appel | Découpage du texte, résumé |
| Hors ligne | Basé sur le cloud, pas d’utilisation hors ligne | Logique de secours |
| Protection des données | Les données vont vers des services externes | Anonymisation, ML sur site |
Meilleures pratiques
✅ FAIRE :
1. Utiliser des prompts structurés - System-Message pour le rôle et le format - User-Message pour l'entrée concrète - Exemples dans le prompt (Few-Shot Learning)
2. Ajuster la température - 0.1-0.3 : sorties cohérentes et factuelles - 0.5-0.7 : réponses plus créatives - 0.9+ : très créatif, moins prévisible
3. Implémenter la gestion des erreurs - Retry pour les erreurs transitoires - Graceful Degradation - Logging pour le débogage
4. Valider les résultats - Validation de schéma JSON - Vérifications de plausibilité - Human-in-the-Loop pour les décisions critiques
5. Utiliser la mise en cache - Mettre en cache les prompts identiques - Précalculer les embeddings - TTL basé sur l'actualité des données
❌ NE PAS FAIRE :
1. Envoyer des données sensibles non cryptées - Anonymiser les données personnelles - Pas de mots de passe/secrets dans le prompt
2. Faire confiance aveuglément aux résultats IA - Toujours valider - En cas de doute : demander à l'utilisateur
3. Prompts trop longs - Limiter le contexte à l'essentiel - Si nécessaire : résumé préalable
4. Appels synchrones dans les chemins critiques de l'UI - Utiliser des jobs en arrière-plan - Utiliser des patterns asynchronesOptimisation des coûts
" Compression du prompt pour réduction des coûtsMETHOD optimize_prompt. " Avant optimisation : 500 tokens " Après optimisation : 150 tokens (économie de 70%)
DATA(lv_optimized) = |Classifie : { iv_category }\n| && |Texte : { substring( val = iv_text len = 500 ) }|. " Maximum 500 caractères
" Au lieu de : " "Tu es un assistant utile qui classe les documents. " Analyse attentivement le texte suivant et attribue-le " à l'une des catégories suivantes : ..."ENDMETHOD.
" Mise en cache pour les requêtes fréquentesCLASS-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 ).
" Vérifier le cache 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.
" Appeler l'IA et mettre en cache rv_result = call_ai( iv_prompt ).
INSERT VALUE #( prompt_hash = lv_hash response = rv_result timestamp = utclong_current( ) ) INTO TABLE gt_cache.ENDMETHOD.Perspectives et feuille de route
SAP développe continuellement l’intégration de l’IA :
| Période | Fonctionnalités attendues |
|---|---|
| 2025 | Joule SDK GA, intégration RAP étendue |
| 2025-2026 | Fine-tuning sur données SAP, modèles multimodaux |
| 2026+ | Support LLM sur site, Edge AI pour S/4HANA |
Tendances :
- Agentic AI : Agents autonomes pour flux de travail complexes
- RAG (Retrieval Augmented Generation) : Intégrer les bases de connaissances de l’entreprise
- Multimodal : Traiter images, documents, voix
- Fine-Tuning : Adapter les modèles à des domaines spécifiques SAP
Conclusion
L’intégration de l’IA dans ABAP Cloud ouvre de nouvelles possibilités pour les applications intelligentes. Avec le SDK Joule et les services AI Foundation, les développeurs peuvent intégrer directement des fonctionnalités d’IA générative dans les applications RAP - de la classification automatique à la génération de texte en passant par la recherche sémantique.
Important : L’IA est un outil puissant, mais pas un substitut à la validation métier. Les résultats doivent toujours être vérifiés et les décisions critiques ne doivent pas être prises entièrement automatiquement.
Sujets connexes
- Joule pour ABAP - Assistant IA pour la génération de code dans ADT
- Consommation d’API externes RAP - Intégrer des API REST dans RAP
- Bases de RAP - Fondamentaux du modèle de programmation d’application RESTful
- Clean ABAP - Code ABAP maintenable