懒懒笔记 | 课代表带你梳理【RAG课程 11&12:优化和加速你的RAG】

人工智能 创建于:21小时前

Yo bro!想我了吗?😏

今天懒懒课代表要带大家回顾第11讲和第12讲的内容——如何让你的RAG系统跑得更快更稳!🚀

在前面的课程中,我们学习了通过多节点组、多路召回等策略提升RAG系统的召回效果。但是!效果提升的同时也带来了新的问题:

🙋‍♂️“系统启动太慢了,每次都要重新加载文档...”

🙋‍♂️“检索响应时间太长,用户体验不好...”

🙋‍♂️“大模型推理速度跟不上...”

可见,打造一个高性能的RAG系统优化是必不可少的环节!第9、10讲正是为此而来——手把手教你如何加速你的RAG~

 

持久化存储,告别漫长的冷启动

 

为什么需要持久化?

 

传统RAG系统将所有数据存在内存中,导致:

📌系统重启后数据丢失

📌每次启动都要重新处理文档

📌内存资源浪费严重

 

解决方案就是——向量数据库!LazyLLM原生支持两种存储后端:

 

实测对比

 

我们对三种存储进行了性能测试(文档量1614个节点):

 

使用Milvus后,二次启动时间直接节省了近90%!这效率提升,爱了爱了~🔝

 

高效检索,向量索引的魔法

前面我们讲了持久化存储能提升系统启动速度,那检索响应速度慢怎么办?答案是:索引

我们可以把索引想象成一本书的目录,查“retrieve”这种词,不用从 A 翻到 Z,只要按字母跳转几次,瞬间定位。没有索引时,搜索过程是线性扫描(O(n)),而有了索引之后,能将搜索时间大幅压缩到 O(log n) 或 O(m)。

以字典为例,假设有 26 个字母开头的 N 个单词,我们要查找“retrieve”:

  • 线性搜索:得翻过前面 17*N 个单词,效率感人💀

  • 字典树索引:按字母逐层匹配,查一次最多只需 m 步(m 为单词长度),比如 “r-e-t-r-i-e-v-e” 一共 8 步 + 每步最多 26 次比对,量化下来提升高达 95%以上!

LazyLLM里的索引系统

LazyLLM 提供了内置的 DefaultIndex 和 SmartEmbeddingIndex 两种索引方案,并支持用户自定义索引结构,只需实现 3 个核心方法:update、remove 和 query。对,就是这么简单粗暴!

与某些“拐弯抹角”的框架(比如 LlamaIndex)不同,LazyLLM 的索引就是索引,不用绕来绕去把 Index 变成什么 retriever 或 query_engine,再倒回来才能用。逻辑直白,工程师狂喜!

来看个对比👇

所以在 LazyLLM 中,索引就是检索的第一步,检索器才是调度大脑。你可以组合不同的索引策略、相似度算法、节点分组逻辑,搭建属于自己的检索系统,灵活又强大。

检索速度起飞的秘密武器

说完了基础索引,我们再来看看RAG 真正跑得快的关键:向量索引

当你的检索器不是按关键词查找,而是“我和它语义像不像”的时候,背后依靠的就是向量相似度——用 embedding 向量去比“谁更接近”。

🤔问题来了:数据多了怎么办?比如百万条知识块,每条都算一次余弦相似度,谁都吃不消...这时候就要靠向量索引出马了!

为什么用向量数据库?

其实我们可以简单地理解:

向量数据库 = 向量索引 + 数据存储 + 快速搜索API

比如我们在 LazyLLM 中配置:​​​​​

  •  
store_conf = {    'type''milvus',    'kwargs': {        'uri'"dbs/test.db",        'index_kwargs': {            'index_type''HNSW',            'metric_type''COSINE'        }    }}

这就已经在背后创建了一个基于 HNSW 的高性能向量索引,再多数据也能“毫秒级”查出最相关的几个。

 

不同索引算法适合什么场景?

实际测试里,HNSW 的查询时间能比线性搜索快 95%以上,而且精度几乎不打折。

Milvus实战

向量索引很强大,但是从0开始手搓一个高性能索引成本比较高。实际研发过程中,我们只需要使用这些高性能的向量数据库,便可实现高性能的向量检索!

 

Milvus 是啥?

为什么要用它?

 

Milvus 是一款高性能、分布式的向量数据库,天然支持大规模稠密向量、稀疏向量、二进制向量的索引与检索。


通俗点说,它就是为语义检索而生的神器:

  • 持久化 ✔️

  • 秒级响应 ✔️

  • 分布式扩展 ✔️

  • 支持复杂过滤 ✔️

关键是:LazyLLM 已完美适配 Milvus!你不需要再去啃 Milvus 的官方文档,只要几行配置,直接用!

 

一键接入 Milvus 索引,只需配置store_conf

LazyLLM 的索引系统支持将 Milvus 作为后端,只需设置好 indices 字段👇​​​​​​

 

store_conf = {    'type''map',  # 数据仍然保存在本地 mapStore 中    'indices': {        'smart_embedding_index': {            'backend''milvus',  # 指定索引使用 milvus 后端            'kwargs': {                'uri''dbs/test.db',                'index_kwargs': {                    'index_type''HNSW',                    'metric_type''COSINE',                }            },        },    },}

 

只需传入 'smart_embedding_index' 索引名,配好 index_type 和 metric_type,就能立即享受 HNSW 索引的极速体验!

 

实测效果:速度直接提升近 87%

我们用默认索引 vs Milvus 索引做了简单测试👇

query"证券监管?", default time: 0.164s  query"证券监管?", milvus time: 0.021s

是不是感受到什么叫真正的“提速”?
用了 Milvus,检索像闪电一样!

更强的是:支持稠密 + 稀疏向量混合召回!

还记得前面说的多路召回吗?LazyLLM 也支持给 Milvus 配多个 embedding source,同时建立多种索引👇​​​​​​​​​​​​​​​​​​​​​​​​​​​

import lazyllmfrom lazyllm import bind, deploymilvus_store_conf = {    'type''milvus',    'kwargs': {        'uri'"milvus.db",        'index_kwargs': [            {                'embed_key''bge_m3_dense',                'index_type''IVF_FLAT',                'metric_type''COSINE',            },            {                'embed_key''bge_m3_sparse',                'index_type''SPARSE_INVERTED_INDEX',                'metric_type''IP',            }        ]    },}bge_m3_dense = lazyllm.TrainableModule('bge-m3')bge_m3_sparse = lazyllm.TrainableModule('bge-m3').deploy_method((deploy.AutoDeploy, {'embed_type''sparse'}))embeds = {'bge_m3_dense': bge_m3_dense, 'bge_m3_sparse': bge_m3_sparse}document = lazyllm.Document(dataset_path='/path/to/your/document',           embed=embeds,           store_conf=milvus_store_conf)document.create_node_group(name="block", transform=lambda s: s.split("\n"if s else '')bge_rerank = lazyllm.TrainableModule("bge-reranker-large")with lazyllm.pipeline() as ppl:    with lazyllm.parallel().sum as ppl.prl:        ppl.prl.retriever1 = lazyllm.Retriever(doc=document,                         group_name="block",                         embed_keys=['bge_m3_dense'],                         topk=3)        ppl.prl.retriever = lazyllm.Retriever(doc=document,                         group_name="block",                         embed_keys=['bge_m3_sparse'],                         topk=3)    ppl.reranker = lazyllm.Reranker(name='ModuleReranker',model=bge_rerank, topk=3) | bind(query=ppl.input)    ppl.formatter = (      lambda nodes, query: dict(          context_str=[node.get_content() for node in nodes],          query=query)    ) | bind(query=ppl.input)
    ppl.llm = lazyllm.OnlineChatModule().prompt(lazyllm.ChatPrompter(instruction=prompt, extra_keys=['context_str']))webpage = lazyllm.WebModule(ppl, port=23492).start().wait()

 

  • 稠密向量走 IVF + Cosine

  • 稀疏向量走 倒排索引 + 内积

组合使用,召回又准又快,还能配合 reranker 进行重排,提升最终输出质量!

 

真的可以“0代码修改”接入Milvus存储!

如果你直接把 Milvus 作为 store_conf['type'],LazyLLM 会自动读取索引配置,不需要再写 indices 字段,简洁清爽!​​​​​​

store_conf = {    'type''milvus',    'kwargs': {        'uri'"dbs/milvus1.db",        'index_kwargs': {            'index_type''HNSW',            'metric_type''COSINE',        }    }}

用Retriever(..., index='smart_embedding_index') 一调就能用了!

 

远程服务端点的接入也超简单!

通过 Docker,2行命令就能本地跑起来:​​​​​​​

curl -sfL https://raw.githubusercontent.com/milvus-io/milvus/master/scripts/standalone_embed.sh -o standalone_embed.shbash standalone_embed.sh start

默认监听 19530 端口,还可以用 user.yaml 自定义配置。

推理加速,让大模型飞起来

量化技术

通过降低模型精度来减少计算量:

  • Qwen2-72B原模型:需要144GB显存

  • AWQ量化后:仅需48GB显存

  • 性能损失<1%,速度提升30-40%

更好的推理框架

在模型不变的情况下,通过选择更好的推理框架,也可以提升大模型的推理性能。LazyLLM支持多种推理框架:

开发者可以根据自己的实际需求,灵活选择推理框架,以实现最适合自己的模型推理体验!

启动量化模型只需一行代码:

llm = TrainableModule('Qwen2-72B-Instruct-AWQ').deploy_method(deploy.vllm)

工程优化,让检索不排队

缓存机制 + 并行执行,看看如何让你的RAG系统“快得聪明,快得合理”。

记忆力上线!用K-V缓存让热门问题一键秒答

很多RAG系统都陷入一个误区:

只重模型效果,不重查询效率。结果就是,每个问题系统都从头来一遍,就像每天出门都要重新背单词——太累啦!

所以我们加入了“缓存大脑”:用一个K-V缓存字典来存储检索结果。

 

实践:模拟KV缓存加速检索

 

使用简单的k-v dict模拟缓存机制,我们搭建一个从RAG系统启动至检索的过程,并设置kv字典用于保存检索过的query与节点集合,并测试有无缓存机制时系统的检索时间:​​​​​​​

milvus_store_conf = {    'type''map',    'indices': {        'smart_embedding_index': {        'backend''milvus',        'kwargs': {            'uri'"dbs/test_cache.db",            'index_kwargs': {                'index_type''HNSW',                'metric_type''COSINE',            }        },        },    },}dataset_path = os.path.join(DOC_PATH, "test")docs = lazyllm.Document(    dataset_path=dataset_path,    embed=embedding_model,    store_conf=milvus_store_conf)docs.create_node_group(name='sentence', parent="MediumChunk", transform=(lambda d: d.split('。')))retriever1 = lazyllm.Retriever(docs, group_name="MediumChunk", topk=6, index='smart_embedding_index')retriever2 = lazyllm.Retriever(docs, group_name="sentence", target="MediumChunk", topk=6, index='smart_embedding_index')retriever1.start()retriever2.start()reranker = Reranker('ModuleReranker', model=rerank_model, topk=3)# 设置固定queryquery = "证券管理的基本规范?"# 运行5次没有缓存机制的检索流程,并记录时间time_no_cache = []for i in range(5):    st = time.time()    nodes1 = retriever1(query=query)    nodes2 = retriever2(query=query)    rerank_nodes = reranker(nodes1 + nodes2, query)    et = time.time()    t = et - st    time_no_cache.append(t)    print(f"No cache 第 {i+1} 次查询耗时:{t}s")# 定义dict[list],存储已检索的query和节点集合,实现简易的缓存机制kv_cache = defaultdict(list)for i in range(5):    st = time.time()    #如果query未在缓存中,则执行正常的检索流程,若query命中缓存,则直接取缓存中的节点集合    if query not in kv_cache:        nodes1 = retriever1(query=query)        nodes2 = retriever2(query=query)        rerank_nodes = reranker(nodes1 + nodes2, query)        # 检索完毕后,缓存query及检索节点        kv_cache[query] = rerank_nodes    else:        rerank_nodes = kv_cache[query]    et = time.time()    t = et - st    time_no_cache.append(t)    print(f"KV cache 第 {i+1} 次查询耗时:{t}s")

 

测试结果

✅缓存机制对“高频问题”的性能优化是碾压式的,系统从“反应慢”秒变“秒答王者”。

 

我全都要!用并行召回提高效率

你可能没注意:

很多多路召回系统,其实是依次执行的!

比如我们定义了两个检索器 retriever1 和 retriever2,常见代码:​​​​​​​

nodes1 = retriever1(query=query)nodes2 = retriever2(query=query)

这样执行其实是串行的,

 

解决方案是什么?

使用 LazyLLM 的 parallel() 实现多路召回并行化!​​​​​​​

with lazyllm.parallel().sum as prl:    prl.r1 = retriever1    prl.r2 = retriever2prl(query)  # 并行执行!

实测时间对比

看起来时间差不到 0.02 秒?但别小看它:在大模型前处理、并发请求量上来时,这种优化可以显著提升吞吐效率!

集大成者!缓存 + 并行 + LLM 一条龙响应

我们最后将所有优化组合起来,构建一个响应更快、结构更优的RAG系统:​​​​​​​

milvus_store_conf = {    'type''milvus',    'kwargs': {        'uri'"dbs/test_rag.db",        'index_kwargs': {        'index_type''HNSW',        'metric_type''COSINE',        }    }}dataset_path = os.path.join(DOC_PATH, "test")# 定义kv缓存kv_cache = defaultdict(list)docs1 = lazyllm.Document(dataset_path=dataset_path, embed=embedding_model, store_conf=milvus_store_conf)docs1.create_node_group(name='sentence', parent="MediumChunk", transform=(lambda d: d.split('。')))prompt = '你是一个友好的 AI 问答助手,你需要根据给定的上下文和问题提供答案。\    根据以下资料回答问题:\    {context_str} \n 'with lazyllm.pipeline() as recall:    # 并行多路召回    with lazyllm.parallel().sum as recall.prl:        recall.prl.r1 = lazyllm.Retriever(docs1, group_name="MediumChunk", topk=6)        recall.prl.r2 = lazyllm.Retriever(docs1, group_name="sentence", target="MediumChunk", topk=6)    recall.reranker = lazyllm.Reranker(name='ModuleReranker',model=rerank_model, topk=3) | lazyllm.bind(query=recall.input)    recall.cache_save = (lambda nodes, query: (kv_cache.update({query: nodes}) or nodes)) | lazyllm.bind(query=recall.input)
with lazyllm.pipeline() as ppl:    # 缓存检查    ppl.cache_check = lazyllm.ifs(        cond=(lambda query: query in kv_cache),        tpath=(lambda query: kv_cache[query]),        fpath=recall    )    ppl.formatter = (        lambda nodes, query: dict(            context_str="\n".join(node.get_content() for node in nodes),            query=query)    ) | lazyllm.bind(query=ppl.input)    ppl.llm = llm.prompt(lazyllm.ChatPrompter(instruction=prompt, extro_keys=['context_str']))w = lazyllm.WebModule(ppl, port=23492, stream=True).start().wait()

通过这两讲的实战锤炼,我们不只是学会了如何“让大模型飞起来”,更明白了:一个优秀的 RAG 系统,不只要答得准,更要答得快、跑得稳、用得爽!

持久化存储让系统告别“冷启动焦虑”,到高效索引+并发召回让检索飞奔不排队,再到缓存机制+异步流程让响应速度起飞🚀,我们一步步,把 RAG 打造成了一个既有大脑也有肌肉的超级问答机器🧠💪

这不是简单的“优化一丢丢”,而是全链路性能大提速 × 工程实战硬核进阶

 

现在,轮到你上场了!
快把你调教好的高性能 RAG 系统亮出来吧!

评论区、B站视频弹幕区、微信交流群的debug session
欢迎你分享成果、输出疑问、畅聊灵感~

让我们一起

慢吞吞的RAG卷成反应超快的智能助手🔥
技术星辰大海等你来闯,一起冲!

未来属于又懂原理、又敢动手的你!

RAG宇宙,走起! 

🔥🔥🔥​​​​​​​

更多技术内容讨论,欢迎移步LazyLLM GZH!

原文地址:https://my.oschina.net/u/8690838/blog/18619316

免责声明:本文来源于互联网,版权归合法拥有者所有,如有侵权请公众号联系管理员

* 本站提供的一些文章、资料是供学习研究之用,如用于商业用途,请购买正版。

工作日志