跳到主要內容
Open In ColabOpen on GitHub

如何新增聊天紀錄

注意

本指南先前使用了 RunnableWithMessageHistory 抽象概念。您可以在 v0.2 文件中存取此版本的說明文件。

從 LangChain v0.3 版本開始,我們建議 LangChain 使用者利用 LangGraph 持久性將記憶體整合到新的 LangChain 應用程式中。

如果您的程式碼已經依賴 RunnableWithMessageHistoryBaseChatMessageHistory,則無需進行任何變更。我們目前沒有計畫在近期內棄用此功能,因為它適用於簡單的聊天應用程式,並且任何使用 RunnableWithMessageHistory 的程式碼都將繼續正常運作。

請參閱〈如何遷移到 LangGraph 記憶體〉以取得更多詳細資訊。

在許多問答應用程式中,我們希望允許使用者進行來回對話,這表示應用程式需要某種形式的「記憶體」來記錄過去的問題和答案,以及一些邏輯將這些內容納入其目前的思考中。

在本指南中,我們將重點放在新增邏輯以整合歷史訊息。

這主要是〈對話式 RAG 教學〉的精簡版本。

我們將介紹兩種方法

  1. 鏈 (Chains),在其中我們始終執行檢索步驟;
  2. 代理 (Agents),在其中我們讓 LLM 自行決定是否以及如何執行檢索步驟(或多個步驟)。

對於外部知識來源,我們將使用與 RAG 教學相同的 Lilian Weng 的〈LLM Powered Autonomous Agents〉部落格文章。

這兩種方法都利用 LangGraph 作為協調框架。LangGraph 實作了內建的 持久層,使其非常適合支援多輪對話的聊天應用程式。

設定

依賴套件

在本逐步解說中,我們將使用 OpenAI 嵌入和 InMemory 向量儲存庫,但此處顯示的所有內容都適用於任何嵌入 (Embeddings) 以及向量儲存庫 (VectorStore)檢索器 (Retriever)

我們將使用以下套件

%%capture --no-stderr
%pip install --upgrade --quiet langgraph langchain-community beautifulsoup4

LangSmith

您使用 LangChain 建構的許多應用程式都將包含多個步驟,並多次調用 LLM。隨著這些應用程式變得越來越複雜,能夠檢查鏈或代理內部究竟發生什麼情況變得至關重要。最好的方法是使用 LangSmith

請注意,LangSmith 不是必需的,但它很有幫助。如果您確實想使用 LangSmith,請在上述連結註冊後,務必設定您的環境變數以開始記錄追蹤。

os.environ["LANGSMITH_TRACING"] = "true"
if not os.environ.get("LANGSMITH_API_KEY"):
os.environ["LANGSMITH_API_KEY"] = getpass.getpass()

組件

我們需要從 LangChain 的整合套件中選擇三個組件。

一個聊天模型

pip install -qU "langchain[openai]"
import getpass
import os

if not os.environ.get("OPENAI_API_KEY"):
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter API key for OpenAI: ")

from langchain.chat_models import init_chat_model

llm = init_chat_model("gpt-4o-mini", model_provider="openai")

一個嵌入模型

pip install -qU langchain-openai
import getpass
import os

if not os.environ.get("OPENAI_API_KEY"):
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter API key for OpenAI: ")

from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings(model="text-embedding-3-large")

以及一個向量儲存庫

pip install -qU langchain-core
from langchain_core.vectorstores import InMemoryVectorStore

vector_store = InMemoryVectorStore(embeddings)

RAG 教學〉索引了 Lilian Weng 的〈LLM Powered Autonomous Agents〉部落格文章。我們將在此重複該操作。以下我們載入頁面內容,將其分割成子文件,並將文件嵌入到我們的向量儲存庫

import bs4
from langchain import hub
from langchain_community.document_loaders import WebBaseLoader
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
from typing_extensions import List, TypedDict

# Load and chunk contents of the blog
loader = WebBaseLoader(
web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),
bs_kwargs=dict(
parse_only=bs4.SoupStrainer(
class_=("post-content", "post-title", "post-header")
)
),
)
docs = loader.load()

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
all_splits = text_splitter.split_documents(docs)
# Index chunks
_ = vector_store.add_documents(documents=all_splits)

如〈RAG 教學〉第二部分所述,我們可以透過將 RAG 應用程式的流程表示為一系列訊息來自然地支援對話式體驗

  1. 使用者輸入作為 HumanMessage
  2. 向量儲存庫查詢作為帶有工具調用的 AIMessage
  3. 檢索到的文件作為 ToolMessage
  4. 最終回應作為 AIMessage

我們將使用工具調用來促進這一點,這還允許查詢由 LLM 生成。我們可以建構一個工具來執行檢索步驟

from langchain_core.tools import tool


@tool(response_format="content_and_artifact")
def retrieve(query: str):
"""Retrieve information related to a query."""
retrieved_docs = vector_store.similarity_search(query, k=2)
serialized = "\n\n".join(
(f"Source: {doc.metadata}\n" f"Content: {doc.page_content}")
for doc in retrieved_docs
)
return serialized, retrieved_docs
API 參考:工具

我們現在可以建構我們的 LangGraph 應用程式。

請注意,我們使用檢查點程式 (checkpointer) 編譯它以支援來回對話。LangGraph 隨附一個簡單的記憶體內檢查點程式,我們將在下面使用它。請參閱其說明文件以取得更多詳細資訊,包括如何使用不同的持久性後端(例如 SQLite 或 Postgres)。

from langchain_core.messages import SystemMessage
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import END, MessagesState, StateGraph
from langgraph.prebuilt import ToolNode, tools_condition


# Step 1: Generate an AIMessage that may include a tool-call to be sent.
def query_or_respond(state: MessagesState):
"""Generate tool call for retrieval or respond."""
llm_with_tools = llm.bind_tools([retrieve])
response = llm_with_tools.invoke(state["messages"])
# MessagesState appends messages to state instead of overwriting
return {"messages": [response]}


# Step 2: Execute the retrieval.
tools = ToolNode([retrieve])


# Step 3: Generate a response using the retrieved content.
def generate(state: MessagesState):
"""Generate answer."""
# Get generated ToolMessages
recent_tool_messages = []
for message in reversed(state["messages"]):
if message.type == "tool":
recent_tool_messages.append(message)
else:
break
tool_messages = recent_tool_messages[::-1]

# Format into prompt
docs_content = "\n\n".join(doc.content for doc in tool_messages)
system_message_content = (
"You are an assistant for question-answering tasks. "
"Use the following pieces of retrieved context to answer "
"the question. If you don't know the answer, say that you "
"don't know. Use three sentences maximum and keep the "
"answer concise."
"\n\n"
f"{docs_content}"
)
conversation_messages = [
message
for message in state["messages"]
if message.type in ("human", "system")
or (message.type == "ai" and not message.tool_calls)
]
prompt = [SystemMessage(system_message_content)] + conversation_messages

# Run
response = llm.invoke(prompt)
return {"messages": [response]}


# Build graph
graph_builder = StateGraph(MessagesState)

graph_builder.add_node(query_or_respond)
graph_builder.add_node(tools)
graph_builder.add_node(generate)

graph_builder.set_entry_point("query_or_respond")
graph_builder.add_conditional_edges(
"query_or_respond",
tools_condition,
{END: END, "tools": "tools"},
)
graph_builder.add_edge("tools", "generate")
graph_builder.add_edge("generate", END)

memory = MemorySaver()
graph = graph_builder.compile(checkpointer=memory)
from IPython.display import Image, display

display(Image(graph.get_graph().draw_mermaid_png()))

讓我們測試一下我們的應用程式。

請注意,它可以適當地回應不需要額外檢索步驟的訊息

# Specify an ID for the thread
config = {"configurable": {"thread_id": "abc123"}}
input_message = "Hello"

for step in graph.stream(
{"messages": [{"role": "user", "content": input_message}]},
stream_mode="values",
config=config,
):
step["messages"][-1].pretty_print()
================================ Human Message =================================

Hello
================================== Ai Message ==================================

Hello! How can I assist you today?

當執行搜尋時,我們可以串流這些步驟以觀察查詢生成、檢索和答案生成

input_message = "What is Task Decomposition?"

for step in graph.stream(
{"messages": [{"role": "user", "content": input_message}]},
stream_mode="values",
config=config,
):
step["messages"][-1].pretty_print()
================================ Human Message =================================

What is Task Decomposition?
================================== Ai Message ==================================
Tool Calls:
retrieve (call_RntwX5GMt531biEE9MqSbgLV)
Call ID: call_RntwX5GMt531biEE9MqSbgLV
Args:
query: Task Decomposition
================================= Tool Message =================================
Name: retrieve

Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}
Content: Fig. 1. Overview of a LLM-powered autonomous agent system.
Component One: Planning#
A complicated task usually involves many steps. An agent needs to know what they are and plan ahead.
Task Decomposition#
Chain of thought (CoT; Wei et al. 2022) has become a standard prompting technique for enhancing model performance on complex tasks. The model is instructed to “think step by step” to utilize more test-time computation to decompose hard tasks into smaller and simpler steps. CoT transforms big tasks into multiple manageable tasks and shed lights into an interpretation of the model’s thinking process.

Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}
Content: Tree of Thoughts (Yao et al. 2023) extends CoT by exploring multiple reasoning possibilities at each step. It first decomposes the problem into multiple thought steps and generates multiple thoughts per step, creating a tree structure. The search process can be BFS (breadth-first search) or DFS (depth-first search) with each state evaluated by a classifier (via a prompt) or majority vote.
Task decomposition can be done (1) by LLM with simple prompting like "Steps for XYZ.\n1.", "What are the subgoals for achieving XYZ?", (2) by using task-specific instructions; e.g. "Write a story outline." for writing a novel, or (3) with human inputs.
================================== Ai Message ==================================

Task Decomposition is the process of breaking down a complicated task into smaller, more manageable steps. It often involves techniques like Chain of Thought (CoT), where the model is prompted to "think step by step," allowing for better handling of complex tasks. This approach enhances model performance and provides insight into the model's reasoning process.

最後,由於我們已使用檢查點程式編譯我們的應用程式,因此歷史訊息會保留在狀態中。這允許模型將使用者查詢置於上下文中

input_message = "Can you look up some common ways of doing it?"

for step in graph.stream(
{"messages": [{"role": "user", "content": input_message}]},
stream_mode="values",
config=config,
):
step["messages"][-1].pretty_print()
================================ Human Message =================================

Can you look up some common ways of doing it?
================================== Ai Message ==================================
Tool Calls:
retrieve (call_kwO5rYPyJ0MftYKoKRFjKpZM)
Call ID: call_kwO5rYPyJ0MftYKoKRFjKpZM
Args:
query: common methods for task decomposition
================================= Tool Message =================================
Name: retrieve

Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}
Content: Tree of Thoughts (Yao et al. 2023) extends CoT by exploring multiple reasoning possibilities at each step. It first decomposes the problem into multiple thought steps and generates multiple thoughts per step, creating a tree structure. The search process can be BFS (breadth-first search) or DFS (depth-first search) with each state evaluated by a classifier (via a prompt) or majority vote.
Task decomposition can be done (1) by LLM with simple prompting like "Steps for XYZ.\n1.", "What are the subgoals for achieving XYZ?", (2) by using task-specific instructions; e.g. "Write a story outline." for writing a novel, or (3) with human inputs.

Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}
Content: Fig. 1. Overview of a LLM-powered autonomous agent system.
Component One: Planning#
A complicated task usually involves many steps. An agent needs to know what they are and plan ahead.
Task Decomposition#
Chain of thought (CoT; Wei et al. 2022) has become a standard prompting technique for enhancing model performance on complex tasks. The model is instructed to “think step by step” to utilize more test-time computation to decompose hard tasks into smaller and simpler steps. CoT transforms big tasks into multiple manageable tasks and shed lights into an interpretation of the model’s thinking process.
================================== Ai Message ==================================

Common ways of Task Decomposition include: (1) using large language models (LLMs) with simple prompts like "Steps for XYZ" or "What are the subgoals for achieving XYZ?"; (2) utilizing task-specific instructions, such as "Write a story outline" for creative tasks; and (3) incorporating human inputs to guide the decomposition process.

請注意,我們可以在 LangSmith 追蹤中觀察發送到聊天模型的完整訊息序列,包括工具調用和檢索到的上下文。

對話歷史記錄也可以透過應用程式的狀態進行檢查

chat_history = graph.get_state(config).values["messages"]
for message in chat_history:
message.pretty_print()
================================ Human Message =================================

Hello
================================== Ai Message ==================================

Hello! How can I assist you today?
================================ Human Message =================================

What is Task Decomposition?
================================== Ai Message ==================================
Tool Calls:
retrieve (call_RntwX5GMt531biEE9MqSbgLV)
Call ID: call_RntwX5GMt531biEE9MqSbgLV
Args:
query: Task Decomposition
================================= Tool Message =================================
Name: retrieve

Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}
Content: Fig. 1. Overview of a LLM-powered autonomous agent system.
Component One: Planning#
A complicated task usually involves many steps. An agent needs to know what they are and plan ahead.
Task Decomposition#
Chain of thought (CoT; Wei et al. 2022) has become a standard prompting technique for enhancing model performance on complex tasks. The model is instructed to “think step by step” to utilize more test-time computation to decompose hard tasks into smaller and simpler steps. CoT transforms big tasks into multiple manageable tasks and shed lights into an interpretation of the model’s thinking process.

Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}
Content: Tree of Thoughts (Yao et al. 2023) extends CoT by exploring multiple reasoning possibilities at each step. It first decomposes the problem into multiple thought steps and generates multiple thoughts per step, creating a tree structure. The search process can be BFS (breadth-first search) or DFS (depth-first search) with each state evaluated by a classifier (via a prompt) or majority vote.
Task decomposition can be done (1) by LLM with simple prompting like "Steps for XYZ.\n1.", "What are the subgoals for achieving XYZ?", (2) by using task-specific instructions; e.g. "Write a story outline." for writing a novel, or (3) with human inputs.
================================== Ai Message ==================================

Task Decomposition is the process of breaking down a complicated task into smaller, more manageable steps. It often involves techniques like Chain of Thought (CoT), where the model is prompted to "think step by step," allowing for better handling of complex tasks. This approach enhances model performance and provides insight into the model's reasoning process.
================================ Human Message =================================

Can you look up some common ways of doing it?
================================== Ai Message ==================================
Tool Calls:
retrieve (call_kwO5rYPyJ0MftYKoKRFjKpZM)
Call ID: call_kwO5rYPyJ0MftYKoKRFjKpZM
Args:
query: common methods for task decomposition
================================= Tool Message =================================
Name: retrieve

Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}
Content: Tree of Thoughts (Yao et al. 2023) extends CoT by exploring multiple reasoning possibilities at each step. It first decomposes the problem into multiple thought steps and generates multiple thoughts per step, creating a tree structure. The search process can be BFS (breadth-first search) or DFS (depth-first search) with each state evaluated by a classifier (via a prompt) or majority vote.
Task decomposition can be done (1) by LLM with simple prompting like "Steps for XYZ.\n1.", "What are the subgoals for achieving XYZ?", (2) by using task-specific instructions; e.g. "Write a story outline." for writing a novel, or (3) with human inputs.

Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}
Content: Fig. 1. Overview of a LLM-powered autonomous agent system.
Component One: Planning#
A complicated task usually involves many steps. An agent needs to know what they are and plan ahead.
Task Decomposition#
Chain of thought (CoT; Wei et al. 2022) has become a standard prompting technique for enhancing model performance on complex tasks. The model is instructed to “think step by step” to utilize more test-time computation to decompose hard tasks into smaller and simpler steps. CoT transforms big tasks into multiple manageable tasks and shed lights into an interpretation of the model’s thinking process.
================================== Ai Message ==================================

Common ways of Task Decomposition include: (1) using large language models (LLMs) with simple prompts like "Steps for XYZ" or "What are the subgoals for achieving XYZ?"; (2) utilizing task-specific instructions, such as "Write a story outline" for creative tasks; and (3) incorporating human inputs to guide the decomposition process.

代理

代理利用 LLM 的推理能力在執行期間做出決策。使用代理可讓您卸載對檢索過程的額外決定權。雖然它們的行為不如上述「鏈」可預測,但它們能夠執行多個檢索步驟來服務於查詢,或在單次搜尋中迭代。

下面我們組裝一個最小的 RAG 代理。使用 LangGraph 的 預建 ReAct 代理建構子,我們可以在一行程式碼中完成此操作。

提示

查看 LangGraph 的〈Agentic RAG〉教學以取得更進階的公式。

from langgraph.prebuilt import create_react_agent

agent_executor = create_react_agent(llm, [retrieve], checkpointer=memory)
API 參考:create_react_agent

讓我們檢查一下圖形

display(Image(agent_executor.get_graph().draw_mermaid_png()))

與我們之前的實作相比,主要區別在於,這裡的工具調用會迴圈回到原始 LLM 調用,而不是結束運行的最終生成步驟。然後模型可以使用檢索到的上下文回答問題,或生成另一個工具調用以獲取更多資訊。

讓我們測試一下。我們建構一個通常需要迭代檢索步驟序列才能回答的問題

config = {"configurable": {"thread_id": "def234"}}

input_message = (
"What is the standard method for Task Decomposition?\n\n"
"Once you get the answer, look up common extensions of that method."
)

for event in agent_executor.stream(
{"messages": [{"role": "user", "content": input_message}]},
stream_mode="values",
config=config,
):
event["messages"][-1].pretty_print()
================================ Human Message =================================

What is the standard method for Task Decomposition?

Once you get the answer, look up common extensions of that method.
================================== Ai Message ==================================
Tool Calls:
retrieve (call_rxBqio7dxthnMuzjr4AIquSZ)
Call ID: call_rxBqio7dxthnMuzjr4AIquSZ
Args:
query: standard method for Task Decomposition
================================= Tool Message =================================
Name: retrieve

Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}
Content: Tree of Thoughts (Yao et al. 2023) extends CoT by exploring multiple reasoning possibilities at each step. It first decomposes the problem into multiple thought steps and generates multiple thoughts per step, creating a tree structure. The search process can be BFS (breadth-first search) or DFS (depth-first search) with each state evaluated by a classifier (via a prompt) or majority vote.
Task decomposition can be done (1) by LLM with simple prompting like "Steps for XYZ.\n1.", "What are the subgoals for achieving XYZ?", (2) by using task-specific instructions; e.g. "Write a story outline." for writing a novel, or (3) with human inputs.

Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}
Content: Fig. 1. Overview of a LLM-powered autonomous agent system.
Component One: Planning#
A complicated task usually involves many steps. An agent needs to know what they are and plan ahead.
Task Decomposition#
Chain of thought (CoT; Wei et al. 2022) has become a standard prompting technique for enhancing model performance on complex tasks. The model is instructed to “think step by step” to utilize more test-time computation to decompose hard tasks into smaller and simpler steps. CoT transforms big tasks into multiple manageable tasks and shed lights into an interpretation of the model’s thinking process.
================================== Ai Message ==================================
Tool Calls:
retrieve (call_kmQMRWCKeBdtXdlJi8yZD9CO)
Call ID: call_kmQMRWCKeBdtXdlJi8yZD9CO
Args:
query: common extensions of Task Decomposition methods
================================= Tool Message =================================
Name: retrieve

Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}
Content: Tree of Thoughts (Yao et al. 2023) extends CoT by exploring multiple reasoning possibilities at each step. It first decomposes the problem into multiple thought steps and generates multiple thoughts per step, creating a tree structure. The search process can be BFS (breadth-first search) or DFS (depth-first search) with each state evaluated by a classifier (via a prompt) or majority vote.
Task decomposition can be done (1) by LLM with simple prompting like "Steps for XYZ.\n1.", "What are the subgoals for achieving XYZ?", (2) by using task-specific instructions; e.g. "Write a story outline." for writing a novel, or (3) with human inputs.

Source: {'source': 'https://lilianweng.github.io/posts/2023-06-23-agent/'}
Content: Fig. 1. Overview of a LLM-powered autonomous agent system.
Component One: Planning#
A complicated task usually involves many steps. An agent needs to know what they are and plan ahead.
Task Decomposition#
Chain of thought (CoT; Wei et al. 2022) has become a standard prompting technique for enhancing model performance on complex tasks. The model is instructed to “think step by step” to utilize more test-time computation to decompose hard tasks into smaller and simpler steps. CoT transforms big tasks into multiple manageable tasks and shed lights into an interpretation of the model’s thinking process.
================================== Ai Message ==================================

The standard method for Task Decomposition involves breaking down complex tasks into smaller, manageable steps. Here are the main techniques:

1. **Chain of Thought (CoT)**: This prompting technique encourages a model to "think step by step," allowing it to utilize more computational resources during testing to decompose challenging tasks into simpler parts. CoT not only simplifies tasks but also provides insights into the model's reasoning process.

2. **Simple Prompting**: This can involve straightforward queries like "Steps for XYZ" or "What are the subgoals for achieving XYZ?" to guide the model in identifying the necessary steps.

3. **Task-specific Instructions**: Using specific prompts tailored to the task at hand, such as "Write a story outline" for creative writing, allows for more directed decomposition.

4. **Human Inputs**: Involving human expertise can also aid in breaking down tasks effectively.

### Common Extensions of Task Decomposition Methods

1. **Tree of Thoughts**: This method extends CoT by exploring multiple reasoning possibilities at each step. It decomposes the problem into various thought steps and generates multiple thoughts per step, forming a tree structure. This can utilize search processes like breadth-first search (BFS) or depth-first search (DFS) to evaluate states through classifiers or majority voting.

These extensions build on the basic principles of task decomposition, enhancing the depth and breadth of reasoning applied to complex tasks.

請注意,代理

  1. 生成查詢以搜尋任務分解的標準方法;
  2. 收到答案後,生成第二個查詢以搜尋其常見擴展;
  3. 在收到所有必要的上下文後,回答問題。

我們可以在 LangSmith 追蹤中看到完整的步驟序列,以及延遲和其他元數據。

後續步驟

我們已介紹了建構基本對話式問答應用程式的步驟

  • 我們使用鏈來建構一個可預測的應用程式,該應用程式為每個使用者輸入生成搜尋查詢;
  • 我們使用代理來建構一個「決定」何時以及如何生成搜尋查詢的應用程式。

若要探索不同類型的檢索器和檢索策略,請造訪操作指南的〈檢索器〉章節。

如需 LangChain 對話記憶體抽象概念的詳細逐步解說,請造訪〈如何新增訊息歷史記錄 (記憶體)〉LCEL 頁面。

若要深入了解代理,請前往〈代理模組〉


此頁面是否對您有幫助?