LCEL Tanıtımı

LCEL (LangChain Expression Language), temel bileşenlerden karmaşık görev zincirleri oluşturmanıza izin veren güçlü bir iş akışı orkestrasyon aracıdır ve akış işleme, eş zamanlı işleme ve günlüğe alma gibi özellikleri destekler.

Temel Örnek: İstekte Bulunma + Model + Çıktı Ayrıştırıcı

Bu örnekte, LCEL (LangChain Expression Language) kullanarak prompt şablonu, model ve çıktı ayrıştırıcı gibi üç bileşeni bir araya getirerek "şaka anlatma" görevini uygulamak için tam bir iş akışı oluşturmayı göstereceğiz. Kod, zincirler oluşturmayı, farklı bileşenleri bağlamak için boru (|) sembolünü kullanmayı ve her bileşenin rolünü ve çıktı sonuçlarını tanıtmayı göstermektedir.

İlk olarak, prompt şablonunu ve modeli bir araya getirerek belirli bir konu hakkında şaka oluşturmayı gösterelim:

Gereksinimleri yükleyin

%pip install --upgrade --quiet langchain-core langchain-community langchain-openai
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

prompt = ChatPromptTemplate.from_template("Bana {topic} ile ilgili bir şaka anlat")
model = ChatOpenAI(model="gpt-4")
output_parser = StrOutputParser()

chain = prompt | model | output_parser

chain.invoke({"topic": "dondurma"})

Çıktı

"Neden partilere dondurma çağırmaz? Çünkü sıcak olduğunda erir!"

Bu kodda, farklı bileşenleri bir zincir halinde birleştirmek için LCEL kullanıyoruz:

chain = prompt | model | output_parser

Buradaki | sembolü, Unix boru işlemcisi'ne benzer şekilde farklı bileşenleri birbirine bağlar ve bir bileşenin çıktısını diğer bileşenin girdisi olarak iletilir.

Bu zincirde, kullanıcı girdisi prompt şablonuna iletilir, ardından prompt şablonunun çıktısı modele iletilir ve son olarak modelin çıktısı çıktı ayrıştırıcıya iletilir. Olup biteni daha iyi anlamak için her bir bileşene ayrı ayrı bakalım.

1. Prompt

prompt, şablon değişkenlerini kabul eden bir BasePromptTemplate'dir ve bir PromptValue üretir. PromptValue, dize üreten platformlar için mantık tanımlayan tüm dil modelleriyle kullanılabilir çünkü BaseMessage üreten ve dize üreten mantığı tanımlar.

prompt_value = prompt.invoke({"topic": "dondurma"})
prompt_value

Çıktı

ChatPromptValue(messages=[HumanMessage(content='Bana dondurma ile ilgili bir şaka anlat')])

Aşağıda, prompt biçimli sonucu sohbet modelleri tarafından kullanılan mesaj biçimine dönüştürüyoruz:

prompt_value.to_messages()

Çıktı

[HumanMessage(content='Bana dondurma ile ilgili bir şaka anlat')]

Aynı zamanda doğrudan bir dizeye de dönüştürülebilir:

prompt_value.to_string()

Çıktı

'İnsan: Bana dondurma ile ilgili bir şaka anlat.'

2. Model

Şimdi, PromptValue'yi model e geçirelim. Bu örnekte, modelimiz ChatModel'dir, bu da bir BaseMessage çıktısı vereceği anlamına gelir.

Modeli doğrudan çağırmayı deneyin:

message = model.invoke(prompt_value)
message

Döndürür:

AIMessage(content="Neden partilere dondurma çağırmaz? Çünkü sıcak olduğunda erir!")

Eğer modelimiz bir LLM türü olarak tanımlanmışsa, birden fazla dize çıktısı verecektir.

from langchain_openai.llms import OpenAI

llm = OpenAI(model="gpt-3.5-turbo-instruct")
llm.invoke(prompt_value)

Model çıktısı:

'\n\nBot: Dondurma arabası neden bozuldu? Çünkü erimeye başladı!'

3. Çıktı Ayrıştırıcı

Son olarak, modelden gelen çıktıyı output_parser'a iletelim, bu da bir BaseOutputParser'dır, yani bir dize veya BaseMessage'ı girdi olarak kabul eder. StrOutputParser özellikle herhangi bir girdiyi basit bir dizeye dönüştürür.

output_parser.invoke(message)
Neden partilere dondurma çağırmaz? \n\nÇünkü sıcak olduğunda erir!

4. Tüm Süreç

Yürütme süreci şöyledir:

  1. chain.invoke({"topic": "dondurma"}) çağrısını yapın, bu, tanımladığımız iş akışını başlatmak ve {"topic": "dondurma"} parametresini geçerek "dondurma" ile ilgili bir şaka oluşturmak anlamına gelir.
  2. Çağrı parametresi {"topic": "dondurma"}'ı zincirin ilk bileşeni olan prompt'a geçirin, bu da görev şablonunu biçimlendirerek Tell me a little joke about ice cream görevini elde etmek için geçerli bir şekilde biçimlendirir.
  3. Tell me a little joke about ice cream görevini model (gpt4 modeli)'ye iletin.
  4. model tarafından döndürülen sonucu output_parser çıkış analizine ileterek model sonucunu biçimlendirir ve nihai içeriği döndürür.

Herhangi bir bileşenin çıktısına ilginiz varsa, herhangi bir zamanda zincirin daha küçük bir sürümünü test etmek için prompt veya prompt | model gibi bir şeyi deneyebilirsiniz:

input = {"topic": "dondurma"}

prompt.invoke(input)

(prompt | model).invoke(input)

RAG Arama Örneği

Şimdi, biraz daha karmaşık bir LCEL örneğini açıklamak istiyoruz. Soruları cevaplarken arka plan bilgisi eklemek için zincirler oluşturmak için geliştirilmiş bir geri alım örneğini çalıştıracağız.

from langchain_community.vectorstores import DocArrayInMemorySearch
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
from langchain_openai.chat_models import ChatOpenAI
from langchain_openai.embeddings import OpenAIEmbeddings

vectorstore = DocArrayInMemorySearch.from_texts(
    ["harrison worked at kensho", "bears like to eat honey"],
    embedding=OpenAIEmbeddings(),
)
retriever = vectorstore.as_retriever()

template = """Sadece aşağıdaki bağlam temel alınarak soruyu cevaplayın:
{context}

Soru: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
model = ChatOpenAI()
output_parser = StrOutputParser()

setup_and_retrieval = RunnableParallel(
    {"context": retriever, "question": RunnablePassthrough()}
)
chain = setup_and_retrieval | prompt | model | output_parser

chain.invoke("harrison hangi şirkette çalıştı?")

Bu durumda, oluşturulmuş zincir şöyle:

chain = setup_and_retrieval | prompt | model | output_parser

Basitçe, yukarıdaki görev şablonu context ve question değerlerini yerine koymak için kullanır. Görev şablonunu oluşturmadan önce, cevap olarak kullanmak amacıyla ilgili belgeleri almak istiyoruz.

Bir test olarak, DocArrayInMemorySearch'ü kullanarak bellek tabanlı bir vektör veritabanını taklit etmek için bir alır oluştururuz, sorgulara dayalı benzer belgeleri alabilen bir getirici tanımlarız. Bu da zincirlenebilir bir çalıştırılabilir bileşendir, ancak ayrı çalıştırmayı da deneyebilirsiniz:

retriever.invoke("harrison hangi şirkette çalıştı?")

Daha sonra, görev için girdiyi hazırlamak için RunnableParallel'ı kullanırız, getiriciyi kullanarak belgeleri arar ve kullanıcının sorusunu RunnablePassthrough kullanarak geçiririz:

setup_and_retrieval = RunnableParallel(
    {"context": retriever, "question": RunnablePassthrough()}
)

Özetle, tam zincir şu şekildedir:

setup_and_retrieval = RunnableParallel(
    {"context": retriever, "question": RunnablePassthrough()}
)
chain = setup_and_retrieval | prompt | model | output_parser

Süreç şöyledir:

  1. İlk olarak, RunnableParallel içerisinde iki girdi içeren bir nesne oluşturun. İlk giriş olan context getirici tarafından çıkarılan belge sonuçlarını içerecektir. İkinci giriş olan question ise kullanıcının orijinal sorusunu içerecektir. Soruyu iletmek için bu girdiyi kopyalamak için RunnablePassthrough kullanırız.
  2. Önceki adımda elde edilen sözlüğü prompt bileşenine iletin. Bu, kullanıcı girişini (yani question) ve elde edilen belgeleri (yani context) kabul eder, bir görev oluşturur ve bir PromptValue çıktısı verir.
  3. model bileşeni oluşturulan görevi alır ve OpenAI'nın LLM modeline değerlendirme için ileterek işletir. Model tarafından üretilen çıktı bir ChatMessage nesnesidir.
  4. Son olarak, output_parser bileşeni bir ChatMessage alır, bunu Python dizesine dönüştürür ve invoke yönteminden döndürür.