【学习记录】LlamaIndex 实战:从数据连接到 RAG 问答(阿里云 DashScope + 混合加载)

本文基于 LlamaIndex 框架,结合阿里云 DashScope(通义千问)的 LLM 和嵌入模型,完整演示了从多源文档加载(本地文件 + 网页)构建向量索引再到自然语言问答的 RAG 全流程。同时深入解析了数据连接器(Data Connectors)的设计与 PDF 解析的底层原理。读完本文,将能独立搭建一套国产化 RAG 系统。


📌 目录

  1. 环境配置与 API 密钥
  2. 替换 OpenAI 为阿里云 DashScope
  3. 混合数据加载:本地文件 + 网页
  4. 构建索引与查询引擎
  5. Data Connectors 深度解析
    • 5.1 支持的数据源
    • 5.2 Llama Hub 与 Llama Cloud
    • 5.3 PDF 解析全流程(4 步详解)
  6. 常见问题与优化建议
  7. 总结

一、环境配置与 API 密钥

import os
from llama_index.core import Settings, VectorStoreIndex, SimpleDirectoryReader
from llama_index.llms.dashscope import DashScope
from llama_index.embeddings.dashscope import DashScopeEmbedding
from llama_index.readers.file import PyMuPDFReader
from llama_index.readers.web import BeautifulSoupWebReader

# 设置阿里云 API Key(请替换为自己的真实密钥)
os.environ["DASHSCOPE_API_KEY"] = "your-dashscope-api-key"

导入必要模块:

Settings:全局配置对象,用于设置 LLM 和 Embedding 模型。

VectorStoreIndex:用于构建向量索引的核心类。

SimpleDirectoryReader:读取本地文件夹中的文档。

DashScope 和 DashScopeEmbedding:阿里云 DashScope 的 LLM 和嵌入模型适配器。

设置环境变量 DASHSCOPE_API_KEY,值为从阿里云百炼平台获取的真实 API Key。注意:实际使用时应避免硬编码,建议从 .env 文件读取或使用环境变量。


二、替换 OpenAI 为阿里云 DashScope

# 配置大语言模型(LLM)
Settings.llm = DashScope(
    model_name="qwen-max",
    api_key=os.getenv("DASHSCOPE_API_KEY")
)

# 配置嵌入模型(Embedding)
Settings.embed_model = DashScopeEmbedding(
    model_name="text-embedding-v2",
    api_key=os.getenv("DASHSCOPE_API_KEY")
)
  • qwen-max:通义千问旗舰版,适合复杂指令和长上下文。
  • text-embedding-v2:输出 1536 维向量,与 OpenAI text-embedding-ada-002 维度一致,可无缝替换。
  • 通过这两行配置,LlamaIndex 内部的检索和生成组件将全部使用阿里云服务。

三、混合数据加载:本地文件 + 网页

from llama_index.core import SimpleDirectoryReader
from llama_index.readers.file import PyMuPDFReader
from llama_index.readers.web import BeautifulSoupWebReader

SimpleDirectoryReader:LlamaIndex 的核心文档加载器,可以读取指定目录下的所有文件(根据扩展名自动选择解析器)。

PyMuPDFReader:基于 PyMuPDF(fitz)的 PDF 解析器,能够提取文本及其在页面上的坐标信息(适合需要位置信息的场景,如 RAG 时引用具体段落)。

BeautifulSoupWebReader:基于 BeautifulSoup 的网页加载器,用于抓取指定 URL 的 HTML 内容并提取纯文本。

3.1 本地文件加载(使用 PyMuPDFReader 获取坐标信息)

local_loader = SimpleDirectoryReader(
    input_dir="./data",
    required_exts=[".pdf", ".docx", ".pptx", ".epub", ".md"],
    file_extractor={
        ".pdf": PyMuPDFReader(),   # 为 PDF 显式指定带坐标的解析器
    }
)
local_docs = local_loader.load_data()

input_dir=“./data”:指定要读取的文件夹路径。

required_exts=[…]:只处理扩展名在列表中的文件,忽略其他类型。

file_extractor={“.pdf”: PyMuPDFReader()}:为 .pdf 文件显式指定使用 PyMuPDFReader(而不是默认的 PDFReader)。这样解析出的 PDF 文档会包含文本的位置坐标(例如每个字符的边界框),便于后续做“段落高亮”或“来源定位”。

对于其他格式(如 .docx, .pptx 等),LlamaIndex 会使用内置的默认加载器(如 DocxReader, PptxReader),不需要在 file_extractor 中列出。

创建一个 BeautifulSoupWebReader 实例,默认使用 requests + beautifulsoup4 抓取网页并提取正文文本。

3.2 网页加载

web_loader = BeautifulSoupWebReader()
web_docs = web_loader.load_data(urls=[
    "https://cloud.tencent.com/developer/article/2499999"
])

local_loader.load_data():递归扫描 ./data 目录下的所有符合要求的文件,将它们分别解析为 LlamaIndex 的 Document 对象列表。每个 Document 包含 text(文本内容)和 metadata(如文件名、页数、创建时间等)。

web_loader.load_data(urls=[…]):抓取指定网页的 HTML,提取主文本内容,并返回一个 Document 对象列表(通常每个 URL 对应一个文档)。

documents = local_docs + web_docs:将本地文档和网页文档合并为一个列表,便于后续构建索引或查询。

3.3 合并文档

documents = local_docs + web_docs
  • 直接使用 + 操作符合并不同来源的 Document 列表,统一送入后续索引。

3.4 调试:打印文档预览

from pprint import pprint
for doc in local_docs:
    pprint(doc.text[:200])   # 打印前200字符预览
    print('-' * 130)

四、构建索引与查询引擎

假设我们已经通过上述步骤得到了 documents 列表(或直接使用之前加载的 documents_cloud),接下来构建索引。

# 构建向量索引
index = VectorStoreIndex.from_documents(documents)

# 创建查询引擎
query_engine = index.as_query_engine()

# 执行查询
response = query_engine.query("2022年实现营业收入?中文回答。")
print(response)

VectorStoreIndex.from_documents() 内部流程

  1. 文本分块(Node Parsing):默认 chunk_size=1024chunk_overlap=20
  2. 向量化:调用 Settings.embed_model 将每个块转为向量。
  3. 存储:存入默认的 SimpleVectorStore(内存中,可替换为 FAISS/Chroma 等持久化存储)。

query_engine.query() 内部流程

  1. 将用户问题向量化。
  2. 检索 top‑k 个最相似的块(默认 k=2)。
  3. 将检索结果与问题组合成提示词。
  4. 调用 Settings.llm 生成答案。
  5. 返回 Response 对象(包含答案文本和源节点)。

代码汇总

import os
from llama_index.core import Settings, VectorStoreIndex, SimpleDirectoryReader
from llama_index.llms.dashscope import DashScope
from llama_index.embeddings.dashscope import DashScopeEmbedding

# ========== 1. 设置 API Key ==========
os.environ["DASHSCOPE_API_KEY"] = "XXX"   # 从阿里云百炼平台获取

# ========== 2. 配置国内模型(替换 OpenAI) ==========
# Settings.llm = DashScope(model_name="qwen-max")               # 大语言模型
# Settings.embed_model = DashScopeEmbedding(model_name="text-embedding-v2",api_key=os.environ["DASHSCOPE_API_KEY"])   # 嵌入模型


Settings.llm = DashScope(
    model_name="qwen-max",
    api_key=os.environ["DASHSCOPE_API_KEY"]  # 显式从环境变量读取并传入
)
Settings.embed_model = DashScopeEmbedding(
    model_name="text-embedding-v2",
    api_key=os.environ["DASHSCOPE_API_KEY"]  # 嵌入模型也需显式传递
)

# ========== 3. 读取本地文档(优化 PDF 解析) ==========
# 只保留一个 reader,避免重复覆盖
reader = SimpleDirectoryReader(
    input_dir="./ragdata",
    required_exts=[".pdf"],
    recursive=True
)
documents_origin = reader.load_data()

# 可选:打印文档内容(确保文本提取正常)
from pprint import pprint
for document in documents_origin:
    pprint(document.text[:5])   # 只打印前500字符,避免过多输出
    print('-'*130)

# ========== 4. 构建索引与查询 ==========
index = VectorStoreIndex.from_documents(documents_origin)
query_engine = index.as_query_engine()
response = query_engine.query("2022年实现营业收入?中文回答。")
print(response)
from llama_index.core import SimpleDirectoryReader
from llama_index.readers.file import PyMuPDFReader
from llama_index.readers.web import BeautifulSoupWebReader

# 本地文件加载器(扩展支持)
# 使用 SimpleDirectoryReader 读取本地目录中的文件
local_loader = SimpleDirectoryReader(
    input_dir="./ragdata",                     # 指定要读取的目录路径
    required_exts=[".pdf", ".docx", ".pptx", ".epub", ".md"],  # 只处理这些扩展名的文件
    file_extractor={
        ".pdf": PyMuPDFReader(),               # 对 PDF 文件使用 PyMuPDFReader(保留文本坐标信息)
    }
)

# 网页加载器:使用 BeautifulSoupWebReader 抓取网页内容
web_loader = BeautifulSoupWebReader()

# 混合加载:同时读取本地文件和网页内容
local_docs = local_loader.load_data()           # 读取本地文件,返回 Document 对象列表
web_docs = web_loader.load_data(urls=[          # 抓取指定 URL 的网页并解析为 Document
   "https://cloud.tencent.com/developer/article/2499999?fromSource=gwzcw.9358214.9358214.9358214&utm_medium=cpc&utm_id=gwzcw.9358214.9358214.9358214"
])
documents = local_docs + web_docs               # 合并本地文档和网页文档

from pprint import pprint
for doc in local_docs:                          # 遍历每个本地文档
    # pprint(doc.text)                          # 可选:打印完整文本(注释掉)
    pprint(doc.text[:5])                        # 只打印每个文档文本的前5个字符,用于快速预览
    print('-'*130)                              # 打印分隔线,便于区分不同文档
import nest_asyncio  # 用于解决 Jupyter 环境中事件循环冲突的问题

nest_asyncio.apply()  # 应用 nest_asyncio 补丁,允许在已有事件循环中运行异步代码

from llama_cloud_services import LlamaParse  # 导入 LlamaCloud 的文档解析服务

# 创建 LlamaParse 解析器实例
parser = LlamaParse(
    api_key="llx-XXX",                # 替换为你的 LlamaCloud API Key
    result_type="markdown",           # 输出格式:markdown(也可选 "text")
    num_workers=3,                    # 并行处理文件时的工作线程数
    verbose=True,                     # 输出详细日志
    language="ch_sim",                # 文档语言:简体中文(默认 en)
)

# 配置文件提取器:对于 .pdf 扩展名,使用上面创建的 parser
file_extractor = {".pdf": parser}

# 使用 SimpleDirectoryReader 读取 ./ragdata 目录中的所有文件,
# 遇到 .pdf 文件时自动调用 LlamaParse 解析,其他文件使用默认读取器
documents_cloud = SimpleDirectoryReader(
    "./ragdata", file_extractor=file_extractor
).load_data()

from pprint import pprint
for doc in documents_cloud:                  # 遍历每个解析后的文档
    # pprint(doc.text)                       # 可选:打印完整文本内容(注释掉)
    pprint(doc.text[:5])                     # 打印每个文档文本的前 5 个字符(预览)
    print('-' * 130)                         # 打印分隔线,便于区分不同文档
index = VectorStoreIndex.from_documents(documents_origin)
#步骤三,构建查询引擎
query_engine = index.as_query_engine()
#步骤四,得到结果
response = query_engine.query("2022年实现营业收入?中文回答。")
print(response)

五、Data Connectors 深度解析

数据连接器是 LlamaIndex 的“触角”,负责从各种数据源读取数据并标准化为 Document 对象。

5.1 支持的数据源

数据源类型 常用加载器 说明
本地文件 SimpleDirectoryReader, PDFReader, PyMuPDFReader 支持 PDF, DOCX, PPTX, EPUB, MD, TXT 等
数据库 SQLDatabase 通过 SQLAlchemy 连接 PostgreSQL, MySQL 等
Web 爬取 BeautifulSoupWebReader, NotionPageReader 静态网页、Notion、Google Drive、Slack
API 自定义(requests + Document 任意 REST API 或 GraphQL

5.2 Llama Hub 与 Llama Cloud

  • Llama Hub:官方集成中心,提供上百种现成数据加载器。通过 download_loader 一键安装。
    from llama_index.core import download_loader
    GoogleDocsReader = download_loader("GoogleDocsReader")
    loader = GoogleDocsReader()
    docs = loader.load_data(document_ids=["..."])
    
  • Llama Cloud:托管式文档解析服务,专攻复杂 PDF(扫描件、多栏、表格)。使用 LlamaParse 调用。
    from llama_parse import LlamaParse
    parser = LlamaParse(api_key="your_key", result_type="markdown")
    documents = parser.load_data("./complex_report.pdf")
    

5.3 PDF 解析全流程(4 步详解)

步骤 核心任务 常见工具/方法 难点与解决方案
1. 输入与预处理 读取 PDF 文件,解密(如有密码),确定页面范围 pdfplumber.open(), PyMuPDF.open() 处理加密 PDF 需提供密码
2. 内容分类与识别 提取文本、图片、表格 - 文本:pypdf, pdfplumber, PyMuPDF
- 表格:pdfplumber.extract_table(), camelot
- OCR:pytesseract + pdf2image
扫描件需 OCR,表格需保留结构
3. 版面布局分析 恢复阅读顺序,处理多栏、跨页、连字符 - 坐标排序(pdfplumber.extract_words()
- 跨页表格拼接
多栏文本需按列重组;跨页段落需合并
4. 内容整合与输出 将处理后的文本、表格、元数据打包为 Document 输出为纯文本、Markdown 或 JSON 保留元数据(页数、位置框)便于溯源

核心要点

  • 使用 PyMuPDFReader 可获取文本位置信息,适合需要引用原文的场景。
  • 对于扫描件或复杂排版,优先考虑 LlamaParse 云端解析。

六、常见问题与优化建议

问题 原因 解决方案
API Key 无效(401) 环境变量未正确传递或 Key 已失效 显式传入 api_key 参数;检查阿里云百炼控制台
PDF 读取乱码(输出 %PDF-1.4... 缺少 PDF 解析依赖或文档为扫描件 pip install pypdf pdfplumber;扫描件使用 LlamaParse
嵌入模型调用超时 网络问题或批次过大 减小 chunk_size,设置 embed_batch_size=10
查询结果不相关 分块不合理或检索 top‑k 太小 调整 chunk_size(512~1024),增大 similarity_top_k=5
混合加载时 document 未定义错误 代码笔误(document 应为 doc 使用正确的变量名,如 doc.text

七、总结

本文完整演示了如何基于 LlamaIndex + 阿里云 DashScope 搭建一个国产化 RAG 系统,并深入解析了数据连接器的设计哲学与 PDF 解析的底层细节。核心步骤回顾:

  1. 配置 LLM 和 Embedding 模型(替换 OpenAI)。
  2. 混合加载本地文件和网页,合并为统一文档列表。
  3. 构建向量索引(自动分块、向量化、存储)。
  4. 执行自然语言查询,获得基于文档内容的答案。
  5. 理解 Data Connectors 的多种数据源,掌握 Llama Hub 和 Llama Cloud 的使用场景。
  6. 掌握 PDF 解析的 4 个核心步骤,能处理复杂排版和扫描件。

通过本文的学习,你应该能够独立构建一个适用于企业知识库、个人文档库、在线资讯监控等场景的 RAG 应用。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐