{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Hafta 9: LLM'ler için Gelişmiş Teknikler: Fine-Tuning ve RAG\n",
        "\n",
        "**Dersin Hedefleri:**\n",
        "1.  Önceden eğitilmiş (pre-trained) LLM'leri özel görevler veya domain'ler için uyarlamanın neden gerekli olduğunu anlamak.\n",
        "2.  İnce Ayar (Fine-Tuning) kavramını ve ne zaman kullanılması gerektiğini öğrenmek.\n",
        "3.  Geri Çağırma Destekli Üretim (Retrieval-Augmented Generation - RAG) mimarisini ve nasıl çalıştığını kavramak.\n",
        "4.  Vektör veritabanlarının (Vector Databases) RAG sistemlerindeki rolünü anlamak.\n",
        "5.  `sentence-transformers` ve `faiss` gibi kütüphanelerle basit bir RAG pipeline'ı prototiplemek.\n",
        "\n",
        "## 1. LLM'leri Neden Özelleştirmeliyiz?\n",
        "\n",
        "Genel amaçlı LLM'ler (GPT-4, Llama 3 gibi) önemli derecede yeteneklidir, ancak bazı sınırlamaları vardır:\n",
        "\n",
        "- **Bilgi Kesintisi (Knowledge Cutoff):** Eğitimleri belirli bir tarihte durduğu için, o tarihten sonraki olaylar veya bilgiler hakkında hiçbir fikirleri yoktur.\n",
        "- **Özel veya Gizli Bilgi Eksikliği:** Şirketinizin dahili belgeleri, özel tıbbi kayıtlar veya kişisel e-postalarınız gibi kamuya açık olmayan verileri bilemezler.\n",
        "- **Stil veya Format Uyumsuzluğu:** Belirli bir jargonu (örn: hukuk, tıp) kullanmakta veya çok spesifik bir JSON formatında çıktı üretmekte zorlanabilirler.\n",
        "\n",
        "Bu sınırlamaları aşmak için iki ana strateji vardır: **Fine-Tuning** ve **RAG**.\n",
        "\n",
        "## 2. İnce Ayar (Fine-Tuning)\n",
        "\n",
        "**Tanım:** Önceden eğitilmiş bir LLM'i alıp, daha küçük ve göreve özgü bir veri seti üzerinde eğitime devam etme işlemidir. Bu süreçte, modelin ağırlıkları (parametreleri) bu yeni veriye uyum sağlamak için hafifçe güncellenir.\n",
        "\n",
        "**Amacı:** Modelin **davranışını**, **stilini** veya **terminolojisini** değiştirmektir.\n",
        "\n",
        "- **Ne zaman kullanılır?**\n",
        "  - Modele belirli bir kişilik (örn: esprili bir asistan) kazandırmak için.\n",
        "  - Medikal rapor özetleme gibi, belirli bir alana özgü terminolojiyi öğretmek için.\n",
        "  - Çok spesifik bir çıktı formatına uymasını sağlamak için.\n",
        "\n",
        "**Zorlukları:**\n",
        "- **Maliyetli:** Büyük miktarda etiketli veri gerektirir ve eğitim süreci (GPU'lar) pahalı olabilir.\n",
        "- **Yıkıcı Unutma (Catastrophic Forgetting):** Model, yeni görevi öğrenirken genel dünya bilgisini unutmaya başlayabilir.\n",
        "- **Gerçekleri Öğretmek İçin Kötü Bir Yoldur:** Fine-tuning, modele yeni gerçekler (facts) enjekte etmek için verimsiz ve güvenilmez bir yöntemdir.\n",
        "\n",
        "> **Modern Yaklaşım:** Tam fine-tuning yerine, **PEFT (Parameter-Efficient Fine-Tuning)** ve **LoRA** gibi teknikler, modelin sadece küçük bir kısmını güncelleyerek bu maliyetleri büyük ölçüde düşürür.\n",
        "\n",
        "## 3. Geri Çağırma Destekli Üretim (Retrieval-Augmented Generation - RAG)\n",
        "\n",
        "**Tanım:** LLM'e, bir soruya cevap vermeden önce, harici bir bilgi tabanından (knowledge base) ilgili bilgileri bulup \"kopya çekme\" yeteneği kazandıran bir mimaridir.\n",
        "\n",
        "**Amacı:** Modelin yanıtlarını, **güncel, güvenilir ve doğrulanabilir** bilgilerle desteklemektir.\n",
        "\n",
        "**Nasıl Çalışır? (Basitleştirilmiş Akış)**\n",
        "\n",
        "1.  **Indeksleme (Offline):**\n",
        "    a.  Bilgi kaynaklarınızı (PDF'ler, web siteleri, dokümanlar) daha küçük parçalara (chunks) ayırırsınız.\n",
        "    b.  Bir embedding modeli kullanarak her bir parçayı bir vektöre dönüştürürsünüz.\n",
        "    c.  Bu vektörleri, hızlı arama için optimize edilmiş bir **Vektör Veritabanına** (örn: FAISS, ChromaDB, Pinecone) yüklersiniz.\n",
        "\n",
        "2.  **Sorgulama (Online):**\n",
        "    a.  Kullanıcı bir soru (`query`) sorar.\n",
        "    b.  Aynı embedding modeli ile kullanıcının sorusunu da bir vektöre dönüştürürsünüz.\n",
        "    c.  Bu sorgu vektörünü kullanarak vektör veritabanında **anlamsal olarak en benzer** (en ilgili) belge parçalarını bulursunuz. Bu işleme **retrieval** denir.\n",
        "    d.  Bulunan bu ilgili parçaları (`context`) ve kullanıcının orijinal sorusunu birleştirerek LLM için bir **prompt** oluşturursunuz.\n",
        "    e.  Prompt örneği: `\"\"\"Aşağıdaki bağlamı kullanarak soruya cevap ver. Bağlam: [Buraya ilgili belge parçaları gelir]. Soru: [Kullanıcının sorusu buraya gelir].\"\"\"`\n",
        "    f.  LLM, kendisine sağlanan bu bağlama dayanarak soruyu cevaplar.\n",
        "\n",
        "RAG, LLM'leri gerçek dünya uygulamaları için güvenilir hale getirmenin en popüler ve etkili yoludur."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Gerekli Kütüphanelerin Kurulumu\n",
        ".pip install sentence-transformers faiss-cpu"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Alıştırma: Bilgi Tabanını Genişletme\n",
        "\n",
        "1.  Yukarıdaki `documents` listesine, şirketin destek politikalarıyla ilgili yeni bir doküman ekleyin. Örneğin: `\"Tüm ürünlerimiz için 7/24 e-posta desteği sunulmaktadır ve kurumsal müşterilerimize özel telefon hattımız mevcuttur.\"`\n",
        "2.  İndeksleme kodunu yeniden çalıştırarak bu yeni dokümanı da vektör veritabanına ekleyin.\n",
        "3.  `answer_question_with_rag` fonksiyonunu `\"Destek seçenekleriniz nelerdir?\"` gibi bir soruyla çağırın.\n",
        "4.  Sistemin, yeni eklediğiniz bilgiyi doğru bir şekilde bulup cevabında kullanabildiğini gözlemleyin. Bu, RAG'ın bilgi tabanını güncellemenin ne kadar kolay olduğunu gösterir (fine-tuning'in aksine)."
      ]
    },
    {
      "cell_type": "markdown",
      "source": "# Production RAG System örneği\n\nrag_system = ProductionRAGSystem(\n    embedding_model=EMBEDDING_MODEL,\n    generative_model=GENERATIVE_MODEL,\n    embedding_dim=EMBEDDING_DIM\n)\n\n# Index knowledge base\nrag_system.index_knowledge_base(documents, metadata)\n\n# Test queries\ntest_queries = [\n    \"Explain how transfer learning works\",\n    \"What's the difference between deep learning and machine learning?\",\n    \"How do large language models process text?\"\n]\n\nfor query in test_queries:\n    result = rag_system.query(\n        query,\n        use_reranking=False,  # Set True for better accuracy (slower)\n        return_sources=True,\n        verbose=True\n    )\n    print(\"\\n\" + \"=\"*80 + \"\\n\")\n\nprint(\"\\n✅ RAG Pipeline Complete!\")\nprint(\"\\n💡 Key Takeaways:\")\nprint(\"   1. Semantic search finds relevant documents by meaning, not keywords\")\nprint(\"   2. Reranking improves accuracy by careful evaluation\")\nprint(\"   3. HyDE reduces query-document mismatch\")\nprint(\"   4. Production RAG combines retrieval + generation with citations\")\nprint(\"\\n🔜 Next: Function calling and agents (Section 4)\")",
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": "# Production RAG System: Complete Pipeline (LiteLLM)\n\nclass ProductionRAGSystem:\n    \"\"\"\n    Complete RAG system combining retrieval + generation\n    \n    Production features:\n    - Error handling\n    - Source attribution\n    - Metrics tracking\n    - Configurable retrieval strategy\n    \"\"\"\n\n    def __init__(\n        self,\n        embedding_model: str = \"gemini/text-embedding-004\",\n        generative_model: str = \"gemini/gemini-pro\",\n        embedding_dim: int = 768\n    ):\n        self.generative_model = generative_model\n\n        # Initialize search engine with reranking capability\n        self.search_engine = RerankedSearchEngine(\n            embedding_model=embedding_model,\n            embedding_dim=embedding_dim,\n            generative_model=generative_model\n        )\n\n        self.query_count = 0\n\n    def index_knowledge_base(\n        self,\n        documents: List[str],\n        metadata: List[Dict] = None\n    ):\n        \"\"\"Index documents into knowledge base\"\"\"\n        self.search_engine.index_documents(documents, metadata)\n        print(f\"✓ Knowledge base ready with {len(documents)} documents\")\n\n    def generate_answer(\n        self,\n        query: str,\n        retrieved_docs: List[Dict],\n        max_context_length: int = 2000\n    ) -> Dict:\n        \"\"\"Generate answer using retrieved context\"\"\"\n        # Build context from retrieved documents\n        context_parts = []\n        sources = []\n\n        for i, doc in enumerate(retrieved_docs[:5], 1):\n            context_parts.append(f\"[Document {i}]\\n{doc['document']}\")\n            sources.append({\n                'document': doc['document'],\n                'metadata': doc['metadata'],\n                'relevance_score': doc.get('rerank_score', doc.get('score', 0.0))\n            })\n\n        context = \"\\n\\n\".join(context_parts)\n\n        # Truncate if too long\n        if len(context) > max_context_length * 4:\n            context = context[:max_context_length * 4]\n            context += \"\\n\\n[... context truncated ...]\"\n\n        # RAG prompt\n        rag_prompt = f\"\"\"You are a helpful AI assistant. Answer the user's question using ONLY the information provided in the context below.\n\nIMPORTANT RULES:\n1. Base your answer strictly on the provided context\n2. If the context doesn't contain enough information, say so explicitly\n3. Cite which document(s) you used (e.g., \"According to Document 2...\")\n4. Be concise but comprehensive\n\nContext:\n{context}\n\nUser Question: {query}\n\nAnswer:\"\"\"\n\n        # Generate response using LiteLLM\n        try:\n            response = completion(\n                model=self.generative_model,\n                messages=[{\"role\": \"user\", \"content\": rag_prompt}],\n                temperature=0.3,  # Low temperature for factual accuracy\n                max_tokens=500\n            )\n\n            answer = response.choices[0].message.content\n            success = True\n            error = None\n\n        except Exception as e:\n            answer = f\"Error generating answer: {str(e)}\"\n            success = False\n            error = str(e)\n\n        return {\n            'answer': answer,\n            'sources': sources,\n            'query': query,\n            'success': success,\n            'error': error\n        }\n\n    def query(\n        self,\n        query: str,\n        use_reranking: bool = False,\n        return_sources: bool = True,\n        verbose: bool = True\n    ) -> Dict:\n        \"\"\"Complete RAG pipeline\"\"\"\n        self.query_count += 1\n\n        if verbose:\n            print(f\"\\n{'='*80}\")\n            print(f\"Query #{self.query_count}: {query}\")\n            print('='*80)\n\n        # Retrieve relevant documents\n        if use_reranking:\n            retrieved = self.search_engine.search_with_reranking(\n                query,\n                initial_k=10,\n                final_k=3\n            )\n        else:\n            retrieved = self.search_engine.search(query, top_k=3, return_scores=True)\n\n        if verbose:\n            print(f\"\\n✓ Retrieved {len(retrieved)} documents\")\n\n        # Generate answer\n        result = self.generate_answer(query, retrieved)\n\n        if verbose:\n            print(f\"\\n📝 Answer:\\n{result['answer']}\")\n            if return_sources:\n                print(f\"\\n📚 Sources:\")\n                for i, src in enumerate(result['sources'], 1):\n                    print(f\"  [{i}] {src['document'][:60]}...\")\n\n        if not return_sources:\n            del result['sources']\n\n        return result\n\nprint(\"✓ ProductionRAGSystem class defined (using LiteLLM)\")",
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": "### 3.4 Production RAG System: End-to-End Pipeline\n\nŞimdi tüm parçaları birleştirerek **production-ready** bir RAG sistemi oluşturacağız:\n- Semantic search (retrieval)\n- Optional reranking\n- Context-aware generation\n- Citation tracking\n- Error handling\n\n**RAG Pipeline:**\n```\nUser Query → Retrieve relevant docs → Build context → LLM generates answer → Return answer + sources\n```",
      "metadata": {}
    },
    {
      "cell_type": "markdown",
      "source": "# HyDE örneği\n\nhyde_engine = HyDESearchEngine(\n    embedding_model=EMBEDDING_MODEL,\n    embedding_dim=EMBEDDING_DIM\n)\nhyde_engine.index_documents(documents, metadata)\n\nquery = \"What techniques help models learn from less data?\"\n\nprint(f\"Query: {query}\\n\")\nprint(\"=\"*80)\nprint(\"\\n[HyDE Search Results]\")\nprint(\"-\"*80)\n\nhyde_results = hyde_engine.search_hyde(query, top_k=3)\n\nfor i, r in enumerate(hyde_results, 1):\n    print(f\"\\n{i}. Document: {r['document']}\")\n    print(f\"   HyDE Score: {r['hyde_score']:.4f} | Standard Score: {r['standard_score']:.4f}\")\n    improvement = r['hyde_score'] - r['standard_score']\n    print(f\"   Improvement: {improvement:+.4f}\")\n\nprint(\"\\n\" + \"=\"*80)\nprint(\"\\n💡 Gözlem:\")\nprint(\"   - HyDE scores genellikle daha yüksek\")\nprint(\"   - Hypothetical doc, gerçek dökümanlarla daha iyi align olur\")\nprint(\"   - Query-document asymmetry problemi azalır\")",
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": "# HyDESearchEngine: Hypothetical Document Embeddings (LiteLLM)\n\nclass HyDESearchEngine(SemanticSearchEngine):\n    \"\"\"\n    HyDE: Generate hypothetical answer, then search with it\n    \n    Paper: \"Precise Zero-Shot Dense Retrieval without Relevance Labels\" (Gao et al., 2022)\n    \n    Why it works: Hypothetical doc is structurally similar to actual documents\n    \"\"\"\n    \n    def __init__(self, *args, generative_model=\"gemini/gemini-pro\", **kwargs):\n        super().__init__(*args, **kwargs)\n        self.generative_model = generative_model\n    \n    def generate_hypothetical_document(self, query: str) -> str:\n        \"\"\"Generate hypothetical document that would answer the query\"\"\"\n        hyde_prompt = f\"\"\"Generate a detailed, informative paragraph that would answer this question.\nWrite as if creating a document. Use technical terminology.\n\nQuestion: {query}\n\nHypothetical document:\"\"\"\n        \n        # Use LiteLLM completion\n        response = completion(\n            model=self.generative_model,\n            messages=[{\"role\": \"user\", \"content\": hyde_prompt}],\n            temperature=0.7,\n            max_tokens=200\n        )\n        \n        return response.choices[0].message.content\n    \n    def search_hyde(\n        self,\n        query: str,\n        top_k: int = 3\n    ) -> List[Dict]:\n        \"\"\"Search using HyDE strategy\"\"\"\n        print(f\"🔮 Generating hypothetical document for: {query}\")\n        \n        # Generate hypothetical document\n        hypothetical_doc = self.generate_hypothetical_document(query)\n        print(f\"✓ Hypothetical: {hypothetical_doc[:100]}...\\n\")\n        \n        # Embed hypothetical document\n        hyde_embedding = self._embed_texts([hypothetical_doc])[0]\n        \n        # Compute similarities\n        similarities = np.dot(self.document_embeddings, hyde_embedding)\n        \n        # Get top-K\n        top_k_indices = np.argsort(similarities)[::-1][:top_k]\n        \n        # Build results with comparison\n        query_emb = self._embed_texts([query])[0]\n        \n        results = []\n        for idx in top_k_indices:\n            hyde_score = float(similarities[idx])\n            standard_score = float(np.dot(self.document_embeddings[idx], query_emb))\n            \n            results.append({\n                'document': self.documents[idx],\n                'metadata': self.metadata[idx],\n                'hyde_score': hyde_score,\n                'standard_score': standard_score\n            })\n        \n        return results\n\nprint(\"✓ HyDESearchEngine class defined (using LiteLLM)\")",
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": "### 3.3 HyDE (Hypothetical Document Embeddings)\n\n**Core Idea** (Gao et al., 2022): Instead of embedding the query directly, first generate a hypothetical answer with LLM, then embed that.\n\n**Intuition:** Hypothetical answer aligns better with actual documents (reduces query-document asymmetry).\n\n**Flow:**\n```\nQuery: \"What is transfer learning?\" (short, question format)\n  ↓\nHypothetical Doc: \"Transfer learning is a technique where...\" (document-like)\n  ↓\nEmbed hypothetical → Better alignment with actual documents\n```\n\n**Mathematical:**\n1. Given query $q$, generate hypothetical document $d^* = \\text{LLM}(q)$\n2. Embed $d^*$ using RETRIEVAL_DOCUMENT task type\n3. Search with $\\text{embed}(d^*)$ instead of $\\text{embed}(q)$",
      "metadata": {}
    },
    {
      "cell_type": "markdown",
      "source": "# Reranking örneği\n\nreranked_engine = RerankedSearchEngine(\n    embedding_model=EMBEDDING_MODEL,\n    embedding_dim=EMBEDDING_DIM\n)\nreranked_engine.index_documents(documents, metadata)\n\nquery = \"How can I use pretrained models for my specific task?\"\n\nprint(f\"\\nQuery: {query}\\n\")\nprint(\"=\"*80)\n\n# Standard semantic search\nprint(\"\\n[A] Standard Semantic Search (embedding-only)\")\nprint(\"-\"*80)\nstandard_results = reranked_engine.search(query, top_k=3, return_scores=True)\nfor i, r in enumerate(standard_results, 1):\n    print(f\"{i}. [Score: {r['score']:.3f}] {r['document'][:60]}...\")\n\n# Reranked search\nprint(\"\\n[B] Two-Stage Search with Reranking\")\nprint(\"-\"*80)\nreranked_results = reranked_engine.search_with_reranking(query, initial_k=10, final_k=3)\nfor i, r in enumerate(reranked_results, 1):\n    emb_score = r.get('score', 0.0)\n    rerank_score = r.get('rerank_score', 0.0)\n    print(f\"{i}. [Rerank: {rerank_score:.3f} | Emb: {emb_score:.3f}] {r['document'][:50]}...\")\n\nprint(\"\\n\" + \"=\"*80)\nprint(\"\\n💡 Gözlem:\")\nprint(\"   - Reranking, LLM'in her dökümanı dikkatle değerlendirmesini sağlar\")\nprint(\"   - Embedding search hızlıdır ama bazen suboptimal\")\nprint(\"   - İki aşama birleşince en iyi sonucu elde ederiz\")",
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": "# RerankedSearchEngine: İki Aşamalı Retrieval (LiteLLM)\n\nclass RerankedSearchEngine(SemanticSearchEngine):\n    \"\"\"\n    Two-stage retrieval: Fast embedding search + LLM-based reranking\n    \n    Complexity:\n    - Stage 1: O(N * d) where N = corpus size\n    - Stage 2: O(K * T) where K = candidates, T = LLM latency\n    \"\"\"\n    \n    def __init__(self, *args, generative_model=\"gemini/gemini-pro\", **kwargs):\n        super().__init__(*args, **kwargs)\n        self.generative_model = generative_model\n        \n    def rerank(\n        self,\n        query: str,\n        candidates: List[Dict],\n        top_k: int = 5\n    ) -> List[Dict]:\n        \"\"\"\n        Rerank candidates using LLM\n        \n        Args:\n            query: Original search query\n            candidates: List of candidate documents from initial retrieval\n            top_k: Number of final results\n            \n        Returns:\n            Reranked list of documents\n        \"\"\"\n        reranked = []\n        \n        for candidate in candidates:\n            doc = candidate['document']\n            \n            # Prompt engineering: Ask LLM to judge relevance\n            relevance_prompt = f\"\"\"Rate the relevance of this document to the query on a scale of 0-10.\nOnly respond with a single number.\n\nQuery: {query}\n\nDocument: {doc}\n\nRelevance score (0-10):\"\"\"\n            \n            try:\n                # Use LiteLLM completion\n                response = completion(\n                    model=self.generative_model,\n                    messages=[{\"role\": \"user\", \"content\": relevance_prompt}],\n                    temperature=0.0,  # Deterministic for scoring\n                    max_tokens=10\n                )\n                \n                # Parse score\n                score_text = response.choices[0].message.content.strip()\n                score = float(score_text)\n                \n                reranked.append({\n                    **candidate,\n                    'rerank_score': score / 10.0,  # Normalize to [0, 1]\n                })\n            except Exception as e:\n                # Fallback: use original embedding score\n                reranked.append({\n                    **candidate,\n                    'rerank_score': candidate.get('score', 0.0)\n                })\n        \n        # Sort by rerank score\n        reranked.sort(key=lambda x: x['rerank_score'], reverse=True)\n        \n        return reranked[:top_k]\n    \n    def search_with_reranking(\n        self,\n        query: str,\n        initial_k: int = 10,\n        final_k: int = 3\n    ) -> List[Dict]:\n        \"\"\"Two-stage search pipeline\"\"\"\n        # Stage 1: Fast embedding-based retrieval\n        candidates = self.search(query, top_k=initial_k, return_scores=True)\n        \n        print(f\"✓ Stage 1: Retrieved {len(candidates)} candidates\")\n        \n        # Stage 2: LLM-based reranking\n        print(f\"✓ Stage 2: Reranking to top-{final_k}...\")\n        reranked = self.rerank(query, candidates, top_k=final_k)\n        \n        return reranked\n\nprint(\"✓ RerankedSearchEngine class defined (using LiteLLM)\")",
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": "### 3.2 Two-Stage Retrieval with Reranking\n\n**Problem:** Embedding-based search is fast but sometimes suboptimal for complex queries.\n\n**Solution:** Two-stage approach\n1. **Stage 1 - Fast Retrieval**: Embedding search retrieves many candidates (top-20)\n2. **Stage 2 - Reranking**: LLM evaluates each candidate carefully (top-5)\n\n**Mathematical Formulation:**\n\nInitial retrieval: $D_{cand} = \\text{TopK}(\\text{sim}(\\phi_q(q), \\phi_d(D)), K=20)$\n\nReranking: $D_{final} = \\text{TopK}(\\text{score}_{rerank}(q, D_{cand}), K=5)$\n\n**Trade-off:** Speed (stage 1) + Accuracy (stage 2)",
      "metadata": {}
    },
    {
      "cell_type": "markdown",
      "source": "# Semantik arama motoru oluştur ve dökümanları indexle\n\nsearch_engine = SemanticSearchEngine(\n    embedding_model=EMBEDDING_MODEL,\n    embedding_dim=EMBEDDING_DIM\n)\n\n# Index documents\nsearch_engine.index_documents(documents, metadata)\n\n# Test query\nquery = \"How do neural networks learn?\"\n\nprint(f\"\\n{'='*70}\")\nprint(f\"Query: {query}\")\nprint('='*70)\n\nresults = search_engine.search(query, top_k=3)\n\nfor i, result in enumerate(results, 1):\n    print(f\"\\n[{i}] Score: {result['score']:.4f}\")\n    print(f\"    Document: {result['document']}\")\n    print(f\"    Category: {result['metadata']['category']}\")\n\nprint(f\"\\n{'='*70}\")\nprint(\"\\n💡 Gözlem:\")\nprint(\"   - Query'de 'neural networks' kelimesi var\")\nprint(\"   - En yüksek skorlu döküman 'Deep Learning' hakkında\")\nprint(\"   - 'Machine Learning' ikinci sırada (daha genel)\")\nprint(\"   - Bu semantic matching'in gücünü gösterir!\")",
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": "# Örnek veri seti: AI/ML konularında dökümanlar\n\ndocuments = [\n    \"Machine learning is a subset of artificial intelligence focused on learning from data.\",\n    \"Deep learning uses neural networks with multiple layers to learn hierarchical representations.\",\n    \"Natural language processing enables computers to understand and generate human language.\",\n    \"Computer vision allows machines to interpret and understand visual information from images.\",\n    \"Reinforcement learning trains agents to make decisions through trial and error.\",\n    \"Transfer learning leverages pre-trained models to solve new related tasks efficiently.\",\n    \"Generative AI creates new content like text, images, and music using learned patterns.\",\n    \"Large language models like GPT and Gemini can understand and generate human-like text.\",\n    \"Embeddings represent words or documents as dense vectors in continuous space.\",\n    \"Retrieval-augmented generation combines information retrieval with text generation.\",\n]\n\n# Metadata ekle\nmetadata = [\n    {\"category\": \"ML Basics\", \"difficulty\": \"beginner\"},\n    {\"category\": \"Deep Learning\", \"difficulty\": \"intermediate\"},\n    {\"category\": \"NLP\", \"difficulty\": \"intermediate\"},\n    {\"category\": \"Computer Vision\", \"difficulty\": \"intermediate\"},\n    {\"category\": \"RL\", \"difficulty\": \"advanced\"},\n    {\"category\": \"Transfer Learning\", \"difficulty\": \"intermediate\"},\n    {\"category\": \"Generative AI\", \"difficulty\": \"advanced\"},\n    {\"category\": \"LLMs\", \"difficulty\": \"advanced\"},\n    {\"category\": \"Embeddings\", \"difficulty\": \"intermediate\"},\n    {\"category\": \"RAG\", \"difficulty\": \"advanced\"},\n]\n\nprint(f\"✓ Dataset: {len(documents)} documents\")\nprint(f\"✓ Categories: {set(m['category'] for m in metadata)}\")",
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": "# SemanticSearchEngine: Production-Ready Arama Motoru (LiteLLM)\n\nclass SemanticSearchEngine:\n    \"\"\"\n    Production-ready semantic search engine using LiteLLM\n    \n    Rigor Principles:\n    1. Separation of concerns: indexing vs query\n    2. Explicit state management\n    3. Type hints for clarity\n    4. Comprehensive error handling\n    \"\"\"\n    \n    def __init__(\n        self, \n        embedding_model: str = \"gemini/text-embedding-004\",\n        embedding_dim: int = 768\n    ):\n        \"\"\"Initialize search engine\"\"\"\n        self.model = embedding_model\n        self.dim = embedding_dim\n        \n        # Index storage\n        self.documents: List[str] = []\n        self.document_embeddings: np.ndarray = None\n        self.metadata: List[Dict] = []\n        self.indexed_count = 0\n        \n    def _embed_texts(\n        self, \n        texts: List[str]\n    ) -> np.ndarray:\n        \"\"\"\n        Generate embeddings for a list of texts using LiteLLM\n        \n        Args:\n            texts: List of text strings\n            \n        Returns:\n            numpy array of shape (len(texts), self.dim)\n        \"\"\"\n        # Use LiteLLM embedding function\n        result = embedding(\n            model=self.model,\n            input=texts\n        )\n        \n        # Extract embeddings from LiteLLM response\n        embeddings_list = [item['embedding'] for item in result.data]\n        embeddings = np.array(embeddings_list)\n        \n        # Verification\n        assert embeddings.shape == (len(texts), self.dim), \\\n            f\"Unexpected embedding shape: {embeddings.shape}\"\n        \n        return embeddings\n    \n    def index_documents(\n        self, \n        documents: List[str],\n        metadata: List[Dict] = None\n    ) -> None:\n        \"\"\"\n        Index a collection of documents\n        \n        Complexity: O(N * d) where N = len(documents), d = embedding_dim\n        \"\"\"\n        print(f\"Indexing {len(documents)} documents...\")\n        \n        # Embed documents\n        embeddings = self._embed_texts(documents)\n        \n        # Store\n        self.documents = documents\n        self.document_embeddings = embeddings\n        self.metadata = metadata if metadata else [{}] * len(documents)\n        self.indexed_count = len(documents)\n        \n        print(f\"✓ Indexed {self.indexed_count} documents\")\n        print(f\"✓ Index size: {self.document_embeddings.nbytes / 1024:.2f} KB\")\n        \n    def search(\n        self, \n        query: str, \n        top_k: int = 5,\n        return_scores: bool = True\n    ) -> List[Dict]:\n        \"\"\"\n        Search indexed documents for query\n        \n        Algorithm:\n        1. Embed query using LiteLLM\n        2. Compute dot product with all document embeddings\n        3. Return top-K by similarity score\n        \n        Complexity: O(N * d) where N = indexed docs, d = embedding dim\n        \"\"\"\n        if self.document_embeddings is None:\n            raise ValueError(\"No documents indexed yet! Call index_documents() first\")\n        \n        # Embed query\n        query_embedding = self._embed_texts([query])[0]\n        \n        # Compute similarities (dot product for normalized embeddings)\n        similarities = np.dot(self.document_embeddings, query_embedding)\n        \n        # Get top-K indices\n        top_k_indices = np.argsort(similarities)[::-1][:top_k]\n        \n        # Build results\n        results = []\n        for idx in top_k_indices:\n            result = {\n                'document': self.documents[idx],\n                'metadata': self.metadata[idx],\n            }\n            if return_scores:\n                result['score'] = float(similarities[idx])\n            results.append(result)\n        \n        return results\n\nprint(\"✓ SemanticSearchEngine class defined (using LiteLLM)\")",
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": "# Kütüphane kurulumları (ilk çalıştırmada bir kez yapın)\n!pip install -q litellm numpy\n\nimport numpy as np\nfrom litellm import embedding, completion\nfrom typing import List, Dict\nimport os\n\n# API Key setup (hafta_08'den devam)\nGEMINI_API_KEY = os.getenv(\"GEMINI_API_KEY\")\nif not GEMINI_API_KEY:\n    from getpass import getpass\n    GEMINI_API_KEY = getpass(\"GEMINI_API_KEY girin: \")\n    os.environ[\"GEMINI_API_KEY\"] = GEMINI_API_KEY\n\n# Model ayarları (simplified defaults)\nEMBEDDING_MODEL = \"gemini/text-embedding-004\"  # LiteLLM format\nEMBEDDING_DIM = 768  # MRL ile optimize edilmiş boyut\nGENERATIVE_MODEL = \"gemini/gemini-pro\"  # Simple, reliable default\n\nprint(\"✓ Setup complete (using LiteLLM)\")\nprint(f\"✓ Embedding model: {EMBEDDING_MODEL}\")\nprint(f\"✓ Generative model: {GENERATIVE_MODEL}\")\nprint(f\"✓ Embedding dimensionality: {EMBEDDING_DIM}\")",
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": "---\n\n## 3. RAG (Retrieval-Augmented Generation): Production-Ready Implementation\n\nHafta 8'de embeddings'in temellerini gördük. Şimdi bu teknolojiyi kullanarak **production-ready** bir RAG sistemi inşa edeceğiz.\n\n### 3.1 Semantik Arama Motoru (Semantic Search Engine)\n\nRAG'ın temelinde **semantik arama** yatar. Geleneksel arama (keyword matching) yerine, **anlam benzerliğine** dayalı arama yaparız.\n\n**Mimari:**\n1. **Offline (İndeksleme):** Dökümanları embedding'e çevir → Vektör DB'ye kaydet\n2. **Online (Arama):** Sorguyu embedding'e çevir → En benzer dökümanları bul\n\n**Neden Production-Ready?**\n- Separation of concerns: İndeksleme vs sorgulama\n- Type hints ve comprehensive error handling\n- Evaluation metrics dahil\n- Optimize edilmiş vektör operasyonları (NumPy)",
      "metadata": {}
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "---\n",
        "\n",
        "## 4. Function Calling (Fonksiyon Çağırma): LLM'lere Araç Kullanma Yeteneği Kazandırma\n",
        "\n",
        "Artık LLM'lerin kendi verilerimizle nasıl konuşacağını öğrendik (RAG). Ama ya LLM'in **aksiyonlar** almasını istersek? Örneğin:\n",
        "- Hava durumu bilgisini bir API'den çekmek\n",
        "- Veritabanında sorgu çalıştırmak\n",
        "- Hesaplama yapmak\n",
        "- E-posta göndermek\n",
        "\n",
        "İşte **Function Calling** tam da bunu sağlar. LLM'e kullanabileceği fonksiyonları tanımlarsınız, model ne zaman hangi fonksiyonu hangi parametrelerle çağırması gerektiğine karar verir.\n",
        "\n",
        "### 4.1 Function Calling Nedir?\n",
        "\n",
        "**Function Calling**, LLM'in bir kullanıcı sorusuna cevap vermek için hangi fonksiyonu hangi parametrelerle çağırması gerektiğini **otomatik olarak belirlemesidir**.\n",
        "\n",
        "**Temel Akış:**\n",
        "\n",
        "```\n",
        "Kullanıcı: \"İstanbul'da hava nasıl?\"\n",
        "    ↓\n",
        "LLM: \"Bu soruyu cevaplayabilmek için get_weather() fonksiyonunu\n",
        "      {city: 'Istanbul'} parametresiyle çağırmalıyım\"\n",
        "    ↓\n",
        "Sistem: get_weather('Istanbul') → \"20°C, açık hava\"\n",
        "    ↓\n",
        "LLM: \"İstanbul'da hava 20 derece ve açık.\"\n",
        "```\n",
        "\n",
        "### 4.2 Google AI Studio'da Function Calling - Adım Adım Rehber\n",
        "\n",
        "**ÖNEMLİ:** Bu eğitimde **gerçek API'lere ihtiyaç yok.** Simüle edilmiş (mock) fonksiyonlar kullanacağız. Böylece API anahtarı olmadan hemen test edebilirsiniz.\n",
        "\n",
        "Google AI Studio, function calling'i test etmek için kullanışlı bir görsel arayüz sunar. Önce burada pratik yapalım, sonra koda geçeriz.\n",
        "\n",
        "#### 📍 Adım 1: Google AI Studio'ya Erişim\n",
        "\n",
        "1. **Tarayıcınızda** şu adresi açın: https://aistudio.google.com/\n",
        "2. Google hesabınızla giriş yapın\n",
        "3. Sol menüden **\"Create new\"** → **\"Chat prompt\"** seçin\n",
        "\n",
        "#### 📍 Adım 2: Model Seçimi\n",
        "\n",
        "1. Sağ panelde **Model** olarak `gemini-2.0-flash-exp` veya `gemini-1.5-pro` seçin\n",
        "2. Bu modeller function calling destekler (ücretsiz.)\n",
        "\n",
        "#### 📍 Adım 3: Fonksiyon Tanımlama\n",
        "\n",
        "1. Sağ panelde **\"Tools\"** sekmesine tıklayın\n",
        "2. **\"Function declarations\"** altında **\"+\"** butonuna basın\n",
        "3. Aşağıdaki gibi bir fonksiyon tanımlayın:\n",
        "\n",
        "**Örnek Fonksiyon: get_current_weather**\n",
        "\n",
        "UI'da şu şekilde doldurun:\n",
        "\n",
        "- **Function name:** `get_current_weather`\n",
        "- **Description:** `Belirtilen şehrin güncel hava durumu bilgisini getir`\n",
        "- **Add parameter** butonuna basın:\n",
        "  - **Parameter name:** `location`\n",
        "  - **Type:** `STRING`\n",
        "  - **Description:** `Şehir adı, örneğin: Istanbul, Ankara, Izmir`\n",
        "  - **Required:** ✓ işaretleyin\n",
        "  \n",
        "- Tekrar **Add parameter:**\n",
        "  - **Parameter name:** `unit`\n",
        "  - **Type:** `STRING`\n",
        "  - **Description:** `Sıcaklık birimi: celsius veya fahrenheit`\n",
        "  - **Required:** Boş bırakın (optional)\n",
        "\n",
        "#### 📍 Adım 4: Test Etme\n",
        "\n",
        "1. Chat alanına şunu yazın: **\"İstanbul'da hava nasıl?\"**\n",
        "\n",
        "2. Model'in yanıtını gözlemleyin - Model otomatik olarak function call üretir:\n",
        "   ```\n",
        "   Function Call Request:\n",
        "   Function: get_current_weather\n",
        "   Arguments: {\n",
        "     \"location\": \"Istanbul\"\n",
        "   }\n",
        "   ```\n",
        "\n",
        "3. **\"Function response\"** alanına sahte (mock) bir cevap girin:\n",
        "   ```json\n",
        "   {\n",
        "     \"temperature\": 18,\n",
        "     \"condition\": \"Parçalı bulutlu\",\n",
        "     \"humidity\": 72,\n",
        "     \"wind_speed\": 15\n",
        "   }\n",
        "   ```\n",
        "\n",
        "4. **\"Continue\"** butonuna basın\n",
        "\n",
        "5. Model bu veriyle final cevabı üretir:\n",
        "   ```\n",
        "   \"İstanbul'da hava şu anda 18 derece ve parçalı bulutlu.\n",
        "    Nem oranı %72, rüzgar hızı 15 km/s.\"\n",
        "   ```\n",
        "\n",
        "** Ne Oldu?**\n",
        "- LLM, soruyu anlayıp hangi fonksiyonu çağırması gerektiğini **otomatik** belirledi\n",
        "- Doğru parametreyi (\"Istanbul\") **çıkardı**\n",
        "- Fonksiyon sonucunu kullanarak doğal dil cevabı üretti\n",
        "\n",
        "#### 📍 Adım 5: Çoklu Fonksiyon Testi\n",
        "\n",
        "Şimdi ikinci bir fonksiyon ekleyelim (**Tools** → **+** butonuna tekrar basın):\n",
        "\n",
        "**Örnek Fonksiyon: calculate**\n",
        "\n",
        "- **Function name:** `calculate`\n",
        "- **Description:** `Matematiksel hesaplama yapar`\n",
        "- **Parameter:**\n",
        "  - **name:** `expression`\n",
        "  - **type:** `STRING`\n",
        "  - **description:** `Hesaplanacak ifade, örn: \"25 * 3\", \"100 / 4\"`\n",
        "  - **required:** ✓\n",
        "\n",
        "**Test Sorusu:** \"İstanbul'da hava kaç derece? Eğer 5 derece daha artarsa ne olur?\"\n",
        "\n",
        "Model sırayla:\n",
        "1. `get_current_weather(\"Istanbul\")` çağırır  \n",
        "   → Mock response: `{\"temperature\": 18, ...}`\n",
        "2. `calculate(\"18 + 5\")` çağırır  \n",
        "   → Mock response: `{\"result\": 23}`\n",
        "3. Final cevap: \"İstanbul'da şu an 18 derece. 5 derece artarsa 23 derece olur.\"\n",
        "\n",
        "---\n",
        "\n",
        "### 4.3 Neden Function Calling Kullanıyoruz?\n",
        "\n",
        "| Özellik | Prompt İle | Function Calling İle |\n",
        "|:--------|:-----------|:--------------------|\n",
        "| **Doğruluk** | LLM tahmin eder, hata yapabilir | Gerçek fonksiyon çalışır, kesin sonuç |\n",
        "| **Güncel Veri** | LLM'in bilgisi sınırlı (knowledge cutoff) | Canlı API/veritabanından güncel veri |\n",
        "| **Yapılandırılmış Çıktı** | Parse gerekir | JSON formatında döner |\n",
        "| **Aksiyon Alma** | Sadece metin üretir | E-posta gönderebilir, DB güncelleyebilir |\n",
        "\n",
        "---\n",
        "\n",
        "### 4.4 RAG + Function Calling = Güçlü Kombinasyon.\n",
        "\n",
        "Şimdi en güçlü kombinasyonu görelim: **RAG ile function calling'i birleştirmek**\n",
        "\n",
        "**Senaryo:** Şirket dökümanlarında arama yapan bir asistan\n",
        "\n",
        "Google AI Studio'da üçüncü bir fonksiyon ekleyin:\n",
        "\n",
        "**Fonksiyon: search_company_docs**\n",
        "\n",
        "- **name:** `search_company_docs`\n",
        "- **description:** `Şirket dökümanlarında anahtar kelime araması yapar`\n",
        "- **parameter:**\n",
        "  - **name:** `query`\n",
        "  - **type:** `STRING`\n",
        "  - **description:** `Aranacak anahtar kelime veya soru`\n",
        "  - **required:** ✓\n",
        "\n",
        "**Test Sorusu:** \"PhotonDB'nin lisans fiyatı nedir ve İstanbul'da hava nasıl?\"\n",
        "\n",
        "Model şu adımları izler:\n",
        "1. `search_company_docs(\"PhotonDB lisans\")` → \"Yıllık abonelik\"\n",
        "2. `get_current_weather(\"Istanbul\")` → \"18°C\"\n",
        "3. Final cevap: Her iki soruyu da cevaplar.\n",
        "\n",
        "Bu, LLM'in **birden fazla bilgi kaynağını koordine etmesine** ve **kompleks görevleri** çözmesine olanak sağlar.\n",
        "\n",
        "---\n",
        "\n",
        "** Sonraki Hücrede:** Google AI Studio'da öğrendiğimiz bu mantığı Python koduyla uygulayacağız."
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### 4.5 Python ile Function Calling Implementasyonu\n",
        "\n",
        "Şimdi Google AI Studio'da öğrendiğimiz mantığı Python koduyla implement edelim. **LiteLLM** kullanarak Gemini API'sine bağlanacağız.\n",
        "\n",
        "**Adımlar:**\n",
        "1. Mock (simüle edilmiş) fonksiyonlar oluşturma\n",
        "2. Fonksiyonları JSON Schema formatında tanımlama\n",
        "3. LLM'e fonksiyonları bildirme\n",
        "4. Function call sonuçlarını işleme\n",
        "5. Final yanıt üretme"
      ],
      "metadata": {}
    },
    {
      "cell_type": "code",
      "source": [
        "!pip install -q litellm"
      ],
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# Gerekli kütüphaneleri yükleyelim\n",
        "# .pip install -q litellm\n",
        "\n",
        "import json\n",
        "import random\n",
        "from litellm import completion\n",
        "\n",
        "# ============================================================================\n",
        "# ADIM 1: Mock (Simüle) Fonksiyonlar Oluşturma\n",
        "# ============================================================================\n",
        "# Bu fonksiyonlar gerçek API çağrısı yapmaz, test verisi döndürür\n",
        "\n",
        "def get_current_weather(location, unit=\"celsius\"):\n",
        "    \"\"\"\n",
        "    Simüle edilmiş hava durumu fonksiyonu (Gerçek API gerekmez.)\n",
        "\n",
        "    Args:\n",
        "        location (str): Şehir adı\n",
        "        unit (str): Sıcaklık birimi (celsius/fahrenheit)\n",
        "\n",
        "    Returns:\n",
        "        dict: Hava durumu bilgisi\n",
        "    \"\"\"\n",
        "    # Simüle edilmiş hava durumu verileri\n",
        "    weather_db = {\n",
        "        \"istanbul\": {\"temp_c\": 18, \"condition\": \"Parçalı bulutlu\", \"humidity\": 72},\n",
        "        \"ankara\": {\"temp_c\": 12, \"condition\": \"Güneşli\", \"humidity\": 45},\n",
        "        \"izmir\": {\"temp_c\": 22, \"condition\": \"Açık\", \"humidity\": 68},\n",
        "        \"antalya\": {\"temp_c\": 25, \"condition\": \"Güneşli\", \"humidity\": 55}\n",
        "    }\n",
        "\n",
        "    location_key = location.lower().replace(\"ı\", \"i\")  # Türkçe karakter normalize\n",
        "    data = weather_db.get(location_key, {\"temp_c\": 20, \"condition\": \"Bilinmiyor\", \"humidity\": 50})\n",
        "\n",
        "    temp = data[\"temp_c\"]\n",
        "    if unit == \"fahrenheit\":\n",
        "        temp = (temp * 9/5) + 32\n",
        "\n",
        "    return {\n",
        "        \"location\": location,\n",
        "        \"temperature\": temp,\n",
        "        \"unit\": unit,\n",
        "        \"condition\": data[\"condition\"],\n",
        "        \"humidity\": data[\"humidity\"]\n",
        "    }\n",
        "\n",
        "\n",
        "def calculate(expression):\n",
        "    \"\"\"\n",
        "    Güvenli matematiksel hesaplama fonksiyonu\n",
        "\n",
        "    Args:\n",
        "        expression (str): Matematiksel ifade, örn: \"2+2\", \"15*7\"\n",
        "\n",
        "    Returns:\n",
        "        dict: Hesaplama sonucu\n",
        "    \"\"\"\n",
        "    try:\n",
        "        # Güvenlik: Sadece temel matematiksel operatörlere izin ver\n",
        "        allowed_chars = set(\"0123456789+-*/(). \")\n",
        "        if not all(c in allowed_chars for c in expression):\n",
        "            return {\"error\": \"Geçersiz karakterler tespit edildi\"}\n",
        "\n",
        "        result = eval(expression)\n",
        "        return {\n",
        "            \"expression\": expression,\n",
        "            \"result\": result\n",
        "        }\n",
        "    except Exception as e:\n",
        "        return {\"error\": f\"Hesaplama hatası: {str(e)}\"}\n",
        "\n",
        "\n",
        "def search_company_docs(query):\n",
        "    \"\"\"\n",
        "    Simüle edilmiş şirket dokümanı arama fonksiyonu\n",
        "\n",
        "    Args:\n",
        "        query (str): Arama sorgusu\n",
        "\n",
        "    Returns:\n",
        "        dict: Bulunan dokümanlar\n",
        "    \"\"\"\n",
        "    # Simüle edilmiş doküman veritabanı (hafta başında oluşturduğumuz RAG verisi)\n",
        "    docs_db = {\n",
        "        \"photondb\": {\n",
        "            \"title\": \"PhotonDB Lisanslama\",\n",
        "            \"content\": \"PhotonDB'nin lisanslama modeli, kullanıcı başına yıllık abonelik şeklindedir. Kurumsal müşteriler için özel paketler mevcuttur.\"\n",
        "        },\n",
        "        \"quantumleap\": {\n",
        "            \"title\": \"QuantumLeap Çip Özellikleri\",\n",
        "            \"content\": \"QuantumLeap, düşük güç tüketimi ile yüksek performans sunan yeni nesil yapay zeka çipidir. 'Yılın Teknolojisi' ödülünü almıştır.\"\n",
        "        },\n",
        "        \"destek\": {\n",
        "            \"title\": \"Destek Politikası\",\n",
        "            \"content\": \"Tüm ürünlerimiz için 7/24 e-posta desteği sunulmaktadır. Kurumsal müşterilerimize özel telefon hattımız mevcuttur.\"\n",
        "        }\n",
        "    }\n",
        "\n",
        "    query_lower = query.lower()\n",
        "    results = []\n",
        "\n",
        "    for key, doc in docs_db.items():\n",
        "        if key in query_lower or any(word in doc[\"content\"].lower() for word in query_lower.split()):\n",
        "            results.append(doc)\n",
        "\n",
        "    if not results:\n",
        "        results.append({\"title\": \"Sonuç bulunamadı\", \"content\": \"İlgili doküman bulunamadı.\"})\n",
        "\n",
        "    return {\"query\": query, \"results\": results}\n",
        "\n",
        "\n",
        "print(\"✅ Mock fonksiyonlar tanımlandı.\")\n",
        "print(\"   - get_current_weather()\")\n",
        "print(\"   - calculate()\")\n",
        "print(\"   - search_company_docs()\")"
      ],
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# ============================================================================\n",
        "# ADIM 2: Fonksiyonları JSON Schema Formatında Tanımlama\n",
        "# ============================================================================\n",
        "# LLM'e hangi fonksiyonların mevcut olduğunu söylemeliyiz\n",
        "\n",
        "tools = [\n",
        "    {\n",
        "        \"type\": \"function\",\n",
        "        \"function\": {\n",
        "            \"name\": \"get_current_weather\",\n",
        "            \"description\": \"Belirtilen şehrin güncel hava durumu bilgisini getirir\",\n",
        "            \"parameters\": {\n",
        "                \"type\": \"object\",\n",
        "                \"properties\": {\n",
        "                    \"location\": {\n",
        "                        \"type\": \"string\",\n",
        "                        \"description\": \"Şehir adı, örneğin: Istanbul, Ankara, Izmir\"\n",
        "                    },\n",
        "                    \"unit\": {\n",
        "                        \"type\": \"string\",\n",
        "                        \"enum\": [\"celsius\", \"fahrenheit\"],\n",
        "                        \"description\": \"Sıcaklık birimi\"\n",
        "                    }\n",
        "                },\n",
        "                \"required\": [\"location\"]\n",
        "            }\n",
        "        }\n",
        "    },\n",
        "    {\n",
        "        \"type\": \"function\",\n",
        "        \"function\": {\n",
        "            \"name\": \"calculate\",\n",
        "            \"description\": \"Matematiksel hesaplama yapar\",\n",
        "            \"parameters\": {\n",
        "                \"type\": \"object\",\n",
        "                \"properties\": {\n",
        "                    \"expression\": {\n",
        "                        \"type\": \"string\",\n",
        "                        \"description\": \"Hesaplanacak matematiksel ifade, örneğin: '25 * 3', '100 / 4'\"\n",
        "                    }\n",
        "                },\n",
        "                \"required\": [\"expression\"]\n",
        "            }\n",
        "        }\n",
        "    },\n",
        "    {\n",
        "        \"type\": \"function\",\n",
        "        \"function\": {\n",
        "            \"name\": \"search_company_docs\",\n",
        "            \"description\": \"Şirket dökümanlarında arama yapar\",\n",
        "            \"parameters\": {\n",
        "                \"type\": \"object\",\n",
        "                \"properties\": {\n",
        "                    \"query\": {\n",
        "                        \"type\": \"string\",\n",
        "                        \"description\": \"Aranacak anahtar kelime veya soru\"\n",
        "                    }\n",
        "                },\n",
        "                \"required\": [\"query\"]\n",
        "            }\n",
        "        }\n",
        "    }\n",
        "]\n",
        "\n",
        "print(\"✅ Fonksiyon tanımları (JSON Schema) hazır.\")\n",
        "print(f\"   Toplam {len(tools)} fonksiyon tanımlandı\")"
      ],
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# ============================================================================\n",
        "# ADIM 3-5: Function Calling Agent (Ana Fonksiyon)\n",
        "# ============================================================================\n",
        "\n",
        "import getpass\n",
        "\n",
        "def run_agent_with_functions(user_query, max_iterations=5):\n",
        "    \"\"\"\n",
        "    Function calling destekli LLM agent'ı\n",
        "\n",
        "    Args:\n",
        "        user_query (str): Kullanıcının sorusu\n",
        "        max_iterations (int): Maksimum fonksiyon çağrısı sayısı\n",
        "\n",
        "    Returns:\n",
        "        str: Final yanıt\n",
        "    \"\"\"\n",
        "    print(\"=\"*80)\n",
        "    print(f\"🤖 AGENT BAŞLADI\")\n",
        "    print(\"=\"*80)\n",
        "    print(f\"📥 Kullanıcı Sorusu: {user_query}\\n\")\n",
        "\n",
        "    # Konuşma geçmişi\n",
        "    messages = [\n",
        "        {\"role\": \"user\", \"content\": user_query}\n",
        "    ]\n",
        "\n",
        "    # Fonksiyon adlarını Python fonksiyonlarına map'le\n",
        "    available_functions = {\n",
        "        \"get_current_weather\": get_current_weather,\n",
        "        \"calculate\": calculate,\n",
        "        \"search_company_docs\": search_company_docs\n",
        "    }\n",
        "\n",
        "    iteration = 0\n",
        "\n",
        "    # Get the API key securely\n",
        "    api_key = getpass.getpass(\"Enter your Google API Key: \")\n",
        "\n",
        "    while iteration < max_iterations:\n",
        "        iteration += 1\n",
        "        print(f\"\\n{'─'*80}\")\n",
        "        print(f\"🔄 İTERASYON {iteration}\")\n",
        "        print(f\"{'─'*80}\")\n",
        "\n",
        "        try:\n",
        "            # LLM'i çağır (tools parametresiyle)\n",
        "            response = completion(\n",
        "                model=\"gemini/gemini-2.0-flash\",  # Gemini function calling destekler\n",
        "                messages=messages,\n",
        "                tools=tools,\n",
        "                temperature=0.1,  # Düşük temperature (deterministik davranış)\n",
        "                api_key=api_key # Pass the API key here\n",
        "            )\n",
        "\n",
        "            response_message = response.choices[0].message\n",
        "\n",
        "            # Function call var mı kontrol et\n",
        "            tool_calls = getattr(response_message, 'tool_calls', None)\n",
        "\n",
        "            if not tool_calls:\n",
        "                # Function call yok, final cevap\n",
        "                final_answer = response_message.content\n",
        "                print(f\"\\n✅ FINAL CEVAP:\\n{final_answer}\")\n",
        "                print(\"=\"*80)\n",
        "                return final_answer\n",
        "\n",
        "            # Function call varsa, çalıştır\n",
        "            print(f\"\\n🔧 Model {len(tool_calls)} fonksiyon çağırmak istiyor:\\n\")\n",
        "\n",
        "            # Mesaj geçmişine ekle\n",
        "            messages.append(response_message)\n",
        "\n",
        "            for tool_call in tool_calls:\n",
        "                function_name = tool_call.function.name\n",
        "                function_args = json.loads(tool_call.function.arguments)\n",
        "\n",
        "                print(f\"   📞 Fonksiyon: {function_name}()\")\n",
        "                print(f\"      Parametreler: {json.dumps(function_args, ensure_ascii=False, indent=10)}\")\n",
        "\n",
        "                # Fonksiyonu çalıştır\n",
        "                function_to_call = available_functions[function_name]\n",
        "                function_response = function_to_call(**function_args)\n",
        "\n",
        "                print(f\"      ✅ Sonuç: {json.dumps(function_response, ensure_ascii=False)}\\n\")\n",
        "\n",
        "                # Fonksiyon sonucunu mesaj geçmişine ekle\n",
        "                messages.append({\n",
        "                    \"role\": \"tool\",\n",
        "                    \"tool_call_id\": tool_call.id,\n",
        "                    \"name\": function_name,\n",
        "                    \"content\": json.dumps(function_response)\n",
        "                })\n",
        "\n",
        "        except Exception as e:\n",
        "            error_msg = f\"❌ Hata: {str(e)}\"\n",
        "            print(error_msg)\n",
        "            return error_msg\n",
        "\n",
        "    return \"⚠️ Maksimum iterasyon sayısına ulaşıldı.\""
      ],
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### 4.6 Function Calling Örnekleri\n",
        "\n",
        "Şimdi agent'ımızı farklı senaryolarla test edelim."
      ],
      "metadata": {}
    },
    {
      "cell_type": "code",
      "source": [
        "# ============================================================================\n",
        "# TEST 1: Basit Hava Durumu Sorg usu\n",
        "# ============================================================================\n",
        "\n",
        "run_agent_with_functions(\"İstanbul'da hava nasıl?\")"
      ],
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# ============================================================================\n",
        "# TEST 2: Çoklu Fonksiyon Kullanımı (Multi-Function)\n",
        "# ============================================================================\n",
        "\n",
        "run_agent_with_functions(\n",
        "    \"İstanbul'da hava kaç derece? Eğer 5 derece daha artarsa kaç olur?\"\n",
        ")"
      ],
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# ============================================================================\n",
        "# TEST 3: RAG + Function Calling Kombinasyonu\n",
        "# ============================================================================\n",
        "\n",
        "run_agent_with_functions(\n",
        "    \"PhotonDB'nin lisans modeli nedir ve İzmir'de hava nasıl?\"\n",
        ")"
      ],
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# ============================================================================\n",
        "# TEST 4: Karmaşık Hesaplama + Bağlam\n",
        "# ============================================================================\n",
        "\n",
        "run_agent_with_functions(\n",
        "    \"Ankara ve Antalya'daki sıcaklık farkı kaç derece? Celsius olarak ifade et\"\n",
        ")"
      ],
      "metadata": {},
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "---\n",
        "\n",
        "### 4.7 Function Calling: Anahtar Kavramlar ve Best Practices\n",
        "\n",
        "####  Function Calling'in Gücü\n",
        "\n",
        "Function calling, LLM'leri **salt metin üretici**'den **aksiyon alabilen agent**'lara dönüştürür:\n",
        "\n",
        "| Yetenek | Function Calling Olmadan | Function Calling İle |\n",
        "|:--------|:-------------------------|:---------------------|\n",
        "| **Hesaplama** | LLM tahmini yapar (hatalı olabilir) | Gerçek hesaplama fonksiyonu çalışır |\n",
        "| **Güncel Veri** | Knowledge cutoff sınırı var | Canlı API'lerden güncel veri çeker |\n",
        "| **Aksiyon** | Sadece metin önerir | E-posta gönderebilir, DB güncelleyebilir |\n",
        "| **Güvenilirlik** | Halüsinasyon riski yüksek | Fonksiyon sonucu garanti edilir |\n",
        "\n",
        "#### 📋 Best Practices\n",
        "\n",
        "**✅ Yapın:**\n",
        "1. **Fonksiyon açıklamalarını net yazın** - LLM, `description` alanına bakarak karar verir\n",
        "2. **Parametreleri detaylı tanımlayın** - Örnekler verin: `\"örn: Istanbul, Ankara\"`\n",
        "3. **Hata yönetimi ekleyin** - Fonksiyonlar `try-except` ile korunmalı\n",
        "4. **Required parametreleri belirtin** - Hangileri zorunlu, hangileri opsiyonel?\n",
        "5. **Mock fonksiyonlarla başlayın** - Gerçek API entegrasyonundan önce test edin\n",
        "\n",
        "**❌ Yapmayın:**\n",
        "1. **Belirsiz açıklamalar** - \"Bir şey yapar\" yerine \"İstanbul'daki sıcaklığı getirir\"\n",
        "2. **Çok fazla fonksiyon** - 10+ fonksiyon LLM'i karıştırır, gruplandırın\n",
        "3. **Güvensiz eval()** - Kullanıcı girdisini direkt çalıştırmayın\n",
        "4. **Sonsuz döngü riski** - `max_iterations` parametresi kullanın\n",
        "5. **API key'leri kod içinde** - Environment variable kullanın\n",
        "\n",
        "#### 🔄 Function Calling Akış Diyagramı\n",
        "\n",
        "```\n",
        "┌─────────────────────────────────────────────────────────────┐\n",
        "│  1. Kullanıcı Sorusu: \"İstanbul'da hava nasıl?\"            │\n",
        "└────────────────────┬────────────────────────────────────────┘\n",
        "                     │\n",
        "                     ▼\n",
        "┌─────────────────────────────────────────────────────────────┐\n",
        "│  2. LLM Analiz: \"Hava durumu bilgisi lazım\"                │\n",
        "│     → Tool seçimi: get_current_weather()                    │\n",
        "│     → Parametre çıkarma: {location: \"Istanbul\"}             │\n",
        "└────────────────────┬────────────────────────────────────────┘\n",
        "                     │\n",
        "                     ▼\n",
        "┌─────────────────────────────────────────────────────────────┐\n",
        "│  3. Sistem: Fonksiyonu Çalıştır                             │\n",
        "│     → Python: get_current_weather(\"Istanbul\")               │\n",
        "│     → Sonuç: {\"temp\": 18, \"condition\": \"Parçalı bulutlu\"}   │\n",
        "└────────────────────┬────────────────────────────────────────┘\n",
        "                     │\n",
        "                     ▼\n",
        "┌─────────────────────────────────────────────────────────────┐\n",
        "│  4. LLM Final Yanıt: Fonksiyon sonucunu kullan             │\n",
        "│     → \"İstanbul'da hava 18 derece ve parçalı bulutlu.\"     │\n",
        "└─────────────────────────────────────────────────────────────┘\n",
        "```\n",
        "\n",
        "####  Gerçek Dünya Kullanım Senaryoları\n",
        "\n",
        "1. **Müşteri Destek Botu**\n",
        "   - `search_knowledge_base()` - KB'de arama\n",
        "   - `create_ticket()` - Destek talebi oluştur\n",
        "   - `check_order_status()` - Sipariş durumu sorgula\n",
        "\n",
        "2. **Veri Analiz Asistanı**\n",
        "   - `query_database()` - SQL sorgusu çalıştır\n",
        "   - `generate_chart()` - Görselleştirme yap\n",
        "   - `calculate_stats()` - İstatistik hesapla\n",
        "\n",
        "3. **Otomasyon Agent'ı**\n",
        "   - `send_email()` - E-posta gönder\n",
        "   - `schedule_meeting()` - Toplantı planla\n",
        "   - `update_crm()` - CRM güncelle\n",
        "\n",
        "#### 🔗 RAG + Function Calling = Enterprise AI\n",
        "\n",
        "En güçlü kombinasyon:\n",
        "\n",
        "```python\n",
        "# Örnek: E-ticaret asistanı\n",
        "\n",
        "tools = [\n",
        "    search_product_catalog(),    # RAG ile ürün araması\n",
        "    check_inventory(),            # Canlı stok kontrolü\n",
        "    get_user_order_history(),    # Veritabanı sorgusu\n",
        "    apply_discount(),             # Hesaplama\n",
        "    create_order()                # Aksiyon\n",
        "]\n",
        "```\n",
        "\n",
        "**Kullanıcı:** \"Dün aldığım ayakkabıya uyan çorap var mı? Varsa %10 indirimli fiyatı ne olur?\"\n",
        "\n",
        "**Agent Adımları:**\n",
        "1. `get_user_order_history()` → Ayakkabı modelini öğren\n",
        "2. `search_product_catalog(\"ayakkabıya uyan çorap\")` → RAG ile eşleşen ürünler\n",
        "3. `check_inventory()` → Stok kontrolü\n",
        "4. `apply_discount(price, 10)` → İndirimli fiyat hesapla\n",
        "5. Final yanıt: \"Evet. Siyah spor çorap stokta. Normal fiyat 50 TL, %10 indirimli 45 TL.\"\n",
        "\n",
        "---\n",
        "\n",
        "### 4.8 Alıştırma: Kendi Fonksiyonunuzu Ekleyin.\n",
        "\n",
        "**Görev:** `convert_currency()` adında bir fonksiyon ekleyin.\n",
        "\n",
        "**Fonksiyon Detayları:**\n",
        "- **Adı:** `convert_currency`\n",
        "- **Açıklama:** \"Para birimi dönüşümü yapar\"\n",
        "- **Parametreler:**\n",
        "  - `amount` (number, required): Miktar\n",
        "  - `from_currency` (string, required): Kaynak para birimi (TRY, USD, EUR)\n",
        "  - `to_currency` (string, required): Hedef para birimi\n",
        "\n",
        "**İpucu:**\n",
        "1. Önce Python fonksiyonunu yazın (simüle edilmiş kurlar kullanın)\n",
        "2. Sonra `tools` listesine JSON schema ekleyin\n",
        "3. `available_functions` dict'ine ekleyin\n",
        "4. Test edin: \"100 TRY kaç USD eder?\"\n",
        "\n",
        "**Beklenen Çıktı:**\n",
        "```\n",
        "Model: convert_currency fonksiyonunu çağırıyor\n",
        "Parametreler: {amount: 100, from_currency: \"TRY\", to_currency: \"USD\"}\n",
        "Sonuç: {\"result\": 3.57, \"rate\": 0.0357}\n",
        "Final: \"100 TRY, 3.57 USD eder (kur: 0.0357)\"\n",
        "```\n",
        "\n",
        "---\n",
        "\n",
        "** Tebrikler.** Artık LLM'lere araç kullanma yeteneği kazandırmayı öğrendiniz. Bu, otonom agent'lar oluşturmanın temel taşıdır."
      ],
      "metadata": {}
    },
    {
      "cell_type": "markdown",
      "source": [
        "---\n",
        "\n",
        "## Hafta 9: Özet ve Anahtar Kavramlar\n",
        "\n",
        "Bu hafta, LLM'leri gerçek dünya uygulamaları için güçlendiren üç ana tekniği öğrendik:\n",
        "\n",
        "### 1️⃣ Fine-Tuning (İnce Ayar)\n",
        "\n",
        "**Ne İşe Yarar:** Modelin davranışını, stilini, terminolojisini değiştirmek\n",
        "\n",
        "**Ne Zaman Kullanılır:**\n",
        "- Özel bir kişilik kazandırmak (örn: resmi/gayri resmi ton)\n",
        "- Domain-specific terminoloji öğretmek (tıp, hukuk)\n",
        "- Spesifik çıktı formatı sağlamak\n",
        "\n",
        "**Dezavantajları:**\n",
        "- Maliyetli (veri + GPU)\n",
        "- Catastrophic forgetting riski\n",
        "- Yeni gerçekler öğretmek için verimsiz\n",
        "\n",
        "---\n",
        "\n",
        "### 2️⃣ RAG (Retrieval-Augmented Generation)\n",
        "\n",
        "**Ne İşe Yarar:** LLM'e harici bilgi tabanından \"kopya çekme\" yeteneği kazandırmak\n",
        "\n",
        "**Nasıl Çalışır:**\n",
        "1. **Offline:** Dökümanları embedding'e çevir → Vektör DB'ye kaydet\n",
        "2. **Online:** Kullanıcı sorusu → Benzer dökümanları bul → LLM'e prompt olarak ver\n",
        "\n",
        "**Avantajları:**\n",
        "- Güncel bilgi (knowledge cutoff yok.)\n",
        "- Güvenilir (kaynak gösterilebilir)\n",
        "- Kolay güncelleme (yeni doküman ekle, model yeniden eğitilmez)\n",
        "\n",
        "**Kullanım Alanları:**\n",
        "- Şirket KB chatbot'ları\n",
        "- Doküman Q&A sistemleri\n",
        "- Araştırma asistanları\n",
        "\n",
        "---\n",
        "\n",
        "### 3️⃣ Function Calling (Fonksiyon Çağırma)\n",
        "\n",
        "**Ne İşe Yarar:** LLM'e araçlar kullanma ve aksiyon alma yeteneği kazandırmak\n",
        "\n",
        "**Nasıl Çalışır:**\n",
        "1. Fonksiyonları JSON schema ile tanımla\n",
        "2. LLM, hangi fonksiyonu hangi parametrelerle çağıracağına karar verir\n",
        "3. Sistem fonksiyonu çalıştırır, sonucu LLM'e geri verir\n",
        "4. LLM, sonucu kullanarak final yanıtı üretir\n",
        "\n",
        "**Avantajları:**\n",
        "- Kesin sonuçlar (hesaplama hataları yok)\n",
        "- Canlı veri (API'ler, veritabanları)\n",
        "- Gerçek aksiyonlar (e-posta gönderme, sipariş oluşturma)\n",
        "\n",
        "**Kullanım Alanları:**\n",
        "- Müşteri destek otomasyonu\n",
        "- Veri analiz asistanları\n",
        "- İş süreçleri otomasyonu\n",
        "\n",
        "---\n",
        "\n",
        "### 🔗 Güçlü Kombinasyon: RAG + Function Calling\n",
        "\n",
        "En güçlü kombinasyon, ikisini birleştirmektir:\n",
        "\n",
        "```python\n",
        "# Örnek: Akıllı müşteri destek botu\n",
        "\n",
        "tools = [\n",
        "    search_knowledge_base(),    # RAG: KB'den ilgili makale bul\n",
        "    get_user_data(),            # Function: CRM'den müşteri bilgisi çek\n",
        "    check_order_status(),       # Function: Sipariş durumu sorgula\n",
        "    create_support_ticket(),    # Function: Destek talebi oluştur\n",
        "]\n",
        "```\n",
        "\n",
        "**Kullanıcı:** \"Siparişim nerede ve iade politikanız nedir?\"\n",
        "\n",
        "**Agent:**\n",
        "1. `check_order_status()` → \"Kargoda, yarın teslim\"\n",
        "2. `search_knowledge_base(\"iade politikası\")` → \"14 gün içinde iade\"\n",
        "3. Final yanıt: Her iki soruyu da cevaplar.\n",
        "\n",
        "---\n",
        "\n",
        "### 📊 Teknik Karşılaştırma\n",
        "\n",
        "| Özellik | Fine-Tuning | RAG | Function Calling |\n",
        "|:--------|:------------|:----|:-----------------|\n",
        "| **Amaç** | Davranış değiştirme | Bilgi genişletme | Aksiyon alma |\n",
        "| **Maliyet** | Yüksek | Orta | Düşük |\n",
        "| **Güncelleme** | Model yeniden eğit | Döküman ekle | Fonksiyon ekle |\n",
        "| **Güvenilirlik** | Orta (halüsinasyon olabilir) | Yüksek (kaynak gösterir) | Çok yüksek (fonksiyon sonucu) |\n",
        "| **Kullanım** | Stil/format/terminoloji | Bilgi sorguları | Hesaplama/API/aksiyon |\n",
        "\n",
        "---\n",
        "\n",
        "###  Hangi Tekniği Ne Zaman Kullanmalı?\n",
        "\n",
        "**Senaryo 1: \"Hukuk ofisimiz için dokümanları resmi dilde özetleyen asistan\"**\n",
        "→ **Fine-Tuning** (resmi dil için) + **RAG** (davalardan bilgi çekmek için)\n",
        "\n",
        "**Senaryo 2: \"Şirket politikalarını cevaplayan HR botu\"**\n",
        "→ **RAG** (politika dokümanlarından bilgi çekmek)\n",
        "\n",
        "**Senaryo 3: \"E-ticaret sipariş takip asistanı\"**\n",
        "→ **Function Calling** (sipariş durumu API'si) + **RAG** (SSS'ler için)\n",
        "\n",
        "**Senaryo 4: \"Veri analiz asistanı\"**\n",
        "→ **Function Calling** (veritabanı sorguları + hesaplamalar)\n",
        "\n",
        "---\n",
        "\n",
        "###  Sonraki Adımlar\n",
        "\n",
        "Artık LLM'leri özelleştirmenin temel tekniklerini öğrendiniz. **Hafta 10**'da şunları göreceğiz:\n",
        "\n",
        "1. **LangChain & LangGraph** - Agent framework'leri\n",
        "2. **ReAct Pattern** - Reasoning + Acting\n",
        "3. **Multi-Agent Systems** - Birden fazla agent'ın iş birliği\n",
        "4. **LLMOps** - Production'da LLM yönetimi (monitoring, versioning, cost optimization)\n",
        "5. **Prompt Versioning & Evaluation** - Prompt'ları nasıl test edip optimize ederiz\n",
        "\n",
        "** Pratik Yapın:**\n",
        "- Google AI Studio'da kendi fonksiyonlarınızı test edin\n",
        "- RAG sisteminize yeni dökümanlar ekleyin\n",
        "- Kendi use case'iniz için fonksiyon kombinasyonları deneyin\n",
        "\n",
        "---\n",
        "\n",
        "**🎓 Ek Kaynaklar:**\n",
        "\n",
        "- **Google AI Studio:** https://aistudio.google.com/\n",
        "- **LiteLLM Docs:** https://docs.litellm.ai/docs/completion/function_call\n",
        "- **OpenAI Function Calling Guide:** https://platform.openai.com/docs/guides/function-calling\n",
        "- **Anthropic Tool Use:** https://docs.anthropic.com/en/docs/build-with-claude/tool-use"
      ],
      "metadata": {}
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.8.0"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}