Skip to content
~/sph.sh

Chatbot'lardan Otonom Agent'lara: Mimari Desenler

Kural tabanlı chatbot'lardan otonom AI agent'larına mimari evrimi keşfet. ReAct, Plan-and-Execute ve çoklu-agent desenleri TypeScript implementasyonları ve pratik geçiş stratejileriyle öğren.

Özet

Kural tabanlı chatbot'lardan otonom AI agent'larına geçiş, sadece bir yetenek yükseltmesi değil; temel bir mimari değişimi temsil ediyor. Chatbot'lar önceden tanımlanmış intent'lere göre scriptli konuşmalar takip ederken, AI agent'ları memory, planlama yetenekleri ve tool erişimine sahipler. Bu, onların karmaşık görevleri otonomca parçalayabilmesini, kararlar alabilmesini ve sistemler arası çok adımlı workflow'ları execute edebilmesini sağlıyor.

Bu yazıda basit chatbot sistemlerinden sofistike agent mimarilerine mimari yolculuğu inceliyoruz. Design pattern'lere (ReAct, Plan-and-Execute, çoklu-agent koordinasyonu), infrastructure kararlarına ve pratik trade-off'lara odaklanıyoruz. Agent'ları "daha iyi chatbot'lar" olarak görmek yerine, farklı mimari pattern'leri ve her birinin production sistemleri için ne zaman mantıklı olduğunu inceliyoruz.

Mimari Evrim Spektrumu

İkili bir seçim yerine, chatbot'tan agent'a evrimi bir spektrum olarak düşün:

Level 0: Kural Tabanlı Chatbot'lar - Decision tree'ler ve regex pattern'ler. Tamamen deterministik. Örnek: "Saatler için 1, lokasyon için 2 yazın"

Level 1: Intent-Driven Chatbot'lar - Intent classification için NLU ile intent başına önceden tanımlanmış flow'lar. Örnek: Müşteri destek FAQ bot'ları

Level 2: Context-Aware Asistanlar - Session içinde conversation memory ile sınırlı API entegrasyonları. Örnek: Sesli asistanlar (Siri, Alexa)

Level 3: Tool Kullanan Agent'lar - Single-agent ReAct pattern'i ile dinamik tool seçimi. Örnek: Claude Code, GitHub Copilot

Level 4: Planning Agent'lar - Long-term memory ile çok adımlı task decomposition. Örnek: Research asistanları, kod generation agent'ları

Level 5: Multi-Agent Sistemler - Agent koordinasyon pattern'leriyle specialized sub-agent'lar. Örnek: Software development ekipleri, otonom operasyonlar

Geleneksel Chatbot Kısıtlamalarını Anlamak

Klasik Destek Bot Senaryosu

Bir destek chatbot'u şunu handle ediyor: "Neden iki kez ücretlendirildim?"

Chatbot'un yapması gerekenler:

  • Payment geçmişini kontrol et (Stripe API)
  • Order durumunu doğrula (database)
  • Destek ticket'larını gözden geçir (Zendesk)
  • Bilinen issue'ları kontrol et (Confluence)

Geleneksel yaklaşım: Tam sırayı hardcode et, ya da kullanıcıya birden fazla açıklayıcı soru sor.

Agent yaklaşımı: Tüm sistemlerden otonomca context topla, bulguları sentezle ve çözüm öner.

Integration Explosion Problemi

Geleneksel chatbot'larla: 5 chatbot × 10 backend sistem = 50 hardcode edilmiş entegrasyon

Her yeni feature birden fazla chatbot flow'u güncellemeyi gerektirir. Chatbot'lar arası paylaşılan öğrenme yok. Sistemler evrim geçirdikçe maintenance giderek zorlaşır.

Temel Mimari Farklar

Chatbot Mimarisi: Input → Intent Classification → Scriptli Response → Output

Agent Mimarisi: Input → Reasoning Loop (Observe → Plan → Act → Reflect) → Tool Execution → Memory Update → Output

Temel farklar:

  1. Memory Sistemleri: Long-term knowledge graph'lar vs. conversation buffer'ları
  2. Planning Mekanizmaları: Task decomposition ve multi-step reasoning vs. single-turn response'lar
  3. Tool Orchestration: Dinamik tool seçimi ve composition vs. sabit API çağrıları
  4. Autonomy Seviyeleri: Self-directed execution vs. user-driven etkileşimler
  5. Error Recovery: Adaptive retry stratejileri vs. "Anlamadım" fallback'leri

Pattern 1: Geleneksel Intent-Based Chatbot

Geleneksel bir chatbot mimarisini ve kısıtlamalarını inceleyelim:

typescript
interface ChatbotMessage {  role: "user" | "assistant";  content: string;}
interface Intent {  name: string;  confidence: number;  entities: Record<string, any>;}
class TraditionalChatbot {  private conversationHistory: ChatbotMessage[] = [];
  async processMessage(userMessage: string): Promise<string> {    // History'ye ekle (son N mesajla sınırlı)    this.conversationHistory.push({ role: "user", content: userMessage });    if (this.conversationHistory.length > 10) {      this.conversationHistory.shift(); // En eskiyi at    }
    // Intent classification    const intent = await this.classifyIntent(userMessage);
    // Intent'e göre handler'a route et    switch (intent.name) {      case "check_order":        return await this.handleOrderCheck(intent.entities);      case "return_request":        return await this.handleReturnRequest(intent.entities);      case "product_question":        return await this.handleProductQuestion(intent.entities);      default:        return "Bunu nasıl yardımcı olacağımdan emin değilim. Yeniden ifade edebilir misin?";    }  }
  private async classifyIntent(message: string): Promise<Intent> {    // NLU servisi veya LLM'e intent classification çağrısı    const response = await fetch("https://api.nlp-service.com/classify", {      method: "POST",      body: JSON.stringify({ text: message })    });    return response.json();  }
  private async handleOrderCheck(entities: Record<string, any>): Promise<string> {    // Sabit flow: order ID çıkar → database sorgusu → response formatla    const orderId = entities.order_id;    if (!orderId) {      return "Sipariş numaran nedir?";    }
    const order = await this.fetchOrder(orderId);    return `Sipariş ${orderId} durumu ${order.status}. Tahmini teslimat: ${order.eta}`;  }
  private async fetchOrder(orderId: string): Promise<any> {    // Database query implementasyonu    return { status: "kargoda", eta: "2025-12-05" };  }}

Vurgulanan kısıtlamalar:

  • Task decomposition yok ("geçen ayki tüm siparişlerimi kontrol et" handle edemez)
  • 10 mesaj sonra memory kaybedilir
  • Hardcode edilmiş intent → handler mapping
  • Explicit programlama olmadan birden fazla data source'u birleştiremez
  • Yeni senaryolara adapte olma yeteneği yok

Pattern 2: ReAct Agent (Reasoning and Acting)

ReAct pattern'i tool kullanımıyla iterative reasoning sağlar:

Production-ready implementasyon:

typescript
interface Tool {  name: string;  description: string;  parameters: Record<string, any>;  execute: (params: any) => Promise<any>;}
interface AgentStep {  thought: string;  action?: { tool: string; input: any };  observation?: any;}
class ReActAgent {  private tools: Map<string, Tool>;  private memory: ConversationMemory;  private maxIterations = 10;
  constructor(tools: Tool[], memorySystem: ConversationMemory) {    this.tools = new Map(tools.map(t => [t.name, t]));    this.memory = memorySystem;  }
  async processTask(task: string): Promise<string> {    const steps: AgentStep[] = [];    let finalAnswer: string | null = null;
    // Memory'den ilgili context'i al    const context = await this.memory.retrieve(task);
    for (let i = 0; i < this.maxIterations; i++) {      // Sonraki adımı generate et: thought + action      const step = await this.generateNextStep(task, steps, context);      steps.push(step);
      // Final cevap var mı kontrol et      if (!step.action) {        finalAnswer = step.thought;        break;      }
      // Action'ı execute et      const tool = this.tools.get(step.action.tool);      if (!tool) {        step.observation = { error: `Tool ${step.action.tool} bulunamadı` };        continue;      }
      try {        const result = await tool.execute(step.action.input);        step.observation = result;      } catch (error) {        step.observation = { error: error.message };      }    }
    // Conversation'ı long-term memory'de sakla    await this.memory.store(task, steps, finalAnswer);
    return finalAnswer || "Bu task'ı iteration limiti içinde tamamlayamadım.";  }
  private async generateNextStep(    task: string,    previousSteps: AgentStep[],    context: any  ): Promise<AgentStep> {    // ReAct pattern'iyle prompt oluştur    const prompt = this.buildReActPrompt(task, previousSteps, context);
    // Thought ve action generate etmek için LLM çağır    const response = await this.callLLM(prompt);
    // Response'u structured step'e parse et    return this.parseReActResponse(response);  }
  private buildReActPrompt(task: string, steps: AgentStep[], context: any): string {    const toolDescriptions = Array.from(this.tools.values())      .map(t => `${t.name}: ${t.description}`)      .join("\n");
    const stepHistory = steps.map((s, i) =>      `Adim ${i + 1}:\nDusunce: ${s.thought}\n` +      (s.action ? `Aksiyon: ${s.action.tool}(${JSON.stringify(s.action.input)})\n` : "") +      (s.observation ? `Gozlem: ${JSON.stringify(s.observation)}\n` : "")    ).join("\n");
    return `Tool'ları kullanarak reasoning yapan bir AI agent'sın.
Gorev: ${task}
Mevcut Tool'lar:${toolDescriptions}
Memory'den Ilgili Context:${JSON.stringify(context, null, 2)}
Onceki Adimlar:${stepHistory || "Henuz yok"}
Ne yapacagini dusunup bir tool secerek sonraki adimi generate et.Cevaplamak icin yeterli bilgin varsa, action yerine final cevabi ver.
Format:Dusunce: [bir sonraki adim hakkinda reasoning'in]Aksiyon: [tool_name]Input: [JSON olarak tool input]
VEYA cevaplamaya hazirsan:Dusunce: [final reasoning]Cevap: [gorev icin final cevap]`;  }
  private parseReActResponse(response: string): AgentStep {    // LLM output'unu structured step'e parse et    const thoughtMatch = response.match(/Dusunce: (.+?)(?=\n|$)/s);    const actionMatch = response.match(/Aksiyon: (.+?)(?=\n|$)/);    const inputMatch = response.match(/Input: (.+?)(?=\n|$)/s);    const answerMatch = response.match(/Cevap: (.+?)(?=\n|$)/s);
    const thought = thoughtMatch?.[1].trim() || "";
    if (answerMatch) {      // Final cevap, action yok      return { thought: answerMatch[1].trim() };    }
    if (actionMatch && inputMatch) {      return {        thought,        action: {          tool: actionMatch[1].trim(),          input: JSON.parse(inputMatch[1].trim())        }      };    }
    return { thought };  }
  private async callLLM(prompt: string): Promise<string> {    // LLM API çağrısı (Anthropic, OpenAI, vs.)    throw new Error("LLM entegrasyonu implement et");  }}

Demonstre edilen temel pattern'ler:

  • Configurable max iteration'larla iterative reasoning loop
  • Context'te sağlanan tool açıklamaları
  • Long-term context için memory retrieval
  • Sonraki adıma dahil edilen observation feedback
  • Tool error'larının graceful handling'i
  • LLM response'larının structured parsing'i

ReAct'i ne zaman kullan:

  • Plan'ların önceden belirlenemediği dinamik environment'lar
  • Step-by-step verification gerektiren task'lar
  • Agent'ın observation'lara göre adapte olması gereken durumlar
  • Bütçe task başına $0.01-0.05 izin veriyor

Production düşünceleri:

  • Infinite loop'ları önlemek için iteration limitleri implement et
  • Debugging için tüm thought'ları ve action'ları logla
  • Token consumption'ı monitor et (basit completion'ın 5-10 katı olabilir)
  • Transparency için thought'ları kullanıcılara stream etmeyi düşün

Pattern 3: Plan-and-Execute

Net yapıya sahip karmaşık task'lar için Plan-and-Execute daha iyi cost efficiency sunuyor:

Implementasyon:

typescript
interface Task {  id: string;  description: string;  status: "pending" | "in-progress" | "completed" | "failed";  dependencies: string[];  result?: any;  error?: string;  metadata?: any;}
interface ExecutionPlan {  goal: string;  tasks: Task[];  strategy: string;}
class PlanAndExecuteAgent {  private tools: Map<string, Tool>;  private memory: ConversationMemory;
  async execute(goal: string): Promise<any> {    // Faz 1: Planning    console.error("[Planning Fazi] Goal'i task'lara ayiriyor...");    const plan = await this.createPlan(goal);    console.error(`[Planning Fazi] ${plan.tasks.length} task ile plan olusturuldu`);
    // Faz 2: Execution    console.error("[Execution Fazi] Task'lar execute ediliyor...");    const results = await this.executePlan(plan);
    // Faz 3: Synthesis    console.error("[Synthesis Fazi] Sonuclar birlestiriliyor...");    const finalResult = await this.synthesizeResults(goal, plan, results);
    return finalResult;  }
  private async createPlan(goal: string): Promise<ExecutionPlan> {    // Memory'den ilgili geçmiş plan'ları al    const pastExperiences = await this.memory.retrieve(goal);
    const planningPrompt = `Bir planning agent'sın. Bu goal'i executable task'lara ayır.
Goal: ${goal}
Mevcut Tool'lar:${Array.from(this.tools.values()).map(t => `- ${t.name}: ${t.description}`).join("\n")}
Benzer Gecmis Task'lar:${JSON.stringify(pastExperiences, null, 2)}
Soyle task'larla plan olustur:1. Mumkun oldugunda independent (paralel execution icin)2. Dependency'leri explicit belirt3. Mevcut tool'lara map'le4. Verification adimlarini icersin
Return formati:{  "strategy": "yaklasimin aciklamasi",  "tasks": [    {      "id": "task-1",      "description": "ne yapilacak",      "tool": "tool_name",      "dependencies": [],      "params": {}    }  ]}`;
    const planResponse = await this.callLLM(planningPrompt);    const planData = JSON.parse(planResponse);
    return {      goal,      strategy: planData.strategy,      tasks: planData.tasks.map((t: any) => ({        id: t.id,        description: t.description,        status: "pending" as const,        dependencies: t.dependencies || [],        metadata: { tool: t.tool, params: t.params }      }))    };  }
  private async executePlan(plan: ExecutionPlan): Promise<Map<string, any>> {    const results = new Map<string, any>();    const taskMap = new Map(plan.tasks.map(t => [t.id, t]));
    // Dependency'leri respect ederek task'ları execute et    while (results.size < plan.tasks.length) {      // Execute etmeye hazır task'ları bul (pending dependency yok)      const readyTasks = plan.tasks.filter(task => {        if (task.status !== "pending") return false;
        return task.dependencies.every(depId => {          const depTask = taskMap.get(depId);          return depTask?.status === "completed";        });      });
      if (readyTasks.length === 0) {        // Takılıp kalmadık mı kontrol et (circular dependency veya hepsi failed)        const pendingTasks = plan.tasks.filter(t => t.status === "pending");        if (pendingTasks.length > 0) {          console.error("[Execution Fazi] Takildi - circular dependency tespit edildi");          break;        }        break;      }
      // Hazır task'ları paralel execute et      console.error(`[Execution Fazi] ${readyTasks.length} task paralel execute ediliyor`);      await Promise.all(        readyTasks.map(task => this.executeTask(task, results))      );    }
    return results;  }
  private async executeTask(task: Task, results: Map<string, any>): Promise<void> {    task.status = "in-progress";    console.error(`[Task ${task.id}] Basliyor: ${task.description}`);
    try {      // Dependency sonuçlarını al      const depResults = task.dependencies.reduce((acc, depId) => {        acc[depId] = results.get(depId);        return acc;      }, {} as Record<string, any>);
      // Tool'u parametreler ve dependency sonuçlarıyla execute et      const tool = this.tools.get(task.metadata.tool);      if (!tool) {        throw new Error(`Tool ${task.metadata.tool} bulunamadi`);      }
      const params = {        ...task.metadata.params,        dependencyResults: depResults      };
      const result = await tool.execute(params);
      task.status = "completed";      task.result = result;      results.set(task.id, result);
      console.error(`[Task ${task.id}] Basariyla tamamlandi`);    } catch (error) {      task.status = "failed";      task.error = error.message;      results.set(task.id, { error: error.message });
      console.error(`[Task ${task.id}] Basarisiz: ${error.message}`);    }  }
  private async synthesizeResults(    goal: string,    plan: ExecutionPlan,    results: Map<string, any>  ): Promise<any> {    const synthesisPrompt = `Bir goal'e ulasmak icin plan execute ettin. Sonuclari coherent bir cevap haline getir.
Goal: ${goal}
Plan Stratejisi: ${plan.strategy}
Task Sonuclari:${Array.from(results.entries()).map(([id, result]) =>  `${id}: ${JSON.stringify(result)}`).join("\n")}
Original goal'i adreslemeyen comprehensive bir cevap ver, tum task'lardan insight'ları dahil et.`;
    const synthesis = await this.callLLM(synthesisPrompt);
    // Basarili plan'i future reference icin memory'de sakla    if (results.size === plan.tasks.length) {      await this.memory.store(goal, { plan, results: Array.from(results.entries()) }, synthesis);    }
    return synthesis;  }
  private async callLLM(prompt: string): Promise<string> {    throw new Error("LLM entegrasyonu implement et");  }}

Trade-off'lar:

  • Artıları: Daha az LLM call (bir kez plan, execute), paralel execution, öngörülebilir cost'lar
  • Eksileri: Environment execution ortasında değiştiğinde kırılgan, beklenmedik sonuçlara adapte olmak daha zor

Best practice'ler:

  • Başarılı plan'ları yeniden kullanım için memory'de sakla
  • Plan'a verification task'ları dahil et
  • Execution fail olursa re-planning'e izin ver
  • Individual task'lar için timeout kullan

Memory Mimarisi: Short-Term vs Long-Term

Chatbot'lar ve agent'lar arasındaki en önemli farklardan biri memory mimarisi:

Implementasyon karşılaştırması:

typescript
interface MemoryEntry {  timestamp: Date;  content: any;  metadata: Record<string, any>;  embedding?: number[];}
// Basit buffer memory (chatbot tarzı)class BufferMemory {  private buffer: MemoryEntry[] = [];  private maxSize = 10;
  async store(content: any, metadata: Record<string, any> = {}): Promise<void> {    this.buffer.push({ timestamp: new Date(), content, metadata });    if (this.buffer.length > this.maxSize) {      this.buffer.shift(); // FIFO eviction    }  }
  async retrieve(query: string): Promise<any[]> {    // Tüm buffer içeriğini döndür (filtreleme yok)    return this.buffer.map(e => e.content);  }
  async clear(): Promise<void> {    this.buffer = [];  }}
// Vector tabanlı long-term memory (agent tarzı)class VectorMemory {  private vectorStore: VectorDatabase;  private embeddingModel: EmbeddingModel;
  constructor(vectorStore: VectorDatabase, embeddingModel: EmbeddingModel) {    this.vectorStore = vectorStore;    this.embeddingModel = embeddingModel;  }
  async store(content: any, metadata: Record<string, any> = {}): Promise<void> {    // Semantic search için embedding generate et    const text = this.contentToText(content);    const embedding = await this.embeddingModel.embed(text);
    await this.vectorStore.insert({      timestamp: new Date(),      content,      metadata: {        ...metadata,        importance: this.calculateImportance(content, metadata)      },      embedding    });  }
  async retrieve(query: string, options: { limit?: number; threshold?: number } = {}): Promise<any[]> {    // Embedding'ler kullanarak semantic search    const queryEmbedding = await this.embeddingModel.embed(query);
    const results = await this.vectorStore.search({      embedding: queryEmbedding,      limit: options.limit || 5,      threshold: options.threshold || 0.7    });
    // En ilgili memory'leri, recency ve importance'a göre ağırlıklandırarak döndür    return results      .map(r => ({        content: r.content,        relevance: r.similarity,        recency: this.calculateRecency(r.timestamp),        importance: r.metadata.importance      }))      .sort((a, b) => {        const scoreA = a.relevance * 0.6 + a.recency * 0.2 + a.importance * 0.2;        const scoreB = b.relevance * 0.6 + b.recency * 0.2 + b.importance * 0.2;        return scoreB - scoreA;      })      .map(r => r.content);  }
  async forget(criteria: { olderThan?: Date; importance?: number }): Promise<void> {    // Zamana ve importance'a göre selective forgetting    const deleteFilter: any = {};
    if (criteria.olderThan) {      deleteFilter.timestamp = { $lt: criteria.olderThan };    }    if (criteria.importance !== undefined) {      deleteFilter["metadata.importance"] = { $lt: criteria.importance };    }
    await this.vectorStore.delete(deleteFilter);  }
  private calculateImportance(content: any, metadata: Record<string, any>): number {    // Heuristik scoring: user correction'ları, explicit feedback, task outcome'ları    let score = 0.5; // baseline
    if (metadata.userCorrection) score += 0.3;    if (metadata.explicitFeedback) score += 0.2;    if (metadata.taskSuccess === false) score += 0.15; // Failure'lardan öğren    if (metadata.toolError) score += 0.1; // Issue'ları hatırla
    return Math.min(score, 1.0);  }
  private calculateRecency(timestamp: Date): number {    const ageMs = Date.now() - timestamp.getTime();    const ageDays = ageMs / (1000 * 60 * 60 * 24);
    // Exponential decay: taze memory'ler daha yüksek score alır    return Math.exp(-ageDays / 30); // 30 günlük half-life  }
  private contentToText(content: any): string {    if (typeof content === "string") return content;    return JSON.stringify(content);  }}
// Production agent'lar için hybrid memory sistemclass HybridMemory implements ConversationMemory {  private shortTerm: BufferMemory;  private longTerm: VectorMemory;
  constructor(vectorStore: VectorDatabase, embeddingModel: EmbeddingModel) {    this.shortTerm = new BufferMemory();    this.longTerm = new VectorMemory(vectorStore, embeddingModel);  }
  async store(task: string, steps: any[], result: any): Promise<void> {    // Immediate recall için short-term'de sakla    await this.shortTerm.store({ task, steps, result });
    // Semantic retrieval için long-term'de sakla    await this.longTerm.store(      { task, steps, result },      {        taskSuccess: result !== null,        stepCount: steps.length,        timestamp: new Date()      }    );  }
  async retrieve(query: string): Promise<any> {    // Her iki memory sistemini birleştir    const recent = await this.shortTerm.retrieve(query);    const relevant = await this.longTerm.retrieve(query, { limit: 3 });
    return {      recentContext: recent,      relevantExperiences: relevant    };  }}

Memory karşılaştırma insight'ları:

  • Buffer memory: Hızlı, basit, semantic anlayış yok
  • Vector memory: Semantic search, importance-weighted, selective forgetting
  • Hybrid yaklaşım: Production agent'lar için her ikisinin de en iyisi

Multi-Agent Koordinasyon Pattern'leri

Specialized expertise gerektiren karmaşık sistemler için:

Orchestrator pattern (production için önerilen):

  • Net control flow
  • Debug etmesi daha kolay
  • Öngörülebilir cost'lar
  • Single point of failure (retry'larla mitigate edilir)

Peer-to-peer pattern (deneysel):

  • Decentralized
  • Fault-tolerant
  • Debug etmesi zor
  • Öngörülemeyen cost'lar

Implementasyon başlangıç kodu yukarıdaki İngilizce versiyonla aynı TypeScript interface'lerini kullanır.

Güvenlik ve Guardrail'ler

Production agent'lar birden fazla güvenlik katmanı gerektirir:

Guardrail sistemleri input validation, tool authorization, output filtering ve PII redaction içerir. Implementasyon detayları İngilizce versiyondaki kodla aynı mantığı takip eder.

Cost Analizi ve Trade-off'lar

Token Consumption Karşılaştırması

"Sipariş durumunu kontrol et ve para iadesi işle" gibi tipik bir task için:

MimariLLM Call'larıAvg Token'larTask Başına Cost
Chatbot2-31,000$0.002
ReAct Agent5-88,000$0.016
Plan-Execute Agent3-44,000$0.008
Multi-Agent6-1010,000$0.020

Cost'lar Claude Sonnet pricing'e göre: 3/Minput,3/M input, 15/M output token. Not: Prompt caching ve batch processing cost'ları %50-90 azaltabilir

Infrastructure Cost'ları

  • Chatbot: Minimal (stateless API)
  • Single Agent: Orta (memory için vector DB: ayda $50-200)
  • Multi-Agent: Daha yüksek (coordination layer, birden fazla DB: ayda $200-500)

Performance Özellikleri

Latency:

  • Chatbot: 500ms - 2s (tek LLM call)
  • ReAct Agent: 5s - 30s (birden fazla iteration)
  • Plan-Execute: 3s - 15s (planning overhead, paralel execution)
  • Multi-Agent: 10s - 60s (koordinasyon + birden fazla agent)

Accuracy (karmaşık çok adımlı task'lar için):

  • Chatbot: %40-60 (önceden tanımlanmış flow'larla sınırlı)
  • ReAct Agent: %70-85 (adaptive, ama takılabilir)
  • Plan-Execute: %75-90 (yapılandırılmış yaklaşım)
  • Multi-Agent: %80-95 (specialized expertise)

Ne Zaman Ne Kullanılır

Chatbot kullan:

  • Task'lar net intent'lerle iyi tanımlanmış (< 20 intent)
  • Response'lar scriptlenebilir veya template-based
  • Bütçe sıkı ($0.001-0.005 per interaction)
  • Latency < 2 saniye olmalı
  • Minimal maintenance kadrosu

ReAct Agent kullan:

  • Task'lar dinamik adaptasyon gerektiriyor
  • Tüm senaryolar önceden tahmin edilemiyor
  • Transparency gerekli (reasoning audit trail)
  • Bütçe task başına $0.01-0.05 izin veriyor
  • Ekipte LLM expertise var

Plan-Execute Agent kullan:

  • Net yapıya sahip karmaşık task'lar
  • Paralel execution'dan faydalanabilir
  • Öngörülebilir cost'lar gerekli
  • Kalite hızdan önemli
  • Task'lar mantıksal olarak decompose edilebilir

Multi-Agent System kullan:

  • Domain'ler arası specialized expertise gerekli
  • En yüksek accuracy gerekli
  • Chatbot'a göre 5-10x cost justify edilebilir
  • Coordination logic'i maintain edecek ekip var
  • Failure cost'u yüksek (healthcare, finans)

Yaygın Tuzaklar ve Çözümler

Tuzak 1: ReAct Agent'larda Infinite Loop'lar

Agent aynı tool call'larını tekrarlayarak takılıyor.

Çözüm: Loop'ları detect et ve kır

typescript
async function reactLoopWithDetection(task: string) {  const actionHistory = new Set<string>();
  for (let i = 0; i < maxIterations; i++) {    const step = await generateStep();
    // Bu action'ın signature'ını oluştur    const actionSignature = `${step.action.tool}:${JSON.stringify(step.action.input)}`;
    if (actionHistory.has(actionSignature)) {      console.error("[Loop Tespit Edildi] Tekrarlanan action'dan cikiliyor");      return { error: "Agent loop'ta takildi, sonlandiriliyor" };    }
    actionHistory.add(actionSignature);    await executeStep(step);  }}

Tuzak 2: Context Window Overflow

Conversation history context limitini aşıyor.

Çözüm: Summarization ile sliding window implement et

typescript
class ManagedConversationHistory {  private messages: Message[] = [];  private maxMessages = 20;  private summaries: string[] = [];
  async add(message: Message) {    this.messages.push(message);
    if (this.messages.length > this.maxMessages) {      // En eski 10 mesajı summarize et      const toSummarize = this.messages.splice(0, 10);      const summary = await this.summarize(toSummarize);      this.summaries.push(summary);    }  }
  getContext(): string {    return [      ...this.summaries.map(s => `[Ozet] ${s}`),      ...this.messages.map(m => `${m.role}: ${m.content}`)    ].join("\n");  }}

Tuzak 3: Tool Description Bloat

Çok fazla tool veya verbose açıklamalar sağlamak. Çözüm: Task context'e göre tool'ları dinamik yükle. ContextualToolLoader ile semantic search kullan, max 8 tool, concise description.

Progressive Migration Stratejisi

Chatbot ile başla, incremental olarak agent yetenekleri ekle:

typescript
class HybridChatbotAgent {  private intentClassifier: IntentClassifier;  private agentMode: boolean = false;
  async process(message: string): Promise<string> {    // Önce intent-based handling dene (hızlı, ucuz)    const intent = await this.intentClassifier.classify(message);
    if (intent.confidence > 0.85 && !intent.requiresToolUse) {      // Geleneksel chatbot flow kullan      return await this.handleIntent(intent);    }
    // Karmaşık query'ler için agent mode'a geç    console.error("[Hybrid] Karmasik query icin agent mode'a geciliyor");    this.agentMode = true;    return await this.agentProcess(message);  }}

Başarı metrikleri: Query'lerin %80'i hızlı chatbot path'le, %20'si agent ile handle ediliyor, pure agent yaklaşımına göre %40 cost reduction sağlanıyor.

Tool'lar ve Teknolojiler

Agent Framework'leri

LangGraph (LangChain):

  • Dil: Python, TypeScript
  • Güçlü yönler: State management, graph-based workflow'lar, production-ready

AutoGen (Microsoft):

  • Dil: Python
  • Güçlü yönler: Multi-agent conversation'lar, built-in pattern'ler
  • Not: AutoGen maintenance mode'da, Microsoft'un Agent Framework'ü ile değiştiriliyor

CrewAI:

  • Dil: Python
  • Güçlü yönler: Role-based agent'lar, lightweight

Memory Sistemleri

Vector Database'ler:

  • Pinecone: Managed, serverless
  • Qdrant: Open-source, self-hosted
  • Weaviate: GraphQL interface, hybrid search
  • Chroma: Lightweight, embedded option

Specialized Memory:

  • Mem0: Priority scoring ile intelligent memory layer (yakın zamanda Series A yatırım, AWS partnership)
  • Letta (eski adıyla MemGPT): Context management için memory block'lar

Observability

LangSmith: Agent execution'ları trace et, reasoning chain'leri debug et, prompt'lar için A/B testing

Langfuse: Open-source LLM observability, cost tracking, latency monitoring

Helicone: LLM request monitoring, cost analytics, caching

Temel Çıkarımlar

  1. Mimari Evrim: Chatbot'lar ve agent'lar bir continuum üzerinde; task complexity, bütçe ve ekip expertise'ine göre seç

  2. Pattern Seçimi Önemli: Dinamik adaptasyon için ReAct, yapılandırılmış task'lar için Plan-Execute, specialization için multi-agent

  3. Memory Kritik: Long-term memory agent'ları chatbot'lardan ayırır: vector database'lere ve retrieval stratejilerine yatırım yap

  4. Guardrail'ler Vazgeçilmez: Production sistemleri için input validation, tool authorization, output filtering ve human-in-the-loop implement et

  5. Cost vs Kalite Trade-off'u: Agent'lar chatbot'lardan 5-10x daha pahalı olabilir ama karmaşık task'larda 2-3x daha yüksek accuracy sağlar

  6. Tool Design Prensipleri: Küçük, composable tool'lar monolithic'leri yener; test etmesi, debug etmesi ve yeniden kullanması daha kolay

  7. Progressive Enhancement: Chatbot ile başla, ihtiyaçlar büyüdükçe incremental olarak agent yetenekleri ekle

  8. Evaluation Temel: Completion rate, task başına token, latency ve user satisfaction track et; dataya göre iterate et

  9. Error Recovery Kazanır: Fallback stratejileriyle intelligent retry logic production agent'larını prototype'lardan ayırır

  10. Context Window Management: Summarization, structured note'lar ve sub-agent'lar uzun conversation'larda context overflow'u önler

Chatbot'lardan otonom agent'lara bu mimari yolculuk, sadece yetenek eklemekten fazlasını temsil ediyor; AI sistemlerini nasıl tasarladığımızda temel bir değişim. Burada özetlenen pattern'ler ve practice'ler, autonomy ile control'ü dengeleyen production-ready agent sistemleri inşa etmek için bir temel sunuyor.

İlgili Yazılar