1. 带外挂知识库的聊天机器人架构
在构建 RAG
(检索增强生成)应用时,通常会使用外部的检索器或知识库来搜索与用户提问相关的信息,然后将检索到的内容填充至提示模板中,最后输入给大语言模型进行特定内容的生成。无论 RAG
应用的架构有多复杂,这个检索-填充-生成的过程都是必不可少的,这也是所有 RAG
应用的基础架构。
在 LangChain
中,可以按照上述流程实现一个带知识库问答能力的聊天机器人。实现的思路如下:
资料推荐
-
与
Postgres
类似,实例化一个全局的 Weaviate 向量数据库连接,避免每次调用时重新建立连接,以提高运行效率。 -
在聊天机器人应用中,将
Weaviate
实例转换为检索器,并且把从检索器返回的Document
列表转换成字符串格式。 -
将处理完毕的检索内容拼接到
LCEL
链(LangChain Expression Language
)输入字典中,当用户发出提问时,即可自动检索对应的内容并填充到提示模板中,从而实现外部知识的挂载和查询。
2. 外挂知识库的聊天机器人案例
在实际项目中,我们不论对接的是什么向量数据库,亦或者是使用云端的向量数据库,也可以使用 Docker 搭建的向量数据库,其实并没有使用差异,修改后的代码如下。
2.1 集成的Weaviate
向量数据库服务的代码示例:
# internal/service/vector_database_service.py
import os
import weaviate
from injector import inject
from langchain_core.documents import Document
from langchain_core.vectorstores import VectorStoreRetriever
from langchain_openai import OpenAIEmbeddings
from langchain_weaviate import WeaviateVectorStore
from weaviate import WeaviateClient
@inject
class VectorDatabaseService:
"""向量数据库服务"""
client: WeaviateClient
vector_store: WeaviateVectorStore
def __init__(self):
"""构造函数,完成向量数据库服务的客户端+LangChain向量数据库实例的创建"""
# 1.创建/连接weaviate向量数据库
self.client = weaviate.connect_to_local(
host=os.getenv("WEAVIATE_HOST"),
port=int(os.getenv("WEAVIATE_PORT"))
)
# 2.创建LangChain向量数据库
self.vector_store = WeaviateVectorStore(
client=self.client,
index_name="Dataset",
text_key="text",
embedding=OpenAIEmbeddings(model="text-embedding-3-small")
)
def get_retriever(self) -> VectorStoreRetriever:
"""获取检索器"""
return self.vector_store.as_retriever()
@classmethod
def combine_documents(cls, documents: list[Document]) -> str:
"""将对应的文档列表使用换行符进行合并"""
return "\n\n".join([document.page_content for document in documents])
2.2 资料推荐
2.3 配置信息
# Weaviate向量数据库配置
WEAVIATE_HOST=127.0.0.1
WEAVIATE_PORT=8080
2.4 聊天机器人处理器
def debug(self, app_id: UUID):
# 4.创建链应用
retriever = self.vector_database_service.get_retriever() | self.vector_database_service.combine_documents
chain = (RunnablePassthrough.assign(
history=RunnableLambda(self._load_memory_variables) | itemgetter("history"),
context=itemgetter("query") | retriever
) | prompt | llm | StrOutputParser()).with_listeners(on_end=self._save_context)