LlamaIndex本地Demo学习笔记

这是我学习LlamaIndex框架的完整记录。通过9个递进式的本地demo,从零开始理解RAG系统的核心概念。

前言

最近在学习大模型应用开发,发现LlamaIndex是一个很有意思的框架。相比LangChain的通用性,LlamaIndex更专注于"索引"和"检索",特别适合构建RAG系统。

本文记录了我从零开始学习LlamaIndex的完整过程,包括9个本地demo和对应的讲解。希望能帮助和我一样的初学者快速上手。


第一部分:核心概念

LlamaIndex的5个核心概念

概念 作用 类比
Document 原始数据 + 元数据 书籍
Node 文档的分块 书籍的章节
Index 向量化的索引 书籍的目录
QueryEngine 单轮查询工具 查字典
ChatEngine 多轮对话工具 和朋友聊天

RAG系统的完整流程

原始数据 → 分块 → 向量化 → 索引 → 检索 → LLM生成答案

第二部分:9个本地Demo

Demo 1: 最简单的RAG系统(01_hello_llamaindex.py)

目标:用10行代码实现一个完整的RAG系统

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

# 配置
Settings.llm = DashScope(model="qwen-plus", api_key=os.getenv("DASHSCOPE_API_KEY"))
Settings.embed_model = DashScopeEmbedding(model="text-embedding-v1", api_key=os.getenv("DASHSCOPE_API_KEY"))

# 创建文档
doc = Document(text="北京朝阳区有一套三居室,价格600万")

# 创建索引
index = VectorStoreIndex.from_documents([doc])

# 查询
query_engine = index.as_query_engine()
response = query_engine.query("这套房子多少钱?")
print(f"回答:{response}")

关键点

  • Settings全局配置LLM和Embedding模型
  • 一行代码创建索引:VectorStoreIndex.from_documents()
  • 一行代码创建查询工具:as_query_engine()

Demo 2: LlamaIndex vs LangChain(02_langchain_comparison.py)

目标:对比两个框架的差异

LlamaIndex方式

index = VectorStoreIndex.from_documents(docs)
query_engine = index.as_query_engine()
response = query_engine.query("问题")

LangChain方式

vectorstore = FAISS.from_texts(texts, embeddings)
retriever = vectorstore.as_retriever()
prompt = ChatPromptTemplate.from_template("...")
chain = prompt | llm | StrOutputParser()
docs = retriever.get_relevant_documents("问题")
response = chain.invoke({"context": context, "question": "问题"})

结论

  • LlamaIndex代码更简洁,专注RAG
  • LangChain更灵活,适合复杂链式逻辑

Demo 3: 核心差异分析(03_core_difference.py)

LlamaIndex的思路

文档 → 向量化 → 索引 → 查询 → 生成答案

LangChain的思路

文本 → 向量化 → 向量存储 → 检索 → 提示词 → LLM → 答案

选择建议

  • 主要做RAG → 选LlamaIndex
  • 需要复杂链式逻辑 → 选LangChain

Demo 4: Document详解(04_document.py)

简单文档

doc = Document(text="北京朝阳区三居室,600万")

带元数据的文档

doc = Document(
    text="北京朝阳区三居室,600万",
    metadata={
        "location": "朝阳区",
        "price": 600,
        "bedrooms": 3
    }
)

为什么需要元数据?

  • 用于过滤和排序
  • 提高检索精度
  • 保存结构化信息

Demo 5: Node详解(05_node.py)

问题:长文档不能直接向量化,怎么办?

解决方案:分块成多个Node

from llama_index.core.node_parser import SimpleNodeParser

# 创建切割机
parser = SimpleNodeParser.from_defaults(
    chunk_size=300,      # 每个Node最多300个字符
    chunk_overlap=50     # 相邻Node重叠50个字符
)

# 执行切割
nodes = parser.get_nodes_from_documents([long_doc])

工作流程

Document(583字符)
    ↓
切割机按照chunk_size=300切割
    ↓
Node 1: 前300个字符 + 50个重叠
Node 2: 从250字符开始 + 后面的字符
    ↓
返回所有Node

为什么需要chunk_overlap?

  • 保持上下文连贯
  • 避免信息丢失
  • 提高检索准确度

Demo 6: Index详解(06_index.py)

目标:理解索引的创建过程

# 创建文档
docs = [
    Document(text="房子1:朝阳区,600万"),
    Document(text="房子2:海淀区,800万"),
    Document(text="房子3:东城区,500万"),
]

# 创建索引
index = VectorStoreIndex.from_documents(docs)

这一步做了什么?

  1. 把每个Document分成Node
  2. 对每个Node调用Embedding模型
  3. 把向量存储在内存中

Index的作用

  • 快速搜索相关文档
  • 支持向量相似度计算
  • 是RAG系统的核心

Demo 7: QueryEngine详解(07_query_engine.py)

目标:实现单轮查询

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

# 查询
response = query_engine.query("朝阳区有什么房子?")

similarity_top_k=2的含义

  • 返回最相关的2个文档
  • 用这2个文档作为上下文
  • 让LLM基于这些上下文生成答案

查询流程

问题:"朝阳区有什么房子?"
    ↓
向量化问题
    ↓
搜索最相关的2个文档
    ↓
构建提示词
    ↓
调用LLM生成答案

Demo 8: ChatEngine详解(08_chat_engine.py)

目标:实现多轮对话

from llama_index.core.memory import ChatMemoryBuffer

chat_engine = index.as_chat_engine(
    chat_mode="condense_question",
    memory=ChatMemoryBuffer.from_defaults(token_limit=3000)
)

# 第1轮
response1 = chat_engine.chat("我想在朝阳区买房")

# 第2轮
response2 = chat_engine.chat("预算600万")

# 第3轮
response3 = chat_engine.chat("推荐哪个?")

chat_mode="condense_question"的工作原理

用户第3轮:推荐哪个?
    ↓
读取对话历史:
  - 第1轮:我想在朝阳区买房
  - 第2轮:预算600万
    ↓
压缩成完整问题:
  "用户想在朝阳区买房,预算600万,现在问推荐哪个?"
    ↓
用这个完整问题去搜索和生成答案

memory=ChatMemoryBuffer.from_defaults(token_limit=3000)

  • 保存最近3000个token的对话历史
  • 避免内存爆炸
  • 保持对话连贯性

Demo 9: 完整的RAG系统(09_complete_rag.py)

目标:整合所有概念,构建完整的RAG系统

from llama_index.core import Document, VectorStoreIndex, Settings
from llama_index.llms.dashscope import DashScope
from llama_index.embeddings.dashscope import DashScopeEmbedding
import os
from dotenv import load_dotenv

# 加载环境变量
load_dotenv()

# 配置
Settings.llm = DashScope(model="qwen-plus", api_key=os.getenv("DASHSCOPE_API_KEY"))
Settings.embed_model = DashScopeEmbedding(model="text-embedding-v1", api_key=os.getenv("DASHSCOPE_API_KEY"))

# 步骤1:准备数据
docs = [
    Document(text="房子1:朝阳区CBD,600万,三居室,精装修"),
    Document(text="房子2:海淀区中关村,800万,四居室,毛坯房"),
    Document(text="房子3:东城区王府井,500万,两居室,简装修"),
]

# 步骤2:创建索引
index = VectorStoreIndex.from_documents(docs)

# 步骤3:创建查询引擎
query_engine = index.as_query_engine(similarity_top_k=2)

# 步骤4:查询
question = "我想在朝阳区买一套三居室,预算600万,有什么推荐?"
response = query_engine.query(question)
print(f"问题:{question}")
print(f"回答:{response}")

完整流程

┌─────────────────────────────────────────────────────────┐
│                    RAG系统完整流程                        │
└─────────────────────────────────────────────────────────┘

1. 数据准备阶段
   ├─ 收集原始数据(Document)
   ├─ 添加元数据(metadata)
   └─ 清洗和预处理

2. 索引构建阶段
   ├─ 分块(Node Parser)
   ├─ 向量化(Embedding Model)
   └─ 存储索引(Vector Store)

3. 查询阶段
   ├─ 用户输入问题
   ├─ 向量化问题
   ├─ 搜索相关文档
   └─ 返回Top-K结果

4. 生成阶段
   ├─ 构建提示词
   ├─ 调用LLM
   └─ 返回答案

第三部分:实战技巧

技巧1:调整相似度阈值

# 返回最相关的3个文档
query_engine = index.as_query_engine(similarity_top_k=3)

# 返回最相关的5个文档(更多上下文)
query_engine = index.as_query_engine(similarity_top_k=5)

技巧2:调整chunk_size

# 小chunk_size(100):更精细的分块,检索精度高
parser = SimpleNodeParser.from_defaults(chunk_size=100)

# 大chunk_size(1000):粗粒度分块,保留更多上下文
parser = SimpleNodeParser.from_defaults(chunk_size=1000)

# 推荐值:256-512
parser = SimpleNodeParser.from_defaults(chunk_size=512)

技巧3:多轮对话的内存管理

# 保存最近3000个token的对话历史
chat_engine = index.as_chat_engine(
    chat_mode="condense_question",
    memory=ChatMemoryBuffer.from_defaults(token_limit=3000)
)

# 保存最近10000个token的对话历史(更长的记忆)
chat_engine = index.as_chat_engine(
    chat_mode="condense_question",
    memory=ChatMemoryBuffer.from_defaults(token_limit=10000)
)

第四部分:常见问题

Q1: 为什么我的查询结果不准确?

可能原因

  1. chunk_size太小,上下文不足
  2. similarity_top_k太小,没有返回足够的相关文档
  3. 文档质量差,包含噪音

解决方案

# 增加chunk_size
parser = SimpleNodeParser.from_defaults(chunk_size=512)

# 增加similarity_top_k
query_engine = index.as_query_engine(similarity_top_k=5)

# 清洗文档数据

Q2: 如何处理超长文档?

# 方案1:使用更小的chunk_size
parser = SimpleNodeParser.from_defaults(chunk_size=256)

# 方案2:使用更大的chunk_overlap
parser = SimpleNodeParser.from_defaults(
    chunk_size=512,
    chunk_overlap=100
)

Q3: 如何提高查询速度?

# 1. 减少similarity_top_k
query_engine = index.as_query_engine(similarity_top_k=2)

# 2. 使用更小的chunk_size
parser = SimpleNodeParser.from_defaults(chunk_size=256)

Q4: 环境变量配置问题

# 确保.env文件中有DASHSCOPE_API_KEY
# 并且在代码中加载了.env文件

from dotenv import load_dotenv
load_dotenv()

下一步计划

创建房产推荐系统的LlamaIndex版本,用LlamaIndex替换RAG部分

这是我的学习笔记,希望能帮助你快速上手LlamaIndex。如果有任何问题,欢迎讨论!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐