Skip to main content

Command Palette

Search for a command to run...

Google Gemini ile Kendi Yapay Zeka Asistanınızı Kodlayın: Adım Adım Rehber

Published
6 min read

Google'ın en yeni yapay zeka modelleri (Gemini 3, Live API) ile çalışan gerçek bir "AI Agent" geliştirmek ister misiniz? Bu rehberde, DevFest Bursa için hazırlanan "Gemini Depo Asistanı" projesini baştan sona nasıl hayata geçireceğinizi anlatacağım.

Başlangıç şablonunda uygulamanın arayüzü ve temel mantığı (Kural Modu) halihazırda çalışmaktadır. Bizim görevimiz, boş bırakılan AI fonksiyonlarını doldurarak asistana "akıl" kazandırmaktır.

🛠️ Başlangıç

  1. 👉 Projeyi Kopyala ve Başla bağlantısına tıklayın.

  2. Sağ üstteki "Copy App" butonuna basın, isim verin ve "Save" diyerek projeyi kendi çalışma alanınıza alın.


📚 Konu Anlatımı: Yapay Zeka Ajanları Nasıl Çalışır?

Kodlamaya başlamadan önce, inşa edeceğimiz sistemin arkasındaki temel kavramları ve mimariyi anlayalım.

1. AI Agent (Yapay Zeka Ajanı) Nedir?

Sıradan bir chatbot sadece metin üretir ve pasiftir. Ancak bir AI Agent:

  • Karar Verir: Kullanıcının niyetini anlar.

  • Eylem Yapar: Veritabanına bağlanır, sipariş verir.

  • Araç (Tool) Kullanır: getStock gibi tanımladığımız fonksiyonları "elleri" gibi kullanır.

Bu projede Gemini'ı sadece sohbet eden bir bot olarak değil, depo veritabanına erişimi olan yetkili bir çalışan olarak kodlayacağız.

2. ReAct Mimarisi (Reasoning + Acting)

Ajanımız şu düşünce döngüsünü (Loop) izler:

  1. Thought (Düşünce): "Kullanıcı stok sordu, getStock aracını çağırmalıyım."

  2. Action (Eylem): Fonksiyonu çalıştırır.

  3. Observation (Gözlem): Veritabanından gelen "150 adet" bilgisini görür.

  4. Response (Yanıt): "Depoda 150 adet ürün var." diyerek kullanıcıya döner.

3. Mimari Notu: Client-Side vs. Server-Side

Bu atölyede hız ve prototipleme kolaylığı için Client-Side (SDK) kullanıyoruz.

  • ⚠️ Uyarı: Gerçek bir prodüksiyon uygulamasında API anahtarlarınızı asla tarayıcıda saklamamalısınız.

  • İdeal: Güvenli bir yapı için Firebase Genkit veya Node.js backend kullanarak Server-Side bir mimari kurmalısınız.


🟢 Adım 1: Araçları Tanımlama (aiService.ts)

AI Agent'ın dünyayla etkileşime geçebilmesi için ona "ellerini" yani araçlarını vermeliyiz.

services/aiService.ts dosyasında toolDeclarations dizisini ve initChatSession fonksiyonunu aşağıdaki gibi düzenleyin.

// Gerekli importlar: Type, GoogleGenAI

export const toolDeclarations = [
  {
    name: "getStock", // Fonksiyonun benzersiz adı
    description: "Depodaki ürünlerin stok durumunu sorgular. Ürün adı belirtilmezse tüm stok listelenir.", // Modelin ne zaman kullanacağını anlaması için açıklama
    parameters: {
      type: Type.OBJECT,
      properties: {
        productName: {
          type: Type.STRING,
          description: "Stok durumu öğrenilmek istenen ürünün adı (Örn: Tişört, Rozet). Boş bırakılırsa hepsi listelenir."
        }
      }
    }
  },
  {
    name: "addStock",
    description: "Depoya yeni ürün stoğu ekler.",
    parameters: {
      type: Type.OBJECT,
      properties: {
        productName: {
          type: Type.STRING,
          description: "Stoğu artırılacak ürünün adı."
        },
        quantity: {
          type: Type.NUMBER,
          description: "Eklenecek miktar (Sayısal değer)."
        }
      },
      required: ["productName", "quantity"] // Zorunlu parametreler
    }
  }
];

export const initChatSession = async () => {
  const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });

  return ai.chats.create({
    model: MODEL_NAME,
    config: {
      systemInstruction: SYSTEM_INSTRUCTION,
      tools: [{ functionDeclarations: toolDeclarations }] // Tanımladığımız araçları modele veriyoruz
    }
  });
};

🟡 Adım 2: Karar Mekanizması (agentService.ts)

Ajanın "beyni" burasıdır. Model bir fonksiyon çağırmaya karar verdiğinde bu isteği yakalayıp işlemeli ve sonucu ona geri bildirmeliyiz. Bu döngü, görev tamamlanana kadar devam eder.

services/agentService.ts dosyasında runAgentWorkflow fonksiyonunu düzenleyin.

// Gerekli importlar: Chat, InventoryDB, processAction vb.

export const runAgentWorkflow = async (
  text: string,
  chat: Chat,
  db: InventoryDB,
  addMessage: (role: Message['role'], content: string, isTool?: boolean) => void,
  updateInventory: (key: InventoryKey, qty: number) => void
) => {
  try {
    // 1. Kullanıcının mesajını modele gönder
    let result = await chat.sendMessage({ message: text });

    // 2. ReAct (Function Calling) Döngüsü
    while (result.functionCalls && result.functionCalls.length > 0) {
      const responseParts = [];

      for (const call of result.functionCalls) {
        // ACTION: İlgili aksiyonu gerçek veritabanında çalıştır
        const actionResult = await processAction("AI", call.name, call.args, db, updateInventory, addMessage);

        // OBSERVATION: Sonucu modele geri bildirmek için hazırla
        responseParts.push({
          functionResponse: {
            name: call.name,
            id: call.id,
            response: { result: actionResult }
          }
        });
      }

      // REASONING: İşlem sonucunu modele gönder ve yeni yanıtı al
      result = await chat.sendMessage({ message: responseParts });
    }

    // 4. Nihai Metin Cevabını Ekrana Yaz
    if (result.text) {
      addMessage('bot', result.text);
    }

  } catch (e) {
    console.error("AI Workflow Error:", e);
    addMessage('bot', "⚠️ AI işlemi sırasında bir hata oluştu.");
  }
};

🟠 Adım 3: Canlı Ses Bağlantısı (liveService.ts)

Gemini Live API, metin tabanlı sohbetin ötesine geçerek WebSocket üzerinden ses akışı sağlar.

  • Düşük Gecikme (Low Latency): Gerçek bir insanla konuşur gibi hızlı tepkiler.

  • Kesintisiz Akış: Ses verisi PCM formatında anlık olarak akar.

services/liveService.ts dosyasında WebSocket bağlantısını kuran startLiveSession ve gelen mesajları işleyen handleServerMessage fonksiyonlarını doldurun.

// Session başlatma ve Audio Context kurulumu
async function startLiveSession(addMessage: any) {
  const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
  // Tarayıcının ses motorunu (AudioContext) başlat
  outputAudioContext = new (window.AudioContext || (window as any).webkitAudioContext)({ sampleRate: 24000 });

  if (outputAudioContext.state === 'suspended') {
    await outputAudioContext.resume();
  }

  // Live API'ye bağlan (WebSocket)
  session = await ai.live.connect({
    model: LIVE_MODEL_NAME,
    config: {
      responseModalities: [Modality.AUDIO], // Sesli cevap istiyoruz
      speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: 'Kore' } } },
      systemInstruction: LIVE_SYSTEM_INSTRUCTION,
      tools: [{ functionDeclarations: toolDeclarations }] // Live modda da stok sorgulayabilmeli!
    },
    callbacks: {
      onopen: () => console.log("WebSocket Açıldı"),
      onmessage: (msg: LiveServerMessage) => handleServerMessage(msg, addMessage), // Mesaj gelince işle
      onclose: () => console.log("WebSocket Kapandı"),
      onerror: (err) => console.error("WebSocket Hatası:", err)
    }
  });

  // Mikrofon akışını başlat ve PCM formatında modele gönder
  audioCleanup = await setupAudioInput((base64PCM) => {
    if(session) {
      session.sendRealtimeInput({
        media: { mimeType: "audio/pcm;rate=16000", data: base64PCM }
      });
    }
  });
}

// Sunucudan gelen mesajları işleme (Ses veya Tool Call)
async function handleServerMessage(msg: LiveServerMessage, addMessage: any) {
  const { serverContent, toolCall } = msg;

  // 1. Modülden SES verisi geldiyse oynat
  if (serverContent?.modelTurn?.parts?.[0]?.inlineData?.data) {
    const base64Audio = serverContent.modelTurn.parts[0].inlineData.data;
    await playAudioChunk(base64Audio);
  }

  // 2. Modülden Tool (Fonksiyon) çağrısı geldiyse çalıştır
  if (toolCall && currentDb && currentUpdateFn) {
    for (const fc of toolCall.functionCalls) {
      const result = await processAction("AI", fc.name, fc.args, currentDb, currentUpdateFn, addMessage);
      if (session) {
        // Sonucu WebSocket üzerinden anında geri gönder
        session.sendToolResponse({
          functionResponses: [{
            id: fc.id,
            name: fc.name,
            response: { result: result }
          }]
        });
      }
    }
  }
}

🔴 Adım 4: Görsel Analiz ve Üretim (imageService.ts)

Burada iki farklı model bir "Pipeline" (Boru Hattı) mantığıyla çalışır: "Gör -> Anla -> Çiz".

  1. Vision Model: Resmi analiz eder ve bir "Prompt" çıkarır.

  2. Generation Model (Imagen): Bu prompt'u kullanarak yeni bir görsel yaratır.

services/imageService.ts dosyasında runImageAnalysis fonksiyonunu ekleyin.

export const runImageAnalysis = async (
  base64Image: string,
  userPrompt: string,
  _chat: any,
  addMessage: (role: Message['role'], content: string, isTool?: boolean, image?: string) => void
) => {
  try {
    const mimeType = base64Image.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/)?.[1] || "image/jpeg";
    const cleanBase64 = base64Image.split(',')[1];
    const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });

    addMessage('bot', "📸 Fotoğrafın analiz ediliyor ve karakter tasarlanıyor...");

    // 1. VISION: Önce resme bakıp prompt çıkarıyoruz
    const visionResponse = await ai.models.generateContent({
      model: VISION_MODEL_NAME,
      contents: {
        parts: [
          { text: IMAGE_SYSTEM_INSTRUCTION_TEMPLATE + "\n\nKullanıcı Notu: " + userPrompt },
          { inlineData: { mimeType: mimeType, data: cleanBase64 } } // Görsel verisi
        ]
      }
    });

    const generatedPrompt = visionResponse.text;
    if (!generatedPrompt) throw new Error("Prompt oluşturulamadı.");
    addMessage('tool', `🎨 **Oluşturulan Prompt:**\n${generatedPrompt}`, true);

    // 2. GENERATION: Çıkarılan prompt ile yeni görsel üretiyoruz (Imagen)
    addMessage('bot', "🎨 Fırçalar hazırlanıyor, avatarın çiziliyor...");

    const imageResponse = await ai.models.generateContent({
      model: IMAGE_GENERATION_MODEL_NAME,
      contents: { parts: [{ text: generatedPrompt }] }, // Vision'dan gelen metni buraya veriyoruz
      config: { imageConfig: { aspectRatio: "1:1" } }
    });

    let generatedBase64 = null;
    let resultMimeType = "image/png";

    // Yanıttan görsel verisini ayıkla
    if (imageResponse.candidates?.[0]?.content?.parts) {
      for (const part of imageResponse.candidates[0].content.parts) {
        if (part.inlineData) {
          generatedBase64 = part.inlineData.data;
          if (part.inlineData.mimeType) resultMimeType = part.inlineData.mimeType;
          break;
        }
      }
    }

    if (generatedBase64) {
      const finalImageUrl = `data:${resultMimeType};base64,${generatedBase64}`;
      addMessage('bot', "İşte DevFest Bursa avatarın! 🚀", false, finalImageUrl);
    } else {
      addMessage('bot', "⚠️ Görsel üretilemedi.");
    }

  } catch (error: any) {
    console.error("Hata:", error);
    addMessage('bot', "⚠️ Avatar oluşturulurken bir hata oldu: " + error.message);
  }
};

🔗 Referanslar ve İleri Okuma

Projede kullanılan teknolojiler ve ileri seviye kaynaklar için:

13 views