2025-12-08
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:
- Memory Sistemleri: Long-term knowledge graph’lar vs. conversation buffer’ları
- Planning Mekanizmaları: Task decomposition ve multi-step reasoning vs. single-turn response’lar
- Tool Orchestration: Dinamik tool seçimi ve composition vs. sabit API çağrıları
- Autonomy Seviyeleri: Self-directed execution vs. user-driven etkileşimler
- 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:
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:
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:
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 belirt
3. Mevcut tool'lara map'le
4. 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ı:
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 sistem
class 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:
| Mimari | LLM Call’ları | Avg Token’lar | Task Başına Cost |
|---|---|---|---|
| Chatbot | 2-3 | 1,000 | $0.002 |
| ReAct Agent | 5-8 | 8,000 | $0.016 |
| Plan-Execute Agent | 3-4 | 4,000 | $0.008 |
| Multi-Agent | 6-10 | 10,000 | $0.020 |
Cost’lar Claude Sonnet pricing’e göre: 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
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
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:
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
-
Mimari Evrim: Chatbot’lar ve agent’lar bir continuum üzerinde; task complexity, bütçe ve ekip expertise’ine göre seç
-
Pattern Seçimi Önemli: Dinamik adaptasyon için ReAct, yapılandırılmış task’lar için Plan-Execute, specialization için multi-agent
-
Memory Kritik: Long-term memory agent’ları chatbot’lardan ayırır: vector database’lere ve retrieval stratejilerine yatırım yap
-
Guardrail’ler Vazgeçilmez: Production sistemleri için input validation, tool authorization, output filtering ve human-in-the-loop implement et
-
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
-
Tool Design Prensipleri: Küçük, composable tool’lar monolithic’leri yener; test etmesi, debug etmesi ve yeniden kullanması daha kolay
-
Progressive Enhancement: Chatbot ile başla, ihtiyaçlar büyüdükçe incremental olarak agent yetenekleri ekle
-
Evaluation Temel: Completion rate, task başına token, latency ve user satisfaction track et; dataya göre iterate et
-
Error Recovery Kazanır: Fallback stratejileriyle intelligent retry logic production agent’larını prototype’lardan ayırır
-
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
SOLID prensiplerininin modern JavaScript geliştirmede nasıl uygulanacağını öğrenin. TypeScript, React hooks ve fonksiyonel pattern'ler ile pratik örnekler - ayrıca ne zaman kullanmalı, ne zaman gereksiz.
AWS Bedrock AgentCore'un agentic AI'ı ölçekte deploy etme altyapı zorluklarını nasıl çözdüğünü öğrenin - prototipten production'a runtime, memory, gateway ve multi-agent koordinasyonu ile.
AgentCore Runtime üzerinde minimal bir Strands agent'ı CDK ile deploy etme rehberi — parametrize stack, arm64 build, deploy ve invoke akışı, ve ilk çağrıdan önce gereken IAM ve Marketplace ön koşulları.
Zapier MCP'nin AI agent'lar için aksiyon bazlı beyaz liste, merkezi kimlik yönetimi ve insan onay mekanizması sunması. Özel proxy çözümlerine yönetilen bir alternatif.
AWS Cognito ve Verified Permissions ile SaaS yetkilendirme mimarisi. Cedar politika dili, çok kiracılı desenler, JWT token akışı, maliyet analizi ve TypeScript örnekleriyle yaygın hatalar.