Ajan Bellek Sistemleri: Yapay Zekanıza Kalıcı Bağlam Kazandırma
Her yapay zeka ajanının bir bellek sorunu var. Modele yapılan her çağrı durumsuz. Model son isteği hatırlamaz. Kullanıcının adını, tercihlerini veya önceki kararları hatırlamaz. Harici bellek olmadan, ajanınız her turda sıfırdan başlar.
Bu rehber, dörtten oluşan bellek stratejilerini — en basitinden en güçlüsüne kadar — ve her birini ne zaman kullanacağınızı ele alır. Tüm örnekler TypeScript ve Anthropic SDK kullanır.
Ajanlar Neden Belleğe İhtiyaç Duyar
Bir kod inceleme ajanı düşünün. İlk çalıştırmada, kullanıcı ekibinin kurallarını açıklar: any türleri yok, hataları her zaman açıkça işleyin, işlevsel stili tercih edin. Ajan iyi bir inceleme üretir.
İkinci çalıştırmada, kullanıcı başka bir dosya gönderir. Ajan her şeyi unuttu. Dün işaretlediği kural ihlallerini kaçırır.
Bu temel sorundur: ajanlar durumsuz, ancak yararlı ajanların sürekliliğe ihtiyacı var.
Bellek, ajanlara üç yetenek kazandırır:
- Geri çağırma — bir konuşmanın önceki kısmından veya önceki bir oturumdan gerçekleri alma
- Kişiselleştirme — zamanla kullanıcı tercihlerine uyum sağlama
- Koordinasyon — çok ajanlı sistemlerde, ajanlar arasında durum paylaşma
Dört Bellek Türü
Kod yazmadan önce stratejileri açıkça adlandırmak yardımcı olur. Ajan belleği dört desene ayrılır:
| Tür | Nerede saklanır | Kapsam | En iyisi |
|---|---|---|---|
| Tampon | Bağlamda (mesaj dizisi) | Mevcut oturum | Kısa konuşmalar |
| Özet | Bağlamda (sıkıştırılmış) | Mevcut oturum | Uzun konuşmalar |
| Anlamsal | Harici (vektör DB) | Oturumlar arası | Bilgi geri çağırma |
| Epizodik | Harici (anahtar-değer deposu) | Oturumlar arası | Kullanıcı gerçekleri, tercihler |
Her stratejinin basitlik, maliyet ve yetenek arasında farklı bir dengesi vardır. Probleminizi çözen minimumu seçin.
Strateji 1: Konuşma Tamponu
En basit strateji, tam konuşma geçmişini her model çağrısına iletmektir. Model her önceki mesajı görür ve tam bağlamla yanıt verir.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
interface Message { role: "user" | "assistant"; content: string;}
// Tampon tam konuşma geçmişini tutarconst buffer: Message[] = [];
async function chat(userMessage: string): Promise<string> { // Yeni kullanıcı mesajını tampona ekleyin buffer.push({ role: "user", content: userMessage });
const response = await client.messages.create({ model: "claude-sonnet-4-6", max_tokens: 1024, // Her çağrıda tam tamponu iletin messages: buffer, });
const assistantMessage = response.content[0].type === "text" ? response.content[0].text : "";
// Asistan yanıtını tampona ekleyin buffer.push({ role: "assistant", content: assistantMessage });
return assistantMessage;}
// Örnek kullanımawait chat("My name is Alex and I prefer Python over TypeScript.");await chat("What language should I use for my next project?");// Model ilk mesajda belirtilen tercihi hatırlarNe zaman kullanılır: 20 turdan az konuşmalar. Basit sohbet botları. Prototipler.
Sınır: Bağlam pencereleri sınırlıdır. Uzun bir oturum sonunda token limitini aşar ve en eski mesajların düşürülmesi gerekir. Model erken bağlamı sessizce kaybeder, bu da tutarsız davranışa yol açar.
Strateji 2: Kayan Özet
Konuşmalar uzadığında, eski turları atmak yerine özetleyin. Kayan bir son mesaj penceresinin öncesinde ne olduğunun sıkıştırılmış bir özetini tutun.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
interface ConversationState { summary: string; // Eski turların sıkıştırılmış geçmişi recentMessages: Array<{ role: "user" | "assistant"; content: string }>; maxRecentTurns: number; // Son N turu harfi harfine tutun}
const state: ConversationState = { summary: "", recentMessages: [], maxRecentTurns: 6, // 3 kullanıcı + 3 asistan turu};
// Eski turları süren özete sıkıştırınasync function compressSummary( currentSummary: string, messagesToCompress: Array<{ role: string; content: string }>): Promise<string> { const formatted = messagesToCompress .map((m) => `${m.role.toUpperCase()}: ${m.content}`) .join("\n");
const response = await client.messages.create({ model: "claude-haiku-4-5-20251001", max_tokens: 512, messages: [ { role: "user", content: `Update the conversation summary by adding the new exchanges below.Return only the updated summary. Keep it under 200 words.
Current summary:${currentSummary || "(none)"}
New exchanges:${formatted}`, }, ], });
return response.content[0].type === "text" ? response.content[0].text : currentSummary;}
async function chat(userMessage: string): Promise<string> { // Tampon limiti aştığında, en eski yarısını sıkıştırın if (state.recentMessages.length >= state.maxRecentTurns * 2) { const toCompress = state.recentMessages.splice(0, state.maxRecentTurns); state.summary = await compressSummary(state.summary, toCompress); }
state.recentMessages.push({ role: "user", content: userMessage });
// Mesaj dizisi oluşturun: sistem özeti + son harfi harfine turlar const messages = [ ...(state.summary ? [ { role: "user" as const, content: `Context from earlier in this conversation:\n${state.summary}`, }, { role: "assistant" as const, content: "Understood. I will use that context.", }, ] : []), ...state.recentMessages, ];
const response = await client.messages.create({ model: "claude-sonnet-4-6", max_tokens: 1024, messages, });
const assistantMessage = response.content[0].type === "text" ? response.content[0].text : "";
state.recentMessages.push({ role: "assistant", content: assistantMessage }); return assistantMessage;}Özet çağrısı hızlı, ucuz bir model (Haiku) kullanır. Ana konuşma yetenekli modeli (Sonnet) kullanır. Bu bölünme, kaliteyi korurken maliyetleri düşük tutar.
Ne zaman kullanılır: 20 turdan fazla oturumlar. Destek ajanları. Uzun süreli görev ajanları.
Sınır: Özetler detayları kaybeder. Bir konuşmanın başlarında belirtilen spesifik gerçekler belirsiz ifadelere sıkıştırılabilir. Spesifik gerçeklerin kesin geri çağrılması için epizodik bellek kullanın.
Strateji 3: Epizodik Bellek
Epizodik bellek, bir kullanıcı veya oturumla ilgili spesifik gerçekleri bir anahtar-değer deposunda saklar. Ajan bir konuşma sırasında gerçekleri çıkarır ve bunları gelecek oturumlarda alır.
import Anthropic from "@anthropic-ai/sdk";import fs from "fs/promises";import path from "path";
const client = new Anthropic();
// Üretimde, bu dosya deposunu Redis veya bir veritabanıyla değiştirinconst MEMORY_PATH = "./memory.json";
interface EpisodicStore { [userId: string]: Record<string, string>;}
async function loadMemory(): Promise<EpisodicStore> { try { const data = await fs.readFile(MEMORY_PATH, "utf-8"); return JSON.parse(data); } catch { return {}; }}
async function saveMemory(store: EpisodicStore): Promise<void> { await fs.writeFile(MEMORY_PATH, JSON.stringify(store, null, 2));}
// Son değişimden yapılandırılmış gerçekleri çıkarınasync function extractFacts( userMessage: string, assistantReply: string): Promise<Record<string, string>> { const response = await client.messages.create({ model: "claude-haiku-4-5-20251001", max_tokens: 256, messages: [ { role: "user", content: `Extract any personal facts, preferences, or important decisions from this exchange.Return a JSON object of key-value pairs. Return {} if there is nothing worth remembering.
User: ${userMessage}Assistant: ${assistantReply}`, }, ], });
try { const text = response.content[0].type === "text" ? response.content[0].text : "{}"; const match = text.match(/\{[\s\S]*\}/); return match ? JSON.parse(match[0]) : {}; } catch { return {}; }}
// Saklanan gerçekleri bir sistem istemi enjeksiyonu olarak biçimlendirinfunction formatMemory(facts: Record<string, string>): string { const entries = Object.entries(facts); if (entries.length === 0) return ""; return ( "What you know about this user:\n" + entries.map(([k, v]) => `- ${k}: ${v}`).join("\n") );}
async function chat(userId: string, userMessage: string): Promise<string> { const store = await loadMemory(); const userFacts = store[userId] ?? {};
const systemPrompt = formatMemory(userFacts);
const response = await client.messages.create({ model: "claude-sonnet-4-6", max_tokens: 1024, system: systemPrompt || undefined, messages: [{ role: "user", content: userMessage }], });
const assistantMessage = response.content[0].type === "text" ? response.content[0].text : "";
// Gerçekleri çıkarın ve gelecek oturumlar için kalıcı hale getirin const newFacts = await extractFacts(userMessage, assistantMessage); if (Object.keys(newFacts).length > 0) { store[userId] = { ...userFacts, ...newFacts }; await saveMemory(store); }
return assistantMessage;}
// Oturum 1await chat("user-123", "I prefer concise answers. No bullet points unless necessary.");// Oturum 2 (farklı bir süreç, günler sonra)await chat("user-123", "Explain how TCP handshakes work.");// Ajan oturum 1'deki biçimlendirme tercihini hatırlarNe zaman kullanılır: Oturumlar arasında çalışan kullanıcı odaklı ajanlar. Kişiselleştirme. Tercih takibi.
Sınır: Gerçekler zamanla birikir. Eski gerçekler yanlış davranışa neden olabilir (kullanıcı tercih ettikleri dili değiştirir; eski tercih hâlâ depoda). Gerçekleri güncellemek veya sona erdirmek için bir mekanizma ekleyin.
Strateji 4: Anlamsal Bellek (Vektör Arama)
Epizodik bellek ayrık gerçekleri saklar. Anlamsal bellek, anlama göre indekslenmiş belgeleri, kodları veya konuşma parçalarını saklar. Ajan bilgiye ihtiyaç duyduğunda, bir sorgu kullanarak indeksi arar.
Bu, Retrieval-Augmented Generation (RAG) temlidir.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
interface Document { id: string; content: string; embedding?: number[];}
// Bir metin dizesi için embedding vektörü oluşturunasync function embed(text: string): Promise<number[]> { // Claude doğrudan bir embeddings endpoint'i sunmaz. // Özel bir embedding modeli kullanın. Bu örnek gösterim için bir taslak kullanır. // Üretimde: text-embedding-3-small (OpenAI), embed-english-v3 (Cohere), // veya nomic-embed-text gibi kendi barındırılan bir model kullanın. throw new Error("Replace this stub with a real embedding call");}
// İki vektör arasındaki kosinüs benzerliğini hesaplayınfunction cosineSimilarity(a: number[], b: number[]): number { const dot = a.reduce((sum, val, i) => sum + val * b[i], 0); const magA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0)); const magB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0)); return dot / (magA * magB);}
class SemanticMemory { private documents: Document[] = [];
// Daha sonra almak için bir belge indeksleyin async add(id: string, content: string): Promise<void> { const embedding = await embed(content); this.documents.push({ id, content, embedding }); }
// Bir sorgu için en alakalı top-k belgeleri alın async search(query: string, topK = 3): Promise<Document[]> { const queryEmbedding = await embed(query); return this.documents .filter((doc) => doc.embedding) .map((doc) => ({ doc, score: cosineSimilarity(queryEmbedding, doc.embedding!), })) .sort((a, b) => b.score - a.score) .slice(0, topK) .map(({ doc }) => doc); }}
const memory = new SemanticMemory();
async function answerWithContext(question: string): Promise<string> { // İlgili belgeleri alın const relevant = await memory.search(question); const context = relevant.map((d) => d.content).join("\n\n---\n\n");
const response = await client.messages.create({ model: "claude-sonnet-4-6", max_tokens: 1024, system: context ? `Answer the question using the following context:\n\n${context}` : undefined, messages: [{ role: "user", content: question }], });
return response.content[0].type === "text" ? response.content[0].text : "";}
// Örnek: bir bilgi tabanı indeksleyin ve sorgulayınawait memory.add("doc-1", "The team uses React 18 with the app router pattern.");await memory.add("doc-2", "All API routes must validate input with Zod schemas.");await memory.add("doc-3", "Database queries use Drizzle ORM. Avoid raw SQL.");
const answer = await answerWithContext("How should I write a new API endpoint?");// Ajan doc-2 ve doc-3'ü alır ve gerekçeli bir yanıt üretirÜretim kullanımı için, bellekteki depoyu özel bir vektör veritabanıyla değiştirin. Pgvector (PostgreSQL uzantısı), zaten Postgres çalıştırıyorsanız en basit seçenektir. Chroma ve Qdrant iyi bağımsız seçeneklerdir.
Ne zaman kullanılır: Dokümantasyon asistanları. Kod arama ajanları. Bilgi tabanı Q&A. Büyük bir korpus genelinde geri çağırma gerektiren her şey.
Belleği MCP’ye Bağlama
MCP sunucu rehberini takip ettiyseniz, belleği bir MCP kaynağı olarak sunabilirsiniz. Bu, bellek katmanınızı Claude Code dahil herhangi bir MCP uyumlu istemci tarafından erişilebilir kılar.
import { Server } from "@modelcontextprotocol/sdk/server/index.js";import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server( { name: "memory-server", version: "1.0.0" }, { capabilities: { tools: {}, resources: {} } });
// Araç: bir gerçeği saklaserver.setRequestHandler("tools/call", async (request) => { if (request.params.name === "remember") { const { key, value, userId } = request.params.arguments as { key: string; value: string; userId: string; }; await storeEpisodicFact(userId, key, value); return { content: [{ type: "text", text: `Stored: ${key} = ${value}` }] }; }
if (request.params.name === "recall") { const { userId } = request.params.arguments as { userId: string }; const facts = await loadEpisodicFacts(userId); return { content: [{ type: "text", text: JSON.stringify(facts, null, 2) }], }; }});
const transport = new StdioServerTransport();await server.connect(transport);MCP konuşan herhangi bir ajan artık remember ve recall’u standart araçlar olarak çağırabilir. Bellek, ajan başına uygulama yerine paylaşılan bir hizmet haline gelir.
Doğru Stratejiyi Seçme
Bu karar tablosunu başlangıç noktası olarak kullanın:
| Durum | Strateji |
|---|---|
| Tek oturum, 20 turdan az | Tampon |
| Tek oturum, uzun süreli | Özet |
| Çok oturumlu, kullanıcı tercihleri | Epizodik |
| Sorgulanacak büyük bilgi tabanı | Anlamsal |
| Yukarıdakilerin hepsi | Birleştirin: oturum için özet + oturumlar arası epizodik/anlamsal |
Tamponla başlayın. Konuşmalar uzadığında bir özet katmanı ekleyin. Kullanıcılar oturumlar arasında geri döndüğünde epizodik bellek ekleyin. Alınacak bir belge korpusu olduğunda anlamsal bellek ekleyin.
İhtiyacınız olmadan önce karmaşıklık eklemeyin. Konuşma tamponu çoğu prototip için doğru cevaptır. Belleği erken aşırı tasarlamak, orantısız fayda olmadan maliyet, gecikme ve bakım yüzeyi ekler.
Sonuç
Bellek tek bir şey değildir — farklı kapsamlarda farklı sorunları ele alan bir strateji yığınıdır. Tampon mevcut oturumu yönetir. Özet bu oturumu uzatır. Epizodik bellek oturumları köprüler. Anlamsal bellek yanıtları bir bilgi tabanına dayandırır.
Bugün sahip olduğunuz sorunu çözen katmanı seçin. Uygulamaları daha sonra değiştirebilmek için temiz bir arayüzün arkasına sarın. Ve ajanlarınız MCP aracılığıyla iletişim kuruyorsa, belleği paylaşılan bir araç olarak ortaya koyun — bu her ajanı basit tutar ve sisteme bir bütün olarak kalıcı bir beyin verir.
İlgili Makaleler
- İlk MCP Sunucunuzu Oluşturma
- Çok Ajanlı Desenler: Orkestratörler, İşçiler ve Boru Hatları
- Araç Kullanım Desenleri: Güvenilir Ajan-Araç Arayüzleri Oluşturma
- Ajantik Geliştirmeye Giriş