拆解 LangChain 的大模型記憶方案
之前我們聊過如何使用LangChain給LLM(大模型)裝上記憶,里面提到對話鏈ConversationChain和MessagesPlaceholder,可以簡化安裝記憶的流程。下文來拆解基于LangChain的大模型記憶方案。
1. 安裝記憶的原理
(1) 核心步驟
給LLM安裝記憶的核心步驟就3個:
- 在對話之前調(diào)取之前的歷史消息。
- 將歷史消息填充到Prompt里。
- 對話結(jié)束后,繼續(xù)將歷史消息保存到到memory記憶中。
(2) 常規(guī)使用方法的弊端
了解這3個核心步驟后,在開發(fā)過程中,就需要手動寫代碼實現(xiàn)這3步,這也比較麻煩,不僅代碼冗余,而且容易遺漏這些模板代碼。
為了讓開發(fā)者聚焦于業(yè)務(wù)實現(xiàn),LangChain貼心地封裝了這一整套實現(xiàn)。使用方式如下。
2. 記憶的種類
記憶分為 短時記憶 和 長時記憶。
在LangChain中使用ConversationBufferMemory作為短時記憶的組件,實際上就是以鍵值對的方式將消息存在內(nèi)存中。
如果碰到較長的對話,一般使用ConversationSummaryMemory對上下文進行總結(jié),再交給大模型?;蛘呤褂肅onversationTokenBufferMemory基于固定的token數(shù)量進行內(nèi)存刷新。
如果想對記憶進行長時間的存儲,則可以使用向量數(shù)據(jù)庫進行存儲(比如FAISS、Chroma等),或者存儲到Redis、Elasticsearch中。
下面以ConversationBufferMemory為例,對如何快速安裝記憶做個實踐。
3. 給LLM安裝記憶 — 非MessagesPlaceholder
(1)ConversationBufferMemory使用示例
使用ConversationBufferMemory進行記住上下文:
memory = ConversationBufferMemory()
memory.save_context(
{"input": "你好,我的名字是半支煙,我是一個程序員"}, {"output": "你好,半支煙"}
)
memory.load_memory_variables({})
(2)LLMChain+ConversationBufferMemory使用示例
# prompt模板
template = """
你是一個對話機器人,以下<history>標簽中是AI與人類的歷史對話記錄,請你參考歷史上下文,回答用戶輸入的問題。
歷史對話:
<history>
{customize_chat_history}
</history>
人類:{human_input}
機器人:
"""
prompt = PromptTemplate(
template=template,
input_variables=["customize_chat_history", "human_input"],
)
memory = ConversationBufferMemory(
memory_key="customize_chat_history",
)
model = ChatOpenAI(
model="gpt-3.5-turbo",
)
chain = LLMChain(
llm=model,
memory=memory,
prompt=prompt,
verbose=True,
)
chain.predict(human_input="你知道我的名字嗎?")
# chain.predict(human_input="我叫半支煙,我是一名程序員")
# chain.predict(human_input="你知道我的名字嗎?")
此時,已經(jīng)給LLM安裝上記憶了,免去了我們寫那3步核心的模板代碼。
對于PromptTemplate使用以上方式,但ChatPromptTemplate因為有多角色,所以需要使用MessagesPlaceholder。具體使用方式如下。
4. 給LLM安裝記憶 — MessagesPlaceholder
MessagesPlaceholder主要就是用于ChatPromptTemplate場景。ChatPromptTemplate模式下,需要有固定的格式。
(1) PromptTemplate和ChatPromptTemplate區(qū)別
ChatPromptTemplate主要用于聊天場景。ChatPromptTemplate有多角色,第一個是System角色,后續(xù)的是Human與AI角色。因為需要有記憶,所以之前的歷史消息要放在最新問題的上方。
(2) 使用MessagesPlaceholder安裝
最終的ChatPromptTemplate + MessagesPlaceholder代碼如下:
chat_prompt = ChatPromptTemplate.from_messages(
[
("system", "你是一個樂于助人的助手。"),
MessagesPlaceholder(variable_name="customize_chat_history"),
("human", "{human_input}"),
]
)
memory = ConversationBufferMemory(
memory_key="customize_chat_history",
return_messages=True,
)
model = ChatOpenAI(
model="gpt-3.5-turbo",
)
chain = LLMChain(
llm=model,
memory=memory,
prompt=chat_prompt,
verbose=True,
)
chain.predict(human_input="你好,我叫半支煙,我是一名程序員。")
至此,我們使用了ChatPromptTemplate簡化了構(gòu)建prompt的過程。
5.使用對話鏈ConversationChain
如果連ChatPromptTemplate都懶得寫了,那直接使用對話鏈ConversationChain,讓一切變得更簡單。實踐代碼如下:
memory = ConversationBufferMemory(
memory_key="history", # 此處的占位符必須是history
return_messages=True,
)
model = ChatOpenAI(
model="gpt-3.5-turbo",
)
chain = ConversationChain(
llm=model,
memory=memory,
verbose=True,
)
chain.predict(input="你好,我叫半支煙,我是一名程序員。") # 此處的變量必須是input
ConversationChain提供了包含AI角色和人類角色的對話摘要格式。ConversationChain實際上是對Memory和LLMChain和ChatPrompt進行了封裝,簡化了初始化Memory和構(gòu)建ChatPromptTemplate的步驟。
6. ConversationBufferMemory
(1) memory_key
ConversationBufferMemory有一個入?yún)⑹莔emory_key,表示內(nèi)存中存儲的本輪對話的鍵,后續(xù)可以根據(jù)鍵找到對應(yīng)的值。
(2) 使用"chat_history"還是"history"
ConversationBufferMemory的memory_key,有些資料里是設(shè)置是memory_key="history",有些資料里是"chat_history"。
這里有2個規(guī)則,如下:
- 在使用MessagesPlaceholder和ConversationBufferMemory時,MessagesPlaceholder的variable_name和ConversationBufferMemory的memory_key可以自定義,只要相同就可以。比如這樣:
chat_prompt = ChatPromptTemplate.from_messages(
[
("system", "你是一個樂于助人的助手。"),
MessagesPlaceholder(variable_name="customize_chat_history"),
("human", "{input}"),
]
)
memory = ConversationBufferMemory(
memory_key="customize_chat_history", # 此處的占位符可以是自定義
return_messages=True,
)
model = ChatOpenAI(
model="gpt-3.5-turbo",
)
chain = ConversationChain(
llm=model,
memory=memory,
prompt=chat_prompt,
verbose=True,
)
chain.predict(input="你好,我叫半支煙,我是一名程序員。") # 此處的變量必須是input
- 如果只是使用ConversationChain,又沒有使用MessagesPlaceholder的場景下,ConversationBufferMemory的memory_key,必須用history。
7. MessagesPlaceholder的使用場景
MessagesPlaceholder其實就是在與AI對話過程中的Prompt的一部分,它代表Prompt中的歷史消息這部分。它提供了一種結(jié)構(gòu)化和可配置的方式來處理這些消息列表,使得在構(gòu)建復(fù)雜Prompt時更加靈活和高效。
說白了它就是個占位符,相當于把從memory讀取的歷史消息插入到這個占位符里了。
比如這樣,就可以表示之前的歷史對話消息:
chat_prompt = ChatPromptTemplate.from_messages(
[
("system", "你是一個樂于助人的助手。"),
MessagesPlaceholder(variable_name="customize_chat_history"),
("human", "{human_input}"),
]
)
是否需要使用MessagesPlaceholder,記住2個原則:
- PromptTemplate類型的模板,無需使用MessagesPlaceholder
- ChatPromptTemplate 類型的聊天模板,需要使用MessagesPlaceholder。但是在使用ConversationChain時,可以省去創(chuàng)建ChatPromptTemplate的過程(也可以不省去)。省去和不省去在輸出過程中有些區(qū)別,如下:
總結(jié)
本文主要聊了安裝記憶的基本原理、快速給LLM安裝記憶、ConversationBufferMemory、MessagesPlaceholder的使用、對話鏈ConversationChain的使用和原理。希望對你有幫助!