Pengenalan LCEL
LCEL (LangChain Expression Language) adalah alat orkestrasi alur kerja yang kuat yang memungkinkan Anda membangun rantai tugas kompleks dari komponen-komponen dasar dan mendukung fitur-fitur bawaan seperti pemrosesan streaming, pemrosesan paralel, dan pencatatan.
Contoh Dasar: Prompt + Model + Output Parser
Pada contoh ini, kami akan menunjukkan bagaimana menggunakan LCEL (LangChain Expression Language) untuk menghubungkan tiga komponen - templat prompt, model, dan output parser - bersama untuk membentuk alur kerja lengkap untuk mengimplementasikan tugas "bercanda". Kode ini menunjukkan bagaimana membuat rantai, menggunakan simbol pipa |
untuk menghubungkan komponen-komponen yang berbeda, dan memperkenalkan peran masing-masing komponen bersama dengan hasil keluarannya.
Pertama, mari kita lihat bagaimana menghubungkan templat prompt dan model untuk menghasilkan lelucon tentang topik tertentu:
Pasang dependensi
%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("Ceritakan lelucon tentang {topik}")
model = ChatOpenAI(model="gpt-4")
output_parser = StrOutputParser()
chain = prompt | model | output_parser
chain.invoke({"topik": "eskrim"})
Keluaran
"Mengapa pesta tidak mengundang eskrim? Karena eskrim mencair saat panas!"
Pada kode ini, kami menggunakan LCEL untuk menghubungkan komponen-komponen yang berbeda menjadi sebuah rantai:
chain = prompt | model | output_parser
Simbol |
di sini mirip dengan operator pipa Unix, yang menghubungkan komponen-komponen yang berbeda bersama dan meneruskan keluaran dari satu komponen sebagai masukan ke komponen berikutnya.
Dalam rantai ini, input pengguna diteruskan ke templat prompt, kemudian keluaran dari templat prompt diteruskan ke model, dan akhirnya keluaran model diteruskan ke output parser. Mari kita lihat masing-masing komponen secara terpisah untuk lebih memahami apa yang terjadi.
1. Prompt
prompt
adalah BasePromptTemplate
yang menerima kamus variabel templat dan menghasilkan PromptValue
. PromptValue
adalah objek yang dibungkus yang berisi prompt, yang dapat diteruskan ke LLM
(sebagai masukan dalam bentuk string) atau ChatModel
(sebagai masukan dalam bentuk urutan pesan). Ini dapat digunakan dengan jenis model bahasa apa pun karena mendefinisikan logika untuk menghasilkan BaseMessage
dan menghasilkan string.
prompt_value = prompt.invoke({"topik": "eskrim"})
prompt_value
Keluaran
ChatPromptValue(messages=[HumanMessage(content='Ceritakan lelucon tentang eskrim')])
Di bawah ini, kami mengonversi hasil yang diformat prompt ke format pesan yang digunakan oleh model percakapan:
prompt_value.to_messages()
Keluaran
[HumanMessage(content='Ceritakan lelucon tentang eskrim')]
Ini juga dapat langsung dikonversi menjadi string:
prompt_value.to_string()
Keluaran
'Human: Ceritakan lelucon tentang eskrim.'
2. Model
Selanjutnya, lewatkan PromptValue
ke model
. Pada contoh ini, model kami adalah ChatModel
, yang berarti akan menghasilkan BaseMessage
.
Coba panggil model
secara langsung:
pesan = model.invoke(prompt_value)
pesan
Hasil:
AIMessage(content="Mengapa eskrim tidak pernah diundang ke pesta?\n\nKarena mereka selalu mencair saat suasana panas!")
Jika model kami didefinisikan sebagai tipe LLM
, itu akan menghasilkan string.
from langchain_openai.llms import OpenAI
llm = OpenAI(model="gpt-3.5-turbo-instruct")
llm.invoke(prompt_value)
Model menghasilkan:
'\n\nBot: Mengapa truk eskrim rusak? Karena itu mengalami leburan!'
3. Output Parser
Terakhir, lewatkan keluaran dari model
ke output_parser
, yang merupakan BaseOutputParser
, yang berarti menerima string atau BaseMessage
sebagai masukan. StrOutputParser
khususnya mengubah masukan apa pun ke dalam bentuk string sederhana.
output_parser.invoke(pesan)
Mengapa eskrim tidak pernah diundang ke pesta? \n\nKarena mereka selalu mencair saat suasana panas!
4. Seluruh Proses
Proses eksekusi dilakukan sebagai berikut:
- Panggil
chain.invoke({"topic": "es krim"})
, yang setara dengan memulai alur kerja yang telah kita definisikan dan meneruskan parameter{"topic": "es krim"}
untuk menghasilkan lelucon tentang "es krim." - Teruskan parameter panggilan
{"topic": "es krim"}
ke komponen pertama dari rantai,prompt
, yang akan memformat templat prompt untuk mendapatkan promptCeritakan sedikit lelucon tentang es krim
. - Teruskan prompt
Ceritakan sedikit lelucon tentang es krim
kemodel
(model gpt4). - Teruskan hasil yang dikembalikan oleh
model
keoutput_parser
output parser, yang akan memformat hasil model dan mengembalikan konten akhir.
Jika Anda tertarik pada keluaran dari komponen manapun, Anda bisa menguji versi lebih kecil dari rantai kapan saja, seperti prompt
or prompt | model
, untuk melihat hasil perantara:
input = {"topic": "es krim"}
prompt.invoke(input)
(prompt | model).invoke(input)
Contoh Pencarian RAG
Selanjutnya, mari kita jelaskan contoh LCEL yang sedikit lebih kompleks. Kita akan menjalankan contoh retrieval yang ditingkatkan untuk menghasilkan rantai, untuk menambahkan beberapa informasi latar belakang saat menjawab pertanyaan.
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 bekerja di kensho", "beruang suka makan madu"],
embedding=OpenAIEmbeddings(),
)
retriever = vectorstore.as_retriever()
template = """Jawablah pertanyaan berdasarkan hanya pada konteks berikut ini:
{context}
Pertanyaan: {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("dimana harrison bekerja?")
Dalam hal ini, rantai yang disusun adalah:
chain = setup_and_retrieval | prompt | model | output_parser
Secara sederhana, templat prompt di atas menerima context
dan question
sebagai nilai untuk menggantikan di prompt. Sebelum membangun templat prompt, kita ingin mengambil dokumen-dokumen terkait untuk digunakan sebagai bagian dari konteks.
Sebagai uji coba, kita menggunakan DocArrayInMemorySearch
untuk mensimulasikan database vektor berbasis memori, mendefinisikan retriever yang dapat mengambil dokumen-dokumen serupa berdasarkan kueri. Ini juga merupakan komponen runnable yang dapat dijalankan secara terpisah:
retriever.invoke("dimana harrison bekerja?")
Kemudian, kita menggunakan RunnableParallel
untuk mempersiapkan input untuk prompt, mencari dokumen menggunakan retriever, dan meneruskan pertanyaan pengguna menggunakan RunnablePassthrough
:
setup_and_retrieval = RunnableParallel(
{"context": retriever, "question": RunnablePassthrough()}
)
Secara ringkas, rantai lengkap adalah:
setup_and_retrieval = RunnableParallel(
{"context": retriever, "question": RunnablePassthrough()}
)
chain = setup_and_retrieval | prompt | model | output_parser
Prosesnya adalah sebagai berikut:
- Pertama, buat objek
RunnableParallel
yang berisi dua entri. Entri pertamacontext
akan mencakup hasil dokumen yang diekstraksi oleh retriever. Entri keduaquestion
akan berisi pertanyaan asli pengguna. Untuk meneruskan pertanyaan, kita menggunakanRunnablePassthrough
untuk menyalin entri ini. - Teruskan kamus dari langkah sebelumnya ke komponen
prompt
. Ini menerima input pengguna (yaitu,question
) serta dokumen yang diambil (yaitu,context
), membangun prompt, dan menghasilkanPromptValue
. - Komponen
model
mengambil prompt yang dihasilkan dan meneruskannya ke model LLM OpenAI untuk evaluasi. Keluaran yang dihasilkan oleh model adalah objekChatMessage
. - Terakhir, komponen
output_parser
mengambilChatMessage
, mengonversinya ke string Python, dan mengembalikannya dari metodeinvoke
.