20 大模型学习——LlamaIndex构建RAG

官方学习文档:Building an LLM Application - LlamaIndex

一、LlamaIndex简介

LlamaIndex 是一个用于 LLM 应用程序的数据框架,用于注入,结构化,并访问私有或特定领域数据。

在本质上,LLM(如GPT)为人类和推断出的数据提供了基于自然语言的交互接口。广泛可用的大模型通常在大量公开可用的数据上进行的预训练,包括来自维基百科、邮件列表、书籍和源代码等。

构建在LLM模型之上的应用程序通常需要使用私有或特定领域数据来增强这些模型。不幸的是,这些数据可能分布在不同的应用程序和数据存储中。它们可能存在于API之后、SQL数据库中,或者存在于PDF文件以及幻灯片中。

LlamaIndex应运而生。

LlamaIndex 提供了5大核心工具:

  • Data connectors 数据连接器
  • Data indexes 数据索引
  • Engines 引擎
  • Data agents 数据智能体
  • Application integrations 应用集成

二、LlamaIndex环境配置

llamaindex不同版本间的兼容性较差。

建议新创建一个环境,python:3.10,torch:2.1.2+cu118

requirements_llamaIndex.txt

# python ==3.10
accelerate==1.0.1
aiohappyeyeballs==2.4.3
aiohttp==3.10.10
aiosignal==1.3.1
altair==5.4.1
annotated-types==0.7.0
anyio==4.6.2.post1
async-timeout==4.0.3
attrs==24.2.0
beautifulsoup4==4.12.3
blinker==1.8.2
cachetools==5.5.0
certifi==2022.12.7
charset-normalizer==2.1.1
click==8.1.7
chroma-hnswlib==0.7.6
chromadb==0.6.3
dataclasses-json==0.6.7
Deprecated==1.2.14
dirtyjson==1.0.8
distro==1.9.0
einops==0.8.0
exceptiongroup==1.2.2
filelock==3.16.1
frozenlist==1.4.1
fsspec==2024.9.0
gitdb==4.0.11
GitPython==3.1.43
greenlet==3.1.1
h11==0.14.0
httpcore==1.0.6
httpx==0.27.2
huggingface-hub==0.23.1
idna==3.4
InstructorEmbedding==1.0.1
Jinja2==3.1.4
jiter==0.6.1
joblib==1.4.2
jsonschema==4.23.0
jsonschema-specifications==2024.10.1
llama-cloud==0.1.2
llama-index==0.11.17
llama-index-agent-openai==0.3.4
llama-index-cli==0.3.1
llama-index-core==0.11.17
llama-index-embeddings-huggingface==0.3.1
llama-index-embeddings-instructor==0.2.1
llama-index-embeddings-openai==0.2.5
llama-index-indices-managed-llama-cloud==0.4.0
llama-index-legacy==0.9.48.post3
llama-index-llms-huggingface==0.3.5
llama-index-llms-openai==0.2.13
llama-index-multi-modal-llms-openai==0.2.2
llama-index-program-openai==0.2.0
llama-index-question-gen-openai==0.2.0
llama-index-readers-file==0.2.2
llama-index-readers-llama-parse==0.3.0
llama-index-vector-stores-chroma==0.4.1
llama-parse==0.5.7
llamaindex-py-client==0.1.19
markdown-it-py==3.0.0
MarkupSafe==3.0.1
marshmallow==3.22.0
mdurl==0.1.2
minijinja==2.2.0
modelscope==1.19.0
mpmath==1.3.0
multidict==6.1.0
mypy-extensions==1.0.0
narwhals==1.9.3
nest-asyncio==1.6.0
networkx==3.4.1
nltk==3.9.1
numpy==1.26.3
openai==1.51.2
packaging==24.1
pandas==2.2.3
pillow==10.2.0
propcache==0.2.0
protobuf==5.28.2
psutil==6.0.0
pyarrow==17.0.0
pydantic==2.9.2
pydantic_core==2.23.4
pydeck==0.9.1
Pygments==2.18.0
pypdf==4.3.1
python-dateutil==2.9.0.post0
pytz==2024.2
PyYAML==6.0.2
referencing==0.35.1
regex==2024.9.11
requests==2.32.3
rich==13.9.2
rpds-py==0.20.0
safetensors==0.4.5
scikit-learn==1.5.2
scipy==1.14.1
sentence-transformers==2.7.0
sentencepiece==0.2.0
six==1.16.0
smmap==5.0.1
sniffio==1.3.1
soupsieve==2.6
SQLAlchemy==2.0.35
streamlit==1.36.0
striprtf==0.0.26
sympy==1.13.3
tenacity==8.5.0
text-generation==0.7.0
threadpoolctl==3.5.0
tiktoken==0.8.0
tokenizers==0.19.1
toml==0.10.2
#torch==2.1.2+cu118
#torchaudio==2.1.2+cu118
#torchvision==0.16.2+cu118
tornado==6.4.1
tqdm==4.66.5
transformers==4.41.1
triton==2.1.0
typing-inspect==0.9.0
typing_extensions==4.12.2
tzdata==2024.2
urllib3==1.26.13
watchdog==4.0.2
wrapt==1.16.0
yarl==1.15.2

三、LlamaIndex调用大模型

默认支持的线上大模型只有ChatGPT,调用其他线上模型则需要额外处理。

1、线上调用ChatGPT

2、线上调用Qwen

需要重写模型方法

from llama_index.core.llms import CustomLLM,LLMMetadata
from dashscope import Generation
from typing import Any

class QwenLLM(CustomLLM):
    @property   # 用于将类中的方法定义为属性
    def metadata(self) -> LLMMetadata:
        return LLMMetadata(
            model_name='qwen-max',  # 模型名称
            context_window=8192,    # 上下文窗口大小(根据通义千问实际值调整)
            num_output=1024,        # 默认输出长度
            is_chat_model=True      # 是否是聊天模型
        )
    
    def complete(self, prompt: str, **kwargs: Any) -> str:
        """同步生成文本(必须实现)"""
        response = Generation.call(
            model='qwen-max',
            prompt=prompt,
            api_key= "sk-******", # 替换为你的API Key
            **kwargs
        )
        return response.output['text']   # 获取大模型输出结果
    
    def stream_complete(self, prompt: str, **kwargs: Any):
        """流式生成文本(可选,但需占位)"""
        return NotImplementedError("通义千问流式输出暂未实现")
qwen_max = QwenLLM()
res = qwen_max.complete("你好,通义千问!")
print(res)
3、本地调用Qwen
from llama_index.llms.huggingface import HuggingFaceLLM
from llama_index.core.llms import ChatMessage

llm = HuggingFaceLLM(
    model_name="F:\models\Qwen\Qwen2.5-0.5B-Instruct",
    tokenizer_name= "F:\models\Qwen\Qwen2.5-0.5B-Instruct",
    model_kwargs={
        "trust_remote_code":True  # 允许加载自定义代码
        },
    tokenizer_kwargs={"trust_remote_code":True},
)

# res = llm.chat(messages=[ChatMessage(role="user",content="什么是量子力学?")])
res = llm.chat(messages=[ChatMessage("什么是量子力学?")])
print(res)

四、LlamaIndex实现RAG引擎

Step01 加载模型

Step02 设置模型

Step03 读取数据

Step04 创建索引

Step05 创建查询引擎

Step06 通过引擎进行查询

1、query_engine
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.huggingface import HuggingFaceLLM

#初始化一个HuggingFaceEmbedding对象,用于将文本转换为向量表示
embed_model = HuggingFaceEmbedding(
    #指定了一个预训练的sentence-transformer模型的路径
    model_name="F:\models\sentence-transformers\paraphrase-multilingual-MiniLM-L12-v2"
    )

llm = HuggingFaceLLM(
    model_name="F:\models\Qwen\Qwen2.5-0.5B-Instruct",
    tokenizer_name="F:\models\Qwen\Qwen2.5-0.5B-Instruct",
    model_kwargs={"trust_remote_code":True},
    tokenizer_kwargs={"trust_remote_code":True}
)
from llama_index.core import Settings,SimpleDirectoryReader,VectorStoreIndex

#将创建的嵌入模型赋值给全局设置的embed_model属性,
#这样在后续的索引构建过程中就会使用这个模型。
Settings.embed_model = embed_model
#设置全局的llm属性,这样在索引查询时会使用这个模型。
Settings.llm = llm
#从指定目录读取所有文档,并加载数据到内存中
documents = SimpleDirectoryReader(input_dir="test_data").load_data()
print("docunments:",documents)
#创建一个VectorStoreIndex,并使用之前加载的文档来构建索引。
# 此索引将文档转换为向量,并存储这些向量以便于快速检索。
index = VectorStoreIndex.from_documents(documents)
# 创建一个查询引擎,这个引擎可以接收查询并返回相关文档的响应。
query_engine = index.as_query_engine()

res = query_engine.query("面膜的主要成分?")
print(res)
2、chat_engine
from llama_index.core import Settings,SimpleDirectoryReader,VectorStoreIndex

Settings.embed_model = embed_model
Settings.llm = llm

documents = SimpleDirectoryReader(input_dir="test_data").load_data()
index = VectorStoreIndex.from_documents(documents)
# 创建chat engine
chat_engine = index.as_chat_engine(
    chat_mode="context",  # 可根据需求更改
    verbose=True
)
# 交互式对话循环
while True:
    test_input = input("用户:")
    if test_input == "exit":
        break
    res = chat_engine.chat(test_input)
    print(f"AI助手:{res}")

五、LlamaIndex构建完整的RAG

构建完整的RAG检索增强生成管道

RAG是一种将检索系统与生成式AI结合的技术架构,主要包含以下组件:

  • 文档加载和处理
  • 向量存储
  • 检索器(Retriever)
  • 响应合成器(Response Synthesizer)
  • 后处理器(Postprocessor)
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.huggingface import HuggingFaceLLM
# 一、模型初始化
# 使用本地路径的HuggingFace模型
embed_model = HuggingFaceEmbedding(
    model_name=r"F:\models\sentence-transformers\paraphrase-multilingual-MiniLM-L12-v2"
    )
llm = HuggingFaceLLM(
    model_name=r"F:\models\Qwen\Qwen2.5-0.5B-Instruct",
    tokenizer_name=r"F:\models\Qwen\Qwen2.5-0.5B-Instruct",
    model_kwargs={"trust_remote_code":True},
    tokenizer_kwargs={"trust_remote_code":True}
)

import chromadb
from llama_index.core import Settings,SimpleDirectoryReader,VectorStoreIndex
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.core import StorageContext
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core.response_synthesizers import get_response_synthesizer
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.postprocessor import SimilarityPostprocessor
from llama_index.core.node_parser import SentenceSplitter
#from llama_index.core.indices.vector_store.retrievers import QueryCache
#from llama_index.core.cache import SimpleCache

# 二、系统配置
Settings.embed_model = embed_model  # 全局嵌入模型
Settings.llm = llm                 # 全局语言模型
# 三、数据加载
documents = SimpleDirectoryReader(input_dir="test_data").load_data()  # 自动解析txt/pdf/docx等常见格式
# 四、向量存储
db = chromadb.PersistentClient(path="chroma_db")  # 数据保存到chroma_db目录
chroma_collection = db.get_or_create_collection("test01")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
# 五、索引构建
"""
1.文档分块
2.生成嵌入向量
3.存储到ChromaDB
"""
# 自定义文本分割器(可选)
text_splitter = SentenceSplitter(
    separator="###",        # 主分割符
    chunk_size=768,          # 中文信息密度高,可适当增大
    chunk_overlap=128,
    paragraph_separator="###"   # 段落分隔符标识    
)

# index = VectorStoreIndex.from_documents(
#     documents=documents,
#     storage_context=storage_context,
#     transformations=[text_splitter]  # 关键注入点(可选)
# )
# 若加载现有向量数据库
index = VectorStoreIndex.from_vector_store(
    vector_store=vector_store,
    storage_context=storage_context
)
# 六、检索系统
"""
1.检索策略:返回相似度top_k的结果
2.扩展性:支持自定义检索算法
"""
#cache = QueryCache()
retriever = VectorIndexRetriever(
    index=index,
    similarity_top_k=10,                 # 召回范围
    similarity_cutoff=0.5,             # 相似度阈值
    vector_store_query_mode="hybrid",  # 混合检索模式(向量+BM25语义,实际触发的是ChromaDB的混合搜索能力)
    alpha=0.5,                         # 权重平衡参数(0=纯关键词,1=纯向量)
)
# 推荐方案
# [ 检索流程 ] 稀疏检索(如BM25) → 召回50条 → 密集检索(向量搜索) → 召回5条 → 合并去重 → 重排序 → 返回Top15
# retriever = VectorIndexRetriever(
#     similarity_top_k=15,        # 最终返回结果数
#     vector_store_query_mode="sparse",  # 启用稀疏检索模式
#     sparse_top_k=50,            # 稀疏检索召回数量
#     dense_top_k=5,              # 密集检索召回数量
#     reranker=BgeRerank()        # 重排序模型
# )
# 七、响应生成
"""
1.检索:找到相关文档片段
2.生成:综合上下文生成自然语言回答
如果不传参数,通常会使用默认配置:简洁的回答风格、基于检索到的前几名结果、使用系统默认的LLM
"""
response_synthesizer = get_response_synthesizer()
query_engine = RetrieverQueryEngine(  # 组装查询引擎
    retriever=retriever,
    response_synthesizer=response_synthesizer,
    #对检索结果进行过滤和排序
    #可以设置相似度阈值
    #支持自定义后处理逻辑
    node_postprocessors=[
        SimilarityPostprocessor(similarity_cutoff=0.5)  # 过滤低质量结果
    ]
)
# 八、执行查询
res = query_engine.query("品牌理念是什么")
print(res)
print("检索到的文档数:", len(res.source_nodes))
print("相似度分数:", [node.score for node in res.source_nodes])
print([node for node in res.source_nodes])

检索结果质量不高

  • 调整similarity_top_k参数
  • 优化文档分块策略
  • 使用混合检索策略

响应速度慢

  • 使用向量数据库索引
  • 启用缓存
  • 优化检索策略

构建一个完整的RAG管道需要考虑多个方面:

  • 合适的向量存储选择
  • 优化的检索策略
  • 高效的响应合成
  • 完善的后处理机制

LlamaIndex 的数据连接器通过 LlamaHub 提供。LlamaHub 是一个开源仓库,包含可轻松集成到任何 LlamaIndex 应用。Llama Hub

从LlamaHub加载数据连接器

以下示例代码从 LlamaHub 加载 Markdown 文档数据连接器。关于该数据连接器的细节,请参考https://llamahub.ai/l/file-markdown

from pathlib import Path
from llama_index import download_loader

MarkdownReader = download_loader("MarkdownReader")

loader = MarkdownReader()
documents = loader.load_data(file=Path('./README.md'))

六、拓展

当未检索出相关文档,需要llm自主回答时:

from llama_index.core.query_engine import BaseQueryEngine
from llama_index.core import get_response_synthesizer
from typing import Optional, Any

class SmartQueryEngine(BaseQueryEngine):
    def __init__(
        self,
        retriever,
        response_synthesizer=None,
        empty_response_prompt="请根据你的知识回答以下问题:\n{query_str}",
        similarity_threshold=0.4
    ):
        super().__init__()
        self.retriever = retriever
        self.response_synthesizer = response_synthesizer or get_response_synthesizer()
        self.empty_response_prompt = empty_response_prompt
        self.similarity_threshold = similarity_threshold

    def _query(self, query_str: str):
        # 执行检索
        nodes = self.retriever.retrieve(query_str)
        
        # 判断逻辑
        if len(nodes) == 0 or max(n.score for n in nodes) < self.similarity_threshold:
            # 自主回答模式
            prompt = self.empty_response_prompt.format(query_str=query_str)
            response = self.response_synthesizer.llm.complete(prompt)
            return Response(response=response.text, source_nodes=[])
        else:
            # 正常RAG模式
            return self.response_synthesizer.synthesize(query_str, nodes)

    async def _aquery(self, query_str: str):
        # 异步实现(逻辑同_query)
        ...

# 初始化自定义引擎
smart_engine = SmartQueryEngine(
    retriever=retriever,
    similarity_threshold=0.5,  # 自主回答阈值
    empty_response_prompt=(
        "你是一个专业助理,请直接回答用户问题,不要提及检索结果。\n"
        "问题:{query_str}"
    )
)

### 使用 LlamaIndex 实现 RAG 技术 #### 创建索引结构 为了实现检索增强生成(RAG),首先需要构建一个高效的索引结构来存储和管理文档的数据嵌入向量。这一步骤对于提高后续查询的速度至关重要[^3]。 ```python from llama_index import GPTSimpleVectorIndex, SimpleDirectoryReader documents = SimpleDirectoryReader('data').load_data() index = GPTSimpleVectorIndex.from_documents(documents) ``` 此代码片段展示了如何加载本地文件夹中的文档并将其转换成适合用于创建索引的形式,之后通过 `GPTSimpleVectorIndex` 类实例化了一个基于这些文档的新索引对象。 #### 构建问答数据集 在准备好了基础架构后,下一步就是为训练模型准备好合适的输入——即问题及其对应的上下文对。可以借助于 `generate_question_context_pairs()` 函数自动生成这样的配对集合[^2]。 ```python from llama_index.core.evaluation import generate_question_context_pairs qa_dataset = generate_question_context_pairs( nodes=index.get_nodes(), llm=llm, num_questions_per_chunk=2 ) ``` 这里调用了 `get_nodes()` 方法获取之前建立好的索引内的节点列表作为参数传递给上述函数;同时指定了每一段文本应该产生的问题数量。 #### 执行检索与生成过程 最后,在拥有完整的索引以及足够的训练样本之后,就可以开始执行真正的 RAG 流程了:当接收到用户的自然语言请求时,先利用已有的索引来查找最有可能包含答案的部分文档段落;接着把这些选出来的片段连同原始提问一起送入预训练的语言模型中完成最终的回答生成工作[^1]。 ```python query_engine = index.as_query_engine() response = query_engine.query("请问什么是检索增强生成?") print(response.response) ``` 这段脚本说明了怎样设置好查询引擎并通过它来进行一次具体的交互操作。用户提出的任何问题都会被转交给底层的索引机制去寻找最佳匹配项,并返回由大型语言模型给出的结果字符串形式的答案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值