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:

  1. 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."
  2. Teruskan parameter panggilan {"topic": "es krim"} ke komponen pertama dari rantai, prompt, yang akan memformat templat prompt untuk mendapatkan prompt Ceritakan sedikit lelucon tentang es krim.
  3. Teruskan prompt Ceritakan sedikit lelucon tentang es krim ke model (model gpt4).
  4. Teruskan hasil yang dikembalikan oleh model ke output_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:

  1. Pertama, buat objek RunnableParallel yang berisi dua entri. Entri pertama context akan mencakup hasil dokumen yang diekstraksi oleh retriever. Entri kedua question akan berisi pertanyaan asli pengguna. Untuk meneruskan pertanyaan, kita menggunakan RunnablePassthrough untuk menyalin entri ini.
  2. Teruskan kamus dari langkah sebelumnya ke komponen prompt. Ini menerima input pengguna (yaitu, question) serta dokumen yang diambil (yaitu, context), membangun prompt, dan menghasilkan PromptValue.
  3. Komponen model mengambil prompt yang dihasilkan dan meneruskannya ke model LLM OpenAI untuk evaluasi. Keluaran yang dihasilkan oleh model adalah objek ChatMessage.
  4. Terakhir, komponen output_parser mengambil ChatMessage, mengonversinya ke string Python, dan mengembalikannya dari metode invoke.