Modern web uygulamaları, kullanıcı deneyimini zenginleştirmek ve iş süreçlerini otomatikleştirmek için sürekli yenilikçi çözümler arıyor. Peki ya bir yapay zeka ajanının, kullanıcılarınızın sorularını anlayıp, arka planda farklı sistemlerle etkileşime girerek dinamik çözümler ürettiğini hayal etseniz? Next.js ve LangGraph.js’in birleşimiyle, üretim-hazır, ölçeklenebilir ve akıllı AI ajanları geliştirmek artık hayal değil. Bu rehberde, bu iki güçlü teknolojiyi kullanarak sıfırdan nasıl otonom ajanlar inşa edeceğinizi adım adım keşfedeceksiniz.
Son yıllarda Büyük Dil Modelleri (LLM’ler), doğal dili anlama ve üretme yetenekleriyle teknoloji dünyasında devrim yarattı. Ancak, çoğu LLM uygulamasının temelinde tek seferlik bir istek-yanıt döngüsü yatar. Bir soru sorarız, bir cevap alırız ve süreç genellikle orada biter. Gerçek dünya senaryolarında ise bu yaklaşım, karmaşık sorunları çözmek veya birden fazla adımı içeren görevleri tamamlamak için yetersiz kalabilir. İşte tam da bu noktada “yapay zeka ajanları” sahneye çıkıyor ve geleneksel LLM’lerin sınırlılıklarını ortadan kaldırarak akıllı sistemlerin yeni bir çağını başlatıyor.
Bir yapay zeka ajanı, basit bir LLM sorgusundan çok daha fazlasıdır. Çevresini algılama, hedefler belirleme, bu hedeflere ulaşmak için bir eylem planı oluşturma, bu planı uygulama ve gerekirse planı dinamik olarak ayarlama yeteneğine sahip otonom bir varlıktır. Bu, ajanın “düşünebileceği” (muhakeme), “araçlar” (harici API’ler, veritabanları, kod yorumlayıcılar) kullanabileceği ve en önemlisi, “hafıza” (durum yönetimi) sayesinde birden fazla etkileşimde bağlamı koruyabileceği anlamına gelir. Örneğin, bir müşteri hizmetleri ajanı, sadece “siparişim nerede?” sorusuna yanıt vermekle kalmaz, aynı zamanda müşterinin geçmiş siparişlerini kontrol edebilir, kargo şirketinin API’siyle entegre olabilir ve hatta ürün iade süreçlerini başlatabilir. Bu karmaşıklık ve çok adımlı etkileşim yeteneği, geleneksel LLM’lerin sağlayamadığı bir değer sunar.
Bu yeteneklerin üretim ortamında, yani gerçek kullanıcıların kullandığı uygulamalarda sorunsuz bir şekilde çalışabilmesi için sağlam bir altyapıya ihtiyacımız var. Next.js, modern web uygulaması geliştirme konusundaki gücüyle (sunucu tarafı renderlama, API rotaları, Edge fonksiyonları ve üstün performans optimizasyonları), bu ajanların kullanıcı arayüzlerini ve arka plan servislerini barındırmak için mükemmel bir platform sunar. Kullanıcı deneyimini ön planda tutan Next.js, aynı zamanda ölçeklenebilirlik ve bakım kolaylığı gibi önemli üretim gereksinimlerini de karşılar. Öte yandan, LangGraph.js, bu ajanların karmaşık karar verme süreçlerini, durum yönetimini ve araç kullanımını grafik tabanlı bir yaklaşımla tanımlamamızı sağlayan özel bir kütüphanedir. LangGraph.js, ajanın adımlarını, olası eylemlerini ve bu eylemler arasındaki geçişleri net bir şekilde modellememize imkan tanır, böylece ajanların davranışları öngörülebilir ve debug edilebilir hale gelir. Bir aracı, geleneksel bir LLM’i dinamik, etkileşimli ve amaca yönelik bir sisteme dönüştüren sihirli bileşendir. Özetle, Next.js bize hızlı, güçlü ve kullanıcı dostu bir “ev” sağlarken, LangGraph.js bu evin içinde yaşayan “beyin”i inşa etmemize yardımcı olur. Bu iki teknolojinin birleşimi, sadece bir sohbet botu değil, gerçek iş yüklerini üstlenebilen, kararlar alabilen ve kullanıcılarla anlamlı bir şekilde etkileşime geçebilen üretim-hazır yapay zeka ajanları geliştirmemizin önünü açar.
Temel Kavramlar: Next.js ve LangGraph.js Bize Ne Sunuyor?
Üretim-hazır yapay zeka ajanları inşa etme yolculuğumuza başlarken, kullandığımız temel yapı taşlarını derinlemesine anlamak kritik önem taşır. Next.js ve LangGraph.js, bu ajanların hem kullanıcıya dönük arayüzünü hem de akıllı karar verme mekanizmasını oluşturmak için seçtiğimiz iki ana teknolojidir. Her ikisinin de kendine özgü güçlü yönleri vardır ve birlikte, sıradan bir uygulamanın ötesine geçen bir deneyim sunarlar.
Next.js Nedir ve Neden AI Ajanları İçin İdealdir?
Next.js, React tabanlı, açık kaynaklı bir web geliştirme framework’üdür. Geliştiricilere sunucu tarafı renderlama (SSR), statik site üretimi (SSG) ve artımlı statik yenileme (ISR) gibi güçlü özellikler sunarak, hem performanslı hem de SEO dostu uygulamalar oluşturma imkanı tanır. AI ajanları bağlamında, Next.js’in sunduğu avantajlar paha biçilmezdir:
- Üstün Kullanıcı Deneyimi (UX): Hızlı yükleme süreleri ve akıcı arayüzler, kullanıcıların ajanlarla etkileşimini keyifli hale getirir. Next.js’in optimizasyon yetenekleri sayesinde, ajanınızın yanıtları ne kadar karmaşık olursa olsun, kullanıcı arayüzü her zaman duyarlı kalır.
- API Rotları ve Sunucusuz Fonksiyonlar: Next.js, doğrudan proje içerisinde API endpoint’leri oluşturmanıza olanak tanır. Bu, LangGraph.js tabanlı ajan mantığınızı barındırmak için ideal bir ortam sağlar. Geleneksel bir backend sunucusu kurma ve yönetme yükünden kurtulursunuz. Özellikle Route Handlers ve Edge Fonksiyonları, düşük gecikmeli ve küresel olarak dağıtılmış ajan servisleri için mükemmel bir çözümdür.
- React Ekosistemi: Bildiğiniz ve sevdiğiniz React bileşenlerini kullanarak ajanınızın sohbet arayüzünü, çıktılarını veya etkileşimli öğelerini kolayca geliştirebilirsiniz. Bu, frontend geliştirme sürecini hızlandırır ve mevcut React projelerine entegrasyonu kolaylaştırır.
- Ölçeklenebilirlik: Vercel gibi platformlarda Next.js uygulamaları dağıtıldığında, API rotaları otomatik olarak sunucusuz fonksiyonlara dönüşür. Bu, uygulamanızın talebe göre kolayca ölçeklenebileceği anlamına gelir; yani, ajanınız milyonlarca kullanıcıya hizmet vermeye hazır olabilir.
- Hibrit Renderlama: Ajan arayüzünüzün belirli kısımlarını statik olarak oluşturabilirken, ajanla etkileşim kısmını sunucu veya istemci tarafında dinamik olarak işleyebilirsiniz. Bu esneklik, hem hız hem de kişiselleştirilmiş deneyim açısından büyük avantaj sağlar.
LangGraph.js Nedir ve Ajanı Akıllı Yapan Nedir?
LangGraph.js, LangChain ekosisteminin güçlü bir uzantısıdır ve özellikle döngüsel mantık (loops), durum yönetimi (state management) ve çok adımlı karar verme süreçleri içeren ajan sistemleri oluşturmak için tasarlanmıştır. Geleneksel LangChain zincirleri doğrusal bir akışa sahipken, LangGraph.js bize bu akışı grafik tabanlı bir şekilde, düğümler ve kenarlar aracılığıyla tanımlama imkanı sunar. Ajanı gerçekten akıllı yapan şey, bu yeteneklerdir:
- Grafik Tabanlı Yapı: Ajanınızın farklı adımlarını (LLM çağrısı, araç kullanımı, karar verme) birer “düğüm” olarak tanımlarsınız. Bu düğümler arasındaki geçişler “kenarlar” ile belirlenir. Bu görsel ve modüler yapı, ajanın karmaşık davranışlarını tasarlamayı ve hata ayıklamayı çok daha kolay hale getirir.
- Döngüsel Muhakeme (Loopy Reasoning): Ajanlar genellikle bir hedefi gerçekleştirmek için birden fazla deneme yapma ve geri bildirim döngülerine ihtiyaç duyarlar. LangGraph.js, bu döngüsel mantığı (örneğin, bir araç başarısız olduğunda tekrar deneme veya farklı bir araca geçme) doğal bir şekilde modellemenizi sağlar.
- Durum Yönetimi: Her adımda ajanın “durumu” güncellenir. Bu durum, ajanın bağlamı, aldığı kararlar, kullandığı araçların çıktıları gibi bilgileri içerir. LangGraph.js, bu durumun düğümler arasında nasıl taşınacağını otomatik olarak yönetir, böylece ajan geçmiş etkileşimlerini “hatırlayabilir” ve daha tutarlı kararlar alabilir.
- Araç Entegrasyonu: Ajanların harici sistemlerle etkileşime girmesi, onların gerçek değerini ortaya koyar. LangGraph.js, LangChain’in zengin araç (tool) koleksiyonuyla sorunsuz bir şekilde entegre olur. Bu araçlar, veritabanı sorgularından API çağrılarına, web kazımadan kod yorumlamaya kadar her şeyi yapabilir. Ajanınız, belirli bir görevi yerine getirmek için hangi aracı ne zaman kullanacağına karar verebilir.
- Koşullu Geçişler: Ajanın bir düğümden diğerine nasıl geçeceği, belirli koşullara bağlı olabilir. Örneğin, bir LLM’in yanıtına göre farklı bir araca yönlendirilebilir veya bir hata durumunda farklı bir hata işleme düğümüne geçebilir. Bu esneklik, ajanın dinamik ve adaptif davranışlar sergilemesini sağlar.
Bu iki teknolojinin sinerjisi, gerçekten dönüştürücü bir potansiyel sunar. Next.js, AI ajanınız için performansı yüksek, ölçeklenebilir ve kullanıcı dostu bir “ön yüz” ve “API katmanı” sağlarken, LangGraph.js ajanın karmaşık “beynini” inşa etmemize, onun düşünmesini, öğrenmesini ve etkileşime girmesini sağlayan mimariyi sunar. Bir sonraki bölümde, bu kavramları somutlaştırarak ilk AI ajanı projemizi nasıl kuracağımızı ve basit bir LangGraph.js ajanı nasıl tanımlayacağımızı göreceğiz.
İlk AI Ajanımızı Oluşturma: Adım Adım Rehber
Teorik temelleri anladığımıza göre, artık kolları sıvayıp ilk yapay zeka ajanımızı inşa etmeye başlayabiliriz. Bu bölümde, Next.js projesini nasıl kuracağımızı, gerekli bağımlılıkları nasıl yükleyeceğimizi ve ardından basit bir LangGraph.js ajanının temel yapısını adım adım nasıl oluşturacağımızı ele alacağız. Amacımız, hava durumu bilgisini sorgulayabilen basit bir ajan geliştirmek olacak. Bu örnek, LangGraph.js’in temel bileşenlerini anlamak için harika bir başlangıç noktasıdır.
Next.js Projesi Kurulumu Nasıl Yapılır?
Öncelikle, yeni bir Next.js projesi oluşturalım. Terminalinizi açın ve aşağıdaki komutu çalıştırın:
npx create-next-app@latest my-ai-agent-app --typescript --eslint --app
Bu komut, App Router tabanlı, TypeScript ve ESLint desteği olan yeni bir Next.js projesi başlatacaktır. Proje oluşturulduktan sonra, dizine girin:
cd my-ai-agent-app
Şimdi ajanımız için gerekli olan paketleri kuralım. LangGraph.js, LangChain'in bir parçası olduğundan, öncelikle LangChain'i, ardından LangGraph.js'i ve dil modelimiz için OpenAI SDK'sını kurmalıyız (alternatif olarak Anthropic veya diğer LLM sağlayıcılarını kullanabilirsiniz):
npm install langchain langgraph openai
Son olarak, API anahtarlarımızı güvenli bir şekilde yönetmek için bir .env.local dosyası oluşturalım. Projenizin kök dizininde bu dosyayı oluşturun ve LLM sağlayıcınızın API anahtarını ekleyin:
# .env.local dosyası
OPENAI_API_KEY="sk-..."
Bu anahtara, Next.js uygulamanızın API rotalarından process.env.OPENAI_API_KEY şeklinde erişebileceksiniz.
Basit Bir LangGraph Ajansının Yapısı Nasıl Olmalıdır?
Bir LangGraph ajanı, düğümlerden (nodes), kenarlardan (edges) ve durumdan (state) oluşur. Düğümler, ajanın gerçekleştirebileceği eylemleri veya mantık parçacıklarını temsil ederken, kenarlar bu düğümler arasındaki geçişleri tanımlar. Durum, ajanın etkileşim boyunca edindiği bilgileri saklar. Hava durumu ajanı örneğimizde, aşağıdaki adımları izleyeceğiz:
- Araç Tanımlama: Hava durumu bilgisini çekecek bir araç (tool) oluşturacağız.
- LLM Tanımlama: Kullanıcının sorgusunu anlayacak ve aracı kullanıp kullanmayacağına karar verecek bir LLM belirleyeceğiz.
- Ajan Durumu Tanımlama: Ajana özgü durumu (mesajlar, alınan kararlar vb.) yönetecek bir yapı oluşturacağız.
- Grafiği Oluşturma: Düğümleri ve aralarındaki geçişleri tanımlayarak LangGraph'ı inşa edeceğiz.
Şimdi, app/api/agent/route.ts adında bir API rotası oluşturalım ve bu rotanın içerisine ajan mantığımızı yerleştirelim.
// app/api/agent/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { StateGraph, END } from '@langchain/langgraph';
import { ChatOpenAI } from '@langchain/openai';
import { Tool } from '@langchain/core/tools';
import { AgentExecutor, AgentStep } from 'langchain/agents';
import { pull } from 'langchain/hub';
import { AgentAction, AgentFinish, BaseMessage } from 'langchain/schema';
import { RunnablePassthrough, RunnableSequence } from '@langchain/core/runnables';
import { MessagesPlaceholder } from '@langchain/core/prompts';
import {
ChatPromptTemplate,
AIMessage,
HumanMessage,
} from '@langchain/core/messages';
// 1. Araç Tanımlama: Hava durumu API'sini simüle eden bir araç
class WeatherTool extends Tool {
name = 'get_current_weather';
description = 'Bir şehir için güncel hava durumu bilgisini döndürür. Parametre olarak şehir adı (string) alır.';
async _call(input: string): Promise {
// Gerçek bir API çağrısı yerine basit bir simülasyon
console.log(Hava durumu sorgulanıyor: ${input});
if (input.toLowerCase() === 'istanbul') {
return 'İstanbul\'da hava şu an parçalı bulutlu ve 15°C.';
} else if (input.toLowerCase() === 'ankara') {
return 'Ankara\'da hava açık ve 10°C.';
} else {
return Üzgünüm, ${input} için hava durumu bilgisi bulunamadı.;
}
}
}
const tools = [new WeatherTool()];
// 2. LLM Tanımlama
const llm = new ChatOpenAI({
model: 'gpt-4o-mini', // Daha hızlı yanıtlar için uygun bir model
temperature: 0,
openAIApiKey: process.env.OPENAI_API_KEY,
});
// Ajan için prompt oluşturma
const prompt = ChatPromptTemplate.fromMessages([
new MessagesPlaceholder('chat_history'),
['human', '{input}'],
new MessagesPlaceholder('agent_scratchpad'),
]);
const bindedModel = llm.bind({
tools: tools.map((tool) => tool.toJSON()),
});
// Ajan düğümü (LLM'i ve araçları kullanarak bir eylem planı oluşturur)
const agentRunnable = RunnableSequence.from([
RunnablePassthrough.assign({
agent_scratchpad: (input: { steps: AgentStep[] }) =>
input.steps.map((s) => s.log),
}),
prompt,
bindedModel,
{
steps: (input: { steps: AgentStep[] }) => input.steps,
output: (output: AgentAction | AgentFinish) => output,
}
]);
// LangGraph için ajan durumu
interface AgentState {
input: string;
chat_history: BaseMessage[];
steps: AgentStep[];
}
// LangGraph oluşturma
const workflow = new StateGraph()
.addNode('agent', async (state: AgentState) => {
// Ajanın bir eylem veya son yanıt üretmesini sağlayan mantık
const response = await agentRunnable.invoke({
input: state.input,
chat_history: state.chat_history,
steps: state.steps,
});
return { ...state, steps: [...state.steps, { log: response.output.log, observation: '' }] };
})
.addNode('tool_executor', async (state: AgentState) => {
// Ajanın seçtiği aracı çalıştıran mantık
const lastStep = state.steps[state.steps.length - 1];
if ('tool' in lastStep.log && 'tool_input' in lastStep.log) {
const tool = tools.find(t => t.name === lastStep.log.tool);
if (tool) {
const observation = await tool.invoke(lastStep.log.tool_input);
return { ...state, steps: [...state.steps.slice(0, -1), { ...lastStep, observation }] };
}
}
return state; // Araç bulunamazsa veya eylem uygun değilse durumu değiştirme
});
// Koşullu kenar: Ajan bir araç kullanmak istiyorsa 'tool_executor'a git, bitirdiyse sona
const shouldContinue = (state: AgentState): string => {
const lastStep = state.steps[state.steps.length - 1];
if (lastStep && 'tool' in lastStep.log) {
return 'tool'; // Araç kullanımı var
}
return 'end'; // Ajan işini bitirdi
};
workflow
.addConditionalEdges('agent', shouldContinue, {
tool: 'tool_executor',
end: END,
})
.addEdge('tool_executor', 'agent'); // Araç çalıştıktan sonra tekrar ajana dön
workflow.setEntryPoint('agent'); // Başlangıç düğümünü belirle
const app = workflow.compile();
export async function POST(req: NextRequest) {
try {
const { message, history } = await req.json();
const result = await app.invoke({
input: message,
chat_history: history || [],
steps: [],
});
// Son adımı kontrol ederek ajanın çıktısını bul
let finalOutput = 'Üzgünüm, bir yanıt oluşturulamadı.';
for (const step of result.steps) {
if ('tool' in step.log) {
finalOutput = step.observation; // Aracın çıktısı
} else if ('output' in step.log) {
finalOutput = step.log.output; // LLM'in son yanıtı
break;
}
}
return NextResponse.json({ response: finalOutput });
} catch (error) {
console.error('API Error:', error);
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
}
}
BaseMessage türünün bir AIMessage veya HumanMessage olduğunu belirtmek, sohbet geçmişini daha doğru yönetmenize yardımcı olur.
Bu kodda, bir kullanıcının sorusunu alan, hava durumu aracını kullanıp kullanmayacağına karar veren ve sonra bu aracı çalıştırarak yanıtı geri döndüren basit bir ajan tasvir edilmiştir. shouldContinue fonksiyonu, ajanın bir sonraki eylemini belirleyen koşullu mantığı içerir. Eğer ajan bir araç kullanması gerektiğine karar verirse, tool_executor düğümüne geçer. Aksi takdirde, işlem sona erer.
Bu API rotasına basit bir frontend isteği göndermek için örneğin app/page.tsx dosyanızı güncelleyebilirsiniz:
// app/page.tsx
'use client'; // Bu bir istemci bileşeni olmalı
import { useState } from 'react';
export default function Home() {
const [input, setInput] = useState('');
const [messages, setMessages] = useState([]);
const [loading, setLoading] = useState(false);
const sendMessage = async () => {
if (!input.trim()) return;
const userMessage = { sender: 'user', text: input };
setMessages((prev) => [...prev, userMessage]);
setInput('');
setLoading(true);
try {
const res = await fetch('/api/agent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: input,
history: messages.map(msg => ({
type: msg.sender === 'user' ? 'human' : 'ai',
content: msg.text,
})),
}),
});
const data = await res.json();
setMessages((prev) => [...prev, { sender: 'ai', text: data.response || data.error }]);
} catch (error) {
console.error('Mesaj gönderme hatası:', error);
setMessages((prev) => [...prev, { sender: 'ai', text: 'Bir hata oluştu.' }]);
} finally {
setLoading(false);
}
};
return (
AI Hava Durumu Ajanı
{messages.map((msg, index) => (
{msg.sender === 'user' ? 'Siz' : 'Ajan'}: {msg.text}
))}
{loading && Ajan düşünüyor...}
setInput(e.target.value)}
onKeyPress={(e) => {
if (e.key === 'Enter') {
sendMessage();
}
}}
placeholder="Şehir adı girin (örn: İstanbul, Ankara)"
style={{ flexGrow: 1, padding: '10px', border: '1px solid #ccc', borderRadius: '4px', marginRight: '10px' }}
disabled={loading}
/>
);
}
Artık projenizi çalıştırabilir ve ajanınızla etkileşime geçebilirsiniz:
npm run dev
Tarayıcınızda http://localhost:3000 adresine giderek ajanınızla sohbet edebilir, İstanbul veya Ankara'nın hava durumunu sorarak aracın nasıl çalıştığını gözlemleyebilirsiniz. Bu bölüm, LangGraph.js ve Next.js'in temel entegrasyonunu sağlam bir şekilde anlamanızı sağlamıştır. Bir sonraki adımda, bu ajanları üretim ortamına hazır hale getirmek için optimizasyon ve ölçeklenebilirlik stratejilerini inceleyeceğiz.
Üretim-Hazır Ajanlar İçin Optimizasyon ve Ölçeklenebilirlik
Basit bir AI ajanını çalışır duruma getirmek harika bir başlangıçtır, ancak bu ajanı gerçek dünya senaryolarında, binlerce kullanıcının aynı anda erişebileceği bir üretim ortamına taşımak farklı zorlukları beraberinde getirir. Performans, ölçeklenebilirlik, durum yönetimi ve güvenlik, üretim-hazır ajanlar için göz ardı edilemez önceliklerdir. Bu bölümde, ajanınızın bu gereksinimleri karşılayabilmesi için neler yapabileceğinizi detaylıca inceleyeceğiz.
Performans İçin Next.js Edge Fonksiyonları Nasıl Kullanılır?
Düşük gecikme süresi, kullanıcı deneyimi açısından kritik öneme sahiptir. Özellikle AI ajanları, LLM'lerle ve harici API'lerle etkileşime girdikleri için doğal olarak bir miktar gecikme yaşayabilirler. Next.js'in Edge Fonksiyonları, bu gecikmeyi minimuma indirmek için harika bir fırsat sunar. Edge Fonksiyonları, içeriği son kullanıcıya coğrafi olarak en yakın sunucularda çalıştırmak üzere tasarlanmıştır.
AI ajanınızın API rotasını Edge runtime'da çalıştırmak için, API rotanızın (örneğin app/api/agent/route.ts) en başına aşağıdaki kodu eklemeniz yeterlidir:
// app/api/agent/route.ts
export const runtime = 'edge'; // Edge runtime'ı etkinleştir
Avantajları:
- Düşük Gecikme: Kullanıcıya coğrafi olarak daha yakın bir konumda çalışarak, LLM çağrıları ve diğer network işlemleri için gecikmeyi önemli ölçüde azaltır.
- Küresel Dağıtım: Ajanınızın dünya çapında tutarlı bir performans sunmasını sağlar.
- Maliyet Etkinliği: Genellikle sunucusuz fonksiyonlar gibi, yalnızca kullanıldığında ödeme yaparsınız.
Zorluklar ve Çözümleri:
Edge runtime, Node.js runtime'ına göre daha kısıtlı bir ortamdır. Bazı Node.js modülleri veya yerel bağımlılıklar Edge'de çalışmayabilir. LangChain ve LangGraph.js kütüphaneleri genellikle Edge uyumlu olacak şekilde tasarlanmıştır, ancak kullandığınız diğer araçlar veya veritabanı sürücüleri bu kısıtlamalara takılabilir.
- Veritabanı Bağlantıları: Edge fonksiyonlarından geleneksel ilişkisel veritabanlarına (PostgreSQL, MySQL) doğrudan bağlantılar bazen sorunlu olabilir. Çözüm olarak, veritabanı proxies (örneğin Supabase, PlanetScale'in Edge uyumlu adaptörleri) veya HTTP tabanlı API'ler aracılığıyla veritabanına erişim sağlayabilirsiniz. NoSQL veritabanları (örneğin DynamoDB'nin HTTP API'si) veya Redis gibi in-memory cache'ler genellikle daha Edge dostudur.
- Büyük Bağımlılıklar: Paket boyutunu minimal tutmak önemlidir. Sadece gerçekten ihtiyacınız olan bağımlılıkları yükleyin.
Durum Yönetimi ve Kalıcılık Nasıl Sağlanır?
LangGraph.js, bir ajanın durumunu düğümler arasında otomatik olarak taşır, ancak bu durum genellikle tek bir "çalıştırma" (run) süresince hafızada tutulur. Bir kullanıcıyla birden fazla etkileşimde bulunan (yani oturum bazlı) bir ajan için bu yeterli değildir. Ajana "hafıza" kazandırmak ve sohbet geçmişini korumak için durumun kalıcı hale getirilmesi gerekir.
Stratejiler:
- Veritabanı Entegrasyonu: En yaygın ve sağlam yöntem, ajan oturum durumunu bir veritabanında saklamaktır.
- PostgreSQL/MongoDB: Karmaşık sohbet geçmişleri ve ajan durumu nesneleri için uygundur. Her kullanıcı oturumu için benzersiz bir
session_idoluşturup, bu ID'ye bağlı olarak mesajları ve ajanın iç durumunu veritabanında saklayabilirsiniz. - Redis: Daha hızlı erişim gerektiren ve daha kısa ömürlü durumlar veya önbellekleme için idealdir. Örneğin, son N mesajı Redis'te tutarken, daha eski mesajları bir ilişkisel veritabanına kaydedebilirsiniz.
session_id oluşturun. Bu ID'yi hem frontend'de hem de backend'de saklayın ve API çağrılarınızla birlikte gönderin. Backend, bu session_id'yi kullanarak ilgili sohbet geçmişini ve ajan durumunu veritabanından çeker.
// Durum yönetimini içeren gelişmiş bir API rotası örneği (basitçe gösterilmiştir)
// app/api/agent/route.ts içindeki POST fonksiyonunu güncelleyelim
// ... (Önceki kodlar) ...
export async function POST(req: NextRequest) {
try {
const { message, sessionId, chatHistory } = await req.json(); // sessionId ve chatHistory al
let currentHistory = chatHistory || [];
// Gerçek bir uygulamada, sessionId'yi kullanarak veritabanından geçmişi çekeriz
// Örnek: const dbHistory = await getChatHistoryFromDB(sessionId);
// currentHistory = [...dbHistory, ...currentHistory];
const result = await app.invoke({
input: message,
chat_history: currentHistory.map((msg: any) =>
msg.type === 'human' ? new HumanMessage(msg.content) : new AIMessage(msg.content)
),
steps: [],
});
// ... (Çıktı işleme kısmı aynı kalır) ...
// Yanıtı veritabanına kaydet (gerçek uygulamada)
// await saveMessageToDB(sessionId, { type: 'human', content: message });
// await saveMessageToDB(sessionId, { type: 'ai', content: finalOutput });
return NextResponse.json({ response: finalOutput, newSessionId: sessionId });
} catch (error) {
console.error('API Error:', error);
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
}
}
LangChainMessageHistory gibi LangChain modüllerini veya doğrudan LangChain Community Stores'u veritabanınızla entegre ederek daha yapısal bir yaklaşım izleyebilirsiniz.
Güvenlik ve Kimlik Doğrulama Neden Önemlidir?
Üretim ortamındaki her uygulama gibi, AI ajanları da güvenlik konusunda titizlikle ele alınmalıdır.
- API Anahtarlarının Korunması: LLM sağlayıcılarınızın (OpenAI, Anthropic vb.) API anahtarlarını asla istemci tarafında veya versiyon kontrolünde (Git) açıkça bulundurmayın. Her zaman ortam değişkenleri (
.env.local) aracılığıyla sunucu tarafında (API rotaları veya Edge fonksiyonları) erişin. - Kullanıcı Kimlik Doğrulama ve Yetkilendirme: Ajanınız belirli kullanıcılara özel veya hassas bilgilere erişiyorsa (örneğin, sipariş geçmişi), kullanıcıların kimlik doğrulaması yapıldığından ve yalnızca kendi verilerine erişebildiklerinden emin olun. JWT (JSON Web Tokens) veya NextAuth.js gibi çözümler bu konuda yardımcı olabilir.
- Giriş Doğrulama ve Temizleme (Input Validation and Sanitization): Kullanıcılardan gelen girişleri her zaman doğrulayın ve temizleyin. Bu, prompt injection gibi saldırıları önlemenin yanı sıra, ajanın beklenmedik veya zararlı girdilerle karşılaşmasını engeller. Özellikle aracı çağrıları yapmadan önce girişleri titizlikle kontrol edin.
- Hata Yönetimi ve Loglama: Üretim ortamında oluşan hataları yakalayın ve detaylı bir şekilde loglayın. Bu, güvenlik zafiyetlerinin veya performans sorunlarının hızlıca tespit edilmesine yardımcı olur.
Bu optimizasyon ve güvenlik stratejilerini uygulayarak, AI ajanlarınızı sadece akıllı değil, aynı zamanda güvenilir, hızlı ve ölçeklenebilir hale getirebilirsiniz. Bir sonraki bölümde, bu prensipleri bir araya getirerek gerçek bir dünya senaryosunu, yani bir e-ticaret müşteri hizmetleri ajanını nasıl inşa edebileceğimize odaklanacağız.
Gerçek Dünya Senaryosu: E-ticaret Müşteri Hizmetleri Ajanı
Teoriyi ve teknik detayları inceledikten sonra, edindiğimiz bilgileri somut bir gerçek dünya senaryosuna uygulayalım: E-ticaret müşteri hizmetleri ajanı. Bu vaka analizi, LangGraph.js ile karmaşık bir ajanı nasıl tasarlayacağımızı ve Next.js'in sunduğu olanaklarla nasıl entegre edeceğimizi gösterecek. Amacımız, kullanıcının sorularını anlayan, ilgili bilgilere erişen ve bağlamı koruyarak dinamik yanıtlar üreten bir ajan yaratmaktır.
Vaka Analizi: E-ticaret Destek Ajanı Nasıl Geliştirilir?
Problem: Geleneksel müşteri hizmetleri sistemleri genellikle insan müdahalesi gerektirir veya basit SSS'lerle sınırlıdır. Müşteriler; sipariş durumu sorgulama, ürün bilgisi alma, iade süreçlerini öğrenme gibi konularda hızlı ve doğru yanıtlara ihtiyaç duyar. Bu durum, insan kaynakları üzerinde büyük bir yük oluşturabilir ve yanıt sürelerini uzatabilir.
Çözüm: Next.js tabanlı bir kullanıcı arayüzü ile güçlendirilmiş, LangGraph.js ile geliştirilmiş bir AI müşteri hizmetleri ajanı. Bu ajan, doğal dil işleme yeteneği sayesinde müşteri sorularını anlayacak, çeşitli "araçları" kullanarak gerekli bilgilere ulaşacak ve kişiselleştirilmiş yanıtlar sunacaktır.
Ajanın Yetenekleri ve Kullanacağı Araçlar:
- Sipariş Durumu Sorgulama: Müşterinin sipariş numarasını veya kullanıcı hesabını kullanarak mevcut ve geçmiş siparişlerin durumunu (kargoya verildi, teslim edildi vb.) sorgulayabilir.
- Araç:
getOrderStatus(orderId: string, userId: string)- Bir veritabanı veya harici kargo API'si ile entegre.
- Araç:
- Ürün Bilgisi Sağlama: Belirli bir ürün hakkında detaylı bilgi (fiyat, stok, özellikler) verebilir veya benzer ürünler önerebilir.
- Araç:
getProductDetails(productIdOrName: string)- Ürün katalog API'si ile entegre. - Araç:
recommendProducts(category: string)- Öneri motoru API'si.
- Araç:
- İade Politikası ve Süreci Açıklama: Müşterilere iade koşulları, süresi ve nasıl yapılacağı hakkında bilgi verebilir.
- Araç:
getReturnPolicy()- Statik bir bilgi bankası veya CMS entegrasyonu.
- Araç:
- Genel Bilgi ve SSS: Sıkça sorulan diğer sorulara (ödeme yöntemleri, kargo ücretleri vb.) yanıt verebilir.
- Araç:
searchKnowledgeBase(query: string)- Vektör veritabanı destekli bir SSS arama motoru.
- Araç:
LangGraph Ajanının Karar Döngüsü:
Bu ajanın LangGraph.js üzerindeki iş akışı, dinamik karar verme yeteneğini sergileyecektir:
- Giriş (Human Message): Kullanıcıdan bir mesaj alınır.
- Soru Tipi Belirleme (Ajan Düğümü): LLM, gelen mesajın hangi kategoriye girdiğini belirlemeye çalışır:
- Sipariş sorgusu mu?
- Ürün bilgisi mi?
- İadeyle ilgili mi?
- Genel bir soru mu?
- Veya hiçbir kategoriye girmiyor mu?
- İlgili Aracı Çağırma (Koşullu Kenarlar): LLM'in belirlediği soru tipine göre ajan, uygun araca yönlendirilir. Örneğin, "Sipariş durumum ne?" gibi bir soru için
getOrderStatusaracına yönlendirilir. Eğer gerekli parametreler (sipariş numarası) eksikse, ajan kullanıcıdan bu bilgiyi ister (döngüsel muhakeme). - Aracın Çalıştırılması (Araç Düğümü): Seçilen araç çalıştırılır ve sonuç ajanın durumuna eklenir.
- Yanıt Oluşturma ve Kullanıcıya Dönme (Ajan Düğümü): Ajan, aracın çıktısını ve sohbet geçmişini kullanarak kullanıcıya doğal dilde bir yanıt oluşturur. Eğer başka bir araca ihtiyaç duyulmuyorsa, süreç sona erer.
- Döngüsel Tekrar: Eğer ilk araç yeterli bilgiyi sağlamazsa veya kullanıcı yeni bir soru sorarsa, ajan tekrar Soru Tipi Belirleme adımına döner.
// app/api/customer-service-agent/route.ts (Örnek bir API rotası yapısı)
import { NextRequest, NextResponse } from 'next/server';
import { StateGraph, END } from '@langchain/langgraph';
import { ChatOpenAI } from '@langchain/openai';
import { Tool } from '@langchain/core/tools';
import { BaseMessage, HumanMessage, AIMessage } from 'langchain/schema';
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts';
import { RunnablePassthrough, RunnableSequence } from '@langchain/core/runnables';
import { convertTo }= require('@langchain/core/messages'); // Örnek için gerekli
// Örnek Araçlar (Gerçek uygulamada detaylı API entegrasyonları olur)
class GetOrderStatusTool extends Tool {
name = 'get_order_status';
description = 'Kullanıcının sipariş numarasını (string) ve kullanıcı ID\'sini (string) alarak sipariş durumunu döndürür.';
async _call(input: string): Promise {
const { orderId, userId } = JSON.parse(input); // Varsayımsal JSON input
if (orderId === '12345' && userId === 'user1') {
return Sipariş ${orderId} şu anda 'Kargoya Verildi' durumundadır. Tahmini teslimat: 3 gün.;
}
return 'Belirtilen sipariş numarası veya kullanıcı ID ile sipariş bulunamadı.';
}
}
class GetProductDetailsTool extends Tool {
name = 'get_product_details';
description = 'Ürün adı (string) veya ID (string) ile ürün detaylarını (fiyat, stok) döndürür.';
async _call(input: string): Promise {
if (input.toLowerCase().includes('laptop')) {
return 'Gaming Laptop: Fiyat 20.000 TL, Stokta 5 adet, Özellikler: i7 işlemci, 16GB RAM.';
}
return 'Belirtilen ürün bulunamadı.';
}
}
const customerServiceTools = [new GetOrderStatusTool(), new GetProductDetailsTool()];
const llm = new ChatOpenAI({
model: 'gpt-4o-mini',
temperature: 0,
openAIApiKey: process.env.OPENAI_API_KEY,
});
const prompt = ChatPromptTemplate.fromMessages([
['system', 'Sen bir e-ticaret müşteri hizmetleri ajanısın. Müşterilere yardımcı olmak için elinden gelenin en iyisini yap. Gerekirse araçları kullan.'],
new MessagesPlaceholder('chat_history'),
['human', '{input}'],
new MessagesPlaceholder('agent_scratchpad'),
]);
const agentRunnable = RunnableSequence.from([
RunnablePassthrough.assign({
agent_scratchpad: (input: { steps: any[] }) =>
input.steps.map((s) => s.log),
}),
prompt,
llm.bind({ tools: customerServiceTools.map(tool => tool.toJSON()) }),
{
steps: (input: { steps: any[] }) => input.steps,
output: (output: any) => output, // LLM'den gelen çıktıyı direkt al
}
]);
interface CustomerServiceAgentState {
input: string;
chat_history: BaseMessage[];
steps: any[]; // AgentStep[] yerine daha genel bir tip
}
const workflow = new StateGraph()
.addNode('agent', async (state: CustomerServiceAgentState) => {
const response = await agentRunnable.invoke({
input: state.input,
chat_history: state.chat_history,
steps: state.steps,
});
return { ...state, steps: [...state.steps, { log: response.output.log, observation: '' }] };
})
.addNode('tool_executor', async (state: CustomerServiceAgentState) => {
const lastStep = state.steps[state.steps.length - 1];
if (lastStep && 'tool' in lastStep.log && 'tool_input' in lastStep.log) {
const tool = customerServiceTools.find(t => t.name === lastStep.log.tool);
if (tool) {
const observation = await tool.invoke(lastStep.log.tool_input);
return { ...state, steps: [...state.steps.slice(0, -1), { ...lastStep, observation }] };
}
}
return state;
});
const shouldContinue = (state: CustomerServiceAgentState): string => {
const lastStep = state.steps[state.steps.length - 1];
if (lastStep && 'tool' in lastStep.log) {
return 'tool';
}
return 'end';
};
workflow
.addConditionalEdges('agent', shouldContinue, {
tool: 'tool_executor',
end: END,
})
.addEdge('tool_executor', 'agent');
workflow.setEntryPoint('agent');
const app = workflow.compile();
export async function POST(req: NextRequest) {
try {
const { message, history, sessionId } = await req.json();
// Gerçek bir uygulamada, burada sessionId'yi kullanarak veritabanından geçmişi çekeriz
// const currentHistoryFromDB = await getChatHistory(sessionId);
// const mergedHistory = [...currentHistoryFromDB, ...history]; // Mevcut geçmişi birleştir
const result = await app.invoke({
input: message,
chat_history: history.map((msg: any) =>
msg.type === 'human' ? new HumanMessage(msg.content) : new AIMessage(msg.content)
),
steps: [], // Her çağrıda yeni bir adım dizisi başlatıyoruz
});
let finalOutput = 'Üzgünüm, bir yanıt oluşturulamadı.';
for (const step of result.steps) {
if ('tool' in step.log) {
finalOutput = step.observation;
} else if ('output' in step.log) {
finalOutput = step.log.output;
break;
}
}
// Gerçek uygulamada, ajanın yanıtını ve kullanıcının mesajını veritabanına kaydederiz
// await saveMessage(sessionId, { type: 'human', content: message });
// await saveMessage(sessionId, { type: 'ai', content: finalOutput });
return NextResponse.json({ response: finalOutput });
} catch (error) {
console.error('Customer Service Agent API Error:', error);
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
}
}
Yukarıdaki kod, e-ticaret ajanı için temel bir API rotası oluşturur. Frontend'de, bu rotayı kullanarak ajana istek gönderecek ve yanıtları gösterecek bir sohbet arayüzü geliştirebiliriz. Bu arayüzün mobil uyumlu olması, günümüz e-ticaret deneyimi için hayati öneme sahiptir. Aşağıda, mobil uyumlu bir arayüz için basit bir CSS medya sorgusu örneği verilmiştir (
