4月6日(RAG系统)

RAG内存效率提高32倍

使用二进制量化技术,使RAG的内存效率提高32倍

工作流程

  • 摄许文档并生成二进制嵌入向量
  • 创建二进制向量索引,并将嵌入向量存储在向量数据库
  • 检索与用户查询相似度最高的前K个文档
  • 大模型基于补充上下文生成回复

加载数据

我们使用LlamaIndex的目录读取工具来导入文档。该工具能够读取多种数据格式

复制代码
from llama_index.core import SimpleDirectoryReader

loader = SimpleDirectoryReader(
    input_dir=docs_dir,
    required_exts=[".pdf"],
    recursive=True
)

docs = loader.load_data()
documents = [doc.text for doc in docs]

生成二进制嵌入

接下来,我们生成浮点32位格式的文本嵌入向量,并将其转换为二进制向量,从而使内存占用与存储空间缩减至原来的32分之1

复制代码
import numpy as np
from llama_index.embeddings.huggingface import HuggingFaceEmbedding

embed_model = HuggingFaceEmbedding(
    model_name="BAAI/bge-large-en-v1.5",
    trust_remote_code=True,
    cache_folder='./hf_cache'
)

for context in batch_iterate(documents, batch_size=512):
    # Generate float32 vector embeddings
    batch_embeds = embed_model.get_text_embedding_batch(context)
    # Convert float32 vectors to binary vectors
    embeds_array = np.array(batch_embeds)
    binary_embeds = np.where(embeds_array > 0, 1, 0).astype(np.uint8)
    # Convert to bytes array
    packed_embeds = np.packbits(binary_embeds, axis=1)
    byte_embeds = [vec.tobytes() for vec in packed_embeds]

    binary_embeddings.extend(byte_embeds)

这被称为二进制量化

向量索引

完成二进制量化后,我们将向量存储到向量数据库中,以便更高效的检索

复制代码
from pymilvus import MilvusClient, DataType

# Initialize client and schema
client = MilvusClient("milvus_binary_quantized.db")
schema = client.create_schema(auto_id=True, enable_dynamic_fields=True)

# Add fields to schema
schema.add_field(field_name="context", datatype=DataType.VARCHAR)
schema.add_field(field_name="binary_vector", datatype=DataType.BINARY_VECTOR)

# Create index parameters for binary vectors
index_params = client.prepare_index_params()
index_params.add_index(
    field_name="binary_vector",
    index_name="binary_vector_index",
    index_type="BIN_FLAT",  # Exact search for binary vectors
    metric_type="HAMMING"   # Hamming distance for binary vectors
)

# Create collection with schema and index
client.create_collection(
    collection_name="fastest-rag",
    schema=schema,
    index_params=index_params
)

# Insert data to index
client.insert(
    collection_name="fastest-rag",
    data=[
        {"context": context, "binary_vector": binary_embedding}
        for context, binary_embedding in zip(batch_context, binary_embeddings)
    ]
)

检索

在检索阶段,我们执行以下操作

  • 对用户查询进行嵌入处理,并将其转化为二进制量化

  • 采用汉明间距作为向量度量指标来对比二进质量化

  • 检索出相似度最高的前5个文本块

  • 并将检索到的文本块添加至上下文信息中

    Generate float32 query embedding

    query_embedding = embed_model.get_query_embedding(query)

    Apply binary quantization to query

    binary_query = binary_quantize(query_embedding)

    Perform similarity search using Milvus

    search_results = client.search(
    collection_name="fastest-rag",
    data=[binary_query],
    anns_field="binary_vector",
    search_params={"metric_type": "HAMMING"},
    output_fields=["context"],
    limit=5 # Retrieve top 5 similar chunks
    )

    Store retrieved context

    full_context = []
    for res in search_results:
    context = res["payload"]["context"]
    full_context.append(context)

生成

接下来,我们利用指令模型构建生成流程

我们将查询与检索到的上下文内容,一同写入提示词模板,再传给大语言模型

复制代码
from llama_index.llms.groq import Groq
from llama_index.core.base.llms.types import (
    ChatMessage, MessageRole )

llm = Groq(
    model="moonshotai/kimi-k2-instruct",
    api_key=groq_api_key,
    temperature=0.5,
    max_tokens=1000
)

prompt_template = (
    "Context information is below.\n"
    "---------------------\n"
    "CONTEXT: {context}\n"
    "---------------------\n"
    "Given the context information above think step by step "
    "to answer the user's query in a crisp and concise manner. "
    "In case you don't know the answer say 'I don't know!'.\n"
    "QUERY: {query}\n"
    "ANSWER: "
)

query = "Provide concise breakdown of the document"

prompt = prompt_template.format(context=full_context, query=query)
user_msg = ChatMessage(role=MessageRole.USER, content=prompt)

# Stream response from LLM
streaming_response = llm.stream_complete(user_msg.content)
  • Kimi-K2 作为 LLM 在 Groq(托管)上托管

经过这一套流程,我们可以得到在30毫秒钟内完成3600万加向量检索,一秒内生成回复

我们构建了速度最快的RAG技术栈,借助二进制量化BQ实现高效检索

代码仓库:https://github.com/patchy631/ai-engineering-hub/tree/main/fastest-rag-milvus-groq

RAG高阶技巧如何实现窗口上下文检索?

基础RAG存在的问题及解决方案

RAG是一种结合了检索和生成的AI应用落地方案,它可以根据给定的问题生成回答,同时利用外部知识库来增强生成和质量的多样性。RA的核心思想是将问题和知识库中的文档进行匹配,然后将匹配到的文档作为生成模型的输入,从而生成更加相关和丰富的回答

RAG的检索流程

RAG的检索流程可以分为以下几个步骤

  1. 加载文档将各种格式的文档加载后转换为文档,例如PDF加载为文本数据,或者将表格转换为多个键值对
  2. 将文档拆分为适合存储的向量单元,以便于向量存储以及检索时文档的匹配
  3. 将文档用向量表示
  4. 将向量化后的分块数据存入数据库
  5. 根据问题和文档向量计算它们之间的相似度,然后根据相似度的高低,选择最相关的文档作为检索结果
  6. 将解索到的文档作为生成模型的输入,根据问题生成回答

问题:

  • 如果我们分块拆的太大,同一块中非相关的内容就越多,对问题的检索匹配度影响越大,会导致检索的不准确
  • 如果分块拆的最小,检索匹配度会提高。然而,在最后的查询环节给大语言提供的上下文信息支撑较少,导致回答不准确

解决方案窗口上下文检索

解决这个问题,一般采用的方案是在拆分时尽量将文本切分到最小的语义单元。这样在检索时不直接使用匹配到的文档,而是通过匹配到的文档扩展及上下文整合在一起,再投递给大语言使用。这样既可以提高检索精度,又可以保证上下文准确性,从而提高生成质量和多样性

具体步骤:

  • 在拆分时,将文本拆分为最小的语义单元
  • 在检索时,根据问题和文档向量计算它们之间的相似度,然后选择最相关的文档作为检索结果,同时记录下他们的编号
  • 在查询时,根据检索结果的编号,从文本中获取他们的上下文信息,然后将它们拼接成一个完整的文档,作为生成模型的输入。根据问题生成答案

窗口上下文检索实践

  1. 分块编码并写入原数据
  1. 检索时,通过元数据中的顺序分块编码来查找上下文
相关推荐
_江南一点雨2 小时前
AI 重构企业级系统?我们做了一次低成本实践
人工智能·重构
名字不好奇2 小时前
Claude Code工作原理深度解析:从技术架构到设计哲学
人工智能·架构
一条咸鱼_SaltyFish2 小时前
DDD 架构重构实践:AI Skills 如何赋能DDD设计与重构
java·人工智能·ai·重构·架构·ddd·领域驱动设计
我是场2 小时前
我的NPI项目 - OTA upgrade是什么?
android·人工智能
GISer_Jing2 小时前
2026年前端AI开发终极指南
前端·人工智能
笨笨饿2 小时前
32_复变函数在工程中实际应用区别于联系
linux·服务器·c语言·人工智能·单片机·算法·学习方法
花千树-0102 小时前
Java AI + TTS:让大模型开口说话
java·人工智能·ai·chatgpt·langchain·aigc·ai编程
图欧学习资源库2 小时前
人工智能领域、图欧科技、IMYAI智能助手2026年2月更新月报
人工智能·科技
沪漂阿龙2 小时前
大语言模型时代的无监督学习:聚类与降维全解析
人工智能·机器学习·语言模型·聚类