拆解LangChain的大模型記憶方案

程序员半支烟發表於2024-07-09

之前我們聊過如何使用LangChain給LLM(大模型)裝上記憶,裡面提到對話鏈ConversationChainMessagesPlaceholder,可以簡化安裝記憶的流程。下文來拆解基於LangChain的大模型記憶方案。

1. 安裝記憶的原理

1.1. 核心步驟

給LLM安裝記憶的核心步驟就3個:

  1. 在對話之前調取之前的歷史訊息。
  2. 將歷史訊息填充到Prompt裡。
  3. 對話結束後,繼續將歷史訊息儲存到到memory記憶中。

1.2. 常規使用方法的弊端

瞭解這3個核心步驟後,在開發過程中,就需要手動寫程式碼實現這3步,這也比較麻煩,不僅程式碼冗餘,而且容易遺漏這些模板程式碼。

為了讓開發者聚焦於業務實現,LangChain貼心地封裝了這一整套實現。使用方式如下。

2. 記憶的種類

記憶分為 短時記憶 和 長時記憶。

在LangChain中使用ConversationBufferMemory作為短時記憶的元件,實際上就是以鍵值對的方式將訊息存在記憶體中。

如果碰到較長的對話,一般使用ConversationSummaryMemory對上下文進行總結,再交給大模型。或者使用ConversationTokenBufferMemory基於固定的token數量進行記憶體重新整理。

如果想對記憶進行長時間的儲存,則可以使用向量資料庫進行儲存(比如FAISS、Chroma等),或者儲存到Redis、Elasticsearch中。

下面以ConversationBufferMemory為例,對如何快速安裝記憶做個實踐。

3. 給LLM安裝記憶 — 非MessagesPlaceholder

3.1. ConversationBufferMemory使用示例

使用ConversationBufferMemory進行記住上下文:

memory = ConversationBufferMemory()
memory.save_context(
    {"input": "你好,我的名字是半支菸,我是一個程式設計師"}, {"output": "你好,半支菸"}
)
memory.load_memory_variables({})

3.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="你知道我的名字嗎?")

此時,已經給LLM安裝上記憶了,免去了我們寫那3步核心的模板程式碼。

對於PromptTemplate使用以上方式,但ChatPromptTemplate因為有多角色,所以需要使用MessagesPlaceholder。具體使用方式如下。

4. 給LLM安裝記憶 — MessagesPlaceholder

MessagesPlaceholder主要就是用於ChatPromptTemplate場景。ChatPromptTemplate模式下,需要有固定的格式。

4.1. PromptTemplate和ChatPromptTemplate區別

ChatPromptTemplate主要用於聊天場景。ChatPromptTemplate有多角色,第一個是System角色,後續的是Human與AI角色。因為需要有記憶,所以之前的歷史訊息要放在最新問題的上方。

4.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簡化了構建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和構建ChatPromptTemplate的步驟。

6. ConversationBufferMemory

6.1. memory_key

ConversationBufferMemory有一個入參是memory_key,表示記憶體中儲存的本輪對話的,後續可以根據找到對應的值。

6.2. 使用"chat_history"還是"history"

ConversationBufferMemorymemory_key,有些資料裡是設定是memory_key="history",有些資料裡是"chat_history"

這裡有2個規則,如下:

  • 在使用MessagesPlaceholderConversationBufferMemory時,MessagesPlaceholdervariable_nameConversationBufferMemorymemory_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中的歷史訊息這部分。它提供了一種結構化和可配置的方式來處理這些訊息列表,使得在構建複雜Prompt時更加靈活和高效。

說白了它就是個佔位符,相當於把從memory讀取的歷史訊息插入到這個佔位符裡了。

比如這樣,就可以表示之前的歷史對話訊息:

chat_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一個樂於助人的助手。"),
        MessagesPlaceholder(variable_name="customize_chat_history"),
        ("human", "{human_input}"),
    ]
)

是否需要使用MessagesPlaceholder,記住2個原則:

  • PromptTemplate型別的模板,無需使用MessagesPlaceholder

  • ChatPromptTemplate 型別的聊天模板,需要使用MessagesPlaceholder。但是在使用ConversationChain時,可以省去建立ChatPromptTemplate的過程(也可以不省去)。省去和不省去在輸出過程中有些區別,如下:

8. 總結

本文主要聊了安裝記憶的基本原理、快速給LLM安裝記憶、ConversationBufferMemoryMessagesPlaceholder的使用、對話鏈ConversationChain的使用和原理。希望對你有幫助!

=====>>>>>> 關於我 <<<<<<=====

本篇完結!歡迎點贊 關注 收藏!!!

原文連結:https://mp.weixin.qq.com/s/cRavfyu--AjBOO3-1aY0UA

相關文章