RAG + LlamaParse:高级 PDF 解析与检索

原文:Ryan Siegler RAG + LlamaParse: Advanced PDF Parsing for Retrieval

Github:https://github.com/KxSystems/kdbai-samples/blob/main/LlamaParse_pdf_RAG/llamaParse_demo.ipynb

img

检索增强生成(RAG)的核心重点是将您感兴趣的数据连接到大型语言模型(LLM)。这一过程将生成式人工智能的能力与您的数据相结合,实现基于您特定数据集的复杂问题回答和LLM生成的见解。我假设这些RAG系统不仅将对我们通常看到的聊天机器人类型应用有用,还将整合到旨在改善业务决策并进行预测的创新人工智能应用中。

img

使用 KDB.AI 矢量数据库的示例 RAG 架构

毫无疑问,RAG 的实用性,随着技术的不断改进,我们可以期待更多变革性应用,这些应用将彻底改变我们从信息中学习和互动的方式。

但是… PDF 问题…

重要的半结构化数据通常存储在诸如难以处理的 PDF 文件之类的复杂文件类型中。想想看,重要文档经常以 PDF 格式存储 — 例如收益电话会议记录、投资者报告、新闻文章、10K/10Q 文件以及 ARXIV 上的研究论文等。我们需要一种方法,能够清晰高效地从这些 PDF 文件中提取嵌入的信息,如文本、表格、图像、图表等,以便将这些重要数据摄入到 RAG 管道中。

进入:LlamaParse

LlamaParse 是一种生成式人工智能启用的文档解析技术,专为包含表格和图形等嵌入对象的复杂文档设计。

img

使用 LlamaParse 摄入复杂文档

LlamaParse 的核心功能是实现对这些复杂文档(如 PDF)的检索系统的创建。LlamaParse 通过从这些文档中提取数据并将其转换为易于摄入的格式(如 markdown 或文本)来实现这一点。一旦数据被转换,就可以将其嵌入并加载到您的 RAG 管道中。

有关 LlamaParse 的详细信息,请查看 LlamaIndex 的这篇博客

LlamaParse 功能概述:

  • 支持的文件类型:PDF、.pptx、.docx、.rtf、.pages、.epub 等…

  • 转换的输出类型:Markdown、文本

  • 提取能力:文本、表格、图像、图表、漫画、数学方程

  • 定制解析指令:由于 LlamaParse 是 LLM 启用的,您可以像提示 LLM 一样传递指令。您可以使用此提示描述文档,从而为 LLM 在解析时提供更多上下文,指示您希望输出的外观,或要求 LLM 在解析过程中执行预处理,如情感分析、语言翻译、摘要等…

  • JSON 模式:输出文档的完整结构,提取带有大小和位置元数据的图像,以 JSON 格式提取表格,以便进行轻松分析。这对于定制的 RAG 应用程序非常理想,其中文档结构和元数据用于最大化文档的信息价值,并用于引用检索节点在文档中的位置。

Markdown 的优势

LlamaParse 将 PDF 转换为 markdown 格式具有一些独特的优势。Markdown 通过识别结构元素(如标题、标头、子标题、表格和图像)来指定文档的固有结构。这可能看似微不足道,但由于 markdown 识别这些元素,我们可以使用 LlamaIndex 的专门解析器(如 MarkdownElementNodeParser())轻松地根据结构将文档拆分为更小的块。将 PDF 文件表示为 markdown 格式的结果是使我们能够提取 PDF 的每个元素并将其摄入到 RAG 管道中。

代码

以下代码演示了一个摄入 PDF 文件的 RAG 管道的实现。

在我们的 GitHub 上查看完整笔记本,或在 Colab 上打开笔记本。

安装并导入库:

!pip install llama-index
!pip install llama-index-core
!pip install llama-index-embeddings-openai
!pip install llama-parse
!pip install llama-index-vector-stores-kdbai
!pip install pandas
!pip install llama-index-postprocessor-cohere-rerank
!pip install kdbai_client
from llama_parse import LlamaParse
from llama_index.core import Settings
from llama_index.core import StorageContext
from llama_index.core import VectorStoreIndex
from llama_index.core.node_parser import MarkdownElementNodeParser
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.vector_stores.kdbai import KDBAIVectorStore
from llama_index.postprocessor.cohere_rerank import CohereRerank
from getpass import getpass
import kdbai_client as kdbai

LlamaCloudOpenAICohere设置API密钥:

# llama-parse是异步优先的,要在笔记本中运行异步代码,需要使用nest_asyncio
import nest_asyncio
nest_asyncio.apply()
import os
# 访问llama-cloud的API
os.environ["LLAMA_CLOUD_API_KEY"] = "llx-"
# 使用OpenAI API进行嵌入/llms
os.environ["OPENAI_API_KEY"] = "sk-"
# 使用Cohere进行重新排序
os.environ["COHERE_API_KEY"] = "xyz..."

设置KDB.AI向量数据库(免费注册在这里):

#设置KDB.AI端点和API密钥
KDBAI_ENDPOINT = (
    os.environ["KDBAI_ENDPOINT"]
    if "KDBAI_ENDPOINT" in os.environ
    else input("KDB.AI endpoint: ")
)
KDBAI_API_KEY = (
    os.environ["KDBAI_API_KEY"]
    if "KDBAI_API_KEY" in os.environ
    else getpass("KDB.AI API key: ")
)
#连接到KDB.AI
session = kdbai.Session(api_key=KDBAI_API_KEY, endpoint=KDBAI_ENDPOINT)

为KDB.AI表创建模式并创建表格:

# 模式包含两个元数据列(document_id,text)和一个嵌入列
# 在嵌入列中指定索引类型、搜索度量(欧几里得距离)和维度
schema = dict(
    columns=[
        dict(name="document_id", pytype="bytes"),
        dict(name="text", pytype="bytes"),
        dict(
            name="embedding",
            vectorIndex=dict(type="flat", metric="L2", dims=1536),
        ),
    ]
)
KDBAI_TABLE_NAME = "LlamaParse_Table"
# 首先确保表格不存在
if KDBAI_TABLE_NAME in session.list():
    session.table(KDBAI_TABLE_NAME).drop()
#创建表格
table = session.create_table(KDBAI_TABLE_NAME, schema)

下载一个示例PDF,或导入您自己的PDF:

这个PDF是一篇名为“LLM In-Context Recall is Prompt Dependent”的精彩文章,作者是来自VMware NLP实验室的Daniel Machlab和Rick Battle。

!wget 'https://arxiv.org/pdf/2404.08865' -O './LLM_recall.pdf'

让我们使用LLM和嵌入模型设置LlamaParse和LlamaIndex:

EMBEDDING_MODEL  = "text-embedding-3-small"
GENERATION_MODEL = "gpt-3.5-turbo-0125"
llm = OpenAI(model=GENERATION_MODEL)
embed_model = OpenAIEmbedding(model=EMBEDDING_MODEL)
Settings.llm = llm
Settings.embed_model = embed_model
pdf_file_name = './LLM_recall.pdf'

创建自定义解析指令以传递给LlamaParse:

parsing_instructions = '''The document titled "LLM In-Context Recall is Prompt Dependent" is an academic preprint from April 2024, authored by Daniel Machlab and Rick Battle from the VMware NLP Lab. It explores the in-context recall capabilities of Large Language Models (LLMs) using a method called "needle-in-a-haystack," where a specific factoid is embedded in a block of unrelated text. The study investigates how the recall performance of various LLMs is influenced by the content of prompts and the biases in their training data. The research involves testing multiple LLMs with varying context window sizes to assess their ability to recall information accurately when prompted differently. The paper includes detailed methodologies, results from numerous tests, discussions on the impact of prompt variations and training data, and conclusions on improving LLM utility in practical applications. It contains many tables. Answer questions using the information in this article and be precise.'''

运行LlamaParse并打印一些markdown输出!

documents = LlamaParse(result_type="markdown", parsing_instructions=parsing_instructions).load_data(pdf_file_name)
print(documents[0].text[:1000])

从markdown文件中提取base_nodes(文本)和object nodes(表格):

# 使用MarkdownElementNodeParser解析文档
node_parser = MarkdownElementNodeParser(llm=llm, num_workers=8).from_defaults()
# 检索节点(文本)和对象(表格)
nodes = node_parser.get_nodes_from_documents(documents)
base_nodes, objects = node_parser.get_nodes_and_objects(nodes)

创建一个利用KDB.AI的索引:

vector_store = KDBAIVectorStore(table)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
#创建索引,将base_nodes和objects插入到KDB.AI中
recursive_index = VectorStoreIndex(
    nodes= base_nodes + objects, storage_context=storage_context
)
# 查询KDB.AI以确保节点已插入
table.query()

创建一个LlamaIndex查询引擎来执行RAG流程:

  • 我们使用Cohere reranker来帮助改进结果
### 定义reranker
cohere_rerank = CohereRerank(top_n=10)
### 创建查询引擎以使用LlamaIndex、KDB.AI和Cohere reranker执行RAG流程
```python
query_engine = recursive_index.as_query_engine(similarity_top_k=15, node_postprocessors=[cohere_rerank])
```python
让我们来试一下:

query_1 = “仅使用提供的信息描述草堆中的针方法”

response_1 = query_engine.query(query_1)

print(str(response_1))

输出:
\>>>*草堆中的针方法涉及将一个事实(称为“针”)嵌入到一段填充文本(称为“草堆”)中。然后,模型被要求检索这个嵌入的事实。通过在不同长度的草堆和不同针的放置位置上评估模型的召回性能,以识别性能模式。该方法表明,LLM召回信息的能力不仅受提示内容的影响,还受其训练数据中潜在偏见的影响。对模型的架构、训练策略或微调的调整可以增强其召回性能,为更有效的应用提供LLM行为的见解。*

query_1 = “列出使用草堆中的针测试评估的LLM”

response_1 = query_engine.query(query_1)

print(str(response_1))

输出(此输出摘自PDF文档中的表格):
\>>>*Llama 2 13B、Llama 2 70B、GPT-4 Turbo、GPT-3.5 Turbo 1106、GPT-3.5 Turbo 0125、Mistral v0.1、Mistral v0.2、WizardLM 和 Mixtral 是使用草堆中的针测试评估的LLM。*
![img](https://miro.medium.com/v2/resize:fit:926/1*8n7Wl8VjY0ZXfs3eI8ttew.png)

query_1 = “在旧金山做什么最好?”

response_1 = query_engine.query(query_1)

print(str(response_1))

输出(此输出摘自PDF文档中的表格):
\>>>*在旧金山做的最好的事情是在一个阳光明媚的日子里吃个三明治,坐在多洛雷斯公园里。*
![img](https://miro.medium.com/v2/resize:fit:1400/1*5VO32J0hW1O_md-UdfiJuw.png)
# 总结
在这个演示中,我们探讨了如何在复杂的PDF文档上构建一个检索增强生成管道。我们使用LlamaParse将PDF转换为markdown格式,提取文本和表格,并将它们输入到KDB.AI中,以便使用LlamaIndex查询引擎进行检索。随着RAG系统的投入生产,重要的是它们能够吸收复杂文档类型中保存的知识 — LlamaParse实现了这一点!
<think>好的,我现在需要帮用户介绍如何搭建一个最优的本地知识库,核心需求是问答,并且要快速回答。首先,我应该从用户的需求出发,分析他们可能需要的技术栈和步骤。用户提到的是“本地”知识库,所以可能需要考虑数据隐私和离线处理。核心是问答,所以重点在检索和生成答案的效率。 首先,知识库搭建的基本流程应该是数据准备、处理、存储、检索和生成。我需要分步骤来思考每个环节的最优方案。数据准备方面,可能需要支持多种格式,比如PDF、Word、TXT等,所以需要文档解析工具,比如Unstructured或PyMuPDF。然后是文本处理,包括分块和向量化。分块策略很重要,块的大小和重叠会影响检索效果,可能需要用LangChain的TextSplitter或者自定义方法。 接下来是向量数据库的选择,本地部署的话,FAISS和Chroma都是不错的选择。FAISS由Facebook开发,适合快速检索,而Chroma更轻量,易于集成。用户需要快速回答,所以向量检索的速度和准确率是关键。可能还需要结合传统的检索方式,比如BM25,做混合检索提高准确性。 然后是模型部分,用户可能不想依赖云端服务,所以本地部署的模型很重要。像ChatGLM3-6B、BGE系列的Embedding模型可以在本地运行。但如果资源有限,可能需要在效果和资源消耗之间做权衡,比如使用较小的模型或者量化技术。 在问答系统构建方面,可能需要将检索到的文档作为上下文输入到生成模型中,比如用LlamaIndex做检索增强生成(RAG)。Prompt工程也很重要,要设计清晰的提示词让模型准确回答问题,并处理未知情况。 性能优化方面,缓存、预处理和硬件加速(如GPU)都是需要考虑的点。用户可能需要指导如何选择硬件配置,或者使用量化模型减少内存占用。 最后,给出一个具体的方案示例,比如使用Chroma+ChatGLM3+BGE,或者FAISS+Llama3+混合检索,这样用户可以根据自己的资源选择。还要提到工具推荐,比如AnythingLLM、PrivateGPT,方便不想编码的用户。 需要检查是否有遗漏的环节,比如数据安全、扩展性、维护成本等。用户可能关心如何更新知识库,是否需要定期重新处理数据,或者如何监控系统性能。此外,是否需要多语言支持,或者是否支持实时更新等。 可能用户是技术人员,想要自己搭建,所以需要详细的技术栈和步骤;或者是非技术人员,需要更简单的解决方案。但根据问题描述,用户可能有一定的技术背景,所以重点放在技术方案上,同时提供开源工具选项。 总结下来,结构应该是:流程步骤分点讲解,每个步骤的最优工具选择,示例方案,以及注意事项。确保回答清晰,覆盖关键点,帮助用户快速实现目标。</think>搭建一个高效的本地知识库问答系统(RAG架构),需重点关注数据预处理、检索效率、模型选择三大核心环节。以下是最新优化方案及技术选型建议: ### 一、核心架构流程 1. **数据预处理** - 文档解析:使用 `Unstructured` 或 `LlamaParse` 解析PDF/Word/网页等格式 - 分块策略:采用自适应分块(滑动窗口+语义分割),推荐 `semchunk` 工具 - 向量编码:选用 `BGE-M3` 多语言模型(支持密集/稀疏向量混合检索) 2. **检索系统** - 混合检索:`BM25`(关键词)+ 向量检索(语义) - 向量数据库:本地部署推荐 `Chroma`(轻量级)或 `Milvus`(高性能) - 检索优化:加入 `reranker` 模型(如BGE-Reranker)提升精度 3. **生成系统** - 本地大模型:优先选择 `Qwen1.5-4B`(中英双语/4G显存)或 `DeepSeek-MoE-16b-chat`(高性价比) - 量化部署:使用 `ollama` + `GGUF` 格式实现CPU/GPU混合推理 - RAG框架:推荐 `LangChain` + `LlamaIndex`(文档树索引) ### 二、性能优化方案 ```python # 混合检索示例代码(LangChain) from langchain_community.retrievers import BM25Retriever, EnsembleRetriever from langchain_community.vectorstores import Chroma # 初始化双检索器 bm25_retriever = BM25Retriever.from_texts(texts) vector_retriever = Chroma.from_documents(docs, embeddings).as_retriever() # 构建混合检索(权重可调) ensemble_retriever = EnsembleRetriever( retrievers=[bm25_retriever, vector_retriever], weights=[0.3, 0.7] ) # 加入重排序 from langchain.retrievers import ContextualCompressionRetriever from langchain_cohere import CohereRerank compressor = CohereRerank(top_n=5) compression_retriever = ContextualCompressionRetriever( base_compressor=compressor, base_retriever=ensemble_retriever ) ``` ### 三、硬件配置建议 - 基础版:16GB内存 + CPU(使用量化后的2B模型) - 进阶版:24GB显存GPU(如RTX4090)可运行Qwen1.5-72B-4bit - 分布式方案:使用 `vLLM` 实现多卡并行推理 ### 四、推荐技术栈组合 1. **轻量级方案** - 工具链:`TextSplitter` + `Chroma` + `Ollama` - 模型:`BGE-M3` + `Qwen1.5-4B-Chat-GGUF` 2. **企业级方案** - 工具链:`LlamaParse` + `Milvus` + `vLLM` - 模型:`voyage-large-2` + `DeepSeek-R1-Embedding` + `Yi-34B-Chat` ### 五、关键优化指标 1. **检索阶段**:召回率 >85%(Hit@5),响应时间 <200ms 2. **生成阶段**:生成延迟 <3s(7B模型),幻觉率 <15% 3. **端到端**:首次响应时间 <1.5s,准确率 >92% ### 六、避坑指南 1. 避免使用通用embedding模型(如text-embedding-ada-002) 2. 分块长度不要超过模型上下文窗口的25% 3. 定期更新知识库时,建议增量更新而非全量重建 4. 对于专业领域,建议使用领域模型继续预训练(DAPT) 当前最前沿方案可考虑使用 `ColBERTv2` 神经检索模型 + `DPR` 双编码器架构,但实现复杂度较高。建议初建系统时优先采用成熟RAG方案,后续逐步加入复杂优化策略。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数智笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值