Esta guía explica cómo SAP (ABAP) se comunica de forma segura con la API de Google Gemini (Generative Language API), desde los certificados en STRUST hasta el destino RFC en SM59 y el productivo HTTP POST en ABAP.
¿Por qué Gemini de SAP?
- Análisis de texto, resúmenes, clasificación y automatización basada en avisos directamente desde ABAP.
- Fácil conexión a través de una clave API y una interfaz REST estandarizada.
- Escalable desde la operación de prueba hasta el uso productivo.
Voraussetzungen
- Sistema SAP con HTTPS activo (ICM) y CommonCryptoLib.
- Permisos para STRUST y SM59.
- Clave API válida de Gemini.
- Utilidad ABAP JSON, por ejemplo.
/UI2/CL_JSON
Paso 1: SM59: Crear un destino HTTP para las API de Google
En SM59, se crea un destino HTTP que SAP utiliza para acceder a servicios web externos o API.
Para , generativelanguage.googleapis.com
la configuración básica se ve así:
Tabla de contenido
Crear una nueva conexión HTTP
- Haga clic en Crear .
- Tipo de conexión G: seleccione la conexión HTTP al servidor externo .
- Dé un nombre descriptivo (p. ej. B.
GOOGLE_GENAI_API
o ).GOOGLE_GEMINI_API
Configuración técnica
- Anfitrión:
generativelanguage.googleapis.com
- Nº de servicio:
443
(HTTPS) - Prefijo de ruta: deje en blanco por ahora: la ruta completa se establece en el código ABAP
(p. ej. B./v1/models/gemini-2.5-flash:generateContent
).
Inicio de sesión y seguridad
- SNC: no se requiere (se usa HTTPS).
- SSL: Seleccione Activo (SSL Client Standard)
(o su identidad de cliente SSL dedicada, si se almacena en STRUST).
Registro y seguridad
Las API de Google generalmente no mantienen datos de usuario / contraseña en el destino. Autenticación
se realiza en el código ABAP, ya sea a través de la clave API en el encabezado o a través de tokens OAuth 2.0 (encabezado
Authorization: Bearer
).
- Usuario/contraseña: déjelo en blanco.
- Certificado de cliente SSL: Corresponde a la selección en Configuración técnica.
Si hay varias identidades, seleccione específicamente si es necesario.
Opciones especiales
- Proxy HTTP: solo se mantiene si el acceso a Internet se ejecuta a través de él.
- Tiempos de espera: ajuste según sea necesario.
Resumen
- Anfitrión:
generativelanguage.googleapis.com
- Servicio:
443
- Prefijo de ruta: en blanco (la ruta se establece en el código)
- SSL: Activo, estándar de cliente SSL
- Inicio de sesión: vacío (autenticación en código ABAP)
Paso 2: STRUST: Certificados para el acceso HTTPS a las API de Google
En STRUST, mantienes los anclajes de confianza SSL/TLS para que tu sistema SAP acepte los certificados de servidor presentados por Google.
¿Dónde registrarse?
En STRUST, cambie al nodo de cliente SSL (predeterminado) (o a su identidad de cliente SSL específica). Justo en el
lista de certificados , puede ver todos los certificados que ya están familiarizados. Aquí, la raíz y, si está disponible,
los certificados intermedios de la entidad emisora de certificados de Google.
¿Qué certificados se requieren?
Google suele utilizar cadenas de CA conocidas, como Google Trust Services (GTS), GlobalSign o DigiCert.
En muchos sistemas, los certificados raíz importantes ya están disponibles (el sistema operativo/CCL a menudo los proporciona).
Si falta algo, agregue la cadena manualmente.
Detección y exportación de certificados
https://generativelanguage.googleapis.com
en el navegador.- Haga clic en el icono del candado y vea la cadena de certificados.
- Certificados raíz y, si procede, intermedios (p. ej. GTS Root R1, GTS CA 1O1) en formato Base64-X.509
.cer
( o.pem
). Alternativamente, descárguelo desde las páginas oficiales de Google Trust Services.
Importar a STRUST
- Abra STRUST y seleccione Cliente SSL (predeterminado ).
- Utilice el botón Importar certificado y seleccione los archivos guardados.
- Agregue los certificados a la lista y, a continuación, realice una copia de seguridad de la base de datos.
- Después de la importación, las entradas deben estar visibles.
Comprueba la conexión
En SM59, ejecute una conexión de prueba en su destino. Si la prueba falla, se produce un error
Certificado raíz o intermedio. Nota: Esta prueba comprueba la red y el protocolo de enlace TLS, no el último
Autenticación a través de la clave API u OAuth.
Resumen
- Ubicación: cliente SSL (predeterminado) (o el cliente SSL PSE utilizado).
- Contenido: Certificados raíz / intermedios confiables de las CA de Google (por ejemplo, GTS Root R1, GTS CA 1O1).
- Acción: Comprobar la presencia, importar y guardar los certificados que faltan.
Siguiente paso en ABAP
La comunicación real de la API (método HTTP, encabezado, cuerpo, autenticación) tiene lugar en el código,
por ejemplo, con CL_HTTP_CLIENT
o CL_REST_HTTP_CLIENT
.
Paso 3: Solicitud ABAP – POST a Gemini (ejemplo mínimo)
Punto de conexión de ejemplo (v1, modelo gemini-2.5-flash
): /v1/models/gemini-2.5-flash:generateContent
REPORT z_ki_google_test.
DATA: lo_http TYPE REF TO if_http_client,
lv_body TYPE string,
lv_result TYPE string,
lv_api_key TYPE string VALUE '***PLACE_YOUR_API_KEY_SECURELY***'.
" 1) HTTP-Client aus SM59-Destination (G: HTTP)
cl_http_client=>create_by_destination(
EXPORTING destination = 'GEMINI'
IMPORTING client = lo_http ).
" 2) Header setzen
lo_http->request->set_method( if_http_request=>co_request_method_post ).
lo_http->request->set_header_field( name = 'Content-Type' value = 'application/json' ).
lo_http->request->set_header_field( name = 'x-goog-api-key' value = lv_api_key ).
" 3) Request-Pfad (Endpoint) – setze ihn NUR hier ODER als Präfix in SM59 (nicht beides!)
lo_http->request->set_header_field(
name = '~request_uri'
value = '/v1/models/gemini-2.5-flash:generateContent' ).
" ---------- Request-JSON aufbauen (mit name_mappings für lowercase keys) ----------
TYPES: BEGIN OF ty_part_req,
text TYPE string,
END OF ty_part_req.
TYPES: ty_t_part_req TYPE STANDARD TABLE OF ty_part_req WITH EMPTY KEY.
TYPES: BEGIN OF ty_content_req,
parts TYPE ty_t_part_req,
END OF ty_content_req.
TYPES: ty_t_content_req TYPE STANDARD TABLE OF ty_content_req WITH EMPTY KEY.
TYPES: BEGIN OF ty_request,
contents TYPE ty_t_content_req,
END OF ty_request.
DATA ls_req TYPE ty_request.
DATA(lv_prompt_text) = |Erkläre KI in einem Satz.|.
APPEND VALUE ty_content_req(
parts = VALUE #( ( text = lv_prompt_text ) ) )
TO ls_req-contents.
DATA lt_req_map TYPE /ui2/cl_json=>name_mappings.
DATA lt_res_map TYPE /ui2/cl_json=>name_mappings.
" Request-Mapping (ABAP-Feldname -> JSON-Key, lowercase)
INSERT VALUE /ui2/cl_json=>name_mapping( abap = 'CONTENTS' json = 'contents' ) INTO TABLE lt_req_map.
INSERT VALUE /ui2/cl_json=>name_mapping( abap = 'PARTS' json = 'parts' ) INTO TABLE lt_req_map.
INSERT VALUE /ui2/cl_json=>name_mapping( abap = 'TEXT' json = 'text' ) INTO TABLE lt_req_map.
" Response-Mapping
INSERT VALUE /ui2/cl_json=>name_mapping( abap = 'CANDIDATES' json = 'candidates' ) INTO TABLE lt_res_map.
INSERT VALUE /ui2/cl_json=>name_mapping( abap = 'CONTENT' json = 'content' ) INTO TABLE lt_res_map.
INSERT VALUE /ui2/cl_json=>name_mapping( abap = 'PARTS' json = 'parts' ) INTO TABLE lt_res_map.
INSERT VALUE /ui2/cl_json=>name_mapping( abap = 'TEXT' json = 'text' ) INTO TABLE lt_res_map.
lv_body = /ui2/cl_json=>serialize(
data = ls_req
name_mappings = lt_req_map
compress = abap_true ).
" 4) Senden & Empfangen
lo_http->request->set_cdata( lv_body ).
lo_http->send( ).
lo_http->receive( ).
" 5) Antwort lesen (Status + Body)
DATA: lv_status TYPE i,
lv_reason TYPE string.
lo_http->response->get_status(
IMPORTING
code = lv_status
reason = lv_reason ).
lv_result = lo_http->response->get_cdata( ).
" ---------- Response-JSON deserialisieren ----------
TYPES: BEGIN OF ty_part_res,
text TYPE string,
END OF ty_part_res.
TYPES: ty_t_part_res TYPE STANDARD TABLE OF ty_part_res WITH EMPTY KEY.
TYPES: BEGIN OF ty_content_res,
parts TYPE ty_t_part_res,
END OF ty_content_res.
TYPES: BEGIN OF ty_candidate,
content TYPE ty_content_res,
END OF ty_candidate.
TYPES: ty_t_candidate TYPE STANDARD TABLE OF ty_candidate WITH EMPTY KEY.
TYPES: BEGIN OF ty_response,
candidates TYPE ty_t_candidate,
END OF ty_response.
DATA ls_resp TYPE ty_response.
TRY.
/ui2/cl_json=>deserialize(
EXPORTING json = lv_result
name_mappings = lt_res_map
CHANGING data = ls_resp ).
DATA(lv_text) = ||.
IF lines( ls_resp-candidates ) > 0
AND lines( ls_resp-candidates[ 1 ]-content-parts ) > 0.
lv_text = ls_resp-candidates[ 1 ]-content-parts[ 1 ]-text.
ENDIF.
"=== ALV-Vorbereitung ======================================================
TYPES: BEGIN OF ty_company_info,
field TYPE string,
value TYPE string,
END OF ty_company_info.
DATA: lt_company_info TYPE STANDARD TABLE OF ty_company_info,
ls_company_info TYPE ty_company_info.
IF lv_status = 200 AND lv_text IS NOT INITIAL.
" Antwort in Zeilen aufteilen und Feld: Wert extrahieren
DATA(lt_lines) = VALUE stringtab( ).
SPLIT lv_text AT cl_abap_char_utilities=>newline INTO TABLE lt_lines.
LOOP AT lt_lines INTO DATA(lv_line).
IF lv_line CS ':'.
SPLIT lv_line AT ':' INTO ls_company_info-field ls_company_info-value.
CONDENSE ls_company_info-field.
SHIFT ls_company_info-value LEFT DELETING LEADING space.
ELSE.
ls_company_info-field = 'Text'.
ls_company_info-value = lv_line.
ENDIF.
APPEND ls_company_info TO lt_company_info.
CLEAR ls_company_info.
ENDLOOP.
ELSE.
" Fehlerfall als ALV zeigen
ls_company_info-field = |HTTP|.
ls_company_info-value = |{ lv_status } { lv_reason }|.
APPEND ls_company_info TO lt_company_info.
CLEAR ls_company_info.
ls_company_info-field = |Fehlermeldung|.
ls_company_info-value = lv_result.
APPEND ls_company_info TO lt_company_info.
ENDIF.
"=== ALV-Ausgabe ===========================================================
DATA: lo_alv TYPE REF TO cl_salv_table,
lo_columns TYPE REF TO cl_salv_columns_table,
lo_column TYPE REF TO cl_salv_column_table.
cl_salv_table=>factory(
IMPORTING r_salv_table = lo_alv
CHANGING t_table = lt_company_info ).
lo_columns = lo_alv->get_columns( ).
lo_columns->set_optimize( abap_true ).
lo_column ?= lo_columns->get_column( 'FIELD' ).
lo_column->set_short_text( 'Kategorie' ).
lo_column->set_medium_text( 'Kategorie' ).
lo_column->set_long_text( 'Informationskategorie' ).
lo_column ?= lo_columns->get_column( 'VALUE' ).
lo_column->set_short_text( 'Inhalt' ).
lo_column->set_medium_text( 'Beschreibung' ).
lo_column->set_long_text( 'Detail' ).
lo_alv->display( ).
CATCH cx_root INTO DATA(lx).
" Fallback: Fehler in einfacher Liste ausgeben
WRITE: / 'JSON-/ALV-Fehler:', lx->get_text( ).
WRITE: / 'Raw-Response:', / lv_result.
ENDTRY.
lo_http->close( ).
Mejores prácticas
- No codifique la clave API: almacenar de forma segura (almacenamiento seguro, variables, controladores SICF).
- Incluir tiempos de espera y reintentos; El error de registro indica claramente.
- Mantenga las indicaciones breves y claras.
- Elección del modelo:
gemini-2.5-flash
por costo/velocidad,gemini-2.5-pro
por calidad.
Solución de problemas
- 401 No autorizado:
x-goog-api-key
falta o es incorrecto. - 400 Solicitud incorrecta: Verifique la estructura JSON (
contents → parts → text
). - 404 No encontrado: Verifique el nombre del modelo o el URI.
- Error de protocolo de enlace SSL: cadena de CA incompleta en STRUST.
- Proxy/Firewall: Verifique los recursos compartidos de red y proxy SM59.
Lista de verificación
- Cadena de CA importada a STRUST
- SM59: Host, puerto 443, SSL activo
- Clave API almacenada de forma segura, conjunto de encabezados
- Punto final correcto:
/v1/models/gemini-2.5-flash:generateContent
- Tiempos de espera, reintentos y registros disponibles