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:
-
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. - Çağrı parametresi
{"topic": "dondurma"}'ı zincirin ilk bileşeni olanprompt'a geçirin, bu da görev şablonunu biçimlendirerekTell me a little joke about ice creamgörevini elde etmek için geçerli bir şekilde biçimlendirir. -
Tell me a little joke about ice creamgörevinimodel(gpt4 modeli)'ye iletin. -
modeltarafından döndürülen sonucuoutput_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:
- İlk olarak,
RunnableParalleliçerisinde iki girdi içeren bir nesne oluşturun. İlk giriş olancontextgetirici tarafından çıkarılan belge sonuçlarını içerecektir. İkinci giriş olanquestionise kullanıcının orijinal sorusunu içerecektir. Soruyu iletmek için bu girdiyi kopyalamak içinRunnablePassthroughkullanırız. - Önceki adımda elde edilen sözlüğü
promptbileşenine iletin. Bu, kullanıcı girişini (yaniquestion) ve elde edilen belgeleri (yanicontext) kabul eder, bir görev oluşturur ve birPromptValueçıktısı verir. -
modelbileş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ı birChatMessagenesnesidir. - Son olarak,
output_parserbileşeni birChatMessagealır, bunu Python dizesine dönüştürür veinvokeyönteminden döndürür.