大模型应用:从问题到答案:LlamaIndex RAG系统工作流程详解.15

一、引言

在前面的文章我们对langchain和RAG都做过深入的探讨,检索增强生成(RAG)是解决模型核心痛点的关键技术,它能让大模型基于本地私有数据生成精准回答,彻底规避 幻觉问题,同时满足数据本地化、合规性要求。

而LlamaIndex是专为 RAG 场景设计的轻量级 Python 框架,定位为大模型的外部数据中间件,核心能力是高效连接本地数据与大模型。相较于 LangChain 等全场景框架,LlamaIndex 更聚焦 RAG 流程的轻量化实现,无需复杂配置即可完成 "文档加载→向量索引→智能问答" 的全闭环,且深度兼容 Qwen、Llama、Baichuan 等开源大模型,支持纯 CPU 本地化部署,是个人开发者和中小企业落地 RAG 应用的首选工具。

今天我们将从核心概念、技术架构、实战落地三个维度,由浅入深拆解 LlamaIndex 的使用方法,配套可直接运行的代码示例,基于本地的 paraphrase-MiniLM-L6-v2 嵌入模型和 Qwen1.5-1.8B-Chat 大模型,并补充关键技术细节与可视化流程图,覆盖从原理到实践的全维度知识。

二、LlamaIndex 核心概念

LlamaIndex 的核心价值在于将非结构化本地数据与大语言模型高效连接,其底层逻辑围绕一组核心概念构建,这些概念构成了 RAG(检索增强生成)流程的完整闭环。以下对每个核心概念进行逐维度、精细化的解析:

1. Document(文档)

1.1 基础定义

Document 是 LlamaIndex 对原始数据源的标准化封装,是整个 RAG 流程的数据入口。它并非仅指传统的文本文档,而是涵盖所有可被加载的结构化 / 非结构化数据的抽象载体,无论是本地 TXT/PDF/Markdown 文件、数据库中的单行数据、网页爬取的文本,还是 API 返回的结构化信息,加载后都会被统一封装为 Document 对象。

1.2 核心属性

每个 Document 对象包含以下关键属性(决定了数据的可用性与溯源性):

  • text:核心属性,存储文档的原始文本内容(如 TXT 文件的全部字符、PDF 解析后的文本);
  • metadata:元数据字典,用于记录文档的上下文信息,是「回答溯源」和「多文档管理」的核心依据,常见字段包括:
    • file_name:文档文件名(如 system_design.txt);
    • file_path:文档绝对路径(如 D:/docs/system_design.txt);
    • creation_date:文档创建时间;
    • category:自定义文档分类(如 "需求文档""技术文档");
    • id_:唯一标识符,LlamaIndex 自动生成(也可自定义),用于区分不同 Document。

1.3 关键特性

  • 多源适配性:LlamaIndex 提供 100+ 内置 Data Loader,可将不同数据源(本地文件、数据库、Notion、Slack 等)转为统一的 Document 对象,无需手动适配格式;
  • 不可变性:原始 Document 一旦加载,内容不会被修改(后续的分割、清洗操作会基于其生成新的 Node,而非修改原 Document);
  • 编码保障:加载中文文档时,需通过 encoding="utf-8" 指定编码,否则会导致 text 字段乱码,直接影响后续向量化和检索精度;
  • 批量加载:通过 SimpleDirectoryReader 可批量加载指定目录下的所有文件,生成 Document 列表,示例:
python 复制代码
from llama_index.core import SimpleDirectoryReader
# 加载目录下所有文件,生成Document列表
documents = SimpleDirectoryReader("./docs", encoding="utf-8").load_data()
# 查看第一个Document的属性
print(f"文档名称:{documents[0].metadata['file_name']}")
print(f"文档内容长度:{len(documents[0].text)}字符")

1.4 应用场景

  • 单文档加载:加载单个 TXT/PDF 文件作为问答数据源;
  • 多文档批量加载:加载整个知识库目录下的所有文档,实现跨文档检索;
  • 动态文档加载:从数据库 / API 实时获取数据并封装为 Document,适配动态更新的数据源。

2. Node(节点)

2.1 基础定义

Node 是 LlamaIndex 对 Document 的最小可检索单元,也被称为文本块。它是将原始长文档切分后生成的子文本片段,是 RAG 流程中向量化和检索的核心对象,长文档直接向量化会丢失局部语义(如一个 10000 字符的文档,其向量无法精准表征某一段落的含义),而将文档切分为小尺寸的 Node 后,每个 Node 的向量能精准对应局部语义,大幅提升检索精度。

2.2 与 Document 的关系

  • 从属关系:一个 Document 可被切分为多个 Node(一对多),每个 Node 会继承原 Document 的 metadata(如文件名、路径),并新增自身的元信息(如在原文档中的位置);
  • 独立性:Node 是独立的检索单元,检索过程中只会匹配与问题最相关的 Node,而非整个 Document;
  • 可追溯性:通过 Node 的 metadata 可反向定位到原始 Document,实现 "回答→Node→Document" 的全链路溯源。

2.3 核心属性

  • text:Node 的核心文本内容(如 512 字符的子文本片段);
  • metadata:继承自原 Document 的元数据 + 新增的节点元数据,如:
    • start_char_idx:Node 在原 Document 中的起始字符位置;
    • end_char_idx:Node 在原 Document 中的结束字符位置;
    • document_id:关联的原始 Document ID;
  • id_:Node 唯一标识符;
  • relationships:记录 Node 与其他 Node / 原 Document 的关系(如前序 Node、后序 Node、父 Document)。

2.4 切分规则

LlamaIndex 提供 SentenceSplitter(默认)、TokenTextSplitter 等 Node 切分器,核心参数决定切分效果:

2.4.1 chunk_size

  • 定义:单个 Node 的最大字符 / Token 数
  • 作用:核心参数,需适配嵌入模型的上下文长度:
      • 轻量模型(如 paraphrase-MiniLM-L6-v2):256~512 字符;
      • 高精度模型(如 bge-large-zh):512~1024 字符;
      • 过小会导致语义割裂(如一句话被切分),过大则失去局部精准性。

2.3.2 chunk_overlap

  • 定义:相邻 Node 的重叠字符数
  • 作用:用于避免语义割裂
    • 如 chunk_size=512 时,chunk_overlap=50 表示后一个 Node 会包含前一个 Node 最后 50 字符,确保上下文连贯;
    • 推荐值:chunk_size 的 10%~20%。

2.3.3 separator

  • 定义:切分分隔符
  • 作用:按语义切分的关键,避免切断完整语义:
      • 中文文档:\n(换行)、。(句号);
      • Markdown 文档:\n## (标题分隔符);
      • 默认值:(空格),易切断语义,需手动指定。

2.5 切分示例实践

python 复制代码
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core import SimpleDirectoryReader

# 1. 加载原始Document
documents = SimpleDirectoryReader("./docs", encoding="utf-8").load_data()
# 2. 自定义切分器(适配中文Markdown文档)
splitter = SentenceSplitter(
    chunk_size=512,          # 单个Node 512字符
    chunk_overlap=50,        # 重叠50字符
    separator="\n## "        # 按Markdown二级标题切分
)
# 3. 切分Document为Node列表
nodes = splitter.get_nodes_from_documents(documents)

# 查看Node属性
print(f"原始Document数量:{len(documents)}")
print(f"切分后Node数量:{len(nodes)}")
print(f"第一个Node的文本:{nodes[0].text[:100]}...")
print(f"第一个Node的来源文档:{nodes[0].metadata['file_name']}")
print(f"Node在原文档中的位置:{nodes[0].metadata['start_char_idx']}-{nodes[0].metadata['end_char_idx']}")

2.6 关键说明

  • 语义优先于长度:切分的核心目标是 "保留完整语义",而非严格遵守 chunk_size------ 若按分隔符切分后的文本块略超过 chunk_size,LlamaIndex 会优先保留完整语义,而非强制截断;
  • 避免过度切分:对于短文档(如 <1000 字符),无需切分,单个 Node 即可满足检索需求;
  • 多语言适配:中文文档需避免按空格切分(中文无空格),优先使用换行、标点作为分隔符。

3. Index(索引)

3.1 基础定义

Index 是 LlamaIndex 对Node 向量的结构化存储与检索引擎,是连接 "文本数据" 与 "相似度检索" 的核心桥梁。其本质是:将所有 Node 的文本内容通过嵌入模型转为高维向量,再将 "向量→Node" 的映射关系存储在特定的数据结构中,当用户提问时,能快速检索出与问题向量最相似的 Node 集合。

3.2 核心价值

  • 提速检索:直接比对文本相似度的时间复杂度为 O (n)(n 为 Node 数量),而基于向量索引的检索时间复杂度可降至 O (log n),即使上千个 Node 也能毫秒级返回结果;
  • 精准匹配:基于向量的语义检索,能突破关键词检索的局限性(如 "电脑卡" 可匹配到 "计算机运行缓慢"),实现 "语义级" 精准匹配;
  • 灵活扩展:支持内存存储(默认)、磁盘存储、第三方向量数据库(Chroma、Pinecone、Milvus 等),适配不同规模的应用场景。

3.3 索引类型

LlamaIndex 提供多种索引类型,适配不同场景,其中 VectorStoreIndex 是最基础、最常用的类型:

3.3.1 VectorStoreIndex

  • 基础介绍:存储 Node 向量,基于余弦相似度检索
  • 适用场景:通用场景(本地文档问答、知识库),支持纯 CPU 运行,轻量化部署

3.3.2 KeywordIndex

  • 基础介绍:基于关键词倒排索引检索
  • 适用场景:精准关键词匹配场景(如产品型号、编号检索),检索速度最快

3.3.3 TreeIndex

  • 基础介绍:构建树形结构,逐层汇总文本内容
  • 适用场景:长文档摘要、多文档汇总场景,适合生成结构化回答

3.3.4 ListIndex

  • 基础介绍:按顺序存储 Node,线性检索
  • 适用场景:小批量 Node(<100)场景,实现简单,无需复杂计算

3.3.5 KnowledgeGraphIndex

  • 基础介绍:构建实体关系图,基于图谱检索
  • 适用场景:实体关系推理场景(如 "XX 产品的适配型号有哪些")

3.4 VectorStoreIndex详细介绍

3.4.1 核心原理

  • 向量化:遍历所有 Node,通过嵌入模型(如 paraphrase-MiniLM-L6-v2)将每个 Node 的 text 转为固定维度的向量(如 paraphrase-MiniLM-L6-v2 输出 384 维向量);
  • 存储:将 "Node ID→向量→Node 元数据" 的映射关系存储在内存(默认)或磁盘;
  • 检索:用户提问时,先将问题转为向量,通过「余弦相似度」计算问题向量与所有 Node 向量的匹配度,返回 Top-K 最相似的 Node。

3.4.2 构建与使用

python 复制代码
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.node_parser import SentenceSplitter

# 1. 加载Document并切分为Node
documents = SimpleDirectoryReader("./docs", encoding="utf-8").load_data()
splitter = SentenceSplitter(chunk_size=512, chunk_overlap=50)
nodes = splitter.get_nodes_from_documents(documents)

# 2. 构建VectorStoreIndex(基于Node向量)
index = VectorStoreIndex(nodes)

# 3. 索引持久化(保存到磁盘,避免重复构建)
index.storage_context.persist(persist_dir="./index_cache")

# 4. 加载缓存索引(后续运行直接加载)
from llama_index.core import StorageContext, load_index_from_storage
storage_context = StorageContext.from_defaults(persist_dir="./index_cache")
index = load_index_from_storage(storage_context)

3.4.3 核心参数调优

  • similarity_top_k:检索返回的最相似 Node 数量(推荐 3~5),数量过少会丢失相关信息,过多会增加大模型推理压力;
  • embed_model:指定嵌入模型(默认 OpenAI Embedding,需替换为本地模型如 paraphrase-MiniLM-L6-v2);
  • show_progress:构建索引时显示进度条(True/False),大批量 Node 时建议开启。

3.5 索引的生命周期

    1. 构建:首次加载 Document 并切分 Node 后,生成向量并构建索引(耗时最长,取决于 Node 数量);
    1. 加载:后续运行从磁盘加载已构建的索引(毫秒级);
    1. 更新:新增 / 删除 Document 时,可增量更新索引(无需重新构建全部);
    1. 销毁:删除索引缓存目录,即可清除索引(需重新构建)。

4. Query Engine(查询引擎)

4.1 基础定义

Query Engine 是 LlamaIndex 封装的端到端问答引擎,是连接Index(索引)与 大模型 的核心桥梁。它将 "问题检索→提示词拼接→大模型生成→回答格式化" 的全流程封装为统一接口,用户只需调用 query() 方法即可完成从 "输入问题" 到 "获取回答" 的闭环,无需手动处理中间环节。

4.2 核心工作流程

Query Engine 的内部运行逻辑是 RAG 流程的核心,可拆解为 6 个步骤:

    1. 问题向量化:将用户输入的自然语言问题,通过与构建索引时相同的嵌入模型转为向量;
    1. 检索相关 Node:调用 Retriever(检索器)从 Index 中检索与问题向量最相似的 Top-K Node;
    1. 提示词拼接:将 "用户问题 + 检索到的 Node 文本" 拼接为符合大模型格式的提示词(Prompt)
    1. 调用大模型:将拼接后的提示词传入大模型(如 Qwen1.5-1.8B-Chat),生成原始回答;
    1. 回答格式化:剔除原始回答中的冗余信息(如重复的提示词内容),仅保留核心回答;
    1. 返回结果:将格式化后的回答返回给用户,同时可携带溯源信息(如回答对应的 Node)。

提示词拼接参考示例:

基于以下信息回答问题: 1. 智能客服系统基于LlamaIndex框架开发,支持纯CPU运行,内存占用≤6GB; 2. 系统采用的嵌入模型为paraphrase-MiniLM-L6-v2,大小80MB; 问题:智能客服系统的内存占用要求是多少?

4.3 核心 Query Engine 类型

  • DefaultQueryEngine:默认查询引擎,适配绝大多数通用场景;
  • RetrieverQueryEngine:自定义 Retriever 的查询引擎,适配个性化检索需求;
  • MultiDocumentQueryEngine:多文档联合查询引擎,适配跨索引检索场景;
  • SubQuestionQueryEngine:子问题查询引擎,将复杂问题拆分为子问题,分别检索后汇总回答(适配复杂推理问题)。

4.4 核心配置参数(as_query_engine())

4.4.1 similarity_top_k

  • 定义:检索的 Top-K Node 数量
  • 作用:通用场景 3~5,复杂推理场景 5~8,避免过多导致提示词过长

4.4.2 response_mode

  • 定义:回答生成模式
  • 作用:- compact:紧凑回答(默认,剔除冗余,最常用);
      • tree_summarize:树形汇总(多 Node 汇总回答);
      • refine:迭代优化(先生成初步回答,再结合更多 Node 优化);
      • simple_summarize:简单汇总(直接汇总 Node 内容)

4.4.3 llm

  • 定义:指定大模型实例
  • 作用:替换为自定义本地大模型(如 LocalQwenLLM),替代默认的 OpenAI GPT

4.4.4 verbose

  • 定义:是否显示检索过程
  • True 显示检索的 Node 数量、相似度等信息(调试用),False 仅返回回答

4.4.5 streaming

  • 定义:是否流式返回回答
  • 作用:True 逐字返回回答(提升交互体验),False 一次性返回(CPU 更高效)

4.5 使用示例

python 复制代码
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader

# 1. 构建索引
documents = SimpleDirectoryReader("./docs", encoding="utf-8").load_data()
index = VectorStoreIndex.from_documents(documents)

# 2. 创建查询引擎(自定义参数)
query_engine = index.as_query_engine(
    similarity_top_k=3,        # 检索Top-3 Node
    response_mode="compact",   # 紧凑回答模式
    verbose=True,              # 显示检索过程
    streaming=False            # 非流式返回
)

# 3. 执行问答
response = query_engine.query("智能客服系统的核心功能有哪些?")

# 4. 查看结果
print(f"最终回答:{response}")
# 查看回答溯源(对应的Node)
print("\n回答来源:")
for node in response.source_nodes:
    print(f"  相似度:{node.score:.2f} | 文本片段:{node.node.text[:100]}...")

4.6 关键特性

  • 端到端封装:用户无需关注中间的向量化、检索、提示词拼接等细节,只需调用 query() 方法;
  • 可扩展性:支持自定义 Retriever、LLM、回答模式,适配个性化需求;
  • 溯源性:返回的 response 对象包含 source_nodes 属性,可追溯回答的数据源,满足合规性要求;
  • 异常处理:内置基础的异常捕获逻辑(如检索不到相关 Node 时,大模型会基于通用知识回答,并提示 "未检索到相关信息")。

5. Retriever(检索器)

5.1 基础定义

Retriever 是 LlamaIndex 中从 Index 中检索相关 Node的核心组件,是 Query Engine 的 "检索子模块"。它的唯一职责是:接收用户问题的向量表示,从 Index 中筛选出与问题语义最相似的 Node 集合,为后续的提示词拼接提供素材。

Retriever 与 Index是配套使用的关系,不同类型的 Index 对应不同的 Retriever,如 VectorStoreIndex 对应 VectorIndexRetriever,确保检索逻辑与索引存储结构匹配。

5.2 核心 Retriever 类型

检索器类型 适配索引类型 检索原理 适用场景
VectorIndexRetriever VectorStoreIndex 余弦相似度匹配向量 通用语义检索场景(90%+ 的 RAG 场景)
KeywordTableRetriever KeywordIndex 关键词匹配(倒排索引) 精准关键词检索(如产品编号、文档名称)
TreeRetriever TreeIndex 树形结构逐层检索 长文档摘要、结构化回答场景
ListRetriever ListIndex 线性遍历 Node 匹配 小批量 Node(<100)、简单检索场景
BM25Retriever 任意 Index BM25 算法(传统信息检索) 补充向量检索,提升关键词匹配精度

5.3 核心检索原理

    1. 向量计算:将用户问题的向量与 Index 中所有 Node 的向量进行余弦相似度计算(余弦值越接近 1,语义越相似);
    • 余弦相似度公式:similarity(A,B)= A⋅B / ∥A∥×∥B∥
    • 其中,A 为问题向量,B 为 Node 向量,∥A∥、∥B∥ 为向量的模长。
    1. 排序筛选:将所有 Node 按相似度从高到低排序,筛选出前 similarity_top_k 个 Node;
    1. 返回结果:返回筛选后的 Node 列表,包含 Node 文本、相似度得分、元数据等信息。

5.4 Retriever 使用

5.4.1 默认 Retriever 使用(通过 Query Engine 间接调用)

python 复制代码
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader

# 1. 构建索引
documents = SimpleDirectoryReader("./docs", encoding="utf-8").load_data()
index = VectorStoreIndex.from_documents(documents)

# 2. 创建Query Engine时,默认使用VectorIndexRetriever
query_engine = index.as_query_engine(similarity_top_k=3)  # Retriever的top_k参数

5.4.2 自定义 Retriever(灵活适配场景)

python 复制代码
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core.query_engine import RetrieverQueryEngine

# 1. 构建索引
documents = SimpleDirectoryReader("./docs", encoding="utf-8").load_data()
index = VectorStoreIndex.from_documents(documents)

# 2. 自定义Retriever(指定top_k和索引)
retriever = VectorIndexRetriever(
    index=index,
    similarity_top_k=3,  # 检索Top-3 Node
    vector_store_query_mode="cosine"  # 检索模式:余弦相似度
)

# 3. 基于自定义Retriever创建Query Engine
query_engine = RetrieverQueryEngine.from_args(
    retriever=retriever,
    response_mode="compact"
)

# 4. 执行检索(手动调用Retriever,无需Query Engine)
nodes = retriever.retrieve("智能客服系统的内存占用要求是多少?")
print(f"检索到的Node数量:{len(nodes)}")
for node in nodes:
    print(f"相似度:{node.score:.2f} | 文本:{node.text[:100]}...")

5.5 检索精度优化

  • 组合检索:将 VectorIndexRetriever(语义检索)与 BM25Retriever(关键词检索)结合,取并集后再筛选,提升检索覆盖率;
  • 调优 similarity_top_k:从 3 开始逐步增加,直到回答精度不再提升(避免过度检索);
  • 过滤低相似度 Node:设置相似度阈值(如 0.5),剔除得分低于阈值的 Node,减少冗余信息;
  • 优化嵌入模型:更换更高精度的嵌入模型(如 bge-large-zh-v1.5),提升向量表征能力。

6. Response Synthesis(回答合成)

6.1 基础定义

Response Synthesis(回答合成)是 LlamaIndex 中将检索到的 Node 文本与用户问题结合,生成最终回答的核心环节,是 Query Engine 的 "生成子模块"。它的核心职责是:将检索到的相关 Node 文本合理组织为提示词,传入大模型并格式化输出结果,确保回答既基于检索到的真实数据,又符合用户的提问意图。

6.2 核心合成模式

LlamaIndex 提供多种合成模式,适配不同的检索结果和提问场景,核心模式的对比与适用场景如下:

6.2.1 compact(默认)

  • 核心逻辑:拼接问题 + Top-K Node 文本,生成紧凑提示词,大模型直接回答
  • 输出特点:简洁、精准、无冗余
  • 适用场景:通用问答场景(如 "系统的核心功能有哪些?")

6.2.2 tree_summarize

  • 核心逻辑:将 Node 按树形结构分层汇总,逐层生成回答,最终汇总为完整回答
  • 输出特点:结构化、全面、适合多 Node 场景
  • 适用场景:多文档汇总、长文本摘要(如 "总结系统的部署要求")

6.2.3 refine

  • 核心逻辑:先基于第一个 Node 生成初步回答,再结合后续 Node 迭代优化回答
  • 输出特点:精准、迭代优化、适合复杂问题
  • 适用场景:复杂推理、细节型问题(如 "系统的内存占用和 CPU 要求分别是什么?")

6.2.4 simple_summarize

  • 核心逻辑:直接汇总所有 Node 的文本内容,生成简洁摘要
  • 输出特点:简洁、仅汇总事实、无推理
  • 适用场景:纯事实汇总(如 "列出系统支持的文档格式")

6.2.5 generation

  • 核心逻辑:不使用检索到的 Node,直接调用大模型回答(退化为纯 LLM 问答)
  • 输出特点:无检索依据、易幻觉
  • 适用场景:仅用于对比测试(验证 RAG 的价值)

6.2.6 accumulate

  • 核心逻辑:按 Node 顺序累加文本,逐步生成回答
  • 输出特点:上下文连贯、适合时序数据
  • 适用场景:时序文档问答(如 "按时间顺序说明系统的迭代历程")

6.3 合成流程拆解

6.3.1 提示词构建:

核心逻辑是 "问题 + 检索到的 Node 文本 + 指令",示例提示词模板:

请严格基于以下提供的信息回答问题,不要使用外部知识。如果信息不足,请回答"未检索到相关信息"。

提供的信息:

{context} # 检索到的Node文本,多个Node用换行分隔

问题:{query}

回答:

其中,{context} 为检索到的 Top-K Node 文本拼接结果,{query} 为用户问题。

6.3.2 大模型调用:

将构建好的提示词传入大模型(如 Qwen1.5-1.8B-Chat),生成原始回答,示例:

原始回答:

请严格基于以下提供的信息回答问题,不要使用外部知识。如果信息不足,请回答"未检索到相关信息"。

提供的信息:

  1. 智能客服系统基于LlamaIndex框架开发,支持纯CPU运行,内存占用≤6GB;

  2. 系统采用的嵌入模型为paraphrase-MiniLM-L6-v2,大小80MB;

问题:智能客服系统的内存占用要求是多少?

回答:智能客服系统的内存占用≤6GB。

6.3.3 回答格式化:

剔除提示词中的冗余内容(如 "提供的信息""请严格基于以下信息" 等),仅保留核心回答("智能客服系统的内存占用≤6GB。")。

6.3.4 结果封装:

将格式化后的回答封装为 Response 对象,包含以下核心属性:

  • text:最终回答文本;
  • source_nodes:回答对应的 Node 列表(溯源用);
  • metadata:回答的元数据(如生成时间、使用的合成模式)。

6.4 自定义合成逻辑

对于个性化的合成需求(如自定义提示词模板、多轮优化回答),可通过继承 BaseSynthesizer 实现自定义合成器:

python 复制代码
from llama_index.core.response_synthesizers import BaseSynthesizer, ResponseTuple
from llama_index.core.llms import LLM
from typing import List
from llama_index.core.schema import NodeWithScore

class CustomSynthesizer(BaseSynthesizer):
    def __init__(self, llm: LLM):
        self.llm = llm

    def get_response(self, query: str, nodes: List[NodeWithScore]) -> ResponseTuple:
        # 1. 自定义提示词模板
        context = "\n".join([f"{i+1}. {node.text}" for i, node in enumerate(nodes)])
        prompt = f"""
        请以"专业、简洁"的风格回答以下问题,回答必须基于以下信息:
        信息:
        {context}
        问题:{query}
        专业回答:
        """
        # 2. 调用大模型生成回答
        response = self.llm.complete(prompt)
        # 3. 封装结果
        return ResponseTuple(response.text, nodes)

# 使用自定义合成器创建Query Engine
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.retrievers import VectorIndexRetriever

# 1. 构建索引和Retriever
documents = SimpleDirectoryReader("./docs", encoding="utf-8").load_data()
index = VectorStoreIndex.from_documents(documents)
retriever = VectorIndexRetriever(index=index, similarity_top_k=3)

# 2. 加载自定义LLM
from custom_llm import LocalQwenLLM  # 自定义Qwen LLM类
llm = LocalQwenLLM("D:\\modelscope\\hub\\qwen\\Qwen1___5-1___8B-Chat")

# 3. 创建自定义合成器
custom_synthesizer = CustomSynthesizer(llm=llm)

# 4. 构建Query Engine
query_engine = RetrieverQueryEngine(
    retriever=retriever,
    response_synthesizer=custom_synthesizer
)

# 5. 执行问答
response = query_engine.query("智能客服系统的核心功能有哪些?")
print(f"自定义合成回答:{response}")

6.5 关键优化原则

  • 提示词简洁化:避免过长的提示词(CPU 场景下,大模型处理长提示词速度慢且易出错),仅保留核心问题和检索到的 Node 文本;
  • 指令明确化:在提示词中明确回答要求(如 "严格基于提供的信息""简洁回答""不要编造信息"),减少大模型幻觉;
  • 格式标准化:对多 Node 场景,按固定格式(如编号列表)拼接 Node 文本,提升大模型理解效率;
  • 异常兜底:当检索到的 Node 为空时,提示大模型返回 "未检索到相关信息",而非编造答案。

三、应用示例

LlamaIndex支持自定义嵌入模型和LLM,我们可以通过相应的类来集成本地模型。这里我们使用sentence-transformers的模型作为嵌入模型,使用Qwen1.5-1.8B-Chat作为大模型。

执行步骤:

    1. 加载文档(这里假设有一个data目录,里面存放了要处理的文档)
    1. 设置嵌入模型(使用本地sentence-transformers模型)
    1. 设置大模型(使用本地Qwen1.5-1.8B-Chat)
    1. 创建索引并查询
python 复制代码
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.huggingface import HuggingFaceLLM
from llama_index.core.prompts import PromptTemplate
from modelscope import snapshot_download
import torch

# 1. 下载或加载本地模型
model_id = "qwen/Qwen1.5-1.8B-Chat"
cache_dir = "D:\\modelscope\\hub"
local_model_path = snapshot_download(model_id, cache_dir=cache_dir)

# 2. 配置本地嵌入模型
embed_model_path = "D:/modelscope/hub/models/sentence-transformers/paraphrase-MiniLM-L6-v2"

Settings.embed_model = HuggingFaceEmbedding(
    model_name=embed_model_path,
    device="cuda" if torch.cuda.is_available() else "cpu"
)

# 3. 配置本地大模型 - 修正版本
system_prompt = """你是一个有帮助的AI助手。请根据提供的上下文信息回答问题。如果无法从上下文中找到答案,请诚实地说明。"""

query_wrapper_prompt = PromptTemplate(
    "下面是一个问题。\n"
    "---------------------\n"
    "{query_str}\n"
    "---------------------\n"
    "请根据你的知识回答这个问题:"
)

# 方法1: 使用正确的参数配置
Settings.llm = HuggingFaceLLM(
    model_name=local_model_path,
    tokenizer_name=local_model_path,
    # 将 device_map 移到 model_kwargs 外面
    device_map="auto",
    model_kwargs={
        "torch_dtype": torch.float16 if torch.cuda.is_available() else torch.float32,
        "cache_dir": cache_dir,
    },
    generate_kwargs={
        "temperature": 0.1,
        "top_p": 0.95,
        "do_sample": True,
    },
    system_prompt=system_prompt,
    query_wrapper_prompt=query_wrapper_prompt,
)

# 4. 加载文档
try:
    documents = SimpleDirectoryReader("./data").load_data()
    print(f"成功加载 {len(documents)} 个文档")
except Exception as e:
    print(f"文档加载失败: {e}")
    print("请确保 ./data 目录存在且包含文档文件")
    print("正在创建示例文档...")
    
    # 创建一个示例文本文件
    import os
    os.makedirs("./data", exist_ok=True)
    with open("./data/example.txt", "w", encoding="utf-8") as f:
        f.write("""LlamaIndex 是一个用于构建检索增强生成(RAG)应用的数据框架。
它允许你连接自定义数据源到大语言模型。
主要功能包括:
1. 数据加载和索引
2. 向量搜索
3. 查询引擎
4. 对话系统""")
    
    documents = SimpleDirectoryReader("./data").load_data()
    print("已创建并加载示例文档")

# 5. 创建向量索引
print("正在创建向量索引...")
index = VectorStoreIndex.from_documents(
    documents,
    embed_model=Settings.embed_model
)

# 6. 创建查询引擎
query_engine = index.as_query_engine(
    llm=Settings.llm,
    similarity_top_k=3
)

# 7. 测试查询
print("\n" + "="*50)
print("LlamaIndex 本地模型示例 - 测试查询")
print("="*50)

# 简单的测试查询
try:
    response = query_engine.query("LlamaIndex 是什么?")
    print(f"问题: LlamaIndex 是什么?")
    print(f"回答: {response}")
except Exception as e:
    print(f"查询失败: {e}")

# 8. 交互式查询
print("\n" + "="*50)
print("进入交互式查询模式 (输入 'quit' 或 'exit' 退出)")
print("="*50)

while True:
    try:
        user_query = input("\n请输入问题: ").strip()
        
        if user_query.lower() in ['quit', 'exit', 'q']:
            print("再见!")
            break
        
        if not user_query:
            continue
        
        response = query_engine.query(user_query)
        print(f"\n回答: {response}")
        
    except KeyboardInterrupt:
        print("\n\n程序被中断")
        break
    except Exception as e:
        print(f"查询出错: {e}")

输出参考:

成功加载 1 个文档

正在创建向量索引...

==================================================

LlamaIndex 本地模型示例 - 测试查询

==================================================

问题: LlamaIndex 是什么?

回答: LlamaIndex 是一个用于构建检索增强生成(RAG)应用的数据框架,它允许用户连接自定义数据源到大语言模型,并提供以下

主要功能:

  1. 数据加载和索引

  2. 向量搜索

  3. 查询引擎

  4. 对话系统

其中,LlamaIndex 的具体功能包括:

  1. 数据加载和索引:用户可以将来自各种来源(如数据库、API、文件系统等)的数据导入到LlamaIndex 中进行存储和索引。这

使得模型能够快速地从这些数据中提取有用的信息,从而提高检索性能和查询效率。

  1. 向量搜索:LlamaIndex 提供了一种高效的方法来搜索和查找向量数据。通过使用词袋模型或TF-IDF 等方法,模型能够识别出

与特定关键词相关的向量,并在搜索过程中优先匹配这些向量。这种搜索方式不仅适用于文本数据,也适用于图像、音频、视频等

多种类型的数据。

  1. 查询引擎:LlamaIndex 为用户提供了一个强大的查询引擎,使他们能够基于预定义的查询条件(如关键词、实体、关系等)对

模型中的向量进行查询。这个引擎还支持模糊查询、

==================================================

进入交互式查询模式 (输入 'quit' 或 'exit' 退出)

==================================================

请输入问题: 公司的年假政策是怎样的?

回答: 公司规定员工每年享受多少天年假?具体为:

1-3年员工享有5天年假,3-5年员工享有10天年假。所有员工都需要遵守保密协议,不得泄露公司商业秘密和技术信息。

请输入问题: 智能客服系统有哪些功能

回答: 智能客服系统的主要功能包括:...

请输入问题: 2024年AI发展趋势是什么

回答: 2024年,生成式AI技术将继续快速发展,多模态能力成为重点。企业应用AI的主要方向包括智能客服、内容生成、数据分析

等。深度学习和神经网络技术不断突破,大模型参数规模持续增长。具体来说,以下几点是2024年AI发展趋势:...

四、总结

基于LlamaIndex框架构建的本地RAG系统实现了从用户提问到智能回答的完整工作流。系统首先将用户输入的自然语言问题通过Sentence-Transformers嵌入模型向量化,随后调用Retriever组件从预先构建的向量索引中检索最相关的Top-K个文档节点。检索到的节点文本与原始问题经过精心设计的提示词模板进行智能拼接,形成完整的上下文输入。

本地部署的Qwen1.5大模型基于该上下文生成初步回答,最后经过后处理模块剔除冗余信息、格式化输出,向用户返回精准、可溯源的最终答案。整个流程充分体现了检索增强生成技术的核心优势,在保证数据隐私的前提下,有效扩展了大语言模型的知识边界,为构建企业级智能问答系统提供了可靠的技术路径。

相关推荐
Zzzzzxl_16 小时前
互联网大厂Java/Agent面试实战:Spring Boot、JVM、微服务与AI Agent/RAG场景问答
java·jvm·spring boot·ai·agent·rag·microservices
Zzzzzxl_16 小时前
互联网大厂Java/Agent面试实战:JVM、Spring Boot、微服务与RAG全栈问答
java·jvm·springboot·agent·rag·microservices·vectordb
Zzzzzxl_17 小时前
互联网大厂Java/Agent面试实战:微服务、RAG与Agent化实战(含答疑解析)
java·jvm·spring boot·agent·milvus·rag·microservices
沛沛老爹2 天前
LangGraph系列9 :调试、日志与可观测性 —— 当你的 AI 智能体突然精神分裂,如何 5 分钟定位故障?
人工智能·langchain·llm·调试·rag·langgraph·ai入门
deephub2 天前
LlamaIndex检索调优实战:七个能落地的技术细节
人工智能·python·大语言模型·rag·llamaindex
新知图书2 天前
使用FastGPT知识库构建智能客服的示例
人工智能·ai agent·智能体·大模型应用开发·大模型应用
Zzzzzxl_2 天前
互联网大厂Java/Agent面试实战:AIGC内容社区场景下的技术问答(含RAG/Agent/微服务/向量搜索)
java·spring boot·redis·ai·agent·rag·microservices
Zzzzzxl_2 天前
互联网大厂Java/Agent面试:Spring Boot、JVM、微服务、RAG与向量检索实战问答
java·jvm·spring boot·kafka·rag·microservices·vectordb
七夜zippoe2 天前
RAG系统架构设计中的向量数据库选型:从原理到企业级实践
pinecone·mvp·ann·rag·qdrant