Bir LLM’den sadece metin üretmesini beklemek, onu bir hesap makinesine sadece toplama yaptırmaya benziyor. Ama ya ona dışarıdaki bir servisten güncel döviz kurunu çekmesini, bir veritabanından stok durumunu sorgulamasını veya bir takvim etkinliği oluşturmasını söyleseydik? İşte tam da bu noktada, Large Language Models (LLM) dünyasında devrim yaratan AI Function Calling mekanizması devreye giriyor. Bu özellik sayesinde, yapay zeka sadece bilgi vermekle kalmıyor, aynı zamanda dış dünyayla etkileşime geçerek somut eylemler gerçekleştirebilen akıllı uygulamaların temelini oluşturuyor.
Bu yazıda, AI Function Calling’in ne olduğunu, neden bu kadar önemli olduğunu ve kendi uygulamalarınızda nasıl kullanabileceğinizi üç temel adımda ele alacağım. Kendi deneyimlerimden yola çıkarak, bu güçlü aracın potansiyelini en iyi şekilde nasıl kullanabileceğinize dair pratik bilgiler sunacağım.
AI Function Calling Nedir ve Neden Önemli?
AI Function Calling, temel olarak bir LLM’in, kullanıcı girdisine veya bağlamına göre hangi harici araçları (fonksiyonlar, API’ler) kullanması gerektiğini anlayıp bu araçları çağırmasını sağlayan bir yetenektir. LLM, kendisine sunulan araç setini (fonksiyon tanımlarını) inceleyerek, kullanıcının niyetini gerçekleştirmek için en uygun aracı seçer ve gerekli parametrelerle birlikte bu aracı çağırması için gerekli bilgiyi üretir. Bu, LLM’leri sadece metin üreten sohbet botlarından, aksiyon alabilen akıllı asistanlara dönüştüren kritik bir adımdır.
Bu yetenek, LLM’lerin temel yeteneklerini genişleterek onları çok daha kullanışlı hale getirir. Örneğin, bir kullanıcı “İstanbul’dan Ankara’ya yarın uçak bileti fiyatları ne kadar?” diye sorduğunda, LLM bu soruyu anlayıp bir uçak bileti arama API’sini çağırması gerektiğini anlar. Ardından, API’ye “İstanbul”, “Ankara”, “yarın” gibi bilgileri parametre olarak göndererek bilet fiyatlarını alır ve bu bilgiyi kullanıcıya anlaşılır bir dilde sunar. Bu sayede, LLM’ler dinamik, gerçek zamanlı verilere erişebilir ve karmaşık görevleri otomatikleştirebilir.
Adım 1: LLM’e Araçlarınızı Tanıtmak (Function Definition)
AI Function Calling’in ilk ve en temel adımı, LLM’e hangi araçlara erişimi olduğunu ve bu araçların nasıl kullanılacağını anlatmaktır. Bu işlem, genellikle JSON formatında, fonksiyonların adlarını, açıklamalarını, alacakları parametreleri ve bu parametrelerin tiplerini tanımlayarak yapılır. LLM, bu tanımları kullanarak hangi durumda hangi fonksiyonu çağırması gerektiğini öğrenir. Bu tanımların netliği ve doğruluğu, fonksiyon çağırma mekanizmasının başarısı için hayati önem taşır.
Bir fonksiyonu LLM’e tanıtmanın en kritik noktası, açıklayıcı ve net bir description alanı sunmaktır. LLM, bu açıklamaları okuyarak sizin yerinize hangi fonksiyonun ne işe yaradığını anlar. Örneğin, bir hava durumu servisi için get_weather(city: str, date: str) fonksiyonunu tanımlarken, city parametresinin “Şehir adı (örneğin, Ankara, New York)” ve date parametresinin “Hava durumunun sorgulanacağı tarih (YYYY-MM-DD formatında)” gibi açıklamalarla zenginleştirilmesi, LLM’in doğru parametreleri sağlamasına yardımcı olur.
Aşağıda, basit bir hava durumu sorgulama fonksiyonu için bir örnek JSON tanımı bulunmaktadır:
{
"name": "get_weather",
"description": "Belirtilen şehir ve tarih için güncel hava durumu bilgisini alır.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "Hava durumunun sorgulanacağı şehir adı (örneğin, İstanbul, Londra)."
},
"date": {
"type": "string",
"description": "Hava durumunun sorgulanacağı tarih (YYYY-MM-DD formatında)."
}
},
"required": ["city", "date"]
}
}
Bu yapı, LLM’e get_weather adında bir fonksiyon olduğunu, bu fonksiyonun city ve date adında iki zorunlu parametre aldığını ve her birinin ne anlama geldiğini açıkça belirtir. LLM, bu bilgiyi kullanarak kullanıcıdan gelen bir sorguyu işlerken, örneğin “Yarın Paris’te hava nasıl olacak?” gibi bir ifadeyle karşılaştığında, bu fonksiyonu çağırması gerektiğini anlayacaktır.
Fonksiyon Tanımlarının Detaylandırılması
Fonksiyon tanımlarını ne kadar detaylı yaparsanız, LLM’in doğru kararlar alma olasılığı o kadar artar. Özellikle, parametrelerin olası değer aralıkları veya beklenen formatlar hakkında ek bilgiler eklemek, karmaşık senaryolarda büyük fark yaratabilir. Örneğin, bir tarih parametresi için “YYYY-MM-DD” formatını belirtmek, LLM’in farklı dilsel ifadelerle gelen tarihleri doğru bir şekilde ayrıştırmasına yardımcı olur.
Ayrıca, birden fazla fonksiyonunuz olduğunda, her birinin benzersiz bir name’e sahip olması ve description alanlarının birbirini tamamlayacak şekilde, ancak ayırt edilebilir olması önemlidir. LLM’in hangi fonksiyonun hangi görevi yerine getirdiğini karıştırmaması için, fonksiyon adlarının ve açıklamalarının eylemi net bir şekilde yansıtması gerekir. Örneğin, get_user_data ve update_user_profile gibi iki fonksiyon varsa, açıklamaları bu ayrımı netleştirmelidir.
Adım 2: LLM’in Karar Verme Süreci ve Fonksiyon Çağrıları
Fonksiyon tanımlarını yaptıktan sonraki adım, kullanıcının bir sorgusu geldiğinde LLM’in bu sorguyu nasıl işlediğini ve fonksiyon çağrılarını nasıl ürettiğini anlamaktır. LLM, gelen kullanıcı girdisini alır ve daha önce kendisine tanıtılan fonksiyon tanımlarıyla karşılaştırır. Bu karşılaştırma sonucunda, kullanıcının niyetini gerçekleştirmek için hangi fonksiyonun en uygun olduğuna karar verir. Eğer bir fonksiyon çağrısı yapılması gerekiyorsa, LLM ilgili fonksiyonu, doğru parametrelerle birlikte çağırmak için özel bir formatta çıktı üretir.
Bu karar verme süreci genellikle LLM’in iç mekanizmalarında gerçekleşir. Kullanıcı “Bugün İzmir’de hava kaç derece?” diye sorduğunda, LLM’in içsel analiz süreci şu adımları izleyebilir:
- Kullanıcı girdisini anla: “Bugün”, “İzmir”, “hava durumu”.
- Mevcut araçları gözden geçir:
get_weatherfonksiyonu hava durumu bilgisini alabiliyor. - Parametreleri çıkar: “İzmir” şehir adı, “Bugün” ise tarih parametresi için uygun bir değer (güncel tarih).
- Fonksiyonu çağır:
get_weather(city="İzmir", date="2026-07-12")şeklinde bir çağrı üret.
LLM’in ürettiği bu fonksiyon çağrısı, genellikle JSON formatında bir nesne olarak döner. Bu, sizin uygulamanızın LLM’in çıktısını kolayca ayrıştırıp anlayabilmesi için tasarlanmıştır.
graph TD
A["Kullanıcı Sorgusu"] --> B["LLM (Prompt & Tool Definitions)"];
B --> C{Gerekli Araç Var mı?};
C -- Evet --> D["LLM: Fonksiyon Çağrısı Üret (JSON)"];
C -- Hayır --> E["LLM: Doğrudan Yanıt Üret"];
D --> F["Uygulama: Fonksiyonu Çalıştır"];
F --> G["Fonksiyon Sonucu"];
G --> H["Uygulama: Sonucu LLM'e Geri Besle"];
H --> I["LLM: Nihai Yanıt Üret"];
I --> J["Kullanıcıya Göster"];
E --> J;
Bu akışta kritik nokta, LLM’in sadece metin üretmekle kalmayıp, doğru araçları seçme ve parametreleri doldurma yeteneğidir. LLM, fonksiyon tanımlarındaki description alanlarını ve parametre açıklamalarını kullanarak hangi fonksiyonun ne zaman çağrılması gerektiğini “anlar”. Bu, LLM’in yorumlama ve problem çözme yeteneğinin bir göstergesidir.
LLM Çıktısını İşleme ve Hata Yönetimi
LLM’den gelen fonksiyon çağrısı çıktısını uygulamanızda alıp işlemek, geliştirme sürecinin önemli bir parçasıdır. Bu çıktıyı ayrıştırdıktan sonra, ilgili fonksiyonu çağırır ve sonucunu alırsınız. Bu noktada hata yönetimi devreye girer. Eğer çağırdığınız harici servis bir hata dönerse (örneğin, ağ hatası, geçersiz parametre, sunucu hatası), bu hatayı yakalamalı ve uygun şekilde işlemelisiniz.
LLM’in kendisi de bazen yanlış fonksiyon çağrısı yapabilir veya parametreleri eksik/yanlış doldurabilir. Bu tür durumları yönetmek için, uygulamanızda bir tür “fallback” mekanizması bulundurmak faydalı olacaktır. Örneğin, LLM’in fonksiyon çağrısı yapması beklenirken sadece metin döndüğü durumları tespit edebilir veya fonksiyon çağrısı başarısız olduğunda kullanıcıya yardımcı olacak alternatif bir yanıt sunabilirsiniz.
Adım 3: Fonksiyon Sonuçlarını LLM’e Geri Beslemek
LLM bir fonksiyonu çağırdıktan sonra, bu fonksiyonun çalıştırılması ve elde edilen sonucun tekrar LLM’e geri beslenmesi gerekir. Bu, LLM’in kullanıcının orijinal isteğini tam olarak yerine getirebilmesi için hayati bir adımdır. LLM, fonksiyonun döndürdüğü çıktıyı alarak, bu bilgiyi kullanarak nihai yanıtını oluşturur. Bu geri besleme döngüsü, LLM’lerin sadece araçları çağırmakla kalmayıp, bu araçların sonuçlarını anlamlandırıp kullanıcıya sunmasını sağlar.
Örneğin, hava durumu fonksiyonu çağrıldıktan sonra {"temperature": "25°C", "condition": "Güneşli", "city": "İzmir"} gibi bir JSON sonucu döndüğünü varsayalım. Bu JSON çıktısını alıp, LLM’e geri gönderirken, orijinal sorguyla birlikte bu yeni bilgiyi de iletmelisiniz. LLM bu bilgiyi işleyerek “Bugün İzmir’de hava 25°C ve güneşli olacak.” gibi anlaşılır bir yanıt üretebilir. Bu, LLM’in sadece ham veriyi değil, bu verinin bağlamını da anlayarak nihai cevabı şekillendirmesini sağlar.
Bu geri besleme mekanizması, LLM’in kullanıcı sorgusunu ve çağrılan fonksiyonun sonucunu birleştirerek daha zengin ve bağlamsal yanıtlar üretmesine olanak tanır. LLM’e dönen verinin yapısı ve içeriği, nihai yanıtın kalitesini doğrudan etkiler. Bu nedenle, fonksiyonlarınızın döndürdüğü veriyi anlaşılır ve LLM’in kolayca işleyebileceği bir formatta tutmak önemlidir.
Gelişmiş Etkileşimler ve Zincirleme Çağrılar
AI Function Calling’in gücü sadece tek bir fonksiyonu çağırmakla sınırlı değildir. Daha karmaşık senaryolarda, bir fonksiyonun çıktısı başka bir fonksiyonun girdisi olabilir. Bu, “chaining” veya “tool use chains” olarak adlandırılır. LLM, bir dizi fonksiyon çağrısını yönetebilir ve bir fonksiyondan gelen bilgiyi bir sonraki fonksiyonu tetiklemek için kullanabilir.
Örneğin, bir kullanıcı “Bugün dolar kuru ile 1000 TL ne kadar eder?” diye sorduğunda, LLM önce bir döviz kuru API’sini çağırıp güncel dolar kurunu öğrenir. Ardından, aldığı kuru kullanarak bir hesaplama fonksiyonunu çağırır. Bu tür zincirleme çağrılar, LLM’leri çok daha sofistike görevleri yerine getirebilen güçlü araçlara dönüştürür.
Python kullanarak bu tür bir akışı nasıl yönetebileceğinize dair basit bir örnek şöyledir:
# Varsayımsal LLM API istemci sınıfı
class LLMClient:
def chat_completion(self, messages, tools):
# LLM'den yanıt al...
# Yanıt, doğrudan metin veya bir tool_call nesnesi olabilir.
pass
# Varsayımsal araçlar/fonksiyonlar
def get_exchange_rate(currency: str) -> float:
print(f"Döviz kuru sorgulanıyor: {currency}")
# Gerçek API çağrısı burada yapılır
if currency == "USD":
return 32.50 # Varsayımsal kur
return 0.0
def calculate_amount_in_currency(amount_tl: float, rate: float) -> float:
print(f"Hesaplama yapılıyor: {amount_tl} TL / {rate} = {amount_tl / rate}")
return amount_tl / rate
# Fonksiyon tanımları (LLM'e gönderilecek)
tools = [
{
"type": "function",
"function": {
"name": "get_exchange_rate",
"description": "Belirtilen para biriminin TL karşısındaki güncel kurunu alır.",
"parameters": {
"type": "object",
"properties": {
"currency": {"type": "string", "description": "Döviz kodu (örn. USD, EUR)."}
},
"required": ["currency"]
}
}
},
{
"type": "function",
"function": {
"name": "calculate_amount_in_currency",
"description": "Verilen TL miktarını, belirtilen döviz kuru üzerinden hesaplar.",
"parameters": {
"type": "object",
"properties": {
"amount_tl": {"type": "number", "description": "Hesaplanacak TL miktarı."},
"rate": {"type": "number", "description": "Kullanılacak döviz kuru."}
},
"required": ["amount_tl", "rate"]
}
}
}
]
llm = LLMClient()
user_query = "Bugün dolar kuru ile 1000 TL ne kadar eder?"
# İlk LLM çağrısı
messages = [{"role": "user", "content": user_query}]
response = llm.chat_completion(messages, tools)
# LLM'den gelen yanıtı işleme
if response.tool_calls: # Varsayımsal bir yapı
for tool_call in response.tool_calls:
if tool_call.function.name == "get_exchange_rate":
currency = tool_call.function.arguments["currency"]
rate = get_exchange_rate(currency)
# Sonucu LLM'e geri beslemek için yeni bir mesaj oluştur
messages.append({
"role": "assistant",
"content": None,
"tool_calls": [tool_call]
})
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(rate) # Fonksiyon sonucunu string olarak gönder
})
# İkinci LLM çağrısı (hesaplama için)
response2 = llm.chat_completion(messages, tools)
if response2.tool_calls:
for tool_call2 in response2.tool_calls:
if tool_call2.function.name == "calculate_amount_in_currency":
amount_tl = tool_call2.function.arguments["amount_tl"]
rate_used = tool_call2.function.arguments["rate"] # LLM'in hangi kuru kullandığını varsayıyoruz
final_amount = calculate_amount_in_currency(float(amount_tl), float(rate_used))
messages.append({
"role": "assistant",
"content": None,
"tool_calls": [tool_call2]
})
messages.append({
"role": "tool",
"tool_call_id": tool_call2.id,
"content": str(final_amount)
})
# Son LLM çağrısı (nihai yanıt için)
final_response = llm.chat_completion(messages, tools)
print(final_response.content) # Nihai yanıt
else:
print("İkinci LLM çağrısında hesaplama fonksiyonu tetiklenemedi.")
else:
print(response.content) # LLM doğrudan yanıt verdi
Bu örnek, LLM’in ilk olarak get_exchange_rate fonksiyonunu çağırmasını, ardından bu sonucun alınıp hesaplama fonksiyonunun tetiklenmesi için tekrar LLM’e gönderilmesini gösteriyor. Bu tür karmaşık akışları yönetmek, uygulamanızın yeteneklerini büyük ölçüde genişletecektir.
Gelişmiş Senaryolar ve Dikkat Edilmesi Gerekenler
AI Function Calling, basit sorguların ötesinde, çok daha sofistike uygulamalar geliştirmek için kullanılabilir. Örneğin, bir müşteri destek botu, bir kullanıcının sorununu anlayıp ilgili belgeleri çekmek, bir destek bileti oluşturmak veya hatta bir uzaktan destek seansını başlatmak için fonksiyon çağrılarını kullanabilir. Bir raporlama aracı, kullanıcıdan aldığı doğal dil sorgusunu yorumlayarak veritabanından veri çeker, bu veriyi işler ve bir grafik veya tablo olarak sunar.
Bu yetenek, LLM’leri otomasyon, veri analizi, içerik oluşturma ve hatta kod yazma gibi alanlarda güçlü birer yardımcı haline getirir. Yapay zeka ile etkileşim kurarken sadece “bana bilgi ver” demek yerine, “bana bu bilgiyi bul ve şunu yap” diyebilmek, etkileşimde bulunduğumuz araçların doğasını değiştiriyor.
Ancak, bu gücün beraberinde getirdiği sorumluluklar da var. Fonksiyon tanımlarınızın karmaşıklığı arttıkça, LLM’in doğru fonksiyonu seçme ve parametreleri doldurma olasılığı düşebilir. Bu nedenle, fonksiyonlarınızı olabildiğince basit, anlaşılır ve odaklı tutmak önemlidir. Her fonksiyonun tek bir işi yapması (Single Responsibility Principle), LLM’in işini kolaylaştırır.
Ayrıca, LLM’in çıktısını her zaman bir insan tarafından gözden geçirme veya doğrulama mekanizması eklemek, özellikle kritik uygulamalarda önerilir. LLM’ler bazen beklenmedik veya yanlış çıktılar üretebilir ve bu çıktılar, otomatik olarak çalıştırıldığında ciddi sorunlara yol açabilir.
Gerçek Dünya Uygulamaları ve Trade-off’lar
AI Function Calling ile geliştirdiğim bir finansal danışmanlık uygulaması örneğini verebilirim. Bu uygulamada, kullanıcının finansal hedeflerini, risk toleransını ve mevcut durumunu doğal dilde girdikten sonra, LLM çeşitli finansal hesaplama fonksiyonlarını (örneğin, yatırım getirisi hesaplama, bütçe planlayıcı, vergi simülatörü) çağırarak kullanıcıya kişiselleştirilmiş öneriler sunuyordu. Bu, kullanıcının karmaşık finansal verilerle uğraşmadan, sadece sohbet ederek finansal kararlar almasına olanak tanıyordu.
Bu tür bir uygulamada en büyük trade-off’lardan biri, LLM’in “anlama” yeteneği ile harici araçların “yapabilme” yeteneği arasındaki dengeyi kurmaktı. LLM’in fonksiyonları doğru anlaması ve doğru parametreleri sağlaması kritikti. Eğer LLM, bir fonksiyonun parametrelerinden birini yanlış anlarsa (örneğin, “yıllık” yerine “aylık” faiz oranı girdiğini varsayarsa), sonuç tamamen yanlış olabilirdi. Bu nedenle, fonksiyon tanımlarının hem LLM tarafından anlaşılabilir olması hem de harici servisler tarafından doğru yorumlanabilmesi için dikkatlice tasarlanması gerekiyordu.
Başka bir senaryoda, bir e-ticaret platformunda ürün arama ve sipariş verme sürecini otomatikleştirmek için AI Function Calling kullandım. Kullanıcılar “Kırmızı, L beden bir tişört istiyorum” gibi ifadelerle arama yapabiliyorlardı. LLM, bu sorguyu ayrıştırıp ürün arama API’sini çağırıyor, ardından bulunan sonuçları işleyip kullanıcıya sunuyordu. Eğer kullanıcı “Bunu sepetime ekle” derse, LLM sepet API’sini çağırıyordu.
Buradaki trade-off, performans ve maliyetti. Her LLM çağrısı, hem zaman hem de maliyet anlamına geliyordu. Özellikle zincirleme çağrılarda, birden fazla LLM isteği yapıldığında bu maliyetler artabiliyordu. Bu yüzden, gereksiz LLM çağrılarından kaçınmak, mümkün olduğunca az çağrıyla işi bitirmek veya bazı işlemleri LLM’e sormadan doğrudan kodunuzla yapmak (örneğin, basit bir toplama işlemi) kritikti.
Ayrıca, LLM’lerin “halüsinasyon” görme eğilimi de bir risk faktörüdür. LLM, var olmayan bir fonksiyonu çağırmaya çalışabilir veya parametreleri uydurabilir. Bu tür durumları engellemek için, LLM’e sunduğunuz araç setini dikkatlice seçmeli ve hata durumlarını yönetmek için sağlam mekanizmalar kurmalısınız.
Sonuc
AI Function Calling, LLM’leri sadece bilgi işlemleyen varlıklardan, eyleme geçen ve dış dünyayla etkileşime giren akıllı ajanlara dönüştüren güçlü bir teknolojidir. Uygulamalarınıza bu yeteneği entegre ederek, daha dinamik, daha kullanışlı ve daha etkileşimli deneyimler sunabilirsiniz. Fonksiyonlarınızı doğru tanımlamak, LLM’in karar verme sürecini anlamak ve elde edilen sonuçları etkili bir şekilde geri beslemek, bu teknolojinin potansiyelini tam olarak ortaya çıkarmanın anahtarıdır.
Bu üç adım — araçlarınızı tanıtma, LLM’in karar verme sürecini anlama ve sonuçları geri besleme — AI Function Calling ile akıllı uygulamalar geliştirmenin temelini oluşturur. Bu temel üzerine inşa ederek, kullanıcılarınızı şaşırtacak ve ihtiyaçlarını daha derinlemesine karşılayacak yenilikçi çözümler üretebilirsiniz. Unutmayın, yapay zeka ile etkileşim kurmak artık sadece sormakla kalmıyor, aynı zamanda “yapmasını istemekle” de ilgili.