用 Python 做一个“能对话”的AI检索问答(RAG迷你版)
用 Python 做一个“能对话”的AI检索问答(RAG迷你版)
很多人想做“AI客服/AI知识库”,但一上来就被大模型、向量库、复杂框架劝退。其实你完全可以先做一个迷你版 RAG(检索增强生成):不用联网、不用GPU、不用复杂依赖,用 Python 把“从本地文档里找答案”的闭环跑起来——这就是最接近落地的 AI 形态之一。
这篇给你一套能跑、能用、能扩展的示例:
- 把一组文本资料切块(chunk)
- 用 TF-IDF 做向量化检索(先不引入向量数据库)
- 根据用户问题检索最相关片段
- 拼成“带引用的回答”(更像真实知识库)
1)RAG 到底是什么?一句话讲清楚
> RAG = 先检索相关资料,再基于资料组织回答。
它的好处非常现实:
- 你不需要模型“背诵”所有知识
- 文档更新后,系统立刻生效
- 可以给出引用片段,降低胡说风险
- 很适合客服、产品文档、FAQ、内部SOP
2)我们要做的迷你系统长什么样?
输入:用户问题(如“退款多久到账?”)
输出:
- 最相关的 2–3 段资料片段
- 一段“总结式回答”
- 标注来源片段编号(可用于前端展示)
3)可运行代码:本地资料 → 检索 → 回答
> 你只要把 DOCS 换成你的真实文档(FAQ、说明书、工单知识)就能用。
# -- coding: utf-8 --
import re
from dataclasses import dataclass
from typing import List, Tuple
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
========= 1) 你的知识库文档(示例) =========
DOCS = [
"退款政策:数字类产品通常不支持无理由退款。若因重复扣款或系统错误导致支付异常,可提交订单号申请核查。",
"下载与权限:会员可在有效期内下载资源。若下载按钮不可用,请先登录账号并检查会员状态是否有效。",
"技术支持:提交工单时请提供截图、报错信息、浏览器版本与复现步骤。通常在24小时内回复。",
"更新说明:资源会同步上游版本发布进行更新。更新频率与上游发布节奏一致。",
"常见问题:若页面显示异常,建议清理缓存并刷新CDN。也可尝试更换浏览器或无痕模式。"
]
========= 2) 文档切块(chunk) =========
def chunk_text(text: str, max_len: int = 80) -> List[str]:
# 简单按句子切,再合并到 max_len 左右
sents = re.split(r"[。!?\n]+", text)
sents = [s.strip() for s in sents if s.strip()]
chunks, buf = [], ""
for s in sents:
if len(buf) + len(s) + 1 <= max_len:
buf = (buf + "。" + s) if buf else s
else:
chunks.append(buf)
buf = s
if buf:
chunks.append(buf)
return chunks
@dataclass
class Chunk:
doc_id: int
chunk_id: int
text: str
def build_chunks(docs: List[str]) -> List[Chunk]:
all_chunks = []
for i, d in enumerate(docs):
cs = chunk_text(d, max_len=80)
for j, c in enumerate(cs):
all_chunks.append(Chunk(doc_id=i, chunk_id=j, text=c))
return all_chunks
========= 3) 检索器(TF-IDF + 余弦相似度) =========
class MiniRAG:
def init(self, docs: List[str]):
self.chunks = build_chunks(docs)
self.texts = [c.text for c in self.chunks]
# 中文简单做法:字符级 TF-IDF,不依赖分词
self.vectorizer = TfidfVectorizer(analyzer="char", ngram_range=(2,4))
self.matrix = self.vectorizer.fit_transform(self.texts)
def retrieve(self, query: str, top_k: int = 3) -&gt; List[Tuple[Chunk, float]]:
qv = self.vectorizer.transform([query])
sims = cosine_similarity(qv, self.matrix).ravel()
idxs = np.argsort(-sims)[:top_k]
return [(self.chunks[i], float(sims[i])) for i in idxs]
def answer(self, query: str, top_k: int = 3) -&gt; str:
hits = self.retrieve(query, top_k=top_k)
# 把检索到的片段拼成“可读的回答”
context_lines = []
for h, score in hits:
context_lines.append(
f"[Doc{h.doc_id}-Chunk{h.chunk_id} | score={score:.3f}] {h.text}"
)
# 简易总结策略:把最相关片段作为主依据,附带建议
main = hits[0][0].text if hits else "暂未在资料中找到明确答案。"
tips = "如果仍未解决,建议提供截图/订单号/复现步骤以便进一步排查。"
return (
f"问题:{query}\n\n"
f"回答(基于资料):\n{main}\n{tips}\n\n"
f"参考片段:\n" + "\n".join(context_lines)
)
if name == "main":
rag = MiniRAG(DOCS)
q = "下载按钮不能用怎么办?"
print(rag.answer(q, top_k=3))
4)怎么把它变成“你自己的AI客服”?
你只需要做三件事:
A. 替换 DOCS 为你的真实资料
来源可以是: - FAQ 页面文本 - 产品说明书 - 工单SOP - 退款/会员/下载规则 - 常见报错处理流程
建议按主题拆成多个文档,越结构化越好。
B. 增加更像“客服”的回答模板
比如对“下载失败”: - 先给三步排查:登录/缓存/浏览器 - 再给升级路径:提交工单、附带信息
C. 做一个简单 API(接到你的网站)
把 rag.answer() 包到接口里即可:
- 前端输入问题
- 后端返回文本 + 引用片段
- 前端可以把引用做成折叠卡片
5)为什么这个迷你版值得做?
因为它是“正确方向”的最小闭环:
- 先解决“从资料里找答案”
- 再升级到向量数据库(FAISS、Milvus、Pinecone)
- 再接入大模型做更强的总结与多轮对话
你不需要一开始就把所有高级组件堆满。能跑通、能上线、能迭代,才是 AI 真正的价值。
6)下一步升级建议(按优先级)
- 把 DOCS 改为读取本地文件夹(md/txt/html)
- 加入“阈值”:相似度低就回复“资料不足”避免瞎答
- 加入“多轮上下文”:记住上一轮问题
- 换更强的向量表示:句向量模型(效果更像“语义理解”)
评论 0