Inside AI Models
← Tüm makaleler
LangChainYapay Zeka Mühendisliği

LangChain'e Giriş: v1 Yaklaşımıyla Chain, RAG ve Agent Geliştirme

24 Ağu 2026 · 8 dk okuma

Paylaş

Tek bir prompt'tan (istem) daha karmaşık bir şey geliştirmeyi denediyseniz, LangChain'in çözmeyi hedeflediği o temel sorunla zaten karşılaşmışsınızdır: Gerçek LLM uygulamaları aslında birer tesisat işidir. Bir model çağırırsınız, ancak önce kullanıcı girdisinden bir prompt oluşturmanız gerekir; yanıtı alırsınız ama sonra onu parse etmeniz (ayrıştırmanız), belki bir tool'a (araç) beslemeniz, yanıtı temellendirmek için belgeler çekmeniz veya üç tur önce ne söylendiğini hatırlamanız gerekir. LangChain, tüm bunlara ortak bir yapı kazandıran ve kodun geri kalanını baştan yazmadan alttaki modeli değiştirmenize olanak tanıyan framework'tür.

Başlamadan önce sizi saatlerce sürecek bir zaman kaybından kurtaracak bir uyarım var: LangChain çok değişti. Bugün arama sonuçlarında üst sıralarda çıkan eğitimlerin çoğu hâlâ v0.1 döneminden kalma eski LLMChain / create_react_agent yapılarını kullanıyor. Aşağıdaki her şey güncel v1 API'sini (langchain-core 1.x, LangGraph 1.0) temel alıyor. Eğer bulduğunuz bir kod parçası LLMChain import ediyorsa, o yazı bu makaleden daha eskidir; burada anlattığımız temel yapılar onun yerini alıyor.

Tek Temel Fikir: Her Şey Bir Runnable

İşte LangChain'i kafanızda oturtacak zihinsel model. Neredeyse her yapı taşı; bir prompt şablonu, bir sohbet modeli, bir çıktı parser'ı, bir retriever (getirici), hatta bütün bir chain (zincir), Runnable (çalıştırılabilir) adı verilen aynı arayüzü uygular. Her Runnable .invoke() (bir kez çalıştır), .stream() (akış olarak ver) ve .batch() (çoklu girdi çalıştır) metotlarına sahiptir. Hepsi aynı arayüzü konuştuğu için onları tek bir operatörle birbirine bağlayabilirsiniz: | (pipe) işareti.

chain = prompt | model | parser

Bu satırı soldan sağa doğru okuyabilirsiniz: Girdiyi prompt üzerinden gönder, çıktısını model'e ilet, oradan çıkanı da parser'a aktar. İşte bu, LCEL'dir (LangChain Expression Language — LangChain İfade Dili). v1'deki tüm yapılandırma mantığı bunun üzerine kuruludur. Şimdi gerçeğini oluşturalım.

Kurulum ve İlk Model Çağrınız

Çekirdek paketi ve istediğiniz sağlayıcı entegrasyonunu kurun. Biz Claude kullanacağız:

pip install -U langchain langchain-anthropic
export ANTHROPIC_API_KEY="your-key"

Bilmeniz gereken en faydalı tek fonksiyon init_chat_model'dir. Herhangi bir sağlayıcıdan model başlatmak için size tutarlı bir yol sunar. Yalnızca "provider:model" formatında bir metin geçersiniz ve ileride sağlayıcıyı değiştirmek isterseniz bunu tek satırda yapabilirsiniz:

from langchain.chat_models import init_chat_model
 
model = init_chat_model("anthropic:claude-sonnet-5")
 
reply = model.invoke("Give me one sentence on what LangChain is.")
print(reply.content)

model.invoke() bir metin (veya mesaj listesi) alır ve bir AIMessage döndürür; metin kısmı .content üzerinde tutulur. Bu işin en temel taşıdır. Şimdi bunları birleştirelim.

İlk Chain'iniz

Bir chain, sadece pipe ile birbirine bağlanmış Runnable'lardan ibarettir. En klasik başlangıç dizilimi prompt → model → parser şeklindedir: Bir prompt şablonu değişkenlerinizi mesajlara dönüştürür, model yanıtlar ve bir parser bu yanıtı temizleyip düz bir metin hâline getirir.

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
 
prompt = ChatPromptTemplate.from_template(
    "Explain {topic} in one sentence, for a {level}."
)
 
chain = prompt | model | StrOutputParser()
 
print(chain.invoke({"topic": "backpropagation", "level": "beginner"}))

Neler yapmadığınıza dikkat edin: Adımlar arasına bağlayıcı kod yazmadınız, mesajları manuel olarak formatlamadınız veya yanıtın içinden .content'i çekip çıkarmakla uğraşmadınız. Tüm veri aktarımını pipe halleder. Ayrıca chain'in kendisi de bir Runnable olduğu için, chain.stream({...}) yanıtı token (jeton) token stream eder; chain.batch([...]) ise bir girdi listesini işler. Üstelik bunun için chain üzerinde hiçbir değişiklik yapmanıza gerek kalmaz.

Pipe işleminin sorunsuz çalışmasının nedeni, her adımın çıktı türünün bir sonraki adımın girdi türüyle eşleşmesidir. Hangi verinin nereye aktığını görmek için aşağıdaki aşamalara göz atın:

chain = prompt | model | parser
invoke({ })
str
dictpromptPromptValue

Şablon değişkenlerini doldurur ve gönderilecek mesajları üretir.

Bir adıma dokun — içine ne tür girip ne çıktığını gör. Pipe (|) tam da bu eşleşmeyi zorunlu kılar.

Tüm işin sırrı bu tür eşleşmesidir. Aynı zamanda bir chain bozulduğunda yaşanan kafa karışıklığının da en yaygın nedenidir; çünkü bir tür uyuşmazlığı, iki adım sonra anlaşılmaz bir hata olarak karşınıza çıkar. Böyle bir durumla karşılaştığınızda pipe'ı soldan sağa doğru takip edin ve her aşamanın tam olarak ne döndürdüğünü kontrol edin.

Structured Output (Yapılandırılmış Çıktı) Alma

Düz metinler genel yazılar için harikadır ancak çoğu zaman veriye, yani kod içinde kullanabileceğiniz alanlara ihtiyaç duyarsınız. Modelin metnini kendi başınıza ayrıştırmak yerine, istediğiniz yapıyı bir Pydantic modeliyle tanımlayın ve gerisini LangChain'e bırakın:

from pydantic import BaseModel, Field
 
class Movie(BaseModel):
    title: str = Field(description="The film's title")
    year: int = Field(description="Release year")
 
extractor = model.with_structured_output(Movie)
 
print(extractor.invoke("Blade Runner came out in 1982."))
# title='Blade Runner' year=1982

with_structured_output, size bir mesaj yerine türü belirlenmiş bir Movie nesnesi veren yeni bir Runnable döndürür. Arka planda sağlayıcının kendi doğal structured output desteğini kullanır, böylece belirsiz sonuçlar yerine doğrulanmış veriler elde edersiniz. "Model bir şeyler söyledi" noktasından "Programımın kullanabileceği bir değere sahibim" noktasına giden en temiz yol budur.

Yanıtları Retrieval ile Temellendirme (RAG)

Modeller sizin verilerinizi bilmez. RAG (Retrieval-Augmented Generation — Geri Getirmeyle Artırılmış Üretim) tam da bunu çözer: Model yanıt vermeden önce kendi belgelerinizin en alakalı parçalarını çeker ve bunları modele bağlam olarak sunarsınız. Buradaki hareketli parçalar bir embeddings (vektör gömme) modeli (metni vektörlere dönüştürür), bir vector store (vektör deposu — bunları tutar) ve bir retriever'dır (en yakın eşleşmeleri bulur).

Baştan dürüstçe belirtmek gerekirse: Anthropic bir embeddings API'si sunmuyor. Bu yüzden Claude'u bir embeddings sağlayıcısıyla eşleştirin. Aşağıda OpenAI kullandık ama Voyage veya yerel bir model de işinizi görecektir. Bu karma yapı bir geçici çözüm değil, aslında bir özelliktir; LangChain'in sağlayıcıdan bağımsız tasarımının tam da hizmet ettiği amaç budur.

from langchain_core.vectorstores import InMemoryVectorStore
from langchain_openai import OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
 
store = InMemoryVectorStore.from_texts(
    [
        "LangChain composes LLM components with the pipe operator.",
        "In LCEL, prompt | model | parser is a single Runnable.",
        "LangGraph handles stateful, multi-step agent workflows.",
    ],
    embedding=OpenAIEmbeddings(),
)
retriever = store.as_retriever(search_kwargs={"k": 2})
 
prompt = ChatPromptTemplate.from_template(
    "Answer using only the context below.\n\n{context}\n\nQuestion: {question}"
)
 
def format_docs(docs):
    return "\n\n".join(d.page_content for d in docs)
 
rag = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | model
    | StrOutputParser()
)
 
print(rag.invoke("How do you compose components in LangChain?"))

Üstteki sözlüğü "prompt girdilerini paralel olarak oluştur" şeklinde okuyabilirsiniz: question kullanıcının sorgusunu doğrudan iletirken, context bu sorguyu retriever üzerinden geçirip eşleşmeleri formatlar. İlk | işaretinden sonraki her şey, zaten bildiğiniz o basit chain'dir. İşte Runnable arayüzünün ödülü budur; retrieval, diğer her şeyle aynı pipe'a sorunsuzca yerleşir.

Tool'lar ve Agent'lar

Bir chain sabit bir yolda ilerler. Bir agent (ajan) ise kendi kararlarını verir; sunduğunuz tool'ları çağırabilir, sonuçları okuyabilir ve işi bitene kadar döngüye girebilir. v1'de bir Python fonksiyonunu @tool dekoratörüyle bir araca dönüştürürsünüz (docstring modele ne yaptığını söyler) ve agent'ı create_agent ile kurarsınız:

from langchain.tools import tool
from langchain.agents import create_agent
 
@tool
def word_count(text: str) -> int:
    """Return the number of words in the given text."""
    return len(text.split())
 
agent = create_agent(
    model="anthropic:claude-sonnet-5",
    tools=[word_count],
    system_prompt="You are precise. Use tools when they help.",
)
 
result = agent.invoke(
    {"messages": [{"role": "user", "content": "How many words are in 'the quick brown fox'?"}]}
)
print(result["messages"][-1].content)

Agent word_count'u görür, onu çağırmaya karar verir, "the quick brown fox" metnini iletip 4 yanıtını alır ve size cevap verir. "Bir tool kullanmalı mıyım?" mantığını siz hiç yazmadınız; bu tamamen agent'ın işidir.

Varsayılan olarak bir agent çağrılar arasındaki her şeyi unutur. Turlar arasında ona hafıza kazandırmak için bir checkpointer (kontrol noktası kaydedici) ekleyin ve bir thread_id ile çağırın. Böylece checkpointer, o iş parçacığı için konuşmayı kalıcı hâle getirir:

from langgraph.checkpoint.memory import InMemorySaver
 
agent = create_agent(
    model="anthropic:claude-sonnet-5",
    tools=[word_count],
    checkpointer=InMemorySaver(),
)
 
config = {"configurable": {"thread_id": "user-1"}}
agent.invoke({"messages": [{"role": "user", "content": "My name is Alper."}]}, config)
followup = agent.invoke({"messages": [{"role": "user", "content": "What's my name?"}]}, config)
print(followup["messages"][-1].content)  # Alper olduğunu bilir

Oradaki InMemorySaver import'u büyük resme dair ilk ipucunuzdur: v1'deki agent'lar LangGraph üzerinde çalışır. create_agent, LangGraph'ın durum makinesi (state machine) yapısı üzerine inşa edilmiş kullanıcı dostu bir hazır fonksiyondur. Gerçek bir dallanmaya, sürece dâhil olan insan onayına veya birbiriyle koordineli çalışan birden fazla agent'a ihtiyaç duyduğunuz an, create_agent'tan mezun olup grafiği kendiniz yazmaya başlarsınız. Ancak tüm kavramlar (durum, tool'lar, checkpointer'lar) doğrudan oraya da taşınır.

Aynı zamanda v1'de retrieval konseptinin gittiği yön de burasıdır: Yukarıdaki sabit RAG chain'i yerine, giderek artan bir şekilde agent'a bir arama tool'u verir ve bir şeyleri ne zaman arayacağına kendisinin karar vermesini sağlarsınız. Chain'in mantığını kurmak daha kolaydır; agent ise daha esnektir. İkisini de bilmekte fayda var.

Neler Olduğunu Görmek: LangSmith

Bir agent ilk kez şaşırtıcı bir karar verdiğinde, print() kullanmak artık yeterli gelmemeye başlar. LangSmith, LangChain'in takip aracıdır: İki çevre değişkeni ayarlarsınız ve her model çağrısı, tool çağrısı ve chain adımı, inceleyebileceğiniz görsel bir iz olarak kaydedilir.

export LANGSMITH_TRACING="true"
export LANGSMITH_API_KEY="your-key"

Kodda hiçbir değişiklik yapmanıza gerek kalmaz; aynı chain ve agent'lar otomatik olarak raporlamaya başlar. Birkaç kereden fazla çalıştırmayı planladığınız herhangi bir proje için bunu erkenden açın; LLM uygulamalarında körlemesine hata ayıklamak, çalışmanın en yavaş yoludur.

LangChain Ne Zaman Kullanılmalı — ve Ne Zaman Kullanılmamalı

Uygulamanızın çok sayıda hareketli parçası veya sağlayıcısı varsa LangChain hakkını verir: retrieval artı tool'lar artı hafıza veya kodu baştan yazmadan Claude, GPT ve yerel bir model arasında geçiş yapması gereken bir proje. Ortak arayüz, hazır entegrasyonlar ve LangSmith ile takip özellikleri burada size gerçekten zaman kazandırır.

Ancak uygulamanız tek bir prompt ve tek bir yanıttan ibaretse yanlış aracı kullanıyorsunuz demektir. Sadece tek bir modeli çağırıp .content okuyorsanız, sağlayıcının kendi SDK'si daha az katmana, daha az soyutlamaya sahiptir ve hata ayıklaması daha kolaydır. Orada LangChain'e başvurmak size hiçbir avantaj sağlamadan süreci dolaylı hâle getirir. İyi bir kural şudur: Doğrudan ham SDK ile başlayın; ancak ne zaman ki LangChain'in zaten standartlaştırdığı tesisat işlerini (chain'ler, retrieval, tool döngüleri) kendi başınıza sıfırdan yazmaya başladığınızı fark ederseniz, işte o an LangChain'e geçin.

Sırada Ne Var

Artık bütün iskelete hâkimsiniz: Her şey bir Runnable'dır, bunları pipe ile birleştirirsiniz ve bu tek fikir üç adımlı bir chain'den bir RAG ardışık düzenine ve tool kullanan bir agent'a kadar ölçeklenebilir. Buradan sonra en yüksek etkiyi yaratacak adımlar LangGraph (create_agent'ın yetmediği ve kontrol akışını kendiniz tasarlamanız gerektiğinde) ve LangSmith'tir (chain'lerinizin gerçekte ne yaptığını görebilmek için).

Gerçekten çalışan en küçük şeyi inşa edin; örneğin kendi beş belgeniz üzerinden soruları yanıtlayan bir chain. Sonra onu büyütün. Tüm bu bilgilerin kalıcı olmasını sağlamanın en hızlı yolu budur.

okunma

Yeni makale yayınlandığında haberiniz olsun ister misiniz?

Yeni bir yazı yayınlandığında size e-posta gelsin. Dilediğiniz an iptal edebilirsiniz.

Yorumlar

Benzer makaleler