Langchain Academy translated
  • module-0
    • LangChain 学院
  • module-1
    • 智能体记忆
    • 智能体
    • 链式结构
    • 部署
    • 路由器
    • 最简单的图结构
  • module-2
    • 支持消息摘要与外部数据库记忆的聊天机器人
    • 支持消息摘要的聊天机器人
    • 多模式架构
    • 状态归约器
    • 状态模式
    • 消息过滤与修剪
  • module-3
    • 断点
    • 动态断点
    • 编辑图状态
    • 流式处理
    • 时间回溯
  • module-4
    • 映射-归约
    • 并行节点执行
    • 研究助手
    • 子图
  • module-5
    • 记忆代理
    • 具备记忆功能的聊天机器人
    • 基于集合架构的聊天机器人
    • 支持个人资料架构的聊天机器人
  • module-6
    • 助手
    • 连接 LangGraph 平台部署
    • 创建部署
    • 双重消息处理
  • Search
  • Previous
  • Next
  • 具备记忆功能的聊天机器人
    • 回顾
    • 目标
    • LangGraph 存储系统简介
    • 具备长期记忆功能的聊天机器人
    • 在LangSmith中查看追踪记录
    • 工作室(Studio)

具备记忆功能的聊天机器人¶

回顾¶

记忆是一种认知功能,使人们能够存储、检索和利用信息来理解当下与未来。

在人工智能应用中,可以使用多种长期记忆类型。

目标¶

本文将介绍如何通过LangGraph记忆存储实现长期记忆的保存与检索。

我们将构建一个同时具备短期(线程内)和长期(跨线程)记忆功能的聊天机器人。

重点将放在长期语义记忆上,这些记忆将存储与用户相关的事实信息。

利用这些长期记忆,我们将创建一个能记住用户特征的个性化聊天机器人。

该系统将在用户对话过程中"实时保存记忆"。

In [22]:
Copied!
%%capture --no-stderr
%pip install -U langchain_openai langgraph langchain_core
%%capture --no-stderr %pip install -U langchain_openai langgraph langchain_core

我们将使用 LangSmith 进行追踪。

In [1]:
Copied!
import os, getpass

def _set_env(var: str):
    if not os.environ.get(var):
        os.environ[var] = getpass.getpass(f"{var}: ")

_set_env("LANGSMITH_API_KEY")
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_PROJECT"] = "langchain-academy"
import os, getpass def _set_env(var: str): if not os.environ.get(var): os.environ[var] = getpass.getpass(f"{var}: ") _set_env("LANGSMITH_API_KEY") os.environ["LANGSMITH_TRACING"] = "true" os.environ["LANGSMITH_PROJECT"] = "langchain-academy"

LangGraph 存储系统简介¶

LangGraph 内存存储提供了一种在 LangGraph 中跨线程存储和检索信息的方法。

这是一个用于持久化 key-value 存储的开源基类。

In [2]:
Copied!
import uuid
from langgraph.store.memory import InMemoryStore
in_memory_store = InMemoryStore()
import uuid from langgraph.store.memory import InMemoryStore in_memory_store = InMemoryStore()

在Store中存储对象(例如记忆)时,我们需要提供:

  • 对象的namespace,这是一个元组(类似于目录结构)
  • 对象的key(类似于文件名)
  • 对象的value(类似于文件内容)

我们使用put方法,通过namespace和key将对象保存到存储中。

langgraph_store.png

In [3]:
Copied!
# Namespace for the memory to save
user_id = "1"
namespace_for_memory = (user_id, "memories")

# Save a memory to namespace as key and value
key = str(uuid.uuid4())

# The value needs to be a dictionary  
value = {"food_preference" : "I like pizza"}

# Save the memory
in_memory_store.put(namespace_for_memory, key, value)
# Namespace for the memory to save user_id = "1" namespace_for_memory = (user_id, "memories") # Save a memory to namespace as key and value key = str(uuid.uuid4()) # The value needs to be a dictionary value = {"food_preference" : "I like pizza"} # Save the memory in_memory_store.put(namespace_for_memory, key, value)

我们使用search方法通过namespace从存储中检索对象。

该方法会返回一个列表。

In [4]:
Copied!
# Search 
memories = in_memory_store.search(namespace_for_memory)
type(memories)
# Search memories = in_memory_store.search(namespace_for_memory) type(memories)
Out[4]:
list
In [5]:
Copied!
# Metatdata 
memories[0].dict()
# Metatdata memories[0].dict()
Out[5]:
{'value': {'food_preference': 'I like pizza'},
 'key': 'a754b8c5-e8b7-40ec-834b-c426a9a7c7cc',
 'namespace': ['1', 'memories'],
 'created_at': '2024-11-04T22:48:16.727572+00:00',
 'updated_at': '2024-11-04T22:48:16.727574+00:00'}
In [6]:
Copied!
# The key, value
print(memories[0].key, memories[0].value)
# The key, value print(memories[0].key, memories[0].value)
a754b8c5-e8b7-40ec-834b-c426a9a7c7cc {'food_preference': 'I like pizza'}

我们也可以通过 get 方法,根据 namespace 和 key 来检索对象。

In [7]:
Copied!
# Get the memory by namespace and key
memory = in_memory_store.get(namespace_for_memory, key)
memory.dict()
# Get the memory by namespace and key memory = in_memory_store.get(namespace_for_memory, key) memory.dict()
Out[7]:
{'value': {'food_preference': 'I like pizza'},
 'key': 'a754b8c5-e8b7-40ec-834b-c426a9a7c7cc',
 'namespace': ['1', 'memories'],
 'created_at': '2024-11-04T22:48:16.727572+00:00',
 'updated_at': '2024-11-04T22:48:16.727574+00:00'}

具备长期记忆功能的聊天机器人¶

我们需要构建一个拥有两种记忆类型的聊天机器人:

  1. 短期记忆(线程内记忆):机器人能够保存对话历史记录,并/或允许在聊天会话中被打断。
  2. 长期记忆(跨线程记忆):机器人可以在所有聊天会话中记住特定用户的信息。
In [9]:
Copied!
_set_env("OPENAI_API_KEY")
_set_env("OPENAI_API_KEY")

对于short-term memory(短期记忆),我们将使用检查点工具。

更多关于检查点工具的详细信息请参阅模块2和我们的概念文档,以下是其主要功能总结:

  • 将每个步骤的图状态写入线程
  • 在线程中持久化保存聊天历史记录
  • 允许图形在执行过程中被中断,并可从线程中的任意步骤恢复

而对于long-term memory(长期记忆),我们将使用前文介绍的LangGraph存储。

In [3]:
Copied!
# Chat model 
from langchain_openai import ChatOpenAI

# Initialize the LLM
model = ChatOpenAI(model="gpt-4o", temperature=0)
# Chat model from langchain_openai import ChatOpenAI # Initialize the LLM model = ChatOpenAI(model="gpt-4o", temperature=0)

聊天记录将通过检查点机制保存至短期记忆。

聊天机器人会对聊天记录进行反思。

随后它将创建并保存一段记忆至 LangGraph 存储库。

该记忆可在未来的聊天会话中调用,用于个性化定制聊天机器人的响应。

In [4]:
Copied!
from IPython.display import Image, display

from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.store.base import BaseStore

from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.runnables.config import RunnableConfig

# Chatbot instruction
MODEL_SYSTEM_MESSAGE = """You are a helpful assistant with memory that provides information about the user. 
If you have memory for this user, use it to personalize your responses.
Here is the memory (it may be empty): {memory}"""

# Create new memory from the chat history and any existing memory
CREATE_MEMORY_INSTRUCTION = """"You are collecting information about the user to personalize your responses.

CURRENT USER INFORMATION:
{memory}

INSTRUCTIONS:
1. Review the chat history below carefully
2. Identify new information about the user, such as:
   - Personal details (name, location)
   - Preferences (likes, dislikes)
   - Interests and hobbies
   - Past experiences
   - Goals or future plans
3. Merge any new information with existing memory
4. Format the memory as a clear, bulleted list
5. If new information conflicts with existing memory, keep the most recent version

Remember: Only include factual information directly stated by the user. Do not make assumptions or inferences.

Based on the chat history below, please update the user information:"""

def call_model(state: MessagesState, config: RunnableConfig, store: BaseStore):

    """Load memory from the store and use it to personalize the chatbot's response."""
    
    # Get the user ID from the config
    user_id = config["configurable"]["user_id"]

    # Retrieve memory from the store
    namespace = ("memory", user_id)
    key = "user_memory"
    existing_memory = store.get(namespace, key)

    # Extract the actual memory content if it exists and add a prefix
    if existing_memory:
        # Value is a dictionary with a memory key
        existing_memory_content = existing_memory.value.get('memory')
    else:
        existing_memory_content = "No existing memory found."

    # Format the memory in the system prompt
    system_msg = MODEL_SYSTEM_MESSAGE.format(memory=existing_memory_content)
    
    # Respond using memory as well as the chat history
    response = model.invoke([SystemMessage(content=system_msg)]+state["messages"])

    return {"messages": response}

def write_memory(state: MessagesState, config: RunnableConfig, store: BaseStore):

    """Reflect on the chat history and save a memory to the store."""
    
    # Get the user ID from the config
    user_id = config["configurable"]["user_id"]

    # Retrieve existing memory from the store
    namespace = ("memory", user_id)
    existing_memory = store.get(namespace, "user_memory")
        
    # Extract the memory
    if existing_memory:
        existing_memory_content = existing_memory.value.get('memory')
    else:
        existing_memory_content = "No existing memory found."

    # Format the memory in the system prompt
    system_msg = CREATE_MEMORY_INSTRUCTION.format(memory=existing_memory_content)
    new_memory = model.invoke([SystemMessage(content=system_msg)]+state['messages'])

    # Overwrite the existing memory in the store 
    key = "user_memory"

    # Write value as a dictionary with a memory key
    store.put(namespace, key, {"memory": new_memory.content})

# Define the graph
builder = StateGraph(MessagesState)
builder.add_node("call_model", call_model)
builder.add_node("write_memory", write_memory)
builder.add_edge(START, "call_model")
builder.add_edge("call_model", "write_memory")
builder.add_edge("write_memory", END)

# Store for long-term (across-thread) memory
across_thread_memory = InMemoryStore()

# Checkpointer for short-term (within-thread) memory
within_thread_memory = MemorySaver()

# Compile the graph with the checkpointer fir and store
graph = builder.compile(checkpointer=within_thread_memory, store=across_thread_memory)

# View
display(Image(graph.get_graph(xray=1).draw_mermaid_png()))
from IPython.display import Image, display from langgraph.checkpoint.memory import MemorySaver from langgraph.graph import StateGraph, MessagesState, START, END from langgraph.store.base import BaseStore from langchain_core.messages import HumanMessage, SystemMessage from langchain_core.runnables.config import RunnableConfig # Chatbot instruction MODEL_SYSTEM_MESSAGE = """You are a helpful assistant with memory that provides information about the user. If you have memory for this user, use it to personalize your responses. Here is the memory (it may be empty): {memory}""" # Create new memory from the chat history and any existing memory CREATE_MEMORY_INSTRUCTION = """"You are collecting information about the user to personalize your responses. CURRENT USER INFORMATION: {memory} INSTRUCTIONS: 1. Review the chat history below carefully 2. Identify new information about the user, such as: - Personal details (name, location) - Preferences (likes, dislikes) - Interests and hobbies - Past experiences - Goals or future plans 3. Merge any new information with existing memory 4. Format the memory as a clear, bulleted list 5. If new information conflicts with existing memory, keep the most recent version Remember: Only include factual information directly stated by the user. Do not make assumptions or inferences. Based on the chat history below, please update the user information:""" def call_model(state: MessagesState, config: RunnableConfig, store: BaseStore): """Load memory from the store and use it to personalize the chatbot's response.""" # Get the user ID from the config user_id = config["configurable"]["user_id"] # Retrieve memory from the store namespace = ("memory", user_id) key = "user_memory" existing_memory = store.get(namespace, key) # Extract the actual memory content if it exists and add a prefix if existing_memory: # Value is a dictionary with a memory key existing_memory_content = existing_memory.value.get('memory') else: existing_memory_content = "No existing memory found." # Format the memory in the system prompt system_msg = MODEL_SYSTEM_MESSAGE.format(memory=existing_memory_content) # Respond using memory as well as the chat history response = model.invoke([SystemMessage(content=system_msg)]+state["messages"]) return {"messages": response} def write_memory(state: MessagesState, config: RunnableConfig, store: BaseStore): """Reflect on the chat history and save a memory to the store.""" # Get the user ID from the config user_id = config["configurable"]["user_id"] # Retrieve existing memory from the store namespace = ("memory", user_id) existing_memory = store.get(namespace, "user_memory") # Extract the memory if existing_memory: existing_memory_content = existing_memory.value.get('memory') else: existing_memory_content = "No existing memory found." # Format the memory in the system prompt system_msg = CREATE_MEMORY_INSTRUCTION.format(memory=existing_memory_content) new_memory = model.invoke([SystemMessage(content=system_msg)]+state['messages']) # Overwrite the existing memory in the store key = "user_memory" # Write value as a dictionary with a memory key store.put(namespace, key, {"memory": new_memory.content}) # Define the graph builder = StateGraph(MessagesState) builder.add_node("call_model", call_model) builder.add_node("write_memory", write_memory) builder.add_edge(START, "call_model") builder.add_edge("call_model", "write_memory") builder.add_edge("write_memory", END) # Store for long-term (across-thread) memory across_thread_memory = InMemoryStore() # Checkpointer for short-term (within-thread) memory within_thread_memory = MemorySaver() # Compile the graph with the checkpointer fir and store graph = builder.compile(checkpointer=within_thread_memory, store=across_thread_memory) # View display(Image(graph.get_graph(xray=1).draw_mermaid_png()))
No description has been provided for this image

当我们与聊天机器人交互时,需要提供两个要素:

  1. 短期记忆(线程内):用于持久化聊天历史的 线程ID。
  2. 长期记忆(跨线程):通过 用户ID 为用户划分长期记忆的命名空间。

下面我们通过实际场景看看这两者如何协同工作。

In [5]:
Copied!
# We supply a thread ID for short-term (within-thread) memory
# We supply a user ID for long-term (across-thread) memory 
config = {"configurable": {"thread_id": "1", "user_id": "1"}}

# User input 
input_messages = [HumanMessage(content="Hi, my name is Lance")]

# Run the graph
for chunk in graph.stream({"messages": input_messages}, config, stream_mode="values"):
    chunk["messages"][-1].pretty_print()
# We supply a thread ID for short-term (within-thread) memory # We supply a user ID for long-term (across-thread) memory config = {"configurable": {"thread_id": "1", "user_id": "1"}} # User input input_messages = [HumanMessage(content="Hi, my name is Lance")] # Run the graph for chunk in graph.stream({"messages": input_messages}, config, stream_mode="values"): chunk["messages"][-1].pretty_print()
================================ Human Message =================================

Hi, my name is Lance
================================== Ai Message ==================================

Hello, Lance! It's nice to meet you. How can I assist you today?
In [6]:
Copied!
# User input 
input_messages = [HumanMessage(content="I like to bike around San Francisco")]

# Run the graph
for chunk in graph.stream({"messages": input_messages}, config, stream_mode="values"):
    chunk["messages"][-1].pretty_print()
# User input input_messages = [HumanMessage(content="I like to bike around San Francisco")] # Run the graph for chunk in graph.stream({"messages": input_messages}, config, stream_mode="values"): chunk["messages"][-1].pretty_print()
================================ Human Message =================================

I like to bike around San Francisco
================================== Ai Message ==================================

That sounds like a great way to explore the city, Lance! San Francisco has some beautiful routes and views. Do you have a favorite trail or area you like to bike in?

我们正在使用 MemorySaver 检查点机制来实现线程内内存管理。

该功能会将聊天记录保存至当前线程。

我们可以查看已保存至线程的聊天历史记录。

In [7]:
Copied!
thread = {"configurable": {"thread_id": "1"}}
state = graph.get_state(thread).values
for m in state["messages"]: 
    m.pretty_print()
thread = {"configurable": {"thread_id": "1"}} state = graph.get_state(thread).values for m in state["messages"]: m.pretty_print()
================================ Human Message =================================

Hi, my name is Lance
================================== Ai Message ==================================

Hello, Lance! It's nice to meet you. How can I assist you today?
================================ Human Message =================================

I like to bike around San Francisco
================================== Ai Message ==================================

That sounds like a great way to explore the city, Lance! San Francisco has some beautiful routes and views. Do you have a favorite trail or area you like to bike in?

请注意我们之前使用存储库编译了该图:

across_thread_memory = InMemoryStore()

同时,我们在图中添加了一个节点(write_memory),该节点会记录聊天历史并将记忆保存至存储库。

现在我们可以验证该记忆是否已成功存入存储库。

In [8]:
Copied!
# Namespace for the memory to save
user_id = "1"
namespace = ("memory", user_id)
existing_memory = across_thread_memory.get(namespace, "user_memory")
existing_memory.dict()
# Namespace for the memory to save user_id = "1" namespace = ("memory", user_id) existing_memory = across_thread_memory.get(namespace, "user_memory") existing_memory.dict()
Out[8]:
{'value': {'memory': "**Updated User Information:**\n- User's name is Lance.\n- Likes to bike around San Francisco."},
 'key': 'user_memory',
 'namespace': ['memory', '1'],
 'created_at': '2024-11-05T00:12:17.383918+00:00',
 'updated_at': '2024-11-05T00:12:25.469528+00:00'}

现在,让我们用相同的用户ID启动一个新会话。

我们将看到聊天机器人记住了用户资料,并据此提供个性化的回复。

In [9]:
Copied!
# We supply a user ID for across-thread memory as well as a new thread ID
config = {"configurable": {"thread_id": "2", "user_id": "1"}}

# User input 
input_messages = [HumanMessage(content="Hi! Where would you recommend that I go biking?")]

# Run the graph
for chunk in graph.stream({"messages": input_messages}, config, stream_mode="values"):
    chunk["messages"][-1].pretty_print()
# We supply a user ID for across-thread memory as well as a new thread ID config = {"configurable": {"thread_id": "2", "user_id": "1"}} # User input input_messages = [HumanMessage(content="Hi! Where would you recommend that I go biking?")] # Run the graph for chunk in graph.stream({"messages": input_messages}, config, stream_mode="values"): chunk["messages"][-1].pretty_print()
================================ Human Message =================================

Hi! Where would you recommend that I go biking?
================================== Ai Message ==================================

Hi Lance! Since you enjoy biking around San Francisco, there are some fantastic routes you might love. Here are a few recommendations:

1. **Golden Gate Park**: This is a classic choice with plenty of trails and beautiful scenery. You can explore the park's many attractions, like the Conservatory of Flowers and the Japanese Tea Garden.

2. **The Embarcadero**: A ride along the Embarcadero offers stunning views of the Bay Bridge and the waterfront. It's a great way to experience the city's vibrant atmosphere.

3. **Marin Headlands**: If you're up for a bit of a challenge, biking across the Golden Gate Bridge to the Marin Headlands offers breathtaking views of the city and the Pacific Ocean.

4. **Presidio**: This area has a network of trails with varying difficulty levels, and you can enjoy views of the Golden Gate Bridge and the bay.

5. **Twin Peaks**: For a more challenging ride, head up to Twin Peaks. The climb is worth it for the panoramic views of the city.

Let me know if you want more details on any of these routes!
In [10]:
Copied!
# User input 
input_messages = [HumanMessage(content="Great, are there any bakeries nearby that I can check out? I like a croissant after biking.")]

# Run the graph
for chunk in graph.stream({"messages": input_messages}, config, stream_mode="values"):
    chunk["messages"][-1].pretty_print()
# User input input_messages = [HumanMessage(content="Great, are there any bakeries nearby that I can check out? I like a croissant after biking.")] # Run the graph for chunk in graph.stream({"messages": input_messages}, config, stream_mode="values"): chunk["messages"][-1].pretty_print()
================================ Human Message =================================

Great, are there any bakeries nearby that I can check out? I like a croissant after biking.
================================== Ai Message ==================================

Absolutely, Lance! Here are a few bakeries in San Francisco where you can enjoy a delicious croissant after your ride:

1. **Tartine Bakery**: Located in the Mission District, Tartine is famous for its pastries, and their croissants are a must-try.

2. **Arsicault Bakery**: This bakery in the Richmond District has been praised for its buttery, flaky croissants. It's a bit of a detour, but worth it!

3. **b. Patisserie**: Situated in Lower Pacific Heights, b. Patisserie offers a variety of pastries, and their croissants are particularly popular.

4. **Le Marais Bakery**: With locations in the Marina and Castro, Le Marais offers a charming French bakery experience with excellent croissants.

5. **Neighbor Bakehouse**: Located in the Dogpatch, this bakery is known for its creative pastries, including some fantastic croissants.

These spots should provide a delightful treat after your biking adventures. Enjoy your ride and your croissant!

在LangSmith中查看追踪记录¶

我们可以看到,记忆内容如预期那样从存储中检索出来,并作为系统提示的一部分提供:

https://smith.langchain.com/public/10268d64-82ff-434e-ac02-4afa5cc15432/r

工作室(Studio)¶

我们还可以在工作室中与聊天机器人进行交互。

Screenshot 2024-10-28 at 10.08.27 AM.png

# Getting Started with PyTorch

## Installation

To install PyTorch, run the following command:

```bash
pip install torch torchvision

Verifying Installation¶

After installation, verify it by running:

import torch
print(torch.__version__)

Basic Tensors¶

Tensors are the central data structure in PyTorch. Here's how to create a simple tensor:

x = torch.tensor([1, 2, 3])
print(x)

Tensor Operations¶

Basic operations include:

  • Addition: torch.add(x, y)
  • Matrix multiplication: torch.mm(x, y)
  • Reshaping: x.view(2, 3)

我明白了,以下是严格遵循您要求的 Markdown 格式中文翻译:

```markdown
# PyTorch 入门指南

## 安装

通过以下命令安装 PyTorch:

```bash
pip install torch torchvision

验证安装¶

安装完成后,运行以下代码验证:

import torch
print(torch.__version__)

基础张量¶

张量是 PyTorch 的核心数据结构。创建简单张量的方法如下:

x = torch.tensor([1, 2, 3])
print(x)

张量运算¶

基础运算包括:

  • 加法:torch.add(x, y)
  • 矩阵乘法:torch.mm(x, y)
  • 形状变换:x.view(2, 3)

Documentation built with MkDocs.

Search

From here you can search these documents. Enter your search terms below.

Keyboard Shortcuts

Keys Action
? Open this help
n Next page
p Previous page
s Search