基于LLM和RAG的领克汽车智能问答系统
基于LLM和RAG的领克汽车智能问答系统

智能RAG问答系统技术报告
1. 概述
本系统基于检索增强生成(Retrieval-Augmented Generation, RAG)范式构建,面向从非结构化文档(如 PDF)中高效检索信息并生成可溯源回答的应用场景。系统通过“索引—检索—生成”的流水线式技术路线,将文档解析、语义向量化、向量检索与大语言模型(LLM)生成有机整合,既保证答案的事实性与可追踪性,又兼顾在资源受限环境下的易部署与可扩展性。
2. 技术路线
系统遵循经典 RAG 三阶段流程:
- 索引(Indexing):对文档进行结构化解析与清洗,采用定长分块(默认 512 字符,50 字符重叠)保持局部上下文连续性;随后使用 SentenceTransformer 嵌入模型对每个文本块进行向量化,构建基于 FAISS 的高维向量索引,并进行归一化以支持余弦相似度检索。
- 检索(Retrieval):针对用户查询生成查询向量,在向量索引上进行内积检索(归一化后等价于余弦相似度),召回相似度最高的 Top-K 文档片段,并携带相似度分数与元数据返回。
- 生成(Generation):将检索到的文档片段作为可控上下文输入给大语言模型(默认集成讯飞星火 API),在答案生成阶段引导模型严格依据检索证据作答,降低“幻觉”风险,并在缺乏证据时显式说明。
3. 系统架构与模块
文档处理(DataLoader / 文档处理器):
- 职责:PDF 文本抽取、清洗与分块;维护数据统计信息。
- 关键点:句子边界优先分割、重叠窗口设计,兼顾召回与上下文完整性。
向量化与索引(ModelBuilder / 向量化器):
- 职责:加载 SentenceTransformer 模型(默认
all-MiniLM-L6-v2),批量编码文档块,持久化到 FAISS 向量索引。 - 实现:内积索引
faiss.IndexFlatIP+ 向量归一化,实现等价余弦相似度检索与高并发读性能。
- 职责:加载 SentenceTransformer 模型(默认
语义检索(Retriever):
- 职责:对查询进行编码与归一化,执行 Top-K 相似度检索,返回片段与分数。
- 能力:支持可配置返回数量、相似度阈值与多模型嵌入替换。
答案生成(Generator / LLM Proxy):
- 职责:将问题与检索上下文拼装为提示词,调用 LLM 生成答案;对异常与超时进行稳健处理。
- 默认实现:讯飞星火 API(可通过环境变量与
config.py配置替换与调参)。
训练与评估(TrainingProcess / ResultVisualizer,可选):
- 职责:针对问答样本进行嵌入对比学习训练与验证,输出指标可视化与报告。
- 指标:检索准确率、相似度分布、回答质量(可扩展主观或客观打分)。
4. 核心算法与实现要点
- 文本分块策略:采用定长窗口与重叠控制,结合句号等自然边界优化切分,减少语义截断对召回的影响。
- 语义向量化:基于 SentenceTransformer(Transformers + Pooling)生成文本块稠密向量,支持 CPU/GPU 透明切换;Batch 编码提升吞吐。
- 向量检索:使用 FAISS 内积索引;在入库与查询阶段进行 L2 归一化,使内积等价余弦相似度,取得稳定排序效果。
- 提示词工程:在系统消息中约束回答依据检索上下文,缺证据时应说明,降低模型幻觉与越权推理风险。
- 稳健性:对外部 API 的网络超时、连接异常、响应格式进行严格校验与降级处理;日志记录贯穿全链路。
4.1 核心代码摘录与实现逻辑
以下为关键路径的核心实现片段(带行号与文件路径标注,便于交叉定位):
def _split_text(self, text: str) -> List[str]:
"""将文本分割成块"""
chunks = []
start = 0
while start < len(text):
end = start + self.chunk_size
chunk = text[start:end]
# 尝试在句号处分割
if end < len(text):
last_period = chunk.rfind('。')
if last_period > self.chunk_size // 2:
chunk = chunk[:last_period + 1]
end = start + last_period + 1
chunks.append(chunk.strip())
start = end - self.chunk_overlap
if start >= len(text):
break
return chunks
实现逻辑:定长窗口 + 重叠滑动,结合句号边界,兼顾语义连续性与召回覆盖。
def build_vector_index(self, chunks: List[str]):
embeddings = self.embedding_model.encode(chunks, show_progress_bar=True)
self.documents = [Document(content=chunk, metadata={"id": i, "chunk_id": i}, embedding=emb)
for i, (chunk, emb) in enumerate(zip(chunks, embeddings))]
self.dimension = embeddings.shape[1]
self.index = faiss.IndexFlatIP(self.dimension)
embeddings_normalized = embeddings / np.linalg.norm(embeddings, axis=1, keepdims=True)
self.index.add(embeddings_normalized.astype('float32'))
实现逻辑:使用内积索引并进行向量归一化,实现等价余弦相似度检索与稳定排序。
def generate_answer(self, question: str, top_k: int = 5) -> Dict[str, Any]:
query_embedding = self.model.encode([question])
query_embedding_normalized = query_embedding / np.linalg.norm(query_embedding, axis=1, keepdims=True)
scores, indices = self.vector_index.search(query_embedding_normalized.astype('float32'), top_k)
relevant_docs = []
for score, idx in zip(scores[0], indices[0]):
if idx < len(self.documents):
doc = self.documents[idx]
relevant_docs.append({
'content': doc.content,
'similarity_score': float(score),
'metadata': doc.metadata
})
context = "\n\n".join([doc['content'] for doc in relevant_docs])
answer = self.llm_proxy.get_response(question, context)
return { 'question': question, 'answer': answer, 'relevant_docs': relevant_docs, 'context': context }
实现逻辑:检索-拼接上下文-经 SparkModelProxy 生成答案,输出含溯源证据与相似度的结构化结果。
class SparkModelProxy:
def __init__(self, model="x1", temperature=0.7):
... # 环境变量/配置加载
self._validate_config()
def get_response(self, prompt, context=""):
payload = {"model": self.model, "messages": [...], "temperature": self.temperature, "max_tokens": self.max_tokens}
response = requests.post(self.api_url, json=payload, headers=headers, timeout=60)
... # 异常处理与内容提取
实现逻辑:对外部 LLM API 进行包装,校验配置、统一超时与错误处理,保证生成阶段稳定性。
5. 数据流与执行流程
- 文档导入:上传/指定 PDF → 解析文本 → 清洗。
- 构建索引:分块 → 语义编码 → 归一化 → 写入 FAISS(持久化存储可选)。
- 在线检索:用户问题 → 查询向量 → FAISS Top-K 相似片段。
- 结果生成:拼装上下文 → 调用 LLM → 输出答案与溯源片段(含相似度)。
6. 配置与运行
- 模型与设备:
all-MiniLM-L6-v2(可替换),设备cpu/cuda可选。 - 环境变量(示例):
spark_api_key、spark_api_url、spark_max_tokens。
- 启动方式:
- 一键启动:
python quick_start.py - Web 界面:
streamlit run enhanced_rag_web.py
- 一键启动:
- 依赖建议:
sentence-transformers、faiss-cpu/faiss-gpu、PyPDF2、streamlit、torch。
7. 性能与可扩展性
- 资源适配:默认轻量嵌入模型,CPU 模式可运行,适配内存受限环境。
- 吞吐优化:批量编码、向量归一化前置、FAISS 批量增量写入;检索阶段 O(1) 量级延迟(取决于索引规模与硬件)。
- 可扩展性:
- 嵌入模型可插拔(如 BGE/M3E/SBERT 家族)。
- 检索策略可扩展(重排序、融合检索、多阶段召回)。
- 文档类型扩展(DOCX/HTML/Markdown 等)。
8. 安全与合规
- 数据最小化:仅存储必要文本片段与向量;敏感数据可在清洗阶段脱敏。
- 访问控制:云端推理需配置 API Key;建议在服务端安全存储密钥与限流。
- 可追溯性:返回答案同时附带来源片段与相似度,便于审计与人工复核。
9. 典型限制与改进方向
- 领域适配:通用嵌入在特定领域(法务、医疗)可能不足,建议进行领域微调或使用专用模型。
- 长上下文:当证据跨片段且跨度较大时,需引入跨段拼接、片段重排或多段摘要以增强可读性与完整性。
- 重排序与融合:引入基于交叉编码器的重排序(re-ranking)或稀疏/稠密融合(如 BM25 + 向量召回)可进一步提升相关性。
- 增量更新:针对动态文档库,建议实现向量索引的增量构建与陈旧片段回收机制。
10. 参考实现位置(代码)
- 索引与检索:
simple_rag_system.py→build_vector_index、retrieve_documents - 完整流程与 Web:
complete_rag_system.py、enhanced_rag_web.py - LLM 代理与稳健性:
complete_rag_system.py→SparkModelProxy
11. 技术栈(Tech Stack)
- 语言与运行时:Python 3.8+
- 向量与检索:FAISS(IP 索引,归一化实现余弦相似度)
- 嵌入模型:Sentence-Transformers(默认
all-MiniLM-L6-v2) - 文档解析:PyPDF2
- Web 界面:Streamlit
- 训练与可视化:Sentence-Transformers、Matplotlib/Seaborn
- 日志与配置:
logging、python-dotenv - 外部模型:讯飞星火 API(可替换)
12. 项目式总结(可写入个人/团队项目经历)
- 背景:针对企业 PDF 手册等非结构化知识,构建 RAG 问答系统,提升知识检索与问答效率。
- 角色与贡献:
- 主导系统架构与核心模块设计(文档处理、向量索引、检索-生成链路)。
- 实现 FAISS 基于向量的高效检索与可溯源答案生成;封装 LLM 代理,完善超时/异常处理。
- 搭建最小可用 Web 演示与日志监控,形成评估报告与指标可视化。
- 难点与优化:
- 通过重叠分块与句子边界切分,降低语义截断带来的检索召回损失。
- 引入向量归一化与内积索引,获得稳定的余弦相似度排序;批量编码提升吞吐。
- 对外部 API 进行健壮封装,处理网络抖动与响应格式异常,保障可用性。
- 成果:
- 在 CPU 环境下完成端到端方案落地,Top-K 检索与上下文受控生成稳定运行。
- 形成技术报告与评估可视化,支持后续领域适配与检索策略升级(如重排序、融合检索)。
11. 版本与维护
- 推荐生成运行日志至
rag_system.log,用于排障与性能分析。 - 建议在 CI 中加入依赖校验与基本回归(索引构建、检索准确性、API 通达性)。
本文档用于网站展示与技术对接,描述了系统的技术路线、架构设计与实现要点,可作为二次开发与部署集成的参考依据。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 奇点智库 SingularityMind!
评论







