--- title: Lezzet Kafe Tool-Calling Asistan emoji: 🍽️ colorFrom: red colorTo: yellow sdk: gradio sdk_version: 5.49.1 app_file: app.py pinned: false --- # 🍽️ Lezzet Kafe — Tool-Calling Destekli Sipariş Asistanı Bu depo iki ödevi birlikte içerir: 1. **Ödev 1 — Custom Chat Template (Jinja2):** `chat_template.jinja` 2. **Ödev 2 — Tool-Calling Destekli Asistan:** SQLite'a bağlı, uçtan uca çalışan restoran sipariş sistemi. --- ## 1) Senaryo Özeti **Lezzet Kafe** için bir sipariş asistanı. Kullanıcı doğal dille konuşur; model niyeti anlayıp uygun **aracı (tool)** çağırır. Tüm veriler gerçek bir **SQLite** veritabanından okunur/yazılır. Üç araç vardır: | Araç | Tür | İşlev | |------|-----|-------| | `get_menu(category?)` | okuma | Menüyü (opsiyonel kategori filtresiyle) getirir | | `create_order(customer, items, table_no?)` | yazma | Sipariş oluşturur, stoktan düşer, tutarı hesaplar | | `check_order_status(order_id)` | okuma | Sipariş durumunu ve içeriğini sorgular | ### Halüsinasyon Engelleme Nihai yanıtlar **yalnızca araçlardan dönen gerçek veriye** dayanır. Menüde olmayan bir ürün istenirse veri katmanı (`tools.py`) `{"error": "... menüde yok"}` döndürür; model ürünü uydurmaz. Stok yetersizse sipariş oluşturulmaz. --- ## 2) Mimari ``` toolcall-restaurant/ ├── app.py # Gradio arayüzü (HF Space) ├── chat_template.jinja # Ödev 1: özel Jinja2 chat template ├── requirements.txt ├── data/restaurant.db # SQLite (çalışırken oluşur) ├── src/ │ ├── database.py # Bağlantı, şema, seed (tek veri katmanı) │ ├── tools.py # get_menu / create_order / check_order_status + TOOL_REGISTRY │ ├── tool_schemas.py # Araçların JSON şemaları (modele verilir) │ ├── llm.py # LLM backend (hf | mock) + tool-call ayrıştırma │ └── agent.py # Sohbet döngüsü: prompt yönetimi + fonksiyon yönlendirme └── scripts/ ├── demo_cli.py # Terminal demo (log ekran görüntüsü için) └── test_smoke.py # 6 birim test ``` **Akış:** kullanıcı → `agent.chat` → `llm.generate` (model tool_call döndürür) → `TOOL_REGISTRY` üzerinden gerçek fonksiyon çalışır → sonuç `tool` rolüyle modele geri döner → model doğal dilde yanıtlar. ### Model / Backend `src/llm.py` üç modda çalışır (`LLM_BACKEND` ortam değişkeni): - **`auto`** (varsayılan) — `HF_TOKEN` tanımlıysa gerçek model (`hf`), değilse `mock`. - **`hf`** — Hugging Face **Inference Providers** (OpenAI-uyumlu, `chat.completions.create(..., tools=...)`, `router.huggingface.co` üzerinden). Varsayılan model **`Qwen/Qwen2.5-72B-Instruct`** (gated değil, tool-calling güçlü). Kendi servis edilebilir modelinizi `MODEL_ID` ile verebilirsiniz. *Not: Ücretsiz HF hesabında aylık ~$0.10 çıkarım kredisi vardır — bir demo için yeterli; kredi biterse `MODEL_ID`'yi daha küçük bir modelle değiştirin, ör. `Qwen/Qwen2.5-7B-Instruct`.* - **`mock`** — API/GPU gerektirmeyen, kural tabanlı deterministik backend. Sadece **offline test ve demo** içindir (bir dil modeli değildir). Arayüz, o an hangi modun aktif olduğunu üstte açıkça gösterir (🟢 gerçek model / 🟡 mock). --- ## 3) Yerelde Çalıştırma ```bash # 1. Kurulum (gradio surumu Space ile ayni tutulur) pip install "gradio==5.49.1" pip install -r requirements.txt # 2. Veritabanını başlat (opsiyonel; app/demo otomatik de yapar) python -m src.database # 3. Testler python -m scripts.test_smoke # 6/6 test geçmeli # 4. Terminal demo (anahtarsız, offline) LLM_BACKEND=mock python -m scripts.demo_cli # 5. Gerçek model ile (HF Inference Providers) export HF_TOKEN=hf_xxx # LLM_BACKEND=auto oldugundan token yeter # export MODEL_ID=Qwen/Qwen2.5-72B-Instruct # istege bagli python -m scripts.demo_cli # 6. Gradio arayüzü python app.py # http://127.0.0.1:7860 ``` --- ## 4) Hugging Face Space Canlı Demo > 🔗 **Canlı Demo:** `https://huggingface.co/spaces//lezzet-kafe-toolcall` > *(Space oluşturduktan sonra bu bağlantıyı güncelleyin.)* **Space kurulumu:** yeni bir Gradio Space oluşturun, bu deponun içeriğini yükleyin. Adım adım GitHub + HF Space push talimatları için **[`DEPLOY.md`](DEPLOY.md)** dosyasına bakın. Gerçek model için Space **Settings → Variables and secrets** kısmına `LLM_BACKEND=hf`, `HF_TOKEN`, `MODEL_ID` ekleyin. Değişken vermezseniz Space `mock` backend ile anahtarsız çalışır. --- ## 5) Örnek Girdi ve Tool-Call Logu `LLM_BACKEND=mock python -m scripts.demo_cli` çıktısı (terminal): ``` 👤 KULLANICI: Merhaba, tatli menusunde neler var? 🔧 TOOL-CALL -> get_menu({"category": "tatli"}) 📦 TOOL-RESULT <- {"count": 3, "items": [{"name": "Baklava", "price": 140.0, ...}, {"name": "Kunefe", "price": 120.0, ...}, {"name": "Sutlac", ...}]} 🤖 ASISTAN : Menude sunlar var: Baklava (140.0 TL), Kunefe (120.0 TL), Sutlac (90.0 TL). 👤 KULLANICI: 2 adet kunefe siparis etmek istiyorum. 🔧 TOOL-CALL -> create_order({"customer": "Musteri", "items": [{"name": "Kunefe", "quantity": 2}]}) 📦 TOOL-RESULT <- {"order_id": 1, "status": "hazirlaniyor", "total": 240.0, ...} 🤖 ASISTAN : Siparis #1 olusturuldu. Toplam: 240.0 TL. 👤 KULLANICI: 1 numarali siparisimin durumu ne? 🔧 TOOL-CALL -> check_order_status({"order_id": 1}) 📦 TOOL-RESULT <- {"order_id": 1, "status": "hazirlaniyor", "total": 240.0, ...} 🤖 ASISTAN : #1 numarali siparisin durumu: hazirlaniyor (toplam 240.0 TL). 👤 KULLANICI: Bir tane uzay burgeri alabilir miyim? # <-- menüde YOK 🔧 TOOL-CALL -> create_order({"customer": "Musteri", "items": [{"name": "Uzay Burgeri", "quantity": 1}]}) 📦 TOOL-RESULT <- {"error": "'Uzay Burgeri' menude yok. Once get_menu ile kontrol edin."} 🤖 ASISTAN : Uzgunum, 'Uzay Burgeri' menude yok. (Asistan ürünü UYDURMADI.) ``` ### 📸 Terminal Ekran Görüntüsü Yukarıdaki demonun (`scripts/demo_cli.py`) gerçek çıktısı: ![Tool-call terminal logu](docs/demo.png) > Görselde son istek (**"uzay burgeri"**) menüde olmadığı için araç > `{"error": "... menüde yok"}` döndürür ve asistan ürünü **uydurmaz** — > istenen halüsinasyon engelleme davranışı. --- ## 6) Ödev 1 — `chat_template.jinja` Hakkında ChatML tarzı (`<|im_start|> ... <|im_end|>`) özel bir şablon: - **Roller:** `system`, `user`, `assistant`, `tool`. - **Tool tanımları:** `tools` değişkeni verilirse araç şemaları system bloğuna JSON olarak gömülür. - **Tool çağrısı:** asistanın `tool_calls` alanı → `{...}`. - **Tool sonucu:** `role="tool"` → `...`. - `add_generation_prompt` ile model asistan turuna hazırlanır. Şablonu `transformers` ile test etme: ```python from transformers import AutoTokenizer tok = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B-Instruct") tok.chat_template = open("chat_template.jinja").read() print(tok.apply_chat_template(messages, tools=TOOLS, add_generation_prompt=True, tokenize=False)) ``` --- ## Lisans Eğitim amaçlı örnek proje.