第 2 节 / 共 8 节

RAG 开发(一):项目启动

🎯 EduRAG 项目背景、架构与技术选型。
📖 笔记

EduRAG 笔记(day02)

EduRAG项目

【了解】今日内容介绍

  • 项目介绍
  • 环境安装
  • ollama介绍
  • Langchain介绍

【掌握】RAG介绍

RAG概念

通⽤的基础大模型存在一些问题:

  • 幻觉问题,LLM有时会在回答中⽣成看似合理但实际上是错误的信息
  • LLM的知识不是实时的,模型训练好后不具备自动更新知识的能力,会导致部分信息滞后
  • LLM领域知识是缺乏的,大模型的知识来源于训练数据,这些数据主要来自公开的互联网和开源数据集,无法覆盖特定领域或高度专业化的内部知识

为解决或缓解上述问题,2020年Facebook发表了一篇论文——《Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks》,提出了RAG(Retrieval Augmented Generation,检索增强生成)技术。

RAG是一种将大规模语言模型(LLM)与外部知识源的检索相结合,以改进问答能力的工程框架。 它使用来自私有或专有数据源的信息来辅助文本生成,从而弥补LLM的局限性,特别是在解决幻觉问题和提升时效性方面。

RAG的作用

克服LLM“幻觉”问题:LLM在生成文本时有时会“一本正经地胡说八道”,即生成听起来合理但实际上不准确或捏造的信息,这被称为“幻觉”。RAG通过提供外部事实依据,显著减少了这种幻觉现象,让LLM的输出更具事实性和可靠性。

获取最新信息:LLM的训练数据通常是静态的,这意味着它们无法获取到训练截止日期之后发生的事件或更新的信息。RAG允许LLM连接到实时或定期更新的外部数据源(如新闻、数据库、内部文档等),从而提供最新、最及时的答案。

领域特定知识增强:对于特定行业或企业内部的知识,LLM的通用训练数据往往不足。RAG能够将LLM与企业内部的知识库文档或特定领域的数据连接起来,使LLM能够回答高度专业化的问题,并提供更符合上下文的答案。

降低模型微调成本:传统上,为了让LLM适应特定任务或数据,需要进行昂贵的微调(Fine-tuning)。RAG提供了一种更经济高效的替代方案,它无需修改LLM的底层参数,只需更新外部知识库即可,大大降低了维护和更新模型的成本。

提高答案的可解释性和溯源性:RAG可以引用其获取信息的来源,这意味着用户可以查看LLM答案所依据的原始文档或数据,增强了答案的透明度和用户信任度。

RAG通过将检索和生成相结合,既保留了传统检索问答的可靠性,又获得了LLM的灵活性和自然表达能力。它能让AI始终基于最新的、可信的知识来回答问题,同时保持对话的流畅自然。

RAG原理

R:Retrieve:检索。基于向量数据库检索相关的上下文信息。

A:Augment:增强。把问题和相关的上下文信息一起送给大模型。

G:Generate:生成。大模型基于增强后的上下文,生成回复内容。

1775133574666

【掌握】项目介绍

项目背景

为了解决人工客服效率低下的问题,公司决定采用LangChain和Qwen大模型,构建了一套智能化的学科在线答疑系统。

该系统基于RAG架构,能够通过实时检索相关知识库中的信息来增强大模型的生成能力,确保回答的准确性和时效性。

与此同时,系统通过自动化处理学生的答疑需求,极大地减轻了人工客服的工作压力,从而实现了高效、低成本的知识服务。

项目架构

  • 第一步:将现有后台搜集的FQA数据集存储到MySQL数据库中
  • 第二步:基于query实现MySQL数据库检索:将query和现有问题匹配(做相似度计算),如果阈值>=0.85,就认为问题比较明确,直接返回对应的答案;否则,进入RAG检索系统
  • 第三步:搭建本地知识库:对本地文档加载读取;进行文档分割;文档向量化;存储向量数据库(Milvus)
  • 第四步:基于query实现Milvus数据库检索:将query进行向量表示,并从Milvus数据库中检索出相似的top-k个文本段。
  • 第五步:将query和检索出的top-k文本段拼接,送入大模型,实现预测。

1775134963576

【实践】环境安装

安装说明

Python虚拟环境

首先创建Python虚拟环境。从Anaconda Prompt命令行进入。

Python版本最好是:3.10.18

# 查看当前的虚拟环境
conda env list

# 创建虚拟环境, 安装3.10的Python解析器
conda create -n EduRAG python=3.10.18

# 切换虚拟环境 
conda activate EduRAG

# 安装依赖包, 在requirements.txt对应路径下执行以下命令
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

1770394428180

安装Docker Desktop

前言

安装Docker的目的,主要是用于安装Milvus、minio软件的。因为Milvus官网,推荐使用Docker来安装Milvus。

而Docker对于Linux的支持很友好,但是对于Windows的支持不尽如意。

1770376795863

Docker官方说明:

1770376818910

安装WSL(Windows Subsystem for Linux)

启用WSL打开具有管理员权限的PowerShell,运行以下命令以安装WSL

安装完成后,重启电脑以完成配置

wsl --install 

1770377504396

输入命令,进入等待中,直至安装完成。

1770379506852

注意:

下载过程可能耗时很长,期间别操作,等着就行。

安装完成后,重启时,可能会弹出如下对话框,代表WSL安装成功,关闭即可。

1770392735292

安装docker

下载并安装Docker Desktop

访问Docker官方网站,下载适用于Windows/Mac的Docker Desktop安装包。(已经提前下载了,大家直接用即可)

1770377101008

双击Docker Desktop安装文件(资料中有),按照提示一路下一步完成安装。

image-20250814175817544

安装完成后,点击**==close and restart==**重启电脑以完成配置。

1770379612096

重启后,如果弹出如下对话框,点击**==Accept==**,如下图:

1770392599508

打开cmd窗口,运行以下命令,验证是否成功安装。

docker --version
docker compose version

1770381044461

配置Docker Compose文件

在本地磁盘(例如:D盘)创建文件夹milvus_data,用于存放Milvus和Redis等软件的配置文件和数据,且在milvus_data文件夹中,添加docker-compose.yml文件。该文件在预习资料中,如下图:

1775065118581

启动Milvus和Redis

拉取镜像并启动容器

进入上述自己创建的milvus_data目录下(docker-compose.yml文件目录),打开cmd终端,运行:

# -d 表示后台运行容器
docker compose up -d

image-20250814180816031

注意:第一次拉取镜像时报错: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)

上述报错主要是网络问题。

咱们当前是window环境,所以直接复制以下内容去docker软件中粘贴即可。

"registry-mirrors": [
    "https://docker.211678.top",
    "https://docker.1panel.live",
    "https://hub.rat.dev",
    "https://docker.m.daocloud.io",
    "https://do.nark.eu.org",
    "https://dockerpull.com",
    "https://dockerproxy.cn",
    "https://docker.awsl9527.cn"
]

打开 docker desktop 软件,第一次可能会看到如下页面,不需要注册,直接点击登录,如下图:

1770393228707

进来后,点击右上角的设置按钮,如下图:

1770393273876

找到左侧的Docker Engine入口,在下面的空白框内,添加上述的镜像地址,如下图:

1770393482758

配置好后,点击Apply & restart,重启Docker。

然后再次去 milvus_data 目录下,打开 cmd终端,运行:

image-20250816223314138

image-20250816233644355

启动成功后去docker看看,如下图:

验证Milvus和Redis

测试Milvus连接

from pymilvus import MilvusClient

# 连接milvus
client = MilvusClient("http://localhost:19530")

# 显示当前milvus的数据库
print('databases-->', client.list_databases())

测试Redis连接

import redis

# 连接到 Redis
client = redis.Redis(host="localhost", port=6379, password=1234, decode_responses=True)

# 测试读写
client.set("test_key", "Hello, Redis!")
value = client.get("test_key")
print(f"Redis value: {value}")

安装Milvus可视化工具Attu

双击Attn安装包进行安装,如出现不允许运行,点开详细进行安装即可。

1770393784963

不需要设置,直接安装结束。之后打开Attu,设置主机IP,如下图:

1770393924442

点击连接,就可以连接成功。

启动说明

启动Docker Desktop

在开始菜单中,找到Docker Desktop应用,单击该应用,即可启动Docker Desktop,如下图。

(启动过程需要等待片刻……)

1775063693431

温馨提示:

可以不在开始菜单,如果桌面上有Docker Desktop图标也可以。

启动Docker

把docker-compose配置文件(在当前目录下),放到一个没有中文,没有空格,没有特殊字符的目录下。

打开cmd终端,切换到docker-compose.yml文件目录下,运行如下命令:

docker compose up -d

1775063977190

如上图所示,我的docker-compose.yml文件是放在D:\code\workspace8\milvus_redis>目录下。

校验服务是否启动

在上述目录下,输入如下命令:

docker ps

如下图所示:1775064208710

可以看到,STATUS列中并没有错误或者重启信息,目前已经运行一会儿了,说明服务都正常运行。

停止服务

当需要关停服务时,仍在docker-compose.yml文件路径下,输入如下命令:

docker compose down

结果如下图所示:

1775064329282

到此,EduRAG项目所需的服务就启动完成,后续只需要稍微配置即可使用。

【了解】工具介绍

Ollama:一个开源的大型语言模型服务工具,它帮助用户快速在本地运行大模型。

LangChain:为各种LLMs实现通用的接口,把LLMs相关的组件“链接”在一起,简化LLMs应用的开发难度,方便开发者快速地开发复杂的LLMs应用。

Redis:高性能缓存数据库,用于缓存高频问答对数据,实现高效检索。

MySQL:关系型数据库,用于存储高频问答对数据。

Milvus:一个开源向量数据库,帮助我们实现向量数据的存储和检索。

【了解】Ollama

简介

一个开源的大型语言模型服务工具,它帮助用户快速在本地运行大模型的能力。

安装

下载

https://ollama.com/ 官网下载,双击安装即可。

运行Ollama

参数量:

  • 1.5B:简单的分类任务够用
  • 7B:可以胜任绝大部分的简单任务,比如句子级别、段落级别的文本分类
  • 32B:复杂任务需要32B

拓展:模型参数

  • bert: 440MB -> 1.1亿参数(104.904174805 * 4) -> 0.1B

  • B: 十亿

  • 一个参数一般都是由一个32位浮点数(FP32 /float32/double, 4个字节)表示

  • 完整的模型:7B(32位浮点数,4个字节) = 7 * 10 ^ 9 * 4个字节 = 1GB * 28

  • 量化过的模型:7B(4位整数) = 7 * 0.5 *10^9 *1/1024/1024/1024= 3.3 GB

  • 模型量化

  • FP32(4个字节) -> int4(0.5 个字节)

  • 拓展:模型蒸馏

  • 教师模型:在服务器上训练一个相对大的模型( 0.4B模型, 440MB, bert)

  • 学生模型:用一个能在目标设备上运行的模型(0.01B模型, 10MB, LSTM ),学习教师模型

在Python中使用 Ollama API

安装

pip install ollama

三种方式:

  • 根据API不同:
  • chat api
  • http api
  • 根据输出形式不同:
  • 全量(默认):生成内容全部就绪时,一次性返回
  • 流式:生成一个token,就返回一个,直到结束

代码:

    import ollama

    # 聊天式
    response = ollama.chat(model="deepseek-r1:1.5b", messages=[{"role": "user", "content": "为什么天空是蓝色的?"}])
    # print('response1-->', response)
    # print('response.message-->', response["message"])

    ## 远程调用的方式
    from ollama import Client

    client = Client(host="http://127.0.0.1:11434")  # 虚拟机的需要改ip
    response = client.chat(model="deepseek-r1:1.5b", messages=[{"role": "user", "content": "你是什么模型?"}])
    # print('response2-->', response)
    # print('response.message.content-->', response["message"]['content'])

    ## 流式调用
    stream = client.chat(model="deepseek-r1:1.5b", messages=[{"role": "user", "content": "你叫什么名字?"}], stream=True)
    for chunk in stream:
        print(chunk["message"]['content'], end="")

LangChain调用

    # 0.x版本导包
    # from langchain_community.llms import Ollama
    # 1.x版本导包
    from langchain_ollama import OllamaLLM

    llm = OllamaLLM(base_url="http://127.0.0.1:11434", model="deepseek-r1:1.5b")
    result = llm.invoke("你叫什么名字?")
    print("result-->", result)

requests调用

    import requests
    import json

    url = "http://127.0.0.1:11434/api/chat"
    headers = {"Content-Type": "application/json"}
    data = {"model": "deepseek-r1:1.5b", "messages": [{"role": "user", "content": "你叫什么名字?"}], "stream": False}
    response = requests.post(url, headers=headers, json=data)
    # print('response1-->', response.json())

    url = "http://127.0.0.1:11434/api/chat"
    headers = {"Content-Type": "application/json"}
    data = {"model": "deepseek-r1:1.5b", "messages": [{"role": "user", "content": "你叫什么名字?"}], "stream": True}
    response = requests.post(url, headers=headers, json=data)

    for chunk in response.iter_lines():
        print(json.loads(chunk.decode("utf-8"))['message']['content'], end='')

OpenAI调用

from openai import OpenAI

client = OpenAI(base_url="http://localhost:11434/v1", api_key="xx")  # vllm部署可以指定api_key

stream = client.chat.completions.create(
    model="deepseek-r1:1.5b",
    messages=[{"role": "user", "content": "写一首关于秋天的五言诗"}],
    stream=True,
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)