Memanfaatkan RunnableWithMessageHistory untuk menambahkan riwayat pesan ke jenis tugas (rantai) tertentu adalah fitur yang sangat berguna dalam pemrograman.
Fitur ini sangat penting ketika mengimplementasikan aplikasi interaktif atau tugas pemrosesan data yang kompleks, di mana ada kebutuhan untuk mempertahankan konteks pesan sebelumnya.
Dengan mengelola riwayat pesan, pengembang dapat mengontrol aliran aplikasi dengan lebih baik dan merespons permintaan sebelumnya dari pengguna dengan tepat.
Contoh
Mengembangkan chatbot percakapan: Sesuaikan respons chatbot berdasarkan riwayat dialog dengan pengguna.
Pemrosesan data yang kompleks: Selama pemrosesan data, Anda dapat merujuk ke hasil dari langkah sebelumnya untuk menentukan logika langkah berikutnya.
Aplikasi yang memerlukan manajemen status: Anda dapat mengingat pilihan pengguna sebelumnya dan memberikan layar atau informasi berikutnya yang sesuai.
RunnableWithMessageHistory adalah alat canggih yang memungkinkan Anda mempertahankan status aplikasi, meningkatkan pengalaman pengguna, dan mengimplementasikan mekanisme respons yang lebih canggih.
Tutorial
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholderfrom langchain_openai import ChatOpenAImodel =ChatOpenAI()prompt = ChatPromptTemplate.from_messages( [ ("system","Anda adalah asisten yang mahir dalam {ability}. Harap jawab dalam 20 karakter atau kurang", ),# menggunakan riwayat percakapan sebagai variabel, dengan riwayat sebagai kunci dari MessageHistoryMessagesPlaceholder(variable_name="history"), ("human", "{input}"), # gunakan masukan pengguna sebagai variabel ])runnable = prompt | model # menggabungkan prompt dan model untuk membuat objek yang dapat dijalankan
Mengelola riwayat pesan sangat penting dalam aplikasi interaktif atau tugas pemrosesan data yang kompleks. Untuk mengelola riwayat pesan secara efektif, Anda memerlukan dua elemen utama
Runnable: objek yang dapat dijalankan yang berinteraksi dengan BaseChatMessageHistory, terutama Retriever dan Chain.
Objek yang dapat dipanggil yang mengembalikan sebuah instance dari BaseChatMessageHistory: Objek untuk mengelola riwayat pesan. Objek ini digunakan untuk menyimpan, mengambil, dan memperbarui riwayat pesan. Riwayat pesan diperlukan untuk menjaga konteks percakapan dan untuk menghasilkan respons berdasarkan masukan pengguna sebelumnya.
Ada banyak cara untuk mengimplementasikan riwayat pesan, dan halaman integrasi memori menjelaskan berbagai pilihan penyimpanan dan integrasi.
Di sini kita akan melihat dua metode utama
Menggunakan ChatMessageHistory dalam memori
Metode ini mengelola riwayat pesan dalam memori. Metode ini sering digunakan selama fase pengembangan atau dalam aplikasi sederhana. Metode in-memory menyediakan kecepatan akses yang cepat, tetapi kekurangannya adalah riwayat pesan akan hilang ketika aplikasi dimulai ulang.
Memanfaatkan penyimpanan persisten dengan RedisChatMessageHistory
Menggunakan Redis memungkinkan Anda untuk menyimpan riwayat pesan secara persisten. Redis merupakan penyimpanan struktur data dalam-memori berkinerja tinggi, bersumber terbuka, dan dapat diandalkan untuk mengelola riwayat pesan dalam lingkungan terdistribusi. Metode ini ideal untuk aplikasi yang kompleks atau layanan yang sudah berjalan lama.
Ketika memilih metode untuk mengelola riwayat pesan, Anda harus mempertimbangkan kebutuhan aplikasi Anda, jumlah lalu lintas yang Anda perkirakan, pentingnya data pesan, dan berapa lama Anda ingin menyimpannya. Metode dalam memori sederhana dan cepat untuk diimplementasikan, tetapi jika Anda memerlukan persistensi data, penyimpanan persisten seperti Redis mungkin lebih cocok.
Riwayat percakapan yang volatil: In-Memory.
Di bawah ini adalah contoh sederhana riwayat obrolan yang disimpan dalam memori.
Parameter pengaturan RunnableWithMessageHistory
Runnable
BaseChatMessageHistory atau objek yang diwarisi. misalnya ChatMessageHistory
input_messages_key: kunci untuk menentukan sebagai input kueri pengguna saat memanggil rantai
history_messages_key: kunci untuk menentukan sebagai riwayat percakapan
from langchain_community.chat_message_histories import ChatMessageHistoryfrom langchain_core.chat_history import BaseChatMessageHistoryfrom langchain_core.runnables.history import RunnableWithMessageHistorystore ={}# Kamus untuk menyimpan riwayat sesi# Fungsi untuk mengambil riwayat sesi berdasarkan ID sesidefget_session_history(session_ids:str) -> BaseChatMessageHistory:print(session_ids)if session_ids notin store:# Jika ID sesi tidak ada di store# Buat objek ChatMessageHistory baru dan simpan ke store store[session_ids]=ChatMessageHistory()return store[session_ids]# Kembalikan riwayat sesi untuk ID sesi tersebutwith_message_history = (RunnableWithMessageHistory( # Buat objek RunnableWithMessageHistory runnable, # Objek Runnable yang akan dijalankan get_session_history, # Fungsi untuk mengambil riwayat sesi input_messages_key="input", # Kunci untuk pesan input history_messages_key="history", # Kunci untuk pesan riwayat ))
Input_messages_key menentukan kunci yang akan diperlakukan sebagai pesan masukan terbaru, dan history_messages_key menentukan kunci yang akan ditambahkan pesan lama.
Pada kode berikut, kita dapat melihat bahwa kunci session_id dimasukkan sebagai Default pada nilai awal RunnableWithMessageHistory, yang secara tidak langsung memberi tahu kita bahwa RunnableWithMessageHistory mengelola utas dialog dengan session_id.
Dengan kata lain, kita dapat melihat bahwa manajemen per-thread diimplementasikan oleh session_id.
if history_factory_config: _config_specs = history_factory_configelse:# If not provided, then we'll use the default session_id field _config_specs = [ConfigurableFieldSpec( id="session_id", annotation=str, name="Session ID", description="Unique identifier for a session.", default="", is_shared=True, ), ]
Jadi, pada invoke(), config = {“configurable”: {“session_id”: “Masukkan ID sesi"}} Anda dapat melihat bahwa kita harus menentukan kode.
with_message_history.invoke(# Mengirimkan pertanyaan terkait matematika "Apa arti dari kosinus?" sebagai input. {"ability": "math", "input": "Apa itu kosinus?"},# Mengirimkan ID sesi "abc123" sebagai informasi konfigurasi. config={"configurable": {"session_id": "abc123"}},)
Jika Anda memasukkan session_id yang sama, Anda akan mendapatkan konten dari utas percakapan sebelumnya, sehingga Anda dapat melanjutkan percakapan!
# Memanggil dengan menyertakan riwayat pesan.with_message_history.invoke(# Menetapkan kemampuan dan input. {"ability": "math", "input": "Tolong jawab dalam bahasa Inggris berdasarkan percakapan sebelumnya."},# Menetapkan opsi konfigurasi. config={"configurable": {"session_id": "abc123"}},)
Namun, jika Anda menentukan session_id yang berbeda, ia tidak akan menjawab dengan benar karena tidak ada riwayat percakapan.
(Pada contoh di bawah ini, Anda dapat melihat bahwa session_id: def234 memberikan jawaban yang salah karena tidak ada)
# Karena session_id baru, tidak mengingat percakapan sebelumnya.with_message_history.invoke(# Mengirimkan kemampuan matematika dan pesan input. {"ability": "math", "input": "Tolong jawab dalam bahasa Inggris berdasarkan percakapan sebelumnya"},# Menetapkan session_id baru. config={"configurable": {"session_id": "def234"}},)
def234
content='Sure, how can I help you?' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 8, 'prompt_tokens': 50, 'total_tokens': 58}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None} id='run-23961fe3-af1b-4711-b0be-34b0a09b6aa4-0' usage_metadata={'input_tokens': 50, 'output_tokens': 8, 'total_tokens': 58}
Parameter konfigurasi yang digunakan untuk melacak riwayat pesan dapat disesuaikan dengan mengoper daftar objek ConfigurableFieldSpec ke parameter history_factory_config.
Pengaturan baru history_factory_config ini akan menimpa pengaturan session_id yang sudah ada.
Contoh di bawah ini menggunakan dua parameter: user_id dan conversation_id.
from langchain_core.runnables import ConfigurableFieldSpecstore ={}# Inisialisasi kamus kosong.defget_session_history(user_id:str,conversation_id:str) -> BaseChatMessageHistory:# Mengembalikan riwayat sesi yang sesuai dengan user_id dan conversation_id yang diberikan.if (user_id, conversation_id) notin store:# Jika kunci tersebut tidak ada di store, buat dan simpan ChatMessageHistory baru. store[(user_id, conversation_id)]=ChatMessageHistory()return store[(user_id, conversation_id)]with_message_history =RunnableWithMessageHistory( runnable, get_session_history, input_messages_key="input", history_messages_key="history", history_factory_config=[ # Ini akan menggantikan pengaturan "session_id" yang ada.ConfigurableFieldSpec( id="user_id", # Digunakan sebagai argumen pertama dari fungsi get_session_history. annotation=str, name="User ID", description="Pengenal unik untuk pengguna.", default="", is_shared=True, ),ConfigurableFieldSpec( id="conversation_id", # Digunakan sebagai argumen kedua dari fungsi get_session_history. annotation=str, name="Conversation ID", description="Pengenal unik untuk percakapan.", default="", is_shared=True, ), ],)
with_message_history.invoke(# Mengirimkan kamus yang berisi kemampuan (ability) dan input (input). {"ability": "math", "input": "Hello"},# Mengirimkan kamus konfigurasi (config). config={"configurable": {"user_id": "123", "conversation_id": "1"}},)
content='Hi! What can I help you with today?' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 34, 'total_tokens': 44}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None} id='run-c22b0ab2-3ae8-474b-956a-615b6d0a2f04-0' usage_metadata={'input_tokens': 34, 'output_tokens': 10, 'total_tokens': 44}
Contoh penggunaan Runnable dengan berbagai keys.
Masukkan objek Pesan, keluarkan sebagai dictionary
Ini mengambil pesan sebagai masukan dan mengembalikan kamus sebagai keluaran.
[Penting]: Hilangkan input_messages_key = “input”, yang akan mengaturnya untuk mengambil objek Pesan sebagai input.
from langchain_core.messages import HumanMessagefrom langchain_core.runnables import RunnableParallel# Membuat chainchain =RunnableParallel({"output_message": ChatOpenAI()})defget_session_history(session_id:str) -> BaseChatMessageHistory:# Jika riwayat percakapan untuk session ID tidak ada di penyimpanan, buat ChatMessageHistory baru.if session_id notin store: store[session_id]=ChatMessageHistory()# Kembalikan riwayat percakapan untuk session ID yang diberikan.return store[session_id]# Membuat objek RunnableWithMessageHistory yang menambahkan fungsi riwayat percakapan ke dalam chain.with_message_history =RunnableWithMessageHistory( chain, get_session_history,# Mengatur kunci pesan input ke "input". (Diabaikan jika input berupa objek Message)# input_messages_key="input",# Mengatur kunci pesan output ke "output_message". (Diabaikan jika output berupa objek Message) output_messages_key="output_message",)# Menjalankan chain dengan pesan dan konfigurasi yang diberikan.with_message_history.invoke(# Atau bisa juga menggunakan "what is the definition of cosine?" [HumanMessage(content="Apa definisi dari kosinus?")], config={"configurable": {"session_id": "abc123"}},)
{'output_message': AIMessage(content='Kosinus adalah fungsi trigonometri yang merupakan rasio panjang sisi sejajar dengan sudut dalam sebuah segitiga siku-siku terhadap panjang sisi miring segitiga tersebut. Dalam matematika, kosinus sering dilambangkan dengan simbol "cos".', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 66, 'prompt_tokens': 15, 'total_tokens': 81}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-9fe92482-e149-4a32-b1b1-4243ad69a9d7-0', usage_metadata={'input_tokens': 15, 'output_tokens': 66, 'total_tokens': 81})}
with_message_history.invoke(# Meminta ulang jawaban sebelumnya dalam bahasa Korea. [HumanMessage(content="Tolong jawab dalam bahasa Inggris berdasarkan pertanyaan sebelumnya!")],# Mengirimkan opsi konfigurasi dalam bentuk kamus. config={"configurable": {"session_id": "abc123"}},)
{'output_message': AIMessage(content='Cosine is a mathematical function that measures the ratio of the length of the adjacent side to the length of the hypotenuse in a right triangle. In trigonometry, the cosine of a specific angle in a right triangle is defined as the length of the adjacent side to that angle divided by the length of the hypotenuse of the triangle.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 70, 'prompt_tokens': 141, 'total_tokens': 211}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-2a4115e8-91a4-4100-9b6e-98459b83dd0a-0', usage_metadata={'input_tokens': 141, 'output_tokens': 70, 'total_tokens': 211})}
Memasukkan objek Pesan, mengeluarkan objek Pesan
[Penting]: output_messages_key = “output_message”, yang akan mengembalikan objek Message sebagai output.
with_message_history =RunnableWithMessageHistory(ChatOpenAI(), # Menggunakan model bahasa ChatOpenAI. get_session_history, # Menetapkan fungsi untuk mengambil riwayat sesi percakapan.# Mengatur kunci pesan input ke "input". (Diabaikan jika input berupa objek Message)# input_messages_key="input",# Mengatur kunci pesan output ke "output_message". (Diabaikan jika output berupa objek Message)# output_messages_key="output_message",)
with_message_history.invoke(# Meminta ulang jawaban dalam bahasa Korea berdasarkan percakapan sebelumnya. [HumanMessage(content="Apa arti dari kosinus?")],# Mengirimkan opsi konfigurasi dalam bentuk kamus. config={"configurable": {"session_id": "def123"}},)
content='Kosinus adalah fungsi matematika yang menghasilkan nilai rasio dari panjang sisi sejajar terhadap panjang sisi miring pada segitiga siku-siku. Dalam trigonometri, kosinus dari suatu sudut dalam sebuah segitiga siku-siku adalah nilai rasio antara panjang sisi sejajar dengan sudut tersebut terhadap panjang sisi miring segitiga. Kosinus biasanya dilambangkan dengan simbol cos.' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 107, 'prompt_tokens': 15, 'total_tokens': 122}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None} id='run-ea96a85b-3072-4c4a-81a0-5ede74b6b4f0-0' usage_metadata={'input_tokens': 15, 'output_tokens': 107, 'total_tokens': 122}
Dict dengan satu key untuk semua input dan output pesan
Pendekatan ini menggunakan satu kunci untuk semua pesan masukan dan pesan keluaran.
Gunakan itemgetter(“input_messages”) untuk mengekstrak pesan masukan.
from operator import itemgetterwith_message_history =RunnableWithMessageHistory(# Menggunakan kunci "input_messages" untuk mengambil pesan input dan mengirimkannya ke ChatOpenAI().itemgetter("input_messages") |ChatOpenAI(), get_session_history, # Fungsi untuk mengambil riwayat sesi. input_messages_key="input_messages", # Menetapkan kunci untuk pesan input.)
with_message_history.invoke( {"input_messages": "Apa arti dari kosinus?"},# Mengirimkan opsi konfigurasi dalam bentuk kamus. config={"configurable": {"session_id": "xyz123"}},)
content='Kosinus adalah sebuah fungsi matematika yang menghasilkan nilai rasio dari panjang sisi sejajar dengan sudut tertentu dalam segitiga siku-siku, dibagi dengan panjang sisi miring segitiga tersebut. Kosinus digunakan dalam trigonometri untuk menghitung panjang sisi segitiga dan sudut-sudutnya.' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 82, 'prompt_tokens': 15, 'total_tokens': 97}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None} id='run-89eaedc8-3ab3-4f73-bac9-e0743eb68b47-0' usage_metadata={'input_tokens': 15, 'output_tokens': 82, 'total_tokens': 97}
Persistent Storage
Penyimpanan persisten mengacu pada mekanisme penyimpanan yang mempertahankan data bahkan ketika sebuah program dihentikan atau sistem di-boot ulang. Hal ini dapat diimplementasikan melalui database, sistem file, atau perangkat penyimpanan non-volatile lainnya.
Penyimpanan persisten sangat penting untuk menyimpan status aplikasi, mempertahankan pengaturan pengguna, dan menyimpan data untuk jangka waktu yang lama. Hal ini memungkinkan program untuk melanjutkan dari tempat yang ditinggalkannya pada proses sebelumnya, dan memungkinkan pengguna untuk terus bekerja tanpa kehilangan data.
RunnableWithMessageHistory tidak bergantung pada cara pemanggilan get_session_history untuk mengambil riwayat pesan obrolan.
Di bawah ini kami tunjukkan cara menggunakan Redis. Untuk mengetahui cara mengimplementasikan riwayat pesan obrolan menggunakan penyedia lain, lihat halaman integrasi memori.
Menginstall Redis
Jika Anda belum menginstal Redis, Anda harus menginstalnya terlebih dahulu.
pip install -qU redis
Menjalankan server Redis
Jika Anda tidak memiliki penerapan Redis yang sudah ada untuk disambungkan, mulai server Redis Stack lokal.
Berikut ini adalah perintah untuk memulai server Redis dengan Docker.
docker run -d -p 6379:6379 -p 8001:8001 redis/redis-stack:latest
Tetapkan URL koneksi basis data Redis ke variabel REDIS_URL.
URL ditetapkan ke “redis://localhost:6379/0”.
# Tentukan URL server Redis.
REDIS_URL = "redis://localhost:6379/0"
Menyiapkan pelacakan LangSmith
Siapkan LangSmith untuk pelacakan. LangSmith tidak diperlukan, tetapi dapat membantu.
from dotenv import load_dotenvimport osload_dotenv()# Tetapkan variabel lingkungan LANGCHAIN_TRACING_V2 ke “true”.os.environ["LANGCHAIN_TRACING_V2"]="true"# Mengatur LANGCHAIN_PROJECTos.environ["LANGCHAIN_PROJECT"]="RunnableWithMessageHistory"
Untuk memperbarui implementasi riwayat pesan, cukup definisikan objek baru yang dapat dipanggil, kali ini mengembalikan sebuah instance RedisChatMessageHistory.
from langchain_community.chat_message_histories import RedisChatMessageHistorydefget_message_history(session_id:str) -> RedisChatMessageHistory:# Mengembalikan objek RedisChatMessageHistory berdasarkan session ID.returnRedisChatMessageHistory(session_id, url=REDIS_URL)with_message_history =RunnableWithMessageHistory( runnable, # Objek yang dapat dijalankan get_message_history, # Fungsi untuk mengambil riwayat pesan input_messages_key="input", # Kunci untuk pesan input history_messages_key="history", # Kunci untuk pesan riwayat)
Anda dapat memanggilnya dengan cara yang sama seperti sebelumnya.
with_message_history.invoke(# Mengirimkan pertanyaan terkait matematika "Apa arti dari kosinus?" sebagai input. {"ability": "math", "input": "Apa arti dari kosinus?"},# Menetapkan session ID menjadi "redis123" sebagai opsi konfigurasi. config={"configurable": {"session_id": "redis123"}},)
Lakukan panggilan kedua dengan menggunakan session_id yang sama. Kali ini, kami akan meminta jawaban sebelumnya dalam bahasa Korea.
with_message_history.invoke(# Meminta terjemahan jawaban sebelumnya ke dalam bahasa Korea. {"ability": "math", "input": "Tolong terjemahkan jawaban sebelumnya ke dalam bahasa Korea."},# Menetapkan session ID menjadi "redis123" sebagai nilai konfigurasi. config={"configurable": {"session_id": "redis123"}},)
Kali ini, kita akan mengajukan pertanyaan dengan menggunakan session_id yang berbeda.
with_message_history.invoke(# Meminta terjemahan jawaban sebelumnya ke dalam bahasa Korea. {"ability": "math", "input": "Tolong terjemahkan jawaban sebelumnya ke dalam bahasa Korea."},# Menetapkan session ID menjadi "redis456" sebagai nilai konfigurasi. config={"configurable": {"session_id": "redis456"}},)