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ıç
👉 Projeyi Kopyala ve Başla bağlantısına tıklayın.
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:
Thought (Düşünce): "Kullanıcı stok sordu, getStock aracını çağırmalıyım."
Action (Eylem): Fonksiyonu çalıştırır.
Observation (Gözlem): Veritabanından gelen "150 adet" bilgisini görür.
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.
export const toolDeclarations = [
{
name: "getStock",
description: "Depodaki ürünlerin stok durumunu sorgular. Ürün adı belirtilmezse tüm stok listelenir.",
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"]
}
}
];
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 }]
}
});
};
🟡 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.
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 {
let result = await chat.sendMessage({ message: text });
while (result.functionCalls && result.functionCalls.length > 0) {
const responseParts = [];
for (const call of result.functionCalls) {
const actionResult = await processAction("AI", call.name, call.args, db, updateInventory, addMessage);
responseParts.push({
functionResponse: {
name: call.name,
id: call.id,
response: { result: actionResult }
}
});
}
result = await chat.sendMessage({ message: responseParts });
}
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.
services/liveService.ts dosyasında WebSocket bağlantısını kuran startLiveSession ve gelen mesajları işleyen handleServerMessage fonksiyonlarını doldurun.
async function startLiveSession(addMessage: any) {
const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
outputAudioContext = new (window.AudioContext || (window as any).webkitAudioContext)({ sampleRate: 24000 });
if (outputAudioContext.state === 'suspended') {
await outputAudioContext.resume();
}
session = await ai.live.connect({
model: LIVE_MODEL_NAME,
config: {
responseModalities: [Modality.AUDIO],
speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: 'Kore' } } },
systemInstruction: LIVE_SYSTEM_INSTRUCTION,
tools: [{ functionDeclarations: toolDeclarations }]
},
callbacks: {
onopen: () => console.log("WebSocket Açıldı"),
onmessage: (msg: LiveServerMessage) => handleServerMessage(msg, addMessage),
onclose: () => console.log("WebSocket Kapandı"),
onerror: (err) => console.error("WebSocket Hatası:", err)
}
});
audioCleanup = await setupAudioInput((base64PCM) => {
if(session) {
session.sendRealtimeInput({
media: { mimeType: "audio/pcm;rate=16000", data: base64PCM }
});
}
});
}
async function handleServerMessage(msg: LiveServerMessage, addMessage: any) {
const { serverContent, toolCall } = msg;
if (serverContent?.modelTurn?.parts?.[0]?.inlineData?.data) {
const base64Audio = serverContent.modelTurn.parts[0].inlineData.data;
await playAudioChunk(base64Audio);
}
if (toolCall && currentDb && currentUpdateFn) {
for (const fc of toolCall.functionCalls) {
const result = await processAction("AI", fc.name, fc.args, currentDb, currentUpdateFn, addMessage);
if (session) {
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".
Vision Model: Resmi analiz eder ve bir "Prompt" çıkarır.
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...");
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 } }
]
}
});
const generatedPrompt = visionResponse.text;
if (!generatedPrompt) throw new Error("Prompt oluşturulamadı.");
addMessage('tool', `🎨 **Oluşturulan Prompt:**\n${generatedPrompt}`, true);
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 }] },
config: { imageConfig: { aspectRatio: "1:1" } }
});
let generatedBase64 = null;
let resultMimeType = "image/png";
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: