gururaser/deprem-asistani-tool-calling
🌍 Deprem Asistanı: Tool Calling Demo
Bir LLM'in public API'lardan veri çekmesini sağlayan Tool Calling (Function Calling) uygulaması. Model, kullanıcının sorusuna göre doğru araçları kendi seçip zincirleyerek çağırır; hangi aracı hangi argümanlarla çağırdığı ve dönen ham JSON arayüzde açıkça gösterilir. Bulunan depremler ayrıca OpenStreetMap haritasında işaretlenir.
Yukarıdaki ekran görüntüsü tek bir soruya verilen yanıtı gösteriyor: model 3 turda 4 araç çağırmış (her biri süresiyle birlikte ayrı kutuda), bulunan 7 deprem haritaya işaretlenmiş.
🔌 Kullanılan Public API'lar (hiçbiri API anahtarı istemiyor)
LLM: deepseek-ai/DeepSeek-V4-Flash:fireworks-ai, Hugging Face Inference Router (https://router.huggingface.co/v1) üzerinden OpenAI uyumlu SDK ile.
🛠️ Tool (Function) Tanımları
Üç araç bilinçli olarak zincirlenebilir seçildi, çünkü tek bir araç soruyu tek turda bitiremiyor, model plan yapmak zorunda kalıyor:
search_earthquakes bölge sınırlarını doğrudan geocode_place çıktısından alır; distance_km de deprem koordinatı ile şehir koordinatını birleştirir. Mesafe hesabı bilerek araca verildi, çünkü modelin aritmetiğine güvenilmiyor.
Parametre sınırları
Bu sınırlar hem kodda uygulanıyor hem de tool şemasında modele bildiriliyor. İkisi MAX_DAYS / MAX_LIMIT sabitlerinden üretiliyor, çünkü şema açıklaması koddan kopunca model varsayılanı üst sınır sanıyor: ilk sürümde days açıklamasında yalnızca "varsayılan 30" yazdığı için model kullanıcıya "araçlarım yalnızca son 30 günü sorgulayabiliyor" diye yanlış bilgi vermişti. test_schema_states_real_limits bunu tekrar etmesin diye kilitliyor.
Bilinen kapsam dışı durumlar
- 1 yıldan eski depremler sorgulanamaz. USGS kataloğu daha geriye gidiyor ama araç
daysdeğerini 365'e sıkıştırıyor. 1999 Gölcük gibi tarihi depremler için model uydurmuyor, AFAD ve USGS arşivlerine yönlendiriyor. - Deprem dışı sorular yanıtlanmıyor. Model kapsamını açıklayıp reddediyor ve hiç araç çağırmıyor, dolayısıyla API'lere gereksiz istek gitmiyor.
- Şiddet (intensity) değil büyüklük (magnitude) döner. USGS bu uç noktada Richter/Mw büyüklüğü veriyor, Mercalli şiddeti vermiyor.
🔄 Örnek Çok Turlu Akış (gerçek çıktı)
Kullanıcı: "Son 3 ayda Ege Denizi'nde 4.5+ büyüklüğünde deprem oldu mu?
En büyüğü İzmir'e kaç km uzaktaydı?"
[Tur 1] Araç Çağrıları (paralel):
-> geocode_place(place='Ege Denizi')
<- {"lat": 38.06, "lon": 25.72, "min_lat": 35.11, "max_lat": 41.02,
"min_lon": 22.53, "max_lon": 28.33}
-> geocode_place(place='İzmir')
<- {"lat": 38.42, "lon": 27.13, ...}
[Tur 2] Araç Çağrıları:
-> search_earthquakes(min_magnitude=4.5, days=90, min_lat=35.11, max_lat=41.02,
min_lon=22.53, max_lon=28.33, order='magnitude', limit=10)
<- {"count": 7, "earthquakes": [{"magnitude": 5.3,
"place": "23 km S of Karpathos, Greece", "lat": 35.2927, "lon": 27.1923,
"depth_km": 10, "time_utc": "2026-07-02 ..."} , ...]}
[Tur 3] Araç Çağrıları:
-> distance_km(lat1=35.2927, lon1=27.1923, lat2=38.4193, lon2=27.1285)
<- {"distance_km": 347.7}
[Tur 4] Nihai Yanıt:
Evet, son 3 ayda Ege Denizi'nde 4.5+ büyüklüğünde 7 deprem kaydedilmiş.
En büyüğü: M5.3, Karpathos (Yunanistan) 23 km güneyi, 10 km derinlik,
İzmir'e uzaklığı ~348 km.
+ Haritada 7 deprem işaretli.Arayüzde her araç çağrısı, süresiyle birlikte açılır-kapanır bir kutuda gösterilir (🔧 [Tur 2] search_earthquakes(...) → içinde dönen ham JSON). Yukarıdaki ekran görüntüsü tam olarak bu akışın çalışan halidir.
⚙️ Teknik Notlar
- Ajan döngüsü: en fazla 6 tur; her turda model birden fazla aracı paralel çağırabilir.
- Cache:
geocode_placevesearch_earthquakeslru_cacheile önbelleklenir; aynı yer adı sohbette tekrar geçtiğinde ağa çıkılmaz (Nominatim politikasının da gereği). - Rate limit: host bazlı throttle,
threading.Lockile korunuyor. Nominatim 1 istek/sn (politika şartı), USGS 0.2 sn. Kilit şart, çünkü Gradio istekleri thread havuzunda koşuyor. - Hata toleransı: ağ ve ayrıştırma hataları exception fırlatmaz,
{"error": ...}olarak modele döner; model durumu görüp toparlayabilir, uygulama çökmez. - Harita: ek bağımlılık yok; Leaflet SRI hash'li olarak
<iframe srcdoc>içine gömülür.
Güvenlik önlemleri
🚀 Lokal Çalıştırma
pip install -r requirements.txt
cp .env.example .env # HF_TOKEN=hf_... satırını doldur
python app.py # http://127.0.0.1:7860Testler (ağ gerektiren iki kontrol dahil):
python test_tools.py☁️ Hugging Face Spaces'e Yayınlama
- https://huggingface.co/new-space → SDK olarak Gradio, donanım olarak ZeroGPU (Free) seç. Ücretsiz hesaplarda Gradio Space'leri
cpu-basicüzerinde barındırılamıyor (402 Payment Required); ücretsiz hesabın hakkı 2 adet ZeroGPU Space'i ile sınırlı. - Space ayarlarında Settings → Variables and secrets → New secret:
HF_TOKEN= kendi HF token'ın (Inference Provider izinli). - Dosyaları yükle:
git clone https://huggingface.co/spaces/<kullanıcı>/<space-adi>
cp app.py requirements.txt README.md image.png <space-adi>/
cd <space-adi> && git add . && git commit -m "Deprem asistanı tool calling demo" && git pushimage.png de kopyalanmalı, aksi halde Space kartındaki ekran görüntüsü kırık çıkar. test_tools.py yüklemek zorunlu değil; Space onu çalıştırmaz.
ZeroGPU notu
Bu uygulamanın GPU'ya ihtiyacı yok; model uzaktaki HF Inference Router'da çalışıyor, yerelde ağırlık yüklenmiyor. Ama ZeroGPU açılışta Gradio'ya bağlı en az bir @spaces.GPU fonksiyonu arıyor ve bulamazsa RuntimeError: No @spaces.GPU function detected during startup ile hiç açılmıyor. Bu yüzden app.py sonunda görünmez bir no-op fonksiyon bağlı. Gerçek sohbet akışı GPU istemediği için ziyaretçilerin günlük ZeroGPU kotasından hiçbir şey harcanmıyor.
requirements.txt içinde gradio, spaces ve huggingface_hub bilerek yok: üçü de Spaces tarafından önceden kurulu ve platformca yönetiliyor, listelemek çözümleme hatasına veya ZeroGPU runtime'ının sessizce bozulmasına yol açıyor. Gradio sürümü README frontmatter'daki sdk_version ile belirleniyor.
.env dosyası push edilmez (.gitignore kapsamında); Space'te token secret'tan okunur.
📁 Dosyalar
les5/
├── app.py # araçlar + JSON şemaları + ajan döngüsü + harita + Gradio UI
├── test_tools.py # assert tabanlı kontroller (haversine, parse, şema, cache, throttle, XSS, SRI)
├── requirements.txt
├── image.png # arayüz ekran görüntüsü (README'de kullanılıyor)
├── .env.example
└── README.md