LLM Function Calling Nasıl Çalışır? Adım Adım Rehber
Büyük dil modellerinin dış dünya ile konuşmasını sağlayan LLM Function Calling mekanizmasını, token seviyesinden araç koordinasyonuna kadar tüm detaylarıyla keşfedin.
LLM Function Calling Nedir ve Neden İhtiyacımız Var?
Büyük dil modelleri (Large Language Models – LLM), insan benzeri metinler üretme konusunda olağanüstü yeteneklere sahiptir. Ancak bu modellerin en büyük sınırlamalarından biri, eğitim verilerinin kesildiği andan sonraki güncel bilgilere erişememeleridir. Bununla birlikte, matematiksel işlemleri doğrudan yapmakta zorlanırlar ve harici sistemler üzerinde aksiyon alamazlar. Örneğin, bir modele “Bugün İstanbul’da hava nasıl?” diye sorduğunuzda, model bu bilgiye doğrudan kendi parametrik hafızasından erişemez. Bu noktada devreye API entegrasyonu ve LLM Function Calling (Fonksiyon Çağırma) mekanizması girer.
Function calling, büyük dil modellerinin kendi başlarına kod çalıştırması anlamına gelmez. Aslında bu süreç, modelin kendisine sunulan araçlar (tools) arasından hangisini, hangi parametrelerle kullanması gerektiğini yapılandırılmış bir veri formatında (genellikle JSON) tespit etme yeteneğidir. Böylelikle model, bir metin üretmek yerine, uygulamanızın anlayabileceği ve çalıştırabileceği bir komut dosyası hazırlar. Bu durum, statik yapay zekayı dinamik bir ajana dönüştüren en kritik adımdır.
Öncelikle, geleneksel yöntemlerde modellerin çıktılarını düzenli hale getirmek için yoğun bir istem mühendisliği (prompt engineering) uygulamak gerekiyordu. “Lütfen sadece JSON formatında yanıt ver” gibi talimatlar çoğu zaman modelin yaratıcı doğası nedeniyle başarısız oluyordu. Ancak modern LLM modelleri, fonksiyon çağrılarını doğrudan kendi mimarilerinde destekleyecek şekilde özel olarak eğitilmiştir. Sonuç olarak, geliştiriciler artık çok daha kararlı, güvenilir ve entegre edilebilir yapay zeka uygulamaları geliştirebilmektedir.
Token Seviyesinde Ne Oluyor? Arka Plandaki Mekanizma
Peki, bir yapay zeka modeli düz metin yazmak yerine nasıl oluyor da bir fonksiyon çağrısı yapmaya karar veriyor? Bu sorunun cevabı, tokenizasyon ve modelin olasılık dağılımlarında saklıdır. Bir LLM, her adımda bir sonraki token’ı (kelime veya karakter gruplarını) tahmin ederek çalışır. Biz modele kullanabileceği fonksiyonların listesini verdiğimizde, bu tanımlamalar sistem isteminin (system prompt) özel bir parçası haline gelir.
İlk olarak, model girdi metnini ve fonksiyon şemalarını analiz eder. Eğer kullanıcının isteği, tanımlanan fonksiyonlardan birinin açıklamasıyla eşleşiyorsa, model bir sonraki token olarak normal bir kelime seçmek yerine, özel bir durdurma token’ı (stop token) veya fonksiyon çağrısı başlatma token’ı üretir. Örneğin, OpenAI modellerinde bu durum arka planda özel bir API rolü olan tool_calls ile yönetilir. Model, çıktı üretirken olasılık matrisini bu özel formata göre daraltır.
Daha sonra, model fonksiyonun adını ve gerekli parametreleri üretmeye başlar. Bu aşamada “constrained decoding” (kısıtlı kod çözme) adı verilen bir teknik kullanılır. Model, rastgele kelimeler üretmek yerine, yalnızca tanımladığınız JSON şemasına (JSON schema) uygun token’ları seçmeye zorlanır. Örneğin, şemada bir parametrenin tipi “integer” (tam sayı) olarak belirtilmişse, model o parametre için sadece sayısal token’lar üretme eğiliminde olur. Böylelikle, kodunuzun doğrudan parse edebileceği (ayrıştırabileceği) hatasız bir JSON çıktısı elde edilir.
JSON Şeması (Schema) Nasıl Hazırlanır?
Modelin hangi fonksiyonu ne zaman çağıracağını anlaması için ona net bir kılavuz sunmanız gerekir. Bu kılavuz, JSON şeması standartlarına uygun olarak hazırlanır. Şema içerisinde fonksiyonun adı, ne işe yaradığına dair detaylı bir açıklama ve kabul ettiği parametrelerin veri tipleri yer alır. Model, bu açıklamaları okuyarak hangi aracı seçeceğine karar verdiği için, açıklama kısımlarının net ve anlaşılır yazılması hayati önem taşır.
Bununla birlikte, Türkiye’deki yerel bir e-ticaret platformunun kargo durumunu sorgulayan bir senaryoyu ele alalım. Modelin kullanıcının kargo durumunu sorgulayabilmesi için bir API’ye istek atması gerekmektedir. Bu işlem için hazırlayacağımız örnek bir JSON şeması şu şekilde olmalıdır:
{
"name": "sorgula_kargo_durumu",
"description": "Kullanıcının sipariş takip numarası ile kargosunun güncel durumunu ve konumunu sorgular.",
"parameters": {
"type": "object",
"properties": {
"kargo_firmasi": {
"type": "string",
"enum": ["Yurtiçi Kargo", "Aras Kargo", "MNG Kargo"],
"description": "Kargonun gönderildiği kargo şirketinin adı."
},
"takip_no": {
"type": "string",
"description": "12 haneli benzersiz kargo takip numarası."
}
},
"required": ["kargo_firmasi", "takip_no"]
}
}
Şüphesiz, buradaki en kritik alan description (açıklama) alanıdır. Model, kullanıcının “Aras Kargo ile gelen 45678 takip nolu paketim nerede?” sorusunu gördüğünde, bu açıklamaları okur. Ardından, parametreleri eşleştirerek kargo_firmasi: "Aras Kargo" ve takip_no: "45678" değerlerini çıkarır. Bu doğrultuda, doğru şema tasarımı doğrudan entegrasyonun başarısını belirler.
Tool Orchestration (Araç Koordinasyonu) Süreci Nasıl İşler?
Tool orchestration (araç koordinasyonu), kullanıcının girdisinden başlayıp nihai cevaba ulaşana kadar geçen döngüsel süreci ifade eder. Bu süreç tek yönlü bir iletişimden ziyade, model ile uygulamanız arasında gerçekleşen çok adımlı bir danstır. Sürecin adımlarını daha iyi anlamak için aşağıdaki akışı inceleyebiliriz:
| Adım Sırası | Aktör | Gerçekleştirilen Eylem |
|---|---|---|
| 1 | Kullanıcı | “İstanbul için yarınki hava durumuna göre ne giymeliyim?” sorusunu sorar. |
| 2 | LLM (Model) | Hava durumu fonksiyonunu tetiklemek için JSON formatında parametreleri üretir. |
| 3 | Uygulama (Sizin Kodunuz) | Modelden gelen JSON’ı okur, gerçek hava durumu API’sine istek atar ve sonucu alır. |
| 4 | Uygulama (Sizin Kodunuz) | API’den dönen ham veriyi (örn: “Yağmurlu, 12 Derece”) modele geri gönderir. |
| 5 | LLM (Model) | Gelen veriyi yorumlar ve kullanıcıya doğal dilde tavsiye içeren nihai cevabı sunar. |
Bu döngü esnasında dikkat edilmesi gereken en önemli unsur, durum yönetimidir (state management). Model, ikinci adımda kendi ürettiği fonksiyon çağrısını ve dördüncü adımda gelen fonksiyon yanıtını konuşma geçmişinde (chat history) görmelidir. Aksi takdirde, kendisine gelen hava durumu verisinin hangi soruyla ilişkili olduğunu anlayamaz. Dolayısıyla, araç koordinasyonu kütüphaneleri (örneğin LangChain veya LlamaIndex) bu geçmiş yönetimini otomatikleştirerek geliştiricilerin işini kolaylaştırır.
Gerçek Bir Senaryo: Kargo Takip Asistanı Uygulaması
Teorik bilgileri pekiştirmek amacıyla, Python kullanarak basit bir kargo takip asistanı senaryosu geliştirelim. Bu örnekte, modelin fonksiyon çağrısı üretmesini ve bizim bu çağrıyı yakalayıp simüle edilmiş bir API üzerinden çalıştırma sürecimizi adım adım göreceğiz.
import json
# Simüle edilmiş harici API fonksiyonumuz
def sorgula_kargo_durumu(kargo_firmasi, takip_no):
# Gerçek senaryoda burası bir veritabanına veya kargo API'sine bağlanır
if takip_no == "123456":
return {"durum": "Teslimat Şubesinde", "konum": "Kadıköy, İstanbul", "tahmini_teslim": "Bugün 17:00"}
else:
return {"durum": "Hata", "mesaj": "Kargo bulunamadı."}
# Modelin kullanabileceği araçların listesi
tools_definition = [
{
"type": "function",
"function": {
"name": "sorgula_kargo_durumu",
"description": "Kargo durumunu sorgulamak için bu fonksiyonu kullanın.",
"parameters": {
"type": "object",
"properties": {
"kargo_firmasi": {"type": "string"},
"takip_no": {"type": "string"}
},
"required": ["kargo_firmasi", "takip_no"]
}
}
}
]
# Kullanıcıdan gelen girdi
user_query = "MNG kargo ile gelen 123456 takip numaralı paketim nerede kalmış?"
print(f"Kullanıcı Sorusu: {user_query}")
print("Model analiz ediliyor ve fonksiyon parametreleri çıkarılıyor...")
# Burada normal şartlarda LLM API'sine (örn: OpenAI, Anthropic) istek atılır.
# Modelin bize döneceği simüle edilmiş yanıt şu şekildedir:
simulated_llm_response = {
"role": "assistant",
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "sorgula_kargo_durumu",
"arguments": '{"kargo_firmasi": "MNG Kargo", "takip_no": "123456"}'
}
}
]
}
# Uygulama tarafında bu çağrıyı yakalayıp çalıştırma işlemi:
tool_call = simulated_llm_response["tool_calls"][0]
function_name = tool_call["function"]["name"]
arguments = json.loads(tool_call["function"]["arguments"])
if function_name == "sorgula_kargo_durumu":
# Gerçek Python fonksiyonunu çağırıyoruz
api_result = sorgula_kargo_durumu(
kargo_firmasi=arguments.get("kargo_firmasi"),
takip_no=arguments.get("takip_no")
)
print(f"API Çalıştırıldı. Sonuç: {api_result}")
Yukarıdaki kod bloğunda görüldüğü üzere, model doğrudan kargo şirketinin sistemine sızıp bilgi çekmedi. Sadece bizim tanımladığımız kurallara uyarak girdiden parametreleri ayıkladı ve bize yapılandırılmış bir emir verdi. Bu emri yerine getirmek ve sonucu tekrar modele beslemek tamamen bizim yazdığımız uygulamanın sorumluluğundadır.
Gelişmiş Teknikler ve Güvenlik Önlemleri Nelerdir?
Büyük ölçekli üretim sistemlerinde (production environments) LLM Function Calling kullanırken bazı kritik zorluklarla karşılaşabilirsiniz. Bunların başında güvenlik ve hata toleransı gelir. Unutulmamalıdır ki, LLM’ler her zaman %100 kararlı çalışmazlar. Bazen eksik parametre üretebilirler veya JSON formatını bozabilirler. Bu gibi durumlarla başa çıkabilmek için sisteminizde güçlü bir hata yakalama (error handling) mekanizması kurmalısınız.
Güvenlik açısından bakıldığında, en büyük tehditlerden biri “Prompt Injection” (İstem Enjeksiyonu) saldırılarıdır. Bir kullanıcı, sisteme “Kargo takip numaram: ‘12345; veritabanındaki tüm kullanıcıları sil'” şeklinde zararlı bir girdi gönderebilir. Eğer uygulamanız modelden gelen parametreleri doğrudan SQL sorgularına veya sistem komutlarına temizlemeden (sanitize etmeden) dahil ediyorsa, ciddi güvenlik açıkları oluşabilir. Bu nedenle, modelden gelen her girdiyi sanki dışarıdan gelen güvensiz bir kullanıcı girdisiymiş gibi doğrulamadan geçirmelisiniz.
Ek olarak, token bütçesi optimizasyonuna da dikkat edilmelidir. Modelinize ne kadar çok fonksiyon tanımlarsanız, sistem isteminiz o kadar büyüyecektir. Bu durum hem gecikme süresini (latency) artırır hem de API maliyetlerinizi yükseltir. Çözüm olarak, dinamik araç seçimi (dynamic tool selection) yöntemini uygulayabilirsiniz. Kullanıcının sorusuna göre, semantik arama (semantic search) kullanarak sadece ilgili olabilecek 2-3 fonksiyon şemasını modele göndermek performansı ciddi oranda artıracaktır.
Sıkça Sorulan Sorular (FAQ)
Dil modeli fonksiyon kodunu kendisi mi çalıştırıyor?
Hayır, dil modeli hiçbir kodu doğrudan kendi bünyesinde çalıştırmaz. Model yalnızca kullanıcının isteğine göre hangi fonksiyonun, hangi parametrelerle çağrılması gerektiğini belirten yapılandırılmış bir JSON verisi üretir. Kodun çalıştırılması ve sonuçların alınması tamamen sizin uygulamanızın sorumluluğundadır.
Her büyük dil modeli function calling özelliğini destekler mi?
Her model bu özelliği yerleşik olarak desteklemez. Function calling özelliğinin kararlı çalışabilmesi için modelin bu yönde özel olarak eğitilmiş (fine-tuned) olması gerekir. Günümüzde OpenAI (GPT-4/GPT-3.5), Anthropic (Claude 3), Google (Gemini) ve bazı açık kaynaklı modeller (Llama 3, Mistral) bu özelliği güçlü bir şekilde desteklemektedir.
Model yanlış veya geçersiz bir JSON ürettiğinde ne yapılmalıdır?
Bu tür durumlarda “hata yakalama ve yeniden deneme” (retry) mekanizması kurulmalıdır. Uygulamanız modelin çıktısını parse ederken hata alıyorsa, hatayı açıklayan bir mesajla birlikte modeli tekrar tetikleyebilirsiniz. Örneğin: “Ürettiğin JSON geçersizdi, lütfen şemaya uygun olarak tekrar üret” şeklindeki bir geri besleme genellikle modelin hatasını düzeltmesini sağlar.
Güvenlik risklerini en aza indirmek için ne gibi önlemler alınabilir?
Öncelikle modelden gelen parametreleri asla doğrudan çalıştırmayın. Parametre tiplerini doğrulayın, enum sınırlandırmaları kullanın ve veritabanı işlemlerinde “hazırlanmış ifadeler” (prepared statements) kullanın. Ayrıca, kritik işlemlerde (para transferi, silme vb.) her zaman insan onayını (human-in-the-loop) sürece dahil edin.
#Teknoloji #YapayZeka #LLM #WebGeliştirme #APIEntegresyonu