当前位置:首页>python>别被 RAG 吓到了,它只是几行 Python 代码和高中的数学知识

别被 RAG 吓到了,它只是几行 Python 代码和高中的数学知识

  • 2026-10-11 06:01:28
别被 RAG 吓到了,它只是几行 Python 代码和高中的数学知识

RAG 是 AI 应用技术中的一项关键技术,全称 Retrieval-Augmented Generation(检索增强生成)。它的核心思想很简单:不让大模型“闭门造车”,而是先去资料库里查一查,再把查到的内容连同问题一起交给模型回答。

本质上,RAG 就是在“检索”和“生成”之间搭了一座桥,让 AI 的回答既有大模型的表达能力,又有外部知识库的准确率。

RAG 通俗定义与核心价值

直白解释

RAG = Retrieval-Augmented Generation,检索增强生成。 核心思想一句话: 不让大模型凭空胡编(幻觉),先去私有知识库检索相关片段,再把「检索到的参考内容 + 用户问题」一起交给大模型作答。

解决两大痛点:

  1. 大模型训练数据有截止时间,无法获取最新资料;
  2. 企业私有文档、内部资料无法喂进模型,RAG 实现外挂知识库问答。

标准6步完整流程

RAG里的高中数学知识

向量是什么?

向量就是既有大小,又有方向的量。比如高速路上“120 km/h 向北”的速度、火箭起飞时“20 m/s² 沿飞行方向”的加速度,都是向量。

如果只说“120 km/h”“20 m/s²”,没说方向,那就是标量。在数学里,向量还可以是一组数,用来表示位置、状态甚至语义,不一定非得画成一根箭头。

相似度计算:余弦相似度

判断两个向量是否代表相近语义,依靠余弦夹角公式:

  • 计算结果范围:[-1, 1]
  • 越接近 1:向量夹角越小,文本语义高度相似
  • 越接近 -1:语义完全相反

pgvector 向量数据库本质就是批量快速做海量向量的余弦相似度近似检索,普通关系型数据库无法高效完成高维向量近邻搜索,这也是向量库存在的意义。

一句话大白话:把文字变成空间中的点,离得近 = 意思差不多。

环境准备

学习 rag 之前需要准备好 python 环境,还有向量数据库。这里 python 我使用 python3.11.7, 向量数据库选择 postgresql(装pgvector插件)。

pyenv 安装 Python

pyenv install 3.11.7
pyenv local 3.11.7
python -m venv .venv
source .venv/bin/activate

PostgreSQL + pgvector

直接安装官方维护的 pg 镜像,即pgvector/pgvector:pg16

docker run -d \
  --name pg-vector \
  -p 5432:5432 \
  -e POSTGRES_PASSWORD=password \
  -e POSTGRES_USER=postgres \
  -v pgvector_data:/opt/pg16/data \
  pgvector/pgvector:pg16

创建数据库并启用pgvector。这一步其实可以省略,因为我们使用的是pgvector/pgvector:pg16镜像,它已经装好了pgvector插件。

createdb rag_demo
psql rag_demo -c "CREATE EXTENSION IF NOT EXISTS vector;"

检索的原理

余弦相似度

RAG 的检索环节,本质是向量空间中的距离计算。常见做法是计算两个向量的余弦相似度:

欧式距离

这其实就是高中向量的点积公式。而在 pgvector 中,<-> 使用的是 L2 欧氏距离,同样也是纯数学运算。

实操

项目结构

(.venv) ➜  ragDemo tree
.
├── README.md
├── __pycache__
│   └── main.cpython-311.pyc
├── main.py
└── requirements.txt

依赖安装

pip install -r requirements.txt

psycopg[binary]
pgvector
openai
sentence-transformers
python-dotenv

整体逻辑说明

  1. 内置知识库文本片段
  2. 使用开源 all-MiniLM-L6-v2 做本地 Embedding,无需付费接口
  3. 批量写入向量到 pgvector
  4. 用户提问向量化,相似度召回 Top3 文本
  5. 拼接上下文 Prompt 调用 LLM 回答

.env

DEEPSEEK_API_KEY=sk-your-deepseek-api-key
DEEPSEEK_BASE_URL=https://api.deepseek.com
DEEPSEEK_MODEL=deepseek-v4-flash

main.py

import os

from dotenv import load_dotenv
from openai import OpenAI
import psycopg
from pgvector.psycopg import register_vector
from sentence_transformers import SentenceTransformer

load_dotenv()

# ====================== 配置区 ======================
DB_CONFIG = {
"dbname": os.getenv("DB_NAME", "rag_demo"),
"user": os.getenv("DB_USER", "postgres"),
"password": os.getenv("DB_PASSWORD", "password"),
"host": os.getenv("DB_HOST", "localhost"),
"port": int(os.getenv("DB_PORT", "5432")),
}

EMBED_MODEL = SentenceTransformer("all-MiniLM-L6-v2")

LLM_CLIENT = OpenAI(
    base_url=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
    api_key=os.environ["DEEPSEEK_API_KEY"],
)
LLM_MODEL_NAME = os.getenv("DEEPSEEK_MODEL", "deepseek-chat")

KNOWLEDGE_LIST = [
"PostgreSQL 是一款功能强大的开源关系型数据库。",
"pgvector 是 PostgreSQL 的向量相似度检索扩展。",
"RAG(检索增强生成)结合检索与生成,让大模型回答更准确、更有依据。",
"Python 在人工智能和数据工程领域被广泛使用。",
"中华人民共和国成立于1949年10月1日,首都为北京。",
"长江是中国第一长河,全长约6397公里,发源于青藏高原。",
"长城是中国古代伟大的防御工程,1987年被列入世界文化遗产。",
"中国有56个民族,汉族人口约占全国总人口的91%以上。",
"北京故宫又称紫禁城,是明清两代的皇家宫殿,位于北京市中心。",
"大熊猫是中国特有的珍稀动物,主要栖息于四川、陕西和甘肃的山区。",
"中国是世界第二大经济体,制造业规模连续多年位居全球第一。",
"春节是中国最重要的传统节日,人们通常会贴春联、吃年夜饭、放鞭炮。",
"上海位于长江入海口,是中国最大的经济中心和重要国际金融城市。",
"汉字是世界上使用人数最多的文字系统,已有数千年的历史。",
]


def get_connection(*, register_vector_adapter=True):
    conn = psycopg.connect(**DB_CONFIG)
if register_vector_adapter:
        register_vector(conn)
return conn


# ====================== 1. 初始化数据库结构 ======================
def init_db():
    conn = get_connection(register_vector_adapter=False)
    cur = conn.cursor()

    cur.execute("CREATE EXTENSION IF NOT EXISTS vector;")
    conn.commit()
    register_vector(conn)

    cur.execute("""
        CREATE TABLE IF NOT EXISTS documents (
            id SERIAL PRIMARY KEY,
            content TEXT UNIQUE,
            embedding VECTOR(384)
        );
    "
"")
    cur.execute("""
        CREATE INDEX IF NOT EXISTS documents_embedding_idx
        ON documents USING hnsw (embedding vector_cosine_ops);
    "
"")

    conn.commit()
    cur.close()
    conn.close()
print("数据库初始化完成")


# ====================== 2. 向量写入数据库 ======================
def embed_and_store(texts=None):
    texts = texts or KNOWLEDGE_LIST
    conn = get_connection()
    cur = conn.cursor()

for text in texts:
        vec = EMBED_MODEL.encode(text)
        cur.execute(
"""
            INSERT INTO documents (content, embedding) VALUES (%s, %s)
            ON CONFLICT (content) DO NOTHING
            "
"",
            (text, vec),
        )

    conn.commit()
    cur.close()
    conn.close()
print(f"知识库向量入库完成(共 {len(texts)} 条,重复内容自动跳过)")


# ====================== 3. 根据问题检索相似文本 ======================
def retrieve_top_k(query: str, top_k=3):
    query_vec = EMBED_MODEL.encode(query)
    conn = get_connection()
    cur = conn.cursor()

    cur.execute(
"""
        SELECT content FROM documents
        ORDER BY embedding <=> %s::vector
        LIMIT %s;
        "
"",
        (query_vec, top_k),
    )

    res = cur.fetchall()
    cur.close()
    conn.close()
return [row[0] for row in res]


# ====================== 4. 组装 Prompt 调用大模型回答 ======================
def ask(question: str):
    context_list = retrieve_top_k(question)
    context_str = "\n".join(context_list)

    prompt = f"""
请严格根据下面参考上下文回答用户问题,上下文没有的内容不要编造:
【参考上下文】
{context_str}

【用户问题】
{question}
"
""
    resp = LLM_CLIENT.chat.completions.create(
        model=LLM_MODEL_NAME,
        messages=[{"role": "user", "content": prompt}],
    )
return resp.choices[0].message.content


if __name__ == "__main__":
    init_db()
    embed_and_store()

    q = "中国有多少个民族"
    ans = ask(q)
print("回答结果:", ans)

这里特地写了一个错误的答案,验证ragDemo 程序稍后是否能召回.

运行效果

执行 python main.py后,从输出可以看出召回成功——“根据上下文,中国有 108 个民族”

结果分析 & RAG 局限性科普

测试现象

提问:中国有多少个民族 输出大概率:根据上下文,中国有108个民族(错误答案)

原因拆解(新手必懂)

  1. 我们入库的知识库完全没有 “56 个民族” 这条信息;
  2. 向量检索召回不到任何有效参考片段;
  3. Prompt 约束失效,大模型脱离上下文自由发挥,产生模型幻觉。

由此总结 RAG 优缺点

✅ RAG 优势

  1. 依托外部知识库,回答可溯源,降低幻觉概率;
  2. 支持实时更新资料,不受模型训练截止时间限制;
  3. 私有文档不传入大模型云端,数据安全性更高。

❌ RAG 入门版明显短板

  1. 知识库内容质量直接决定答案对错,缺数据就会瞎编;
  2. 文本切块 (Chunk) 大小不合理会导致语义割裂、召回错误;
  3. 仅做字面语义相似度匹配,无法做深层逻辑推理;
  4. 简单余弦检索容易出现噪声召回(语义字面相近但无关)。

总结

最开始我们说 RAG 不用害怕,看完整篇实操也印证了这句话:

它既没有复杂的高深算法,底层只是向量点积的高中数学;也不需要昂贵的重型基础设施,Docker 一键拉起 pgvector、写几十行 Python 代码就能实现完整能力。

这次测试里 “中国 108 个民族” 的错误回答,也给我们提了醒:RAG 只是解决大模型知识滞后、私有资料无法注入的工具,答案靠谱与否,最终决定权永远在我们自己维护的知识库手里。

如果你刚接触 AI 应用开发,完全可以从这个最小 Demo 开始动手,亲手跑一遍向量入库、相似度召回、LLM 回答全流程,就能彻底搞懂 RAG 的来龙去脉。

最新文章

随机文章