返回顶部
热门问答 更多热门问答
技术文章 更多技术文章

langchain qwen实现RAG数据之间的隔离

[复制链接]
链载Ai 显示全部楼层 发表于 3 天前 |阅读模式 打印 上一主题 下一主题

我们在开发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开源矢量数据库,通过保存文档时定义权限字段,用户在检索文档时,只有请求参数中含有我们的文档权限时,才会访问到我们的数据,主要是通过检索时按指定字段来过滤实现该功能的。



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

链载AI是专业的生成式人工智能教程平台。提供Stable Diffusion、Midjourney AI绘画教程,Suno AI音乐生成指南,以及Runway、Pika等AI视频制作与动画生成实战案例。从提示词编写到参数调整,手把手助您从入门到精通。
  • 官方手机版

  • 微信公众号

  • 商务合作

  • Powered by Discuz! X3.5 | Copyright © 2025-2025. | 链载Ai
  • 桂ICP备2024021734号 | 营业执照 | |广西笔趣文化传媒有限公司|| QQ