二、(基础)使用 LangChain 构建一个有上下文记忆简单聊天-1

169 阅读5分钟

前言

  1. 上一章讲解了如何构建一个简单的 LangChain 应用,但此时的应用是没有记忆的,不知道以前我们说了什么,现在我们使用的大模型应用都是有记忆的,虽然这个记忆有一定长度。
  2. 本章将讲解如何使用LangChain 构建一个有上下文记忆简单聊天机器人。

准备工作

  • 需要了解 LangChain 的基础用法,可以阅读上一篇文章
  • pip安装LangChain:pip install langchain
  • 调用大模型key,我们主要是学习为主,能白嫖自然白嫖,不需要多么快速的响应,下面是对应的申请方式,都是免费的,其他模型都是需要对应token花费钱的。注意:我们只要申请openai的key,openai更加通用
    • 腾讯元宝:hunyuan-lite,申请地址
    • 智谱AI:GLM-4-Flash-250414,申请地址,更推荐,响应速度快,更精准,对 openai 接口兼容性更好
  • 以上两个模型都是免费的,可以放心使用,注意申请 openai 访问方式的key
  • 建议使用 Jupyter Notebook,更加方便,安装教程

1. 没有记忆的示例

我们先看一个体现 LangChain 调用大模型没有记忆的示例

from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
from langchain_core.output_parsers import StrOutputParser

model = ChatOpenAI(model="GLM-4-Flash-250414", api_key="申请的key", base_url="https://open.bigmodel.cn/api/paas/v4/")

parser = StrOutputParser()
chain = model | parser

# 大模型没有记忆
print(chain.invoke([HumanMessage(content="你好!我是小明")]))
print(chain.invoke([HumanMessage(content="我叫什么名字?")]))

模型给的回答,可以看出现在是没有记忆的

你好,小明!很高兴见到你。请问有什么我可以帮助你的吗?
抱歉,我无法回答这个问题。作为一个人工智能助手,我没有访问您的个人信息的权限,因此无法得知您的名字。如果您需要帮助或有其他问题,请随时告诉我。

1.1. 手动增加记忆

为了解决这个问题,我们需要将整个对话历史传递给模型。

from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
from langchain_core.output_parsers import StrOutputParser

model = ChatOpenAI(model="GLM-4-Flash-250414", api_key="申请的key", base_url="https://open.bigmodel.cn/api/paas/v4/")

parser = StrOutputParser()
chain = model | parser

# 需要把上下文全部发送
print(chain.invoke(
    [
        HumanMessage(content="你好!我是小明"),
        AIMessage(content="你好,小明!很高兴见到你。请问有什么我可以帮助你的吗?"),
        HumanMessage(content="我叫什么名字?"),
    ]
))

模型现在知道我们刚才问的什么了,但是这种方式很繁琐需要我们手动维护对话

你的名字是“小明”。

2. 增加消息历史

首先,让我们确保安装 langchain-community,因为我们将使用其中的集成来存储消息历史 pip install langchain_community

导入相关类并设置我们的链,该链包装模型并添加此消息历史。这里的一个关键部分是我们作为 get_session_history 传入的函数。这个函数预计接受一个 session_id 并返回一个消息历史对象。这个 session_id 用于区分不同的对话,并应作为配置的一部分在调用新链时传入

from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_core.chat_history import BaseChatMessageHistory, InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory

model = ChatOpenAI(model="GLM-4-Flash-250414", api_key="申请的key", base_url="https://open.bigmodel.cn/api/paas/v4/")

store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    # print(store, "store[session_id]")
    return store[session_id]

parser = StrOutputParser()
chain = model | parser
with_message_history = RunnableWithMessageHistory(chain, get_session_history)
# 创建一个 config,每次都传递给可运行的部分
config = {"configurable": {"session_id": "aaaaaa"}}

response = with_message_history.invoke(
    [HumanMessage(content="你好!我是小明")],
    config=config,
)

print(response)

response = with_message_history.invoke(
    [HumanMessage(content="我叫什么名字?")],
    config=config,
)

print(response)

现在通过定义的 session_id 已经实现了记忆本次对话,可以修改 session_id 来创建不同的对话,也可以随时切换回来(需要将会话内容储存起来,比如说数据库)

你好,小明!很高兴见到你。有什么我可以帮助你的吗?
你叫小明。

3. 提示词模板

调用大模型得到完整的答案需要大量特定的提示词,为了方便用户尽可能少的输入提示词,方便用户操作,我们需要提前预设提示词,提示词模板帮助将原始用户信息转换为大型语言模型可以处理的格式。

添加一个提示词模板。为此,我们将创建一个 ChatPromptTemplate。利用 MessagesPlaceholder 来传递所有消息。

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "你是个乐于助人的助手。尽你所能回答所有问题。",
        ),
        MessagesPlaceholder(variable_name="messages"),
    ]
)
chain = prompt | model

融合入刚才的上下文代码中

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.chat_history import BaseChatMessageHistory, InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.messages import HumanMessage

model = ChatOpenAI(model="GLM-4-Flash-250414", api_key="申请的key", base_url="https://open.bigmodel.cn/api/paas/v4/")
prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "你是个乐于助人的助手。尽你所能用{language}回答所有问题",
        ),
        MessagesPlaceholder(variable_name="messages"),
    ]
)
chain = prompt | model
store = {}

def get_session_history(session_id: str) -> BaseChatMessageHistory:
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]
with_message_history = RunnableWithMessageHistory(chain, get_session_history, input_messages_key="messages")
config = {"configurable": {"session_id": "aaaaaa"}}

response = with_message_history.invoke(
    {"messages": [HumanMessage(content="你好!我是小明")], "language": "英语"},
    config=config,
)

print(response.content)

response = with_message_history.invoke(
    {"messages": [HumanMessage(content="我叫什么名字?")], "language": "英语"},
    config=config,
)

print(response.content)

模型的回答,已经按照预设的提示词模版进行了回答 注意:各个模型参数回答等有差异,本示例中不能百分百确定正确返回

Hello, Xiao Ming! How can I assist you today?
Your name is Xiao Ming.

本章讲解了如何使用 LangChain 构建一个有上下文记忆简单聊天程序,并且增加了提示词模版,但是我们还是没有很好的体现和管理对话历史,控制上下文长度等。下篇我们将讲解如何管理对话历史