云计算百科
云计算领域专业知识百科平台

DeepSeek + RAG 手把手实战:从 0 到 1 打造你的个人知识库助手(附 Python 源码)

作者:飞哥(一位喜欢讲故事的全栈开发者,擅长把复杂的代码翻译成“人话”)
阶段:GenAI 与大模型应用
关键词:RAG, Embedding, 向量数据库, 知识库问答

大家好,我是飞哥。

前两周我们学会了让 AI “好好说话”(Prompt)和“动手干活”(Function Calling)。
但你在实际开发中,一定遇到过这样的尴尬场景:

  • 你问 ChatGPT:“评价一下我们公司最新的 V3.0 架构。”
  • ChatGPT 一脸懵逼:“亲,我只知道 2023 年以前的互联网公开信息,我不认识你们公司啊。”

或者更惨的:

  • ChatGPT 为了迎合你,开始一本正经地胡说八道(这叫 幻觉 Hallucination)。

怎么解决?重新训练一个模型?太贵太慢了!
最聪明的办法是:考试时允许 AI “开卷”查资料。
这就是 RAG (Retrieval-Augmented Generation,检索增强生成)。


1. 什么是 RAG?(三步理解法)

第一步:锚定已知 ⚓️

回想一下你做 “开卷考试” 的过程:

  • 看到题目(用户提问)。
  • 翻书,找到相关的段落(检索 Retrieval)。
  • 抄(或者总结)到试卷上(生成 Generation)。
  • RAG 的流程一模一样:先查库,再回答。

    第二步:生动类比 📖

    • 纯 LLM (ChatGPT):像一个 博学但与世隔绝的老教授。他懂历史天文,但不知道今天的新闻,也不知道你公司的机密。
    • RAG:就是给这位老教授配了一个 超级图书管理员。
      • 当你问问题时,图书管理员先去档案室(你的私有数据库)把相关文件找出来,递给教授。
      • 教授看着文件,结合自己的学识,给你一个完美的答案。

    第三步:提炼骨架 🦴

    RAG = 检索 (Retrieval) + 增强 (Augmented) + 生成 (Generation)

  • 用户提问:“怎么配置 Android 混淆?”
  • 检索:系统去你的文档库里找,找到了 proguard-rules.pro 相关的三个段落。
  • 增强:把这三个段落拼在 Prompt 里:“请根据以下资料回答用户问题…”
  • 生成:AI 输出最终答案。

  • 2. 核心技术:AI 怎么知道“哪段话”是相关的?

    这里涉及到一个魔法概念:Embedding (嵌入/向量化)。
    计算机不认识字,只认识数字。Embedding 就是把一段文字变成 一串数字坐标。

    2.1 什么是 Embedding?📍

    • 类比:图书馆的 索书号。
      • “苹果”和“香蕉”的索书号会离得很近(都是水果)。
      • “苹果”和“iPhone”的索书号也会离得很近(科技语境)。
      • “苹果”和“卡车”的索书号就会离得很远。

    Embedding 模型会把每一句话变成一个高维向量(比如 OpenAI 的 text-embedding-3-small 模型生成的向量长度是 1536)。
    意思越相近的话,它们在数学空间里的距离(余弦相似度 Cosine Similarity)就越近。

    2.2 关键步骤:切片 (Chunking) 🔪

    你可能会问:“为什么不把整本书直接塞给 AI?”

  • 窗口限制 (Context Window):就像人的短期记忆有限。
    • 大模型一次只能处理一定长度的文字(称为 Token,比如 4k、32k 或 128k)。
    • 如果你把整本《红楼梦》一次性发给它,超出的部分它根本“看不见”,或者会直接报错。
  • 精度问题:即使模型能吃下整本书,关键信息也容易被淹没在大量的“废话”中(大海捞针),导致回答不准确。
  • 所以,我们需要把大文档切成小块(Chunk)。

    • 不仅是切断:好的切片策略会保留上下文(比如句子不能切一半,段落之间要有重叠 Overlap)。
    • 常用策略:固定字符数切分、按段落切分、递归字符切分(Recursive)。

    3. 实战作业:构建你的第一个“个人知识库” 📚

    任务背景:你有一份本地的 Android开发规范.md,你希望做一个问答工具,能回答关于这份文档的问题。

    3.1 架构流程图

    #mermaid-svg-Otf6mJSDonbcSEj5{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Otf6mJSDonbcSEj5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Otf6mJSDonbcSEj5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Otf6mJSDonbcSEj5 .error-icon{fill:#552222;}#mermaid-svg-Otf6mJSDonbcSEj5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Otf6mJSDonbcSEj5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Otf6mJSDonbcSEj5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Otf6mJSDonbcSEj5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Otf6mJSDonbcSEj5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Otf6mJSDonbcSEj5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Otf6mJSDonbcSEj5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Otf6mJSDonbcSEj5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Otf6mJSDonbcSEj5 .marker.cross{stroke:#333333;}#mermaid-svg-Otf6mJSDonbcSEj5 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Otf6mJSDonbcSEj5 p{margin:0;}#mermaid-svg-Otf6mJSDonbcSEj5 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-Otf6mJSDonbcSEj5 .cluster-label text{fill:#333;}#mermaid-svg-Otf6mJSDonbcSEj5 .cluster-label span{color:#333;}#mermaid-svg-Otf6mJSDonbcSEj5 .cluster-label span p{background-color:transparent;}#mermaid-svg-Otf6mJSDonbcSEj5 .label text,#mermaid-svg-Otf6mJSDonbcSEj5 span{fill:#333;color:#333;}#mermaid-svg-Otf6mJSDonbcSEj5 .node rect,#mermaid-svg-Otf6mJSDonbcSEj5 .node circle,#mermaid-svg-Otf6mJSDonbcSEj5 .node ellipse,#mermaid-svg-Otf6mJSDonbcSEj5 .node polygon,#mermaid-svg-Otf6mJSDonbcSEj5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Otf6mJSDonbcSEj5 .rough-node .label text,#mermaid-svg-Otf6mJSDonbcSEj5 .node .label text,#mermaid-svg-Otf6mJSDonbcSEj5 .image-shape .label,#mermaid-svg-Otf6mJSDonbcSEj5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Otf6mJSDonbcSEj5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Otf6mJSDonbcSEj5 .rough-node .label,#mermaid-svg-Otf6mJSDonbcSEj5 .node .label,#mermaid-svg-Otf6mJSDonbcSEj5 .image-shape .label,#mermaid-svg-Otf6mJSDonbcSEj5 .icon-shape .label{text-align:center;}#mermaid-svg-Otf6mJSDonbcSEj5 .node.clickable{cursor:pointer;}#mermaid-svg-Otf6mJSDonbcSEj5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Otf6mJSDonbcSEj5 .arrowheadPath{fill:#333333;}#mermaid-svg-Otf6mJSDonbcSEj5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Otf6mJSDonbcSEj5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Otf6mJSDonbcSEj5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Otf6mJSDonbcSEj5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Otf6mJSDonbcSEj5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Otf6mJSDonbcSEj5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Otf6mJSDonbcSEj5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Otf6mJSDonbcSEj5 .cluster text{fill:#333;}#mermaid-svg-Otf6mJSDonbcSEj5 .cluster span{color:#333;}#mermaid-svg-Otf6mJSDonbcSEj5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Otf6mJSDonbcSEj5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Otf6mJSDonbcSEj5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Otf6mJSDonbcSEj5 .icon-shape,#mermaid-svg-Otf6mJSDonbcSEj5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Otf6mJSDonbcSEj5 .icon-shape p,#mermaid-svg-Otf6mJSDonbcSEj5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Otf6mJSDonbcSEj5 .icon-shape rect,#mermaid-svg-Otf6mJSDonbcSEj5 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Otf6mJSDonbcSEj5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Otf6mJSDonbcSEj5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Otf6mJSDonbcSEj5 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    1.切片 Chunking

    2.Embedding

    3.Embedding

    4.检索 Search

    5.Top K 相关资料

    6.生成回答

    文档 Document

    文本块 Chunks

    向量数据库 Vector DB

    用户提问 Query

    问题向量

    LLM 大模型

    最终答案

    3.2 核心组件选择

    • Embedding 模型:推荐 OpenAI text-embedding-3-small(便宜、效果好)或 HuggingFace 开源模型(如 BGE, M3E)。
    • 向量数据库:
      • 入门/本地:ChromaDB, FAISS(轻量级,Python 直接调)。
      • 生产/云端:Pinecone, Weaviate, Milvus(高性能,支持亿级数据)。

    3.3 极简代码实现 (Python)

    这是一个可以运行的最小化 RAG Demo(不依赖复杂的向量数据库,直接用内存模拟,方便理解原理)。

    前置准备:pip install openai numpy

    import os
    import numpy as np
    from openai import OpenAI

    # 1. 初始化客户端
    # 注意:这里我们需要两个客户端,因为 DeepSeek 目前只负责对话,向量化还得找 OpenAI (或其他)

    # 客户端 A:负责 Embedding (使用 OpenAI)
    # client_emb = OpenAI(api_key="YOUR_OPENAI_API_KEY")

    # 客户端 B:负责对话生成 (使用 DeepSeek)
    client_llm = OpenAI(
    api_key="YOUR_DEEPSEEK_API_KEY",
    base_url="https://api.deepseek.com"
    )

    # 2. 模拟知识库:公司内部报销政策 (这是大模型绝对不知道的私有知识)
    knowledge_base = [
    "出差补贴政策:一线城市(北上广深)每天补贴 300 元,其他城市每天补贴 150 元。",
    "交通报销规定:高铁二等座全额报销,飞机经济舱需提前 3 天申请,打车仅限晚 9 点后。",
    "餐饮报销额度:单人单餐上限 50 元,超过部分自理,禁止报销酒水。",
    "住宿标准:一线城市上限 600 元/晚,非一线城市上限 400 元/晚,必须提供增值税专用发票。"
    ]

    # 辅助函数:获取文本的 Embedding 向量 (本地模拟版,方便教学)
    def get_embedding(text):
    # === 教学模式:本地模拟 Embedding (无需 API Key) ===
    # 原理:关键词匹配模拟向量相似度
    # 定义向量的维度意义:[补贴相关, 交通相关, 餐饮相关, 住宿相关]
    vector = [0.0, 0.0, 0.0, 0.0]

    if "补贴" in text or "一线城市" in text:
    vector[0] = 1.0
    if "交通" in text or "高铁" in text or "飞机" in text or "打车" in text:
    vector[1] = 1.0
    if "餐饮" in text or "吃" in text or "酒水" in text:
    vector[2] = 1.0
    if "住宿" in text or "酒店" in text or "发票" in text:
    vector[3] = 1.0

    if sum(vector) == 0:
    return np.random.rand(4)

    return vector

    # === 如果您有 OpenAI Key,可以使用下面的真实代码 ===
    # response = client_emb.embeddings.create(
    # input=text,
    # model="text-embedding-3-small"
    # )
    # return response.data[0].embedding

    #辅助函数:计算余弦相似度
    def cosine_similarity(v1, v2):
    return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))

    def simple_chat(user_query):
    print(f"\\n[无 RAG] 正在直接问大模型:{user_query} …")
    response = client_llm.chat.completions.create(
    model="deepseek-chat",
    messages=[
    {"role": "system", "content": "你是一个有用的助手。"},
    {"role": "user", "content": user_query},
    ],
    temperature=0.7, # 闲聊模式可以稍微高一点,更有创造性
    )
    return response.choices[0].message.content

    def rag_chat(user_query):
    print(f"\\n[有 RAG] 正在思考问题:{user_query} …")

    # === 步骤 1:检索 (Retrieval) ===

    # 1.1 把用户问题变成向量
    query_vector = get_embedding(user_query)

    # 1.2 遍历知识库,计算相似度
    similarities = []
    for doc in knowledge_base:
    doc_vector = get_embedding(doc)
    score = cosine_similarity(query_vector, doc_vector)
    similarities.append((score, doc))

    # 1.3 找出最相似的 Top 1
    similarities.sort(key=lambda x: x[0], reverse=True)
    best_doc = similarities[0][1]
    print(f"✅ 找到最相关资料 (相似度 {similarities[0][0]:.4f}):\\n -> {best_doc}")

    # === 步骤 2:生成 (Generation) ===

    # 2.1 组装 Prompt
    prompt = f"""
    你是一个公司的行政助手。请基于以下【公司内部政策】回答员工问题。
    ⚠️ 严格基于参考资料回答,不要编造。

    参考资料:
    {best_doc}

    员工问题:
    {user_query}
    """

    # 2.2 调用 LLM
    response = client_llm.chat.completions.create(
    model="deepseek-chat",
    messages=[
    {"role": "system", "content": "你是一个有用的助手。"},
    {"role": "user", "content": prompt}
    ],
    temperature=0.1 # 关键设置:RAG 任务建议设低温度,让回答更严谨,减少幻觉
    )

    return response.choices[0].message.content

    # Main 运行
    if __name__ == "__main__":
    test_query = "我去上海出差,每天补贴多少钱?"

    # 1. 对比:无 RAG (直接问)
    answer_no_rag = simple_chat(test_query)
    print(f"\\n❌ [无 RAG] AI 回答:\\n{answer_no_rag}\\n")
    print("-" * 50)

    # 2. 对比:有 RAG (查资料后再问)
    answer_rag = rag_chat(test_query)
    print(f"\\n✅ [有 RAG] AI 回答:\\n{answer_rag}")


    4. 总结

  • RAG 是企业级 AI 应用的基石。它解决了 AI 知识过时 和 数据隐私 两大痛点。
  • Embedding 是 RAG 的核心,它实现了“语义搜索”(搜意思,而不是搜关键词)。
  • 下一篇预告:
    基本的 RAG 做出来了,但你会发现效果有时候并不好:

    • 切片切断了上下文怎么办?
    • 搜出来的资料不准怎么办?
      下一篇,我们将学习 RAG 进阶与评估,引入 Rerank (重排序) 和 混合检索,打造生产级别的 RAG 系统!

    📢 关注“飞哥”

    看得懂、学得会、用得上,我是飞哥,一个拥有 10 年经验的全栈 AI 开发者。

    • 👍 点赞/收藏:方便日后复习,也防止走丢。
    • 🗣️ 交流讨论:在评论区留下你的问题,飞哥看到必回!
    赞(0)
    未经允许不得转载:网硕互联帮助中心 » DeepSeek + RAG 手把手实战:从 0 到 1 打造你的个人知识库助手(附 Python 源码)
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!