IA dans ABAP Cloud : Joule SDK & SAP AI Foundation

Catégorie
Trends
Publié
Auteur
Johannes

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

OptionDescriptionDisponibilité
Joule dans ADTAssistant IA pour la génération de code dans l’éditeurDepuis 2024
SDK JouleIntégration IA programmatique pour les applicationsDepuis 2025
AI FoundationServices ML/AI basés sur BTPDepuis 2023
Generative AI HubIntégration LLM centralisée sur BTPDepuis 2024
SAP AI CoreDéploiement de modèles ML personnalisésDepuis 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

ClasseDescription
CL_JOULE_AI_CLIENTClasse principale pour les appels IA
CL_JOULE_PROMPT_BUILDERConstruction de prompts
CL_JOULE_RESPONSE_HANDLERTraitement des réponses
IF_JOULE_COMPLETIONInterface pour les complétions
CX_JOULE_ERRORClasse d’exception

Configurer le Communication Scenario

  1. 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>
  1. Communication Arrangement dans le Fiori Launchpad :
ChampValeur
ScenarioZ_JOULE_AI_SCENARIO
Arrangement NameZ_JOULE_PROD
Communication SystemSAP_AI_CORE
Auth MethodOAuth 2.0 Client Credentials
Token Endpointhttps://[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 produit
DATA(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 Definition
managed 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

LimitationDescriptionSolution de contournement
LatenceLes appels IA durent 1-10 secondesTraitement asynchrone, mise en cache
CoûtsLes tokens sont facturésOptimisation du prompt, mise en cache
Limites de débitLimites d’appels APILogique de retry, mise en file d’attente
HallucinationLes LLM peuvent générer de fausses informationsValidation, revue humaine
Limite de contexteNombre max de tokens par appelDécoupage du texte, résumé
Hors ligneBasé sur le cloud, pas d’utilisation hors ligneLogique de secours
Protection des donnéesLes données vont vers des services externesAnonymisation, 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 asynchrones

Optimisation des coûts

" Compression du prompt pour réduction des coûts
METHOD 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équentes
CLASS-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ériodeFonctionnalités attendues
2025Joule SDK GA, intégration RAP étendue
2025-2026Fine-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