RAG(检索增强生成)
如果说 LLM 是 AI 的大脑,那么 RAG 就是它的书架。没有书架的脑子再聪明,也记不住你昨晚写的 Bug 在哪一行。
什么是 RAG?
传统的 LLM(如 GPT-4、Gemini)在训练完成那一刻,它的知识就"锁死"了(即知识截止日期)。而 RAG 的核心思想是:在模型回答问题之前,先去外部资料库里"查一查",把查到的相关信息喂给模型,让它参考着给答案。
RAG 的三个核心步骤
- 检索 (Retrieval):当用户提问时,系统先去文档库(PDF、数据库、代码仓库等)里寻找最相关的片段
- 增强 (Augmentation):把找出的相关片段和原始问题拼在一起,组成信息量更大的 Prompt
- 生成 (Generation):模型阅读参考资料,写出准确、有据可查的回答


RAG 解决三大痛点
| 痛点 | RAG 方案 |
|---|---|
| 幻觉 (Hallucination) | 要求模型"根据已知事实回答",不确定时明确说"参考资料中未提及" |
| 知识实时性 | 只需更新外部文档库,不需要重新训练模型 |
| 数据安全与隐私 | 公司机密数据存本地 RAG 库,模型只在推理时临时读取,不需要微调 |
在 AI Agent 中,RAG 的工作流程
1. 数据准备 (The Brain’s Library)
- 切片 (Chunking):把长文档切成小段
- 向量化 (Embedding):把文字转成数字向量
- 向量数据库 (Vector DB):存进 Pinecone、Milvus、Weaviate 等
2. 实时检索 (The Search)
当用户提问 “我们公司去年的前端架构规范是什么?” → Agent 把问题转成向量 → 去数据库里匹配语义最接近的文档片段。
3. 最终输出
Prompt 示例:“你是一个专业的架构师。以下是从公司内部文档检索到的参考资料:[资料A:Vue 3 规范…] [资料B:Go 语言 DDD 实践…]。请根据这些资料回答用户问题:[用户问题]。”
向量:文本的"数字身份证"
向量是把一段文字的语义信息转换成一串固定长度的数字列表,让计算机能"看懂"文字的含义并做相似度计算。
向量维度
向量的维度决定了语义匹配的精准度。例如三维向量 [0.3, 0.32, 0.24],可以规定每一维代表不同主题(情绪、动作、倾向等)的得分强度。
- 维度越多 → 语义特征记录越精细 → 匹配越精准 → 但计算量越大
- 一般 1536 维度即可满足大部分场景
余弦相似度
向量的数字序列共同决定了向量在高维空间的方向和长度。余弦相似度抛开长度只考虑方向夹角:夹角越小越相似。

公式
$$\cos(\vec{\alpha}, \vec{\beta}) = \frac{\vec{\alpha} \cdot \vec{\beta}}{||\vec{\alpha}|| \times ||\vec{\beta}||}$$

点积
对应分量相乘再相加。例如 a[1,3], b[2,4] → 点积 = 1×2 + 3×4 = 14
模长
所有分量的平方之和的平方根。

例如 a[3,4,2,1] → 模长 = √(9+16+4+1) = √30
Go 实现
package utils
import (
"errors"
"math"
)
// CosSim 计算两个向量的余弦相似度
func CosSim(a, b []float64) (float64, error) {
if len(a) != len(b) {
return 0.0, errors.New("vector lengths do not match")
}
if len(a) == 0 {
return 0.0, errors.New("vectors are empty")
}
normA := Norm(a)
if normA == 0 {
return 0.0, errors.New("vector 'a' is a zero vector")
}
normB := Norm(b)
if normB == 0 {
return 0.0, errors.New("vector 'b' is a zero vector")
}
return Product(a, b) / (normA * normB), nil
}
// Product 计算两个向量的点积(内积)
func Product(a, b []float64) float64 {
result := 0.0
for i := range a {
result += a[i] * b[i]
}
return result
}
// Norm 计算向量的 L2 范数(模长)
func Norm(a []float64) float64 {
result := 0.0
for _, v := range a {
result += v * v
}
return math.Sqrt(result)
}为什么不直接用"长文本模型"?
现在 Gemini 或 GPT 的上下文窗口越来越大(百万 token 级)。那还需要 RAG 吗?
答案:依然需要。
| 维度 | 长上下文 | RAG |
|---|---|---|
| 成本 | 塞几十万字非常贵 | 只塞最相关的几千字,省钱 |
| 效率 | 读几十万字需要时间 | 毫秒级检索 |
| 精准度 | “中间遗忘"现象 | 更精准定位信息 |