LangChain教程 - 如何构建一个 LLM 驱动的聊天机器人

系列文章索引
LangChain教程 - 系列文章

前提条件

在开始本教程之前,您需要对以下概念有所了解:

  • 聊天模型:大语言模型(LLM)是支持聊天机器人的核心。
  • Prompt 模板:用于引导模型如何响应用户输入。
  • 聊天历史记录:保存用户和模型之间的对话,以便后续调用。

概述

在本教程中,我们将逐步讲解如何设计和实现一个基于 LLM 的聊天机器人。该聊天机器人不仅可以进行对话,还能够记住之前的交互内容。

相关概念

  • Conversational RAG:实现一个能够基于外部数据源进行对话的机器人。
  • Agents:构建一个可以执行任务的聊天机器人。

本教程将涵盖聊天机器人的基础内容,帮助您逐步理解更高级的应用,如 Conversational RAG 和 Agents。

环境设置

本教程推荐使用 Jupyter Notebook 来运行代码示例。这是因为在使用 LLM 时,可能会遇到诸如输出不符合预期、API 故障等问题,交互式环境可以更好地帮助您理解和调试代码。

安装依赖

首先,确保已经安装以下库:

pip install langchain langchain_community langchain-openai

LangSmith

构建聊天机器人时,随着模型调用步骤的增加,了解链条内部的工作变得至关重要。LangSmith 是一个用于监控模型调用链路的工具。可以通过以下方式启用 LangSmith:

import getpass
import os

os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = getpass.getpass()

快速开始

首先,我们学习如何使用一个语言模型。

LangChain 支持多种语言模型,您可以根据需求选择合适的模型,例如 OpenAI 的 GPT-3.5 Turbo:

from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-3.5-turbo")

简单对话示例

使用 model.invoke 方法来调用模型,并传递消息:

from langchain_core.messages import HumanMessage

response = model.invoke([HumanMessage(content="Hi! I'm Bob")])
print(response.content)

模型将输出类似 “Hello Bob! How can I assist you today?” 的响应。

管理对话状态

直接使用模型并不能记住先前的对话,因此无法回答基于上下文的问题。例如,如果我们接下来问:

response = model.invoke([HumanMessage(content="What's my name?")])
print(response.content)

模型会返回 “I don’t have access to your personal information”。

为了让聊天机器人记住上下文,我们可以传入整个对话历史:

from langchain_core.messages import AIMessage

response = model.invoke([
    HumanMessage(content="Hi! I'm Bob"),
    AIMessage(content="Hello Bob! How can I assist you today?"),
    HumanMessage(content="What's my name?")
])
print(response.content)

现在,模型可以正确回答:“Your name is Bob.”

消息历史

要管理对话的状态,我们可以使用 Message History 类。这个类将跟踪用户和模型的输入和输出,并将它们存储在某种数据存储中。

首先,安装 langchain-community,我们将用到其中的消息历史存储功能:

pip install langchain_community

然后,设置一个内存中的聊天历史对象:

from langchain_core.chat_history import InMemoryChatMessageHistory, BaseChatMessageHistory

store = {}

def get_session_history(session_id: str) -> BaseChatMessageHistory:
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]

将聊天历史与模型结合:

from langchain_core.runnables.history import RunnableWithMessageHistory

with_message_history = RunnableWithMessageHistory(model, get_session_history)

每次调用模型时,我们需要为其提供一个 session_id 以区分不同的对话会话:

config = {"configurable": {"session_id": "abc2"}}

response = with_message_history.invoke([HumanMessage(content="Hi! I'm Bob")], config=config)
print(response.content)

现在聊天机器人可以记住对话信息。再次调用时,它可以记住名字:

response = with_message_history.invoke([HumanMessage(content="What's my name?")], config=config)
print(response.content)

使用 Prompt 模板

我们可以使用 Prompt Templates 来自定义模型的响应规则。添加一个系统消息,用于指导聊天机器人:

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant. Answer all questions in {language}."),
    MessagesPlaceholder(variable_name="messages")
])

chain = prompt | model

将历史记录功能与 Prompt 模板结合:

with_message_history = RunnableWithMessageHistory(chain, get_session_history)
config = {"configurable": {"session_id": "abc5"}}
response = with_message_history.invoke([HumanMessage(content="Hi! I'm Jim")], config=config)
print(response.content)

可以继续扩展 Prompt 模板,例如支持多语言功能:

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant. Answer all questions in {language}."),
    MessagesPlaceholder(variable_name="messages")
])

chain = prompt | model

response = chain.invoke({"messages": [HumanMessage(content="Hi! I'm Bob")], "language": "Spanish"})
print(response.content)

模型将以西班牙语回答:“¡Hola, Bob! ¿En qué puedo ayudarte hoy?”

管理聊天历史长度

聊天记录可能会不断增加,导致超出 LLM 的上下文窗口。因此,限制消息数量非常重要。我们可以使用 trim_messages 函数来修剪对话历史:

from langchain_core.messages import trim_messages, SystemMessage

messages = [
    SystemMessage(content="You're a good assistant"),
    HumanMessage(content="Hi! I'm Bob"),
    # 更多消息...
]

trimmed_messages = trim_messages(messages, max_tokens=65, strategy="last")

使用这个修剪后的消息历史进行模型调用,可以避免上下文超出限制。

流式输出

为了提升用户体验,可以使用模型的 stream 方法来逐步返回响应:

config = {"configurable": {"session_id": "abc15"}}

for r in with_message_history.stream({"messages": [HumanMessage(content="Hi! I'm Todd. Tell me a joke.")], "language": "English"}, config=config):
    print(r.content, end="")

模型将流式返回每个生成的 token,提高响应的实时性。

完整代码

import os
import getpass
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage, trim_messages
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.chat_history import InMemoryChatMessageHistory, BaseChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory

# 设置 OpenAI API 密钥
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")

# 创建一个聊天模型(使用 OpenAI GPT-3.5)
model = ChatOpenAI(model="gpt-3.5-turbo")

# 消息历史存储
store = {}

# 获取会话历史记录
def get_session_history(session_id: str) -> BaseChatMessageHistory:
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()
    return store[session_id]

# 创建带有 Prompt 模板的链
prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant. Answer all questions in {language}."),
    MessagesPlaceholder(variable_name="messages")
])

# 将消息修剪工具添加到链中,避免消息过多超出上下文窗口
trimmer = trim_messages(
    max_tokens=100,   # 限制最大 token 数
    strategy="last",  # 保留最后的消息
    token_counter=model,  # 使用模型计算 token
    include_system=True,
    allow_partial=False,
    start_on="human"  # 从人类消息开始修剪
)

# 定义处理消息历史和修剪功能的链
from operator import itemgetter
from langchain_core.runnables import RunnablePassthrough

chain = (
    RunnablePassthrough.assign(messages=itemgetter("messages") | trimmer)  # 先修剪消息
    | prompt  # 然后传递到 Prompt 模板
    | model  # 调用模型生成响应
)

# 将消息历史包装到链中
with_message_history = RunnableWithMessageHistory(
    chain,
    get_session_history,
    input_messages_key="messages"
)

# 测试会话:输入用户消息,并指定语言为西班牙语
config = {"configurable": {"session_id": "session1"}}

# 初始消息
response = with_message_history.invoke(
    {"messages": [HumanMessage(content="

Hi! I'm Todd.")], "language": "Spanish"},
    config=config,
)
print("Response:", response.content)

# 继续对话
response = with_message_history.invoke(
    {"messages": [HumanMessage(content="What's my name?")], "language": "Spanish"},
    config=config,
)
print("Response:", response.content)

# 流式响应
print("\nStreaming Response:")
for r in with_message_history.stream(
    {
        "messages": [HumanMessage(content="Tell me a joke.")],
        "language": "English"
    },
    config=config,
):
    print(r.content, end="")

通过以上步骤,您已经构建了一个能够记住对话历史、支持多语言、并且具有流式输出的 LLM 驱动聊天机器人。

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值