
模型的知识停在训练的那一天,也不知道你公司的规章、你的产品文档。把这些资料整段塞进提示词?资料一多就超出上下文,而且很贵。RAG(检索增强生成) 的做法是:先从资料里找出和问题相关的几段,只把这几段交给模型回答。
整体流程
图表加载中…
分两个阶段:
- 建索引(资料变了才需要重做):把文档切成小段,每段算一个向量,存起来。
- 问答(每次提问):把问题也算成向量,找出最相近的几段,连同问题一起发给模型。
Embedding:把文字变成向量

Embedding 模型把一段文字变成一串数字(向量),意思相近的文字,向量也相近。比如「营业时间」和「几点开门」字面不同,向量却很接近,这是关键词搜索做不到的。
两个向量有多「近」,常用余弦相似度衡量:越接近 1 越相似。
向量这一步用什么模型
HiveGPT 的分组目前以对话模型为主。下面的例子用开源的 BAAI/bge-small-zh-v1.5 在本地算向量:中文效果不错、模型不到 100MB、不花钱,CPU 也能跑。生成回答这一步再调 HiveGPT。
切块

切块直接决定检索效果:
- 块太大:一块里混着好几个主题,检索不准,还浪费 token。
- 块太小:一句话缺上下文,模型看不懂。
- 常见做法:按段落或标题切,每块 200–500 字,相邻块重叠 50 字左右,避免一句话被切断。
- 每块保留来源(文件名、标题),回答时才能注明出处。
完整代码
资料用一家虚构咖啡店的几条说明:
python
# pip install sentence-transformers openai numpy
import os
import numpy as np
from openai import OpenAI
from sentence_transformers import SentenceTransformer
DOCS = [
{"source": "门店须知", "text": "小蜂咖啡营业时间:周一到周五 8:00–21:00,周末 9:00–22:00,法定节假日照常营业。"},
{"source": "会员规则", "text": "会员每消费 1 元积 1 分,每 100 分可抵扣 5 元,积分有效期 12 个月。"},
{"source": "菜单", "text": "拿铁 28 元,美式 22 元,燕麦拿铁 32 元;大杯加 4 元。"},
{"source": "门店须知", "text": "店内提供免费 Wi-Fi,可以带宠物,但请勿让宠物上桌。"},
]
embedder = SentenceTransformer("BAAI/bge-small-zh-v1.5")
doc_vecs = embedder.encode([d["text"] for d in DOCS], normalize_embeddings=True)
def search(question, k=2):
q = embedder.encode([question], normalize_embeddings=True)[0]
scores = doc_vecs @ q # 向量已归一化,点积就是余弦相似度
top = np.argsort(-scores)[:k]
return [(DOCS[i], float(scores[i])) for i in top]
client = OpenAI(base_url="https://hivegpt.cn/v1", api_key=os.environ["HIVEGPT_API_KEY"])
def answer(question):
hits = search(question)
context = "\n".join(f"[{i + 1}]({d['source']}){d['text']}" for i, (d, _) in enumerate(hits))
resp = client.chat.completions.create(
model="gpt-5.5",
messages=[
{"role": "system", "content": "只根据「资料」回答问题,并在句末用 [1]、[2] 标出依据。资料里没有的内容,直接说「资料里没有提到」,不要猜。"},
{"role": "user", "content": f"资料:\n{context}\n\n问题:{question}"},
],
)
return resp.choices[0].message.content
print(answer("周六几点开门?"))
print(answer("店里能带狗吗?"))
print(answer("你们有外卖吗?")) # 资料里没有,应当说不知道资料多了以后,把 doc_vecs 换成向量数据库(如 pgvector、Milvus、Qdrant),检索写法类似。
动手:只根据资料回答
下面已经把「检索到的资料」放进系统提示词,你只需要提问。试试资料里有的(「拿铁大杯多少钱?」)和没有的(「有没有蛋糕?」),看模型会不会乱编:
▶ 动手试试
系统提示词(这次请求一起发送,点开查看)
你是小蜂咖啡的客服。只根据下面的资料回答,句末用 [1]、[2] 标出依据;资料里没有的内容,直接说「资料里没有提到」,不要猜。
资料:
[1](门店须知)营业时间:周一到周五 8:00–21:00,周末 9:00–22:00,法定节假日照常营业。
[2](会员规则)会员每消费 1 元积 1 分,每 100 分可抵扣 5 元,积分有效期 12 个月。
[3](菜单)拿铁 28 元,美式 22 元,燕麦拿铁 32 元;大杯加 4 元。
[4](门店须知)店内提供免费 Wi-Fi,可以带宠物,但请勿让宠物上桌。
登录后运行登录 HiveGPT 后每天有免费运行次数
周末营业到 22:00 [1]。可以带宠物,但请不要让宠物上桌 [4]。效果不好时查哪里
| 现象 | 常见原因 | 怎么改 |
|---|---|---|
| 资料里明明有,却答「没提到」 | 没检索到:块切得不好,或 k 太小 | 先打印检索结果看看;调整切块,k 改成 3–5 |
| 回答里有资料之外的内容 | 提示词约束不够 | 明确「只根据资料」,并要求标注出处 |
| 专有名词、型号搜不到 | 向量对精确字面不敏感 | 加上关键词检索(BM25),两种结果合并 |
| 检索到一堆相似但没用的段落 | 只看相似度排序 | 多取一些,再用重排(rerank)模型精选 |
排查 RAG 的第一步永远是先看检索到了什么:检索错了,模型再聪明也答不对。
小结
- RAG = 检索相关资料 + 让模型只根据资料回答,适合知识库、客服、文档问答。
- 向量让「意思相近」的文字能被找到;切块大小和重叠会直接影响效果。
- 要求标注出处、允许说「不知道」,可以大幅减少编造。
下一课:让模型自己决定先查什么、再算什么——Agent 循环。