1. 项目背景与核心价值
最近在技术社区看到不少关于RAG(Retrieval-Augmented Generation)的讨论,但大多数文章要么停留在概念层面,要么直接调用现成的框架接口。这让我想起刚入行时导师说过的话:"想真正掌握一个技术,就得从轮子造起"。于是花了两个周末时间,仅用Python标准库实现了一个最小化的RAG内核。
这个项目的独特价值在于:
- 完全基于python内置的json、re、collections等基础库
- 从零实现文档加载、文本分块、向量化、检索到生成的完整链路
- 每个环节都保留可插拔的接口设计
- 代码总量控制在200行以内
特别说明:本文实现的简易版重点在于揭示核心机制,生产环境建议使用专业向量数据库。但通过这个手撕过程,你会对以下问题有全新认知:
- 为什么RAG需要特定的文本分块策略?
- 向量相似度计算到底在比什么?
- 检索结果如何影响最终生成质量?
2. 核心架构设计
2.1 整体流程分解
mermaid复制graph TD
A[原始文档] --> B[文本分块]
B --> C[向量化存储]
D[用户问题] --> E[向量检索]
C --> E
E --> F[上下文组装]
F --> G[提示词构建]
G --> H[文本生成]
2.2 关键技术选型
| 模块 | 实现方案 | 替代方案 | 选择理由 |
|---|---|---|---|
| 文本分块 | 滑动窗口+语义分割 | 固定长度分块 | 兼顾段落完整性和上下文连续性 |
| 向量化 | TF-IDF + 余弦相似度 | Word2Vec/BM25 | 零依赖且可解释性强 |
| 检索 | 暴力搜索(top_k=3) | 近似最近邻 | 小规模数据下效率可接受 |
| 生成 | 模板填充+随机采样 | 预训练语言模型 | 避免引入外部依赖,聚焦检索机制演示 |
3. 关键实现细节
3.1 文本预处理管道
python复制def chunk_text(text, window_size=3, stride=1):
sentences = re.split(r'(?<!\w\.\w.)(?<![A-Z][a-z]\.)(?<=\.|\?)\s', text)
chunks = []
for i in range(0, len(sentences), stride):
chunk = ' '.join(sentences[i:i+window_size])
if len(chunk.split()) > 10: # 过滤过短片段
chunks.append(chunk)
return chunks
避坑指南:直接按固定长度分块会导致:
- 表格/代码块被强行拆分
- 关键实体被截断
- 指代关系丢失
3.2 轻量级向量化方案
python复制from collections import defaultdict
import math
class TFIDFVectorizer:
def __init__(self):
self.doc_freq = defaultdict(int)
self.vocab = set()
def fit(self, docs):
for doc in docs:
words = set(re.findall(r'\w+', doc.lower()))
for word in words:
self.doc_freq[word] += 1
self.vocab.update(words)
def transform(self, text):
word_counts = defaultdict(int)
words = re.findall(r'\w+', text.lower())
for word in words:
word_counts[word] += 1
vector = []
for word in self.vocab:
tf = word_counts[word] / len(words)
idf = math.log(len(self.doc_freq) / (1 + self.doc_freq[word]))
vector.append(tf * idf)
return vector
4. 检索增强生成实现
4.1 混合检索策略
python复制def retrieve(query, chunks, top_k=3):
query_vec = vectorizer.transform(query)
scores = []
for chunk in chunks:
chunk_vec = vectorizer.transform(chunk)
score = cosine_similarity(query_vec, chunk_vec)
scores.append((score, chunk))
return sorted(scores, reverse=True)[:top_k]
4.2 生成模板设计
python复制GENERATION_TEMPLATE = """
基于以下上下文:
{context}
请回答这个问题:
{question}
要求:
- 如果上下文不相关,回答"根据现有资料无法确定"
- 保持回答简洁,不超过3句话
"""
5. 效果优化技巧
5.1 分块策略调优
- 技术文档:增大window_size至5-7句
- 对话记录:按说话人分割后处理
- 学术论文:优先按章节标题分割
5.2 检索增强技巧
python复制# 查询扩展示例
def expand_query(query):
synonyms = {
"怎么": ["如何", "怎样"],
"安装": ["部署", "配置"]
}
for word, syns in synonyms.items():
if word in query:
query += " " + " ".join(syns)
return query
6. 完整示例演示
测试维基百科"Python编程语言"条目:
python复制doc = """Python是一种广泛使用的高级编程语言..."""
chunks = chunk_text(doc)
vectorizer.fit(chunks)
question = "Python适合哪些应用场景?"
results = retrieve(expand_query(question), chunks)
context = "\n".join([c for _,c in results])
prompt = GENERATION_TEMPLATE.format(
context=context,
question=question
)
print(generate_response(prompt))
输出示例:
code复制Python适合Web开发、数据分析、人工智能和科学计算等领域。因其语法简洁且拥有丰富的库生态系统,也常用于教学和快速原型开发。
7. 生产环境升级建议
当需要投入实际使用时,建议逐步替换以下组件:
- 向量化:改用Sentence-BERT或OpenAI embeddings
- 检索:采用FAISS或Milvus等向量数据库
- 生成:集成LLM API或本地部署模型
但核心架构依然保持:
文档处理 → 向量索引 → 检索 → 生成 的闭环流程