AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


langchain+qwen实现RAG数据之间的隔离
发布日期:2024-05-10 15:38:29 浏览次数: 1981


我们在开发RAG增加检索应用时,总会遇到数据隔离的问题,比如,你上传的资料数据,不能让别人检索到,不然会存在数据安全问题。所以我们得为每个用户的数据进行隔离, 他们都不应该看到对方的数据,除非数据已经授权或者分配权限给他们访问。


本文主要实现该功能,只有进行了分配权限才能访问数据,该功能在企业或者RAG增强检索当中是比较常用的。


主要的实现步骤

不清楚RAG增强检索的可以看之前的文章

1.先加载文档

2.对文档数据进行分块

3.分块之后增加role权限字段,用于过滤权限

4.在检索文档数据时,会按role的值进行过滤

5.把过滤好后的文档数据,加上用户提的问题一起发给llm模型处理(通义千问)

6.LLM处理后,返回结果


该功能实现的核心是增加role 权限字段过滤,在用户提问的时候,就分配好了他能检索哪方面的数据。


矢量数据库的选型

为了以后能够检索大量的文档,我们需要把文档数据存储起来,这里会用到矢量数据库。


矢量数据库目前暂定为qdrant,选它的原因主要是因为它是开源的,能兼容多平台,在windows系统下也能快速安装,使用起来很方便。

也挺想用milvus矢量数据库的,它也是开源的,文档也挺全面,在windows下使用相对麻烦,就放弃了。


使用到的技术

langchain+qwen+qdrant


开发前的准备

通义千问API-KEY一个


开始教程

安装依赖

# 如果缺少了依赖请参考之前的文章,这里只安装了qdrant相关的
pip install --upgrade --quiet  qdrant-client

引入依赖

from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_community.vectorstores import Qdrant
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from qdrant_client.http import models as rest
from langchain.prompts import PromptTemplate
from langchain.chains.retrieval_qa.base import RetrievalQA

import os

qwen_api_key = "你的通义千问API-KEY"
os.environ["DASHSCOPE_API_KEY"] = qwen_api_key

构建向量化对象

embeddings = DashScopeEmbeddings(
   model="text-embedding-v1", dashscope_api_key=qwen_api_key
)

定义读取文档函数,并进行文档分块

读取文档数据的方法,并定义角色metadata.roles字段,分块后的文档都会有该字段,表示这个文档有哪些角色可以访问。

def readPdfData(documents):
   page = []

   for document_path, roles in documents.items():
       # 读取当前目录下文件名为test.pdf和byd.pdf的文件
       pdf_loader = PyPDFLoader(document_path, extract_images=True)
       text_splitter = RecursiveCharacterTextSplitter(
           chunk_size=1000, chunk_overlap=200, add_start_index=True
     )

       loaded_documents = pdf_loader.load_and_split()

       for doc in loaded_documents:
           doc.metadata["roles"] = roles
           
       # 对文档进行分块
       split_documents = text_splitter.split_documents(loaded_documents)
       page.extend(split_documents)
   return page

读取test文件,并设置角色过滤字段rolesali,意思是检索文档数据时,参数中roles字段包含ali 的时候才能访问这个文档数据

# 该pdf为阿里的财报
split_documents = readPdfData({"test.pdf": ["ali"]})

构建qdrant矢量数据库对象

存储数据的集合名称为my_documents构建的方式是把文档数据加载到内存,仅用于测试,想构成存储到磁盘或者使用本地服务器的话参考官方提供的例子。

# 内存方式
qdrant = Qdrant.from_documents(
   split_documents,
   embeddings,
   prefer_grpc=True,
   location=":memory:",  # 加载到内存
   collection_name="my_documents",
)

在之前的集合追加文档

读取maotai文件,并也为该文件分配了maotai权限

# 该文档为茅台的财报,可多个pdf
documents = {
   "maotai.pdf": ["maotai"],
}

split_documents = readPdfData(documents)

# 追加新的文档
qdrant.add_documents(split_documents, batch_size=20)

定义检索对象

检索文档数据库时,只会检索metadata.roles字段为maotaiali的数据,其它数据都会过滤掉。

user_roles = ["maotai", "ali"]

qdrant_retriever = qdrant.as_retriever(
   search_kwargs={
       "filter": rest.Filter(
           must=[
               rest.FieldCondition(
                   key="metadata.roles",
                   match=rest.MatchAny(any=user_roles)
             )
         ]
     )
 }
)

构建大语言模型对象

llm = ChatTongyi(model="qwen-plus")

向qwen模型提问

根据用户提的问题,先去文档数据库中检索, 检索时会按我们定义的user_roles过滤掉数据,拿到相似的结果后,会根据用户的问题+相似的结果,一起发送给qwen模型,模型处理后会把最终的结果返回给我们。

prompt_template = """
Question: {question}
使用源回答问题。如果没有答案,请说"文本中没有答案".

Source: {context}

### Response:
"""
# 构建提问模板
prompt = PromptTemplate(
   template=prompt_template, input_variables=["context", "question"]
)

# 构建过滤请求对象
retrieval_qa = RetrievalQA.from_chain_type(
   llm=llm,
   chain_type="stuff",
   retriever=qdrant_retriever,
   return_source_documents=True,
   chain_type_kwargs={"prompt": prompt},
)

# 发送请求
response = retrieval_qa.invoke({"query": "贵州茅台?"})
print(response["result"])

以下是返回的结果,可以看出我们已经能访问到maotai.pdf文件的数据了

在2023年第三季度报告中,贵州茅台的主要财务数据显示:

1. 营业收入为103,268,354,688.44元,同比增长18.48%。
2. 归属于上市公司股东的净利润为52,876,217,064.12元,同比增长19.09%。
3. 流动资产中,货币资金为70,641,010,014.72元,拆出资金为95,625,606,731.69元。
4. 负债和所有者权益总计为262,076,424,771.47元,所有者权益合计为225,018,799,759.41元。

此外,报告还列出了贵州茅台的前10名无限售条件股东,其中中国贵州茅台酒厂(集团)有限责任公司是最大股东,持有679,211,576股。其他股东包括香港中央结算有限公司、贵州省国有资本运营有限责任公司等。

还是提同样的问题,我们把该访问maotai.pdf的角色授权roles字段改为a,这里我们就不会访问到数据了,因为我们没有maotai权限,我们定义了只有这个权限才能访问该文件。


把上面【定义检索对象】这个代码改为以下代码,执行,然后再重新执行【向qwen模型提问】中的代码进行提问

# 改为a权限
user_roles = ["a", "ali"]

qdrant_retriever = qdrant.as_retriever(
   search_kwargs={
       "filter": rest.Filter(
           must=[
               rest.FieldCondition(
                   key="metadata.roles",
                   match=rest.MatchAny(any=user_roles)
             )
         ]
     )
 }
)

最终返回的结果如下:

 贵州茅台在上述文本中没有被提及。


最后总结:本次我们实现了用户之间的数据隔离,在存储文档时我们用到了qdrant开源矢量数据库,通过保存文档时定义权限字段,用户在检索文档时,只有请求参数中含有我们的文档权限时,才会访问到我们的数据,主要是通过检索时按指定字段来过滤实现该功能的。




53AI,企业落地应用大模型首选服务商

产品:大模型应用平台+智能体定制开发+落地咨询服务

承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

与创始人交个朋友

回到顶部

 
扫码咨询