作者:微信小助手
发布时间:2025-01-12T23:47:57
“ 向量数据库是RAG技术的重要底座之一 ” 关于RAG和向量数据库的基础知识这里就不再做介绍了,不懂的可以翻看之前的文章。 今天的主要目的是使用milvus向量数据库来实现RAG检索增强,后面会附上代码。
使用milvus实现RAG
RAG的核心在于检索,而与传统字符匹配和分词检索方式不同的是RAG主要是基于语义检索的方式,也就是向量检索。而一个好的向量数据库就成为RAG技术环节中必不可少的一环。
备注:RAG并不是只支持向量检索,也可以使用传统的检索方式,主要根据不同的应用场景选择最合适的方式;RAG的核心是准确,高效的检索到有效数据,也就是说RAG重视的是结果,而不是过程。 DFires,公众号:AI探索时代RAG与本地知识库,向量数据库,以及知识图谱的联系与区别
milvus是我国企业自主开发的一款向量数据库,根据其官方介绍,milvus既可以方便本地开发测试,也可以大规模集群部署支持上百亿的向量检索需求。
而且,milvus同时还支持多种检索方式和算法:
并且集成多种语言的SDK:
当然,今天的主要目的不是为了给milvus产品打广告,而是使用milvus实现RAG——检索增强问答系统。
https://milvus.io/docs/zh
milvus官方文档
安装milvus
milvus有多种版本,每种版本支持的场景不太一样,但安装方式都非常简单。
轻量级可以直接通过pip进行安装,并直接嵌入到python代码中:
# 本地python sdk 使用
pip install mivlus
而单机版和集群版都是通过docker 进行安装,官方提供了详细的安装命令:
# 单机安装 linux系统 需要梯子
curl -sfL https://raw.githubusercontent.com/milvus-io/milvus/master/scripts/standalone_embed.sh -o standalone_embed.sh
bash standalone_embed.sh start
版本 | 说明 | 使用场景 | 数据量 |
milvus Lite | 轻量级版本 | 常用于本地开发使用 | 百万到千万级向量数据 |
milvus Standalone | 单机版 | 常用于小规模用户使用 | 上亿级向量数据 |
milvus Distributed | 集群版 | 常用于大规模业务场景 | 百亿级向量数据 |
为了方便管理,milvus提供了一个可视化的客户端工具——attu,只需要一个命令就可以安装:
# attu 安装命令 这个{milvus server IP} 要换成你自己的服务器地址或本地地址
docker run -p 8000:3000 -e MILVUS_URL={milvus server IP}:19530 zilliz/attu:v2.4
项目地址: https://github.com/zilliztech/attu
这里有一个坑, zilliz/attu:v2.4 attu的镜像有些平台会拉不下来,大家可以换个思路使用自己的电脑或者能够拉下attu镜像的服务器,拉取之后把使用 save命令把镜像保存下来,然后再部署到你的电脑或服务器上。
当然,attu客户端工具并不是必须的,只是方便管理milvus ,也可以不使用。
如下是attu的管理页面:
用户本地或在服务上安装好milvus向量数据库之后,就可以直接使用了。
在python 中连接milvus数据库的方式有两种,然后用户所有与milvus数据库的操作都可以基于milvus_client客户端对象实现。
# milvus 客户端有两种连接方式 一种是本地开发测试使用 一种是独立部署
# 独立部署方式
# milvus_client = MilvusClient(
# uri="你的milvus ip:19530",
# token="root:milvus" # 默认用户名和密码
# )
# 本地方式 会在本地创建一个milvus 数据库
milvus_client = MilvusClient("milvus_demo.db")
其次,milvus也像传统的关系型数据库一样,拥有数据库的概念;不同的数据可以放到不同的数据库中,默认数据库就是default,如果你没有声明或创建数据库,那么你的数据默认都在default数据库中。
但milvus的主要操作是通过Collections来实现的;Collections就类似于传统数据库中的表结构。
如下图所示:
在学会milvus数据库的基本使用之后,就可以通过嵌入模型把数据导入到milvus数据库的collection中;然后通过调用大模型实现RAG问答。
详细实现可以查看官方文档:
https://milvus.io/docs/zh/build-rag-with-milvus.md
完整代码如下所示,这里使用的是阿里云的通义千问模型,嵌入模型使用的也是阿里云的嵌入模型;当然milvus官方也提供了一些嵌入模型,用户也可以根据自己的喜好选择一些第三方的模型。
需要安装的python 包,当然作者这里只是记录了一部分包,如果代码执行出错提示缺包,用户自行下载即可。
!pip install jq
!pip install pymilvus[model]
!pip install pymilvus
!pip install tqdm
!pip install openai
# python 环境是3.9
这里的代码是完整的可执行代码,只需要把文件路径换成你本地下载的文件即可;还有就是大模型客户端可以选择你自己的模型和key。
这里的测试文件使用的是milvus官方提供的问题文档,下载地址:
</ul>
<pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer">https:<span class="code-snippet__comment">//github.com/milvus-io/milvus-docs/releases/download/v2.4.6-preview/milvus_docs_2.4.x_en.zip</span></span></code></pre>
</section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js" data-lang="python"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">from</span> glob <span class="code-snippet__keyword">import</span> glob</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">from</span> openai <span class="code-snippet__keyword">import</span> OpenAI</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">from</span> pymilvus <span class="code-snippet__keyword">import</span> MilvusClient</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">from</span> tqdm <span class="code-snippet__keyword">import</span> tqdm</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> json</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">text_lines = []</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment"># 路径 milvus_docs/en/faq/*.md</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">for</span> file_path <span class="code-snippet__keyword">in</span> glob(<span class="code-snippet__string">"换成你的文件路径"</span>, recursive=<span class="code-snippet__keyword">True</span>):</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">with</span> open(file_path, <span class="code-snippet__string">"r"</span>) <span class="code-snippet__keyword">as</span> file:</span></code><code><span class="code-snippet_outer"> file_text = file.read()</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"> text_lines += file_text.split(<span class="code-snippet__string">"# "</span>)</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">print(<span class="code-snippet__string">"text_lines: "</span>, text_lines)</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment"># 这里使用的是openai的工具包 可以连接openai, 通义千问, 等多种兼容openai格式的大模型服务商 这里使用的是阿里的通义千问模型</span></span></code><code><span class="code-snippet_outer">openai_client = OpenAI(</span></code><code><span class="code-snippet_outer"> api_key=<span class="code-snippet__string">"换成你的key"</span>,</span></code><code><span class="code-snippet_outer"> base_url=<span class="code-snippet__string">"https://dashscope.aliyuncs.com/compatible-mode/v1"</span></span></code><code><span class="code-snippet_outer">)</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment"># 嵌入模型</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__function"><span class="code-snippet__keyword">def</span> <span class="code-snippet__title">emb_text</span><span class="code-snippet__params">(text)</span>:</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">return</span> (</span></code><code><span class="code-snippet_outer"> openai_client.embeddings.create(input=text, model=<span class="code-snippet__string">"text-embedding-v3"</span>).data[<span class="code-snippet__number">0</span>].embedding</span></code><code><span class="code-snippet_outer"> )</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">test_embedding = emb_text(<span class="code-snippet__string">"This is a test"</span>)</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">embedding_dim = len(test_embedding)</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">print(embedding_dim)</span></code><code><span class="code-snippet_outer">print(test_embedding[:<span class="code-snippet__number">10</span>])</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment"># milvus 客户端有两种连接方式 一种是本地开发测试使用 一种是独立部署</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment"># 独立部署方式</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment"># milvus_client = MilvusClient(</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment"># uri="你的milvus ip:19530",</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment"># token="root:milvus"</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment"># )</span></span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment"># 本地方式 会在本地创建一个milvus 数据库</span></span></code><code><span class="code-snippet_outer">milvus_client = MilvusClient(<span class="code-snippet__string">"milvus_demo.db"</span>)</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">collection_name = <span class="code-snippet__string">"my_rag_collection"</span></span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">if</span> milvus_client.has_collection(collection_name):</span></code><code><span class="code-snippet_outer"> milvus_client.drop_collection(collection_name)</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">milvus_client.create_collection(</span></code><code><span class="code-snippet_outer"> collection_name=collection_name,</span></code><code><span class="code-snippet_outer"> dimension=embedding_dim,</span></code><code><span class="code-snippet_outer"> metric_type=<span class="code-snippet__string">"IP"</span>,</span></code><code><span class="code-snippet_outer"> consistency_level=<span class="code-snippet__string">"Strong"</span></span></code><code><span class="code-snippet_outer">)</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">data = []</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">for</span> i, line <span class="code-snippet__keyword">in</span> enumerate(tqdm(text_lines, desc=<span class="code-snippet__string">"Creating embeddings"</span>)):</span></code><code><span class="code-snippet_outer"> data.append({<span class="code-snippet__string">"id"</span>: i, <span class="code-snippet__string">"vector"</span>: emb_text(line), <span class="code-snippet__string">"text"</span>: line})</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">print(<span class="code-snippet__string">"data: "</span>, len(data))</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">milvus_client.insert(collection_name=collection_name, data=data)</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">question = <span class="code-snippet__string">"How is data stored in milvus?"</span></span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">search_res = milvus_client.search(</span></code><code><span class="code-snippet_outer"> collection_name=collection_name,</span></code><code><span class="code-snippet_outer"> data=[</span></code><code><span class="code-snippet_outer"> emb_text(question)</span></code><code><span class="code-snippet_outer"> ],</span></code><code><span class="code-snippet_outer"> limit=<span class="code-snippet__number">3</span>,</span></code><code><span class="code-snippet_outer"> search_params={<span class="code-snippet__string">"metric_type"</span>: <span class="code-snippet__string">"IP"</span>, <span class="code-snippet__string">"params"</span>: {}},</span></code><code><span class="code-snippet_outer"> output_fields=[<span class="code-snippet__string">"text"</span>]</span></code><code><span class="code-snippet_outer">)</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">retrieved_lines_with_distances = [</span></code><code><span class="code-snippet_outer"> (res[<span class="code-snippet__string">"entity"</span>][<span class="code-snippet__string">"text"</span>], res[<span class="code-snippet__string">"distance"</span>]) <span class="code-snippet__keyword">for</span> res <span class="code-snippet__keyword">in</span> search_res[<span class="code-snippet__number">0</span>]</span></code><code><span class="code-snippet_outer">]</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">print(<span class="code-snippet__string">"retrieved: "</span>, retrieved_lines_with_distances)</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">print(json.dumps(retrieved_lines_with_distances, indent=<span class="code-snippet__number">4</span>))</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">context = <span class="code-snippet__string">"\n"</span>.join(</span></code><code><span class="code-snippet_outer"> [line_with_distance[<span class="code-snippet__number">0</span>] <span class="code-snippet__keyword">for</span> line_with_distance <span class="code-snippet__keyword">in</span> retrieved_lines_with_distances]</span></code><code><span class="code-snippet_outer">)</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">SYSTEM_PROMPT = <span class="code-snippet__string">"""</span></span></code><code><span class="code-snippet_outer">Human: You are an AI assistant. You are able to find answers to the questions from the contextual passage snippets provided.</span></code><code><span class="code-snippet_outer">"""</span></code><code><span class="code-snippet_outer">USER_PROMPT = <span class="code-snippet__string">f"""</span></span></code><code><span class="code-snippet_outer">Use the following pieces of information enclosed in
<context> tags to provide an answer to the question enclosed in
<question> tags.
</question>
</context></span></code><code><span class="code-snippet_outer">
<context></context></span></code><code><span class="code-snippet_outer"><span class="code-snippet__subst">{context}</span></span></code><code><span class="code-snippet_outer"></span></code><code><span class="code-snippet_outer">
<question></question></span></code><code><span class="code-snippet_outer"><span class="code-snippet__subst">{question}</span></span></code><code><span class="code-snippet_outer"></span></code><code><span class="code-snippet_outer">"""</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer">response = openai_client.chat.completions.create(</span></code><code><span class="code-snippet_outer"> model=<span class="code-snippet__string">"qwen-plus"</span>, <span class="code-snippet__comment"># 可以换成你自己喜欢的模型</span></span></code><code><span class="code-snippet_outer"> messages=[</span></code><code><span class="code-snippet_outer"> {<span class="code-snippet__string">"role"</span>: <span class="code-snippet__string">"system"</span>, <span class="code-snippet__string">"content"</span>: SYSTEM_PROMPT},</span></code><code><span class="code-snippet_outer"> {<span class="code-snippet__string">"role"</span>: <span class="code-snippet__string">"user"</span>, <span class="code-snippet__string">"content"</span>: USER_PROMPT},</span></code><code><span class="code-snippet_outer"> ],</span></code><code><span class="code-snippet_outer">)</span></code><code><span class="code-snippet_outer">print(response.choices[<span class="code-snippet__number">0</span>].message.content)</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><br></span></code></pre>
</section>
<p style="color: rgb(62, 62, 62);font-size: 16px;white-space-collapse: collapse;max-width: 100%;box-sizing: border-box;min-height: 1em;overflow-wrap: break-word !important;"><br></p>
当然,上面文章写的不明白的地方,用户可以自己看milvus的官方文档,操作起来真的特别简单,这个案例就是作者自己根据milvus的官方文档实现的。
文档地址:https://milvus.io/docs/zh/build-rag-with-milvus.md