ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

RAG问答系统学习(一)

RAG问答系统学习(一)

不用向量数据库,手写一个最小 RAG 问答系统

最近在学习 RAG。为了理解 RAG 的基本流程,我没有一开始就使用 LangChain、LlamaIndex 或向量数据库,而是使用 Python 标准库实现了一个最小版本。

项目代码:

GitHub:rag-qa-learning

一、RAG 是什么

RAG 的全称是 Retrieval-Augmented Generation,中文一般翻译为“检索增强生成”。

它的基本流程是:

用户问题 ↓ 从文档中检索相关片段 ↓ 把相关片段交给大模型 ↓ 生成带上下文的回答

普通大模型只能根据训练数据回答问题。如果问题涉及自己的文档、公司资料或最新内容,模型可能不知道,甚至会产生错误信息。

RAG 会先从用户提供的文档中找到相关内容,再根据这些内容回答问题。

二、这个项目实现了什么

这个学习项目实现了以下功能:

  • 在网页中粘贴文档;
  • 给文档设置来源名称;
  • 按段落和句子切分文本;
  • 建立内存中的文档索引;
  • 使用关键词匹配检索相关片段;
  • 返回检索到的内容;
  • 显示引用来源;
  • 提供一个本地网页界面。

启动后访问:

http://127.0.0.1:8097/

三、项目结构

项目目前比较简单:

rag-qa-stage07/ ├── README.md ├── server.py └── .gitignore

server.py同时包含:

  • HTTP 服务;
  • 网页界面;
  • 文档切分;
  • 关键词提取;
  • 文档检索;
  • JSON 接口。

四、如何运行

环境要求:

Python 3.10+

启动程序:

cd rag-qa-stage07 python3 server.py

然后打开浏览器:

http://127.0.0.1:8097/

左侧输入文档内容,填写来源名称,点击“建立索引”。

右侧输入问题,点击“检索并回答”。

五、文档切分

项目使用chunk_text函数切分文档:

def chunk_text(text, source='粘贴文档'): parts = [ p.strip() for p in re.split( r'\n\s*\n|(?<=[。!?.!?])\s+', text ) if p.strip() ] return [ { 'id': len(DOCS) + i + 1, 'source': source, 'text': p } for i, p in enumerate(parts) ]

这里使用正则表达式按照两种情况切分:

  1. 空行;
  2. 中英文句号、问号和感叹号。

例如:

退款期限是七天。 超过七天后不支持退款。

会被切成两个文档片段。

真实项目中的文本切分通常还会考虑:

  • 标题层级;
  • 段落结构;
  • Token 数量;
  • 上下文重叠;
  • Markdown 和 HTML 结构;
  • PDF 页面和表格。

六、关键词检索

项目使用terms函数提取关键词:

def terms(s): return set( re.findall(r'[\w一-龥]{2,}', s.lower()) )

它会提取长度至少为两个字符的中英文词语。

检索函数如下:

def retrieve(q, k=4): qt = terms(q) scored = [] for d in DOCS: dt = terms(d['text']) score = len(qt & dt) / (len(qt) or 1) if score: scored.append((score, d)) return [ d for _, d in sorted( scored, key=lambda x: x[0], reverse=True )[:k] ]

计算方式比较简单:

问题关键词和文档关键词的交集数量 ÷ 问题关键词数量

交集越多,分数越高,越可能被返回。

七、为什么要返回引用片段

项目不会只返回一个答案,还会返回检索到的原文片段:

{ "answer": "根据检索到的内容:...", "hits": [ { "source": "product.md", "text": "退款期限是七天" } ] }

这样用户可以看到答案来自哪份文档,也可以检查回答是否符合原文。

这对于企业知识库很重要,因为 RAG 系统不仅要回答问题,还要让用户知道答案依据是什么。

八、这个项目还不是真正的生产级 RAG

当前版本主要用于学习 RAG 的基础流程,只是一个运行小demo,还存在一些限制:

  • 文档只保存在内存中;
  • 程序重启后索引会丢失;
  • 使用关键词匹配,没有使用 Embedding;
  • 没有接入向量数据库;
  • 没有调用大语言模型;
  • 没有用户权限;
  • 没有文档版本管理;
  • 没有敏感信息过滤;
  • 没有完善的评测集;
  • 没有流式输出。

因此它更适合作为理解原理的最小 Demo。

九、下一步改进方向

后续可以按照下面的顺序升级:

1. 增加 Markdown、PDF 和 Word 导入

目前只能在网页中粘贴文本,后续可以使用解析器读取文件。

2. 使用 Embedding

将关键词匹配替换为向量相似度检索,支持语义相近但关键词不同的问题。

3. 接入向量数据库

可以尝试:

  • FAISS;
  • Chroma;
  • Milvus;
  • pgvector。

4. 接入大语言模型

将检索到的片段放入 Prompt,让大模型根据上下文生成自然语言回答。

5. 增加评测

可以使用:

  • Recall@K;
  • Hit@K;
  • MRR;
  • 答案准确率;
  • 引用正确率。

十、总结

通过这个项目,我理解了一个最小 RAG 系统至少包含以下几个步骤:

文档导入 → 文本切分 → 建立索引 → 检索相关片段 → 返回引用 → 生成回答

虽然这个项目还没有使用向量数据库和大语言模型,但它帮助我理解了 RAG 的核心数据流。后续使用 LlamaIndex、LangChain 等框架时,可以更容易理解它们封装了哪些功能。

完整代码:

GitHub - jaijiaying/rag-qa-learning: A learning project for building a minimal RAG question-answering system with Python. · GitHub

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进