# 动手落地AI：我的第一个四行代码构建RAG应用

> 作者：晨旭｜发布：2025-11-22｜系列：动手落地AI
> 来源：https://chenxu.xin/writing/first-rag-in-four-lines
> 首发：首发于公众号，原文 https://mp.weixin.qq.com/s/tek0CXCUps5IelNg-kSVvQ；此页为作者本人维护的存档

用 LlamaIndex 四行代码跑通第一个 RAG 应用，并逐行拆解这四行背后的加载、切片向量化、检索策略与生成合成。

---

在很长一段时间里，我对 RAG 的理解停留在架构图上：我知道要有数据处理、要有向量数据库、要有检索生成。

但在真正参加一个 AI 项目开发时，我就会有一种隔靴搔痒的感觉。不懂代码逻辑，这似乎成了我深入理解 AI 边界的最大阻碍。

这周，我做了一个决定：打破这个边界。我跟着教程，亲手用 Python 写出了我的第一个 RAG 应用（Naïve RAG 应用）。

曾经以为隔着座山的 RAG 开发，原来只隔着这四行代码。今天这篇文章，我想通过这四行代码，解剖 RAG 的真实运作机理。

## 见证奇迹的四行代码

如果我不亲手去尝试，我不敢相信，传说中复杂的 RAG 系统，在 LlamaIndex 这个框架下，可以被浓缩到如此极致：

```python
# 1. 加载数据
documents = SimpleDirectoryReader("./data").load_data()

# 2. 构建索引（切片 + 向量化 + 存储）
index = VectorStoreIndex.from_documents(documents)

# 3. 构建查询引擎（检索策略）
query_engine = index.as_query_engine()

# 4. 提问并生成
response = query_engine.query("晨光的名字是什么")
```

但我们不能只看到热闹。这四行代码，其实分别对应了 RAG 架构中四个最核心的组件。接下来，让我们逐行拆解。

## 第一行：数据的入口

```python
documents = SimpleDirectoryReader("./data").load_data()
```

这一行代码在做什么？它在把我们人类能看懂的 PDF、Word、TXT，变成机器能处理的对象。

**技术拆解**：`SimpleDirectoryReader` 是 LlamaIndex 的搬运工。它不仅是读取文件，更是把非结构化的数据标准化。

**Garbage In, Garbage Out**：在实操中我发现，这行代码虽然简单，但也是最容易翻车的地方。如果你喂给它一个排版复杂的 PDF（比如双栏论文或带有复杂表格的财报），它读出来的可能是一堆乱码。这让我深刻理解了：

> RAG 产品的护城河，往往不在模型端，而在数据处理端。谁能把复杂文档解析得更干净，谁的 RAG 效果就更好。

## 第二行：构建大脑（Indexing）

```python
index = VectorStoreIndex.from_documents(documents)
```

这是含金量最高的一行代码。它在后台悄悄完成了三件大事：Chunking（切片）、Embedding（嵌入）和 Storage（存储）。

**技术拆解**：

1. **切片**：它把长文档切成一个个小块（Node），就像把一本书撕成一页页笔记。
2. **向量化**：它调用 Embedding 模型（如 OpenAI 或 BGE），把这些文字片段变成一串串数字（向量）。
3. **存储**：它把这些向量存进内存或向量数据库中，建立索引。

**成本与效果的权衡**：这里有一个关键参数值得关注——`chunk_size`（切片大小）。

- 切得太碎？语义不连贯，模型看不懂。
- 切得太大？包含噪音多，而且检索一次消耗的 Token 多，成本飙升。

这行代码背后，其实是我们在做检索颗粒度和 Token 成本之间的博弈。

## 第三行：定义检索策略（Retrieval）

```python
query_engine = index.as_query_engine()
```

这一行定义了系统怎么找答案。

**技术拆解**：默认情况下，它采用的是向量相似度检索。当用户提问时，系统会把问题也变成向量，然后去数据库里计算距离，找出最接近的几个片段。

**Top_K 的艺术**：这里隐藏着一个参数 `similarity_top_k`（默认为 2），意思是找出最相似的 2 个片段给大模型。

- 如果是做法律咨询，可能需要把 K 调大（比如 5），确保信息不遗漏（高召回）；
- 如果是做闲聊，K 小一点（比如 1）可能反应更快。

需要根据业务场景来定义这个 K 值。

## 第四行：生成回答（合成）

```python
response = query_engine.query("你的问题...")
```

最后这一行，才是大模型真正登场的时刻。

**技术拆解**：这一步发生了什么？框架在后台做了一个「Prompt 拼凑」的工作。它把第三步检索到的知识片段和你的问题，填入到一个预设的 Prompt 模板中：

> 请基于以下背景信息：（检索到的片段），回答用户的问题……

**RAG 真相**：RAG 并不是大模型学会了知识，它只是在做一场开卷考试。它的本质是：检索系统 + 阅读理解系统。

## 从 Demo 到 Product：四行代码之外的世界

虽然四行代码就能跑通 Demo，但通过这次学习，我更清醒地认识到，要把它变成一个可商用的产品，还需要做多少工作：

1. **数据持久化**：Demo 里的索引在内存里，程序一关就没了。做产品必须引入 FAISS 或 Milvus 等向量数据库，把知识存下来。
2. **复杂解析**：简单的 Reader 搞不定表格，我们需要引入 LlamaParse 或 MinerU 这样的高级解析工具。
3. **效果评测**：不能靠肉眼看回答好不好，需要引入自动化评测机制。

## 写在最后

这次手搓代码的经历，对我来说是一次视角的重构。

以前看 RAG，我看的是架构图上的一个个方框；现在看 RAG，我看到的是一个个可调节的参数，是一行行数据流动的逻辑：

```text
解析器 → 切片策略 → 向量数据库（FAISS）→ Embedding 模型 → 检索策略 → 生成模式
```

这每一个环节，都是可以优化的战场。

这一次手搓代码的经历，让我对 AI 产品有了全新的掌控感。不要被代码吓倒——在 AI 时代，代码正在变得越来越像自然语言。

行动是缓解焦虑的唯一解药。哪怕只写四行代码，也感到自己好像比昨天更强了一点。

## 核心结论

- LlamaIndex 把 RAG 浓缩成四行代码，但每一行背后都是一片可以优化的战场。（判断）
  永久链接：https://chenxu.xin/writing/first-rag-in-four-lines#four-lines-hide-a-battlefield
- RAG 产品的护城河往往不在模型端，而在数据处理端：谁能把复杂文档解析得更干净，谁的效果就更好。（判断 · 把握中等）
  永久链接：https://chenxu.xin/writing/first-rag-in-four-lines#rag-moat-is-data-processing
- chunk_size 是检索颗粒度与 Token 成本的博弈，similarity_top_k 要按业务场景定。
  永久链接：https://chenxu.xin/writing/first-rag-in-four-lines#chunk-size-is-a-tradeoff
- RAG 不是大模型学会了知识，它只是在做一场开卷考试：检索系统 + 阅读理解系统。
  永久链接：https://chenxu.xin/writing/first-rag-in-four-lines#rag-is-open-book-exam
- 从 Demo 到 Product，还差数据持久化、复杂文档解析和自动化评测这三件事。（判断 · 把握中等）
  永久链接：https://chenxu.xin/writing/first-rag-in-four-lines#demo-to-product-gap

---

本文出自晨光里的AI（https://chenxu.xin），作者晨旭。
引用时请保留来源链接。文中标注「判断」「假设」的部分是作者的个人看法，不是事实。
