TDEV
İletişime Geçin
← Blog

ABAP AI SDK ile Clean Core Uyumlu LLM Entegrasyonu

Bu yazı, ABAP tarafında üretken yapay zeka senaryoları kurmak isteyen, Clean Core kısıtlarını ciddiye alan geliştiriciler için.

ABAP kodundan bir Büyük Dil Modeli (LLM) çağırmanın klasik yolu, elle kurulan bir HTTP istemcisiydi: destination ayarla, token al, JSON gövdesini string olarak birleştir, yanıtı parse et. Bu yaklaşım çalışır ama üç yerden sızdırır. Kimlik doğrulama ve token yenileme sizin sorumluluğunuzdadır; model sağlayıcısının API sözleşmesi değiştiğinde kodunuz sessizce kırılır; ve kullandığınız HTTP/JSON yardımcı sınıflarının ABAP Cloud’da released olup olmadığı ayrı bir dert haline gelir.

SAP bu işi standartlaştırdı. ABAP AI SDK, ABAP sisteminin içinde duran, released ABAP sınıflarından oluşan bir reuse kütüphanesi. SAP’nin kendi ABAP cheat sheet deposu bunu açıkça “custom implementation’larda LLM’lerle etkileşim için ABAP AI SDK içindeki released ABAP sınıfları” diye tanımlıyor. Released olması, Clean Core açısından belirleyici olan nokta: ABAP Cloud’da kullanabileceğiniz, sürüm yükseltmelerinde sözleşmesi korunan bir API kullanıyorsunuz demek.


ISLM olmadan SDK çalışmaz

ABAP AI SDK’nın en çok yanlış anlaşılan yanı, tek başına bir istemci kütüphanesi olmaması. SDK, Intelligent Scenario Lifecycle Management (ISLM) ile entegre çalışır ve kod yazmaya başlamadan önce iki repository objesinin var olması gerekir:

Aradaki ayrım pratikte şu işe yarar: kodunuz gpt-4o ya da başka bir modele değil, ZDEMO_ABAP_INT_SCEN gibi bir senaryo adına bağlıdır. Model değiştiğinde ABAP kodu değişmez, senaryo modelinin tanımı değişir. Bu, üretimde model değiştirmeyi bir transport işi olmaktan çıkarır.

Bunların yanında ISLM tarafında yapılması gereken yönetimsel adımlar var — SDK’yı kullanmadan önce bu hazırlığın tamamlanmış olması gerekiyor. Hangi ABAP ortamında ve hangi release’ten itibaren kullanılabildiği sisteminize göre değişir; devreye almadan önce kendi sisteminizin SAP Help dokümantasyonundan doğrulayın.

flowchart LR
  A[ABAP kodu] -->|senaryo adı| B[ABAP AI SDK]
  B --> C[ISLM]
  C -->|intelligent scenario model| D[generative AI hub]
  D --> E[LLM]
  E --> D
  D --> C
  C --> B
  B -->|completion| A

En küçük çalışan örnek

SDK’nın giriş noktası bir factory. Completion API örneğini senaryo adından üretiyorsunuz:

CLASS zcl_ai_ornek DEFINITION
  PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.
ENDCLASS.

CLASS zcl_ai_ornek IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.
    TRY.
        FINAL(ai_api) = cl_aic_islm_compl_api_factory=>get( )->create_instance( 'ZDEMO_ABAP_INT_SCEN' ).
        FINAL(result) = ai_api->execute_for_string( `Tell me a joke.` ).
        FINAL(completion) = result->get_completion( ).
        out->write( completion ).
      CATCH cx_aic_api_factory cx_aic_completion_api INTO FINAL(error).
        out->write( error->get_text( ) ).
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

Dikkat edilecek üç nokta var.

cl_aic_islm_compl_api_factory=>get( ) factory’nin kendisini döndürür, create_instance( ) ise senaryoya bağlı completion API örneğini. İkisini tek satırda zincirlemek yaygın kullanım.

Dönen nesne if_aic_completion_api arayüzünü uygular. Kendi sınıflarınızda bu arayüzü tip olarak kullanmak, birim testlerinde test double geçirmenizi mümkün kılar — aşağıda buna döneceğiz.

Hata yönetimi iki ayrı exception’ı yakalar: cx_aic_api_factory örnekleme aşamasındaki sorunlar için, cx_aic_completion_api çağrının kendisi için. Bunları tek bir CATCH cx_root altında toplamayın; ilki genellikle yapılandırma hatasıdır (senaryo yok, yetki yok), ikincisi çalışma zamanı hatasıdır (model yanıt vermedi, kota doldu). İkisine verilecek tepki farklıdır.


Parametreler: temperature ve token sınırı

Varsayılanlarla bırakmak nadiren doğrudur. Parametreler ayrı bir setter nesnesi üzerinden ayarlanır ve çağrıdan önce verilmelidir:

FINAL(params) = ai_api->get_parameter_setter( ).
params->set_maximum_tokens( 500 ).
params->set_temperature( '0.5' ).

FINAL(llm_result) = ai_api->execute_for_string( `What is ABAP?` ).
FINAL(llm_answer) = llm_result->get_completion( ).

SAP’nin örneğinde temperature bir karakter literali olarak geçiliyor ('0.5') — ABAP’ta ondalık değerleri yazmanın alışıldık biçimi. Değeri sayısal bir değişkenden veriyorsanız tip dönüşümünü kendiniz kontrol edin.

Sınıflandırma, veri çıkarma, JSON üretme gibi deterministik olmasını istediğiniz işlerde temperature’ı düşük tutun. set_maximum_tokens( ) ise maliyet kontrolünün en doğrudan aracı; kullanıcı girdisinin uzunluğu değişkense bunu sabit bırakmak sizi beklenmedik faturalardan korur.


Çok turlu konuşma

Tek string yerine rol bazlı mesaj listesi göndermek gerektiğinde message container kullanılır:

FINAL(message_container) = ai_api->create_message_container( ).
message_container->set_system_role( `You are a professional translator` ).
message_container->add_user_message( `Can you translate German into English?` ).
message_container->add_assistant_message( `Yes` ).

FINAL(llm_answer) = ai_api->execute_for_messages( message_container )->get_completion( ).

set_system_role( ) bir kez çağrılır ve modelin davranışını belirler. add_user_message( ) ve add_assistant_message( ) ile önceki turları ekleyerek bağlam taşırsınız — SDK sizin adınıza konuşma geçmişi tutmaz, bu tamamen sizin sorumluluğunuzda.

Buradaki tuzak sessizdir: her turda geçmişin tamamını gönderdiğiniz için prompt token sayısı doğrusal büyür. Uzun konuşmalarda geçmişi kırpma stratejisi belirlemezseniz hem maliyet hem gecikme kontrolden çıkar.


Prompt’u koddan ayırın

Prompt metnini ABAP kaynak kodunun içine gömmek, her metin değişikliğini bir transport’a bağlar. SDK bunun için ayrı bir template factory sunuyor:

FINAL(prompt_temp) = cl_aic_islm_prompt_tpl_factory=>get( )->create_instance(
    islm_scenario = islm_scenario
    template_id   = prompt_template ).

FINAL(prompt) = prompt_temp->get_prompt( ).

Template’e özgü hatalar cx_aic_prompt_template ile gelir. Prompt’un yaşam döngüsünü koddan ayırmak, LLM tabanlı özelliklerde en çok değişen parçayı en kolay değiştirilebilir yere taşır — prompt mühendisliği doğası gereği iteratiftir.


Ölçmediğiniz maliyeti yönetemezsiniz

Sonuç nesnesi yalnız metni değil, çağrının ölçümlerini de taşır:

FINAL(completion_token_count) = llm_result->get_completion_token_count( ).
FINAL(prompt_token_count)     = llm_result->get_prompt_token_count( ).
FINAL(total_token_count)      = llm_result->get_total_token_count( ).
FINAL(runtime)                = llm_result->get_runtime_ms( ).

Bu dört değeri ilk günden kendi log tablonuza yazın. Üretimde “LLM özelliği pahalı” tartışması her zaman çıkar ve elinizde senaryo bazında token dağılımı yoksa tartışma veriye değil izlenime dayanır. get_runtime_ms( ) ayrıca senkron çağrının kullanıcı deneyimine etkisini ölçmenin en basit yolu.


Test edilebilirlik: factory’yi metot içine gömmeyin

LLM çağıran kodun birim testi, üzerinde en az düşünülen konu. Yanlış yapılışı şöyle görünür:

METHOD analiz_et.
  " Test edilemez: bağımlılık metodun içinde üretiliyor.
  FINAL(ai_api) = cl_aic_islm_compl_api_factory=>get( )->create_instance( 'ZDEMO_ABAP_INT_SCEN' ).
  result = ai_api->execute_for_string( iv_prompt )->get_completion( ).
ENDMETHOD.

Bu metodu çalıştıran her test gerçek bir LLM çağrısı yapar. Sonuç: testler ağ bağlantısına ve kotaya bağımlı olur, yavaşlar ve — en kötüsü — LLM yanıtları deterministik olmadığı için aynı girdiyle farklı sonuç verip kararsız hale gelir.

Bağımlılığı dışarı alın:

CLASS zcl_ai_analiz DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    METHODS constructor IMPORTING io_ai_api TYPE REF TO if_aic_completion_api.
    METHODS analiz_et   IMPORTING iv_prompt TYPE string
                        RETURNING VALUE(rv_sonuc) TYPE string
                        RAISING   cx_aic_completion_api.
  PRIVATE SECTION.
    DATA mo_ai_api TYPE REF TO if_aic_completion_api.
ENDCLASS.

CLASS zcl_ai_analiz IMPLEMENTATION.
  METHOD constructor.
    mo_ai_api = io_ai_api.
  ENDMETHOD.

  METHOD analiz_et.
    rv_sonuc = mo_ai_api->execute_for_string( iv_prompt )->get_completion( ).
  ENDMETHOD.
ENDCLASS.

Üretimde constructor’a factory’den gelen örneği verirsiniz; testte if_aic_completion_api arayüzünü uygulayan, sabit bir metin döndüren bir test double. Böylece test ettiğiniz şey modelin cevabı değil, cevabı nasıl işlediğiniz olur — asıl kırılabilecek yer de zaten orası.

Test double’ı yazarken arayüzün tamamını implement etmeniz gerekeceğini hesaba katın; ADT’nin “add implementation” yardımıyla iskeleti üretip kullanmadığınız metotları boş bırakmak en pratik yol.

ABAP AI SDK mi, BTP tarafında bir servis mi?

Her AI senaryosu ABAP’a ait değil. Kaba bir ayrım:

ABAP AI SDK’yı seçin — işlem ABAP verisiyle sıkı bağlıysa, sonuç bir ABAP nesnesine yazılacaksa, yetkilendirme mevcut ABAP yetki nesnelerinden geliyorsa. Malzeme metninden öznitelik çıkarmak, uzun bir uzun metni özetleyip belgeye iliştirmek gibi işler buraya girer.

BTP tarafına taşıyın — vektör veritabanı, doküman parçalama, çoklu model orkestrasyonu ya da ABAP dışı kaynaklardan besleme gerekiyorsa. Bu tür bileşenleri ABAP içinde kurmaya çalışmak, SDK’nın kapsamını aştığınız anlamına gelir.

Best practices ve tuzaklar

Senkron çağrıyı kullanıcı etkileşimine koymayın. Bir LLM çağrısı saniyeler sürebilir. RAP behavior implementation’ında determination veya validation içinde senkron LLM çağırmak, kaydetme işlemini modelin yanıt süresine bağlar. Bu işleri arka plan işine veya kullanıcının açıkça tetiklediği bir action’a taşıyın.

Arayüz üzerinden bağımlılık alın. Completion API’yi metot içinde factory’den üretmek yerine sınıfın constructor’ında if_aic_completion_api tipinde alın. Böylece birim testinde gerçek modeli çağırmadan test double geçirebilirsiniz; aksi halde testleriniz ağ bağlantısına ve LLM kotasına bağımlı hale gelir — ve LLM yanıtları deterministik olmadığı için test sonuçları kararsızlaşır.

Modelin çıktısına güvenmeyin, doğrulayın. Modelden JSON istiyorsanız dönen metnin geçerli JSON olduğunu varsaymayın. Parse hatasını yakalayın ve bir yeniden deneme ya da güvenli varsayılan tanımlayın.

Kişisel veriyi prompt’a koymadan önce durun. Prompt içeriği sistem sınırınızın dışına çıkar. Hangi alanların modele gidebileceğini veri koruma tarafıyla netleştirin; gerekirse maskeleme katmanını çağrıdan önce uygulayın.

Senaryo adını sabit kodlamayın. Yukarıdaki örneklerde okunabilirlik için literal kullandık. Gerçek uygulamada senaryo adını bir sabit ya da yapılandırma tablosundan okuyun; sistemler arası (geliştirme/test/üretim) farklılık ihtimali yüksek.


Sonuç

ABAP AI SDK’nın getirdiği asıl kazanç bir HTTP istemcisi yazmaktan kurtulmak değil, bağımlılığın yönünü değiştirmek. Kodunuz artık bir model sağlayıcısının API sözleşmesine değil, kendi sisteminizde tanımlı bir intelligent scenario’ya bağlı. Model değişimi yapılandırma işi haline geliyor, released API kullandığınız için ABAP Cloud kısıtları içinde kalıyorsunuz ve token ölçümleri kutudan çıkıyor.

Karşılığında ISLM tarafında bir hazırlık maliyeti var: senaryo ve model objelerini kurmadan tek satır kod çalışmıyor. Küçük bir deneme yapmak isteyen için bu bir sürtünme; üretime çıkacak bir özellik için ise zaten yapılması gereken işin öne alınmış hali.

Başlamak için en verimli yol, SAP’nin kendi ABAP cheat sheet deposundaki generative AI bölümünü kendi sisteminizde çalıştırmak — örnekler if_oo_adt_classrun ile konsol uygulaması olarak yazılmış, yani bir Fiori arayüzü kurmadan doğrudan ADT içinden deneyebilirsiniz.

Kaynaklar

Anahtar Kelimeler