1. 从零构建RAG系统的核心逻辑
RAG(Retrieval-Augmented Generation)系统已经成为当前AI领域最热门的技术架构之一。作为一名长期从事AI应用开发的工程师,我发现很多开发者虽然会调用现成的RAG框架,但对底层实现原理却知之甚少。本文将带你用最基础的Python库,从零开始构建一个完整的RAG系统,深入理解其每个环节的技术细节。
1.1 RAG系统的基本架构
一个典型的RAG系统包含以下核心组件:
- 文档加载与预处理:从各种格式(PDF、Word、HTML等)中提取原始文本
- 文本分块处理:将长文档分割成适合检索的小片段
- 向量化处理:将文本转换为数值向量(Embedding)
- 向量存储与检索:建立向量数据库并实现相似度搜索
- 生成式回答:基于检索结果生成最终回答
python复制# 基础环境配置
import os
import numpy as np
import json
import fitz # PyMuPDF
from openai import OpenAI
from sklearn.metrics.pairwise import cosine_similarity
# 初始化API客户端
client = OpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
1.2 文档处理的核心实现
文档处理是RAG系统的第一步,也是决定后续效果的基础环节。我们以PDF文档为例,展示完整的处理流程:
python复制def extract_text_from_pdf(pdf_path):
"""从PDF中提取文本内容"""
document = fitz.open(pdf_path)
all_text = ""
for page_num in range(document.page_count):
page = document[page_num]
text = page.get_text("text")
all_text += text
return all_text
def chunk_text(text_input, chunk_size=1000, overlap_size=100):
"""文本分块处理"""
text_chunks = []
for i in range(0, len(text_input), chunk_size - overlap_size):
text_chunks.append(text_input[i:i + chunk_size])
return text_chunks
关键技巧:重叠分块(overlap)能有效避免语义断裂,通常设置10-15%的重叠比例效果最佳。
2. 向量化与语义搜索的实现细节
2.1 文本向量化原理
文本向量化是将自然语言转换为机器可理解的数值向量的过程。我们使用阿里云的text-embedding-v3模型:
python复制def create_embeddings(texts, model="text-embedding-v3"):
"""生成文本嵌入向量"""
if isinstance(texts, str):
texts = [texts]
completion = client.embeddings.create(
model=model,
input=texts,
encoding_format="float"
)
data = json.loads(completion.model_dump_json())
return [item["embedding"] for item in data["data"]]
2.2 语义搜索算法实现
基于余弦相似度的搜索算法是RAG系统的核心:
python复制def semantic_search(query, text_chunks, embeddings=None, k=2):
"""语义搜索实现"""
if embeddings is None:
embeddings = create_embeddings(text_chunks)
query_embedding = create_embeddings(query)[0]
similarity_scores = []
for i, chunk_embedding in enumerate(embeddings):
score = cosine_similarity([query_embedding], [chunk_embedding])[0][0]
similarity_scores.append((i, score))
similarity_scores.sort(key=lambda x: x[1], reverse=True)
top_indices = [index for index, _ in similarity_scores[:k]]
return [text_chunks[index] for index in top_indices]
性能优化:在实际应用中,建议使用FAISS或Annoy等专业向量数据库替代纯Python实现,可提升100倍以上的检索速度。
3. 生成式回答的工程实践
3.1 回答生成的核心逻辑
基于检索结果的生成式回答需要考虑上下文限制和确定性控制:
python复制SYSTEM_PROMPT = (
"你是一个AI助手,必须严格根据提供的上下文内容进行回答。"
"如果无法从提供的上下文中直接得出答案,请回复:'我无法根据现有信息回答这个问题。'"
)
def generate_response(system_prompt, user_message, model="qwen-max"):
"""基于上下文的回答生成"""
response = client.chat.completions.create(
model=model,
temperature=0.0, # 确定性输出
max_tokens=512,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_message}
]
)
return response.choices[0].message.content.strip()
3.2 完整工作流示例
python复制# 1. 加载并处理文档
pdf_path = "knowledge_base/智能编码助手通义灵码.pdf"
extracted_text = extract_text_from_pdf(pdf_path)
text_chunks = chunk_text(extracted_text, 1000, 100)
# 2. 创建向量索引
embeddings = create_embeddings(text_chunks)
# 3. 执行查询
query = '通义灵码的智能体能力是什么?'
top_chunks = semantic_search(query, text_chunks, embeddings, k=2)
# 4. 生成回答
user_prompt = "\n".join([f"上下文 {i+1}:\n{chunk}" for i, chunk in enumerate(top_chunks)])
user_prompt += f"\n\n问题:{query}"
answer = generate_response(SYSTEM_PROMPT, user_prompt)
print("AI回答:")
print(answer)
4. 高级优化技巧实战
4.1 语义分块技术
传统固定长度分块的局限性在于可能切断完整语义。我们实现基于句子相似度的动态分块:
python复制def semantic_chunking(text, threshold=0.85):
"""基于语义相似度的动态分块"""
sentences = [s.strip() for s in text.split('。') if s.strip()]
if len(sentences) < 2:
return [text]
embeddings = create_embeddings(sentences)
breakpoints = []
for i in range(len(embeddings)-1):
similarity = cosine_similarity(
[embeddings[i]],
[embeddings[i+1]]
)[0][0]
if similarity < threshold:
breakpoints.append(i)
chunks = []
start = 0
for bp in breakpoints:
chunks.append('。'.join(sentences[start:bp+1]) + '。')
start = bp+1
chunks.append('。'.join(sentences[start:]))
return chunks
4.2 上下文增强检索
通过引入相邻块上下文提升检索质量:
python复制def context_enriched_search(query, chunks, embeddings, k=1, window_size=1):
"""带上下文的增强检索"""
query_embedding = create_embeddings(query)[0]
scores = []
for i, emb in enumerate(embeddings):
score = cosine_similarity([query_embedding], [emb])[0][0]
scores.append((i, score))
scores.sort(key=lambda x: x[1], reverse=True)
top_idx = scores[0][0]
start = max(0, top_idx - window_size)
end = min(len(chunks), top_idx + window_size + 1)
return chunks[start:end]
5. 生产环境中的问题排查
5.1 常见问题及解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 检索结果不相关 | 分块大小不合适 | 调整chunk_size(500-1500)和overlap(10-15%) |
| 回答脱离上下文 | 系统提示词不严格 | 强化SYSTEM_PROMPT的限制条件 |
| 响应速度慢 | 纯Python向量计算 | 改用FAISS或专业向量数据库 |
| 答案不完整 | 检索数量k太小 | 增加k值(3-5)并结合重排序 |
5.2 回答质量评估体系
建立自动化评估机制确保系统稳定性:
python复制def evaluate_response(query, answer, ideal_answer):
"""回答质量评估"""
eval_prompt = f"""
用户问题: {query}
AI回答: {answer}
参考答案: {ideal_answer}
评分标准:
1.0 - 完全符合
0.5 - 部分符合
0.0 - 不符合
"""
eval_response = generate_response(
system_prompt="你是一个专业评估员,请严格按标准评分",
user_message=eval_prompt,
model="qwen-max"
)
return float(eval_response.strip())
6. 企业级优化方案
6.1 块标题技术(CCH)
为每个文本块添加语义标题提升检索准确率:
python复制def generate_chunk_header(chunk):
"""生成文本块标题"""
response = client.chat.completions.create(
model="qwen-max",
temperature=0,
messages=[
{"role": "system", "content": "请为文本生成简洁标题"},
{"role": "user", "content": chunk}
]
)
return response.choices[0].message.content.strip()
def enhance_with_headers(chunks):
"""为所有块添加标题"""
enhanced = []
for chunk in chunks:
header = generate_chunk_header(chunk)
enhanced.append(f"{header}\n{chunk}")
return enhanced
6.2 问题生成增强
通过逆向问题生成提升检索召回率:
python复制def generate_questions(chunk, num=3):
"""基于文本块生成相关问题"""
response = client.chat.completions.create(
model="qwen-max",
temperature=0.7,
messages=[
{"role": "system", "content": "你是一个问题生成专家"},
{"role": "user", "content": f"基于以下文本生成{num}个问题:\n{chunk}"}
]
)
return [q.strip() for q in response.choices[0].message.content.split('\n') if q.strip()]
在实际项目中,将这些技术组合使用可以显著提升RAG系统的表现。我建议先从基础版本开始,逐步引入高级优化技巧,并通过AB测试验证每种技术的实际效果。