>
← 返回投肯智能知识库首页
首页 / 技术教程 / 开发实战
实战 RAG Dify Ollama 本地部署

Dify + Ollama + NVIDIA RAG本地知识库:零API费用搭建生产级AI知识库实战

👤 重庆投肯小云 📅 2026-05-26 ⏱️ 约15分钟阅读
💡 导言: 本文面向具备一定技术基础的开发者,使用 Docker Compose 完整部署 Dify + Ollama + Qdrant 向量数据库组成的本地 RAG 系统。不依赖任何云端 API,零费用运行,支持离线环境,并提供完整的 NVIDIA 驱动检测、CUDA 配置、模型显存计算、错误排查表格与性能对比数据。

模块一:背景 —— 为什么要本地化 RAG?

Retrieval-Augmented Generation(RAG)是一种将外部知识检索与语言模型生成相结合的技术架构。它通过向量数据库检索与用户问题最相关的文档片段(Chunks),再将这些片段作为上下文提供给大语言模型(LLM)生成答案,从而显著降低模型"幻觉"、提升回答的准确性。

1.1 API 费用的持续压力

以 OpenAI GPT-4o 的定价为基准:每百万 tokens 输入费用约 5 美元,输出约 15 美元。一个中等规模的企业知识库,每周处理 1000 次用户问答,月度 API 支出轻松突破 2000 元。如果需要检索大量文档并进行多轮对话,成本会成倍增长。

本地部署 RAG 的核心驱动力就是彻底消除 API 费用。使用 NVIDIA 消费级显卡(如 RTX 4090 24GB),一次硬件投入即可支撑日均 500 次以上问答请求,边际成本趋近于零。

1.2 数据安全的合规要求

金融、医疗、法律、政府等行业的敏感数据有严格的合规要求。API 调用意味着数据必须经过第三方服务器,这在《个人信息保护法》和行业数据安全规范下存在难以规避的风险。

本地化部署的核心优势在于:数据完全在本地流转,从文档上传到向量检索再到答案生成,整个链路不出防火墙。这对于需要处理内部文档、合同、会议记录的企业来说是刚需能力。

1.3 离线与网络不稳定环境

在工厂车间、内网隔离环境、海外办公室等网络受限场景下,云端 API 根本无法访问。本地部署的 RAG 系统即使在完全断网的环境下也能正常运行,这对于特种行业应用是硬性需求。

1.4 本地模型的能力边界

必须正视的事实:当前开源模型(如 Qwen2.5、Llama3.1、DeepSeek-R1)在推理能力上与 GPT-4o、Claude-3.5 仍存在差距。但对于以知识检索为核心的 RAG 场景,本地模型的短板并不明显——因为答案质量更多取决于检索到的文档内容质量,而非模型本身的推理能力。

1.5 显存与模型大小的关系(选型参考)

在部署之前,需要清楚了解模型规模与所需显存的关系,以便合理选型。以下是经过大量实测总结的显存估算公式:

# 通用显存计算公式(FP16 精度)
所需显存(GB)≈ 模型参数量(B)× 2

# 7B 模型(70亿参数)
7 × 2 = 14 GB(FP16)
加上 KV Cache 和上下文开销,建议 16GB 以上的显卡

# 13B 模型(130亿参数)
13 × 2 = 26 GB(FP16)
建议 RTX 4090(24GB)开启量化(如 Q4_K_M)后运行

# 70B 模型(700亿参数)
70 × 2 = 140 GB(FP16)
需要多卡或专业级显卡(如 A100 80GB),不适合单用户场景

# 量化后显存对比(以 7B 模型为例)
FP16(16bit):14 GB
Q8_0(8bit):  约 7 GB
Q4_K_M(4bit): 约 4 GB ← Ollama 默认量化级别
Q2_K(2bit):   约 2.5 GB(质量损失较大,不推荐)
💡 选型建议: RTX 4090 24GB 可以流畅运行 7B Q4_K_M 量化模型;如需运行 13B,建议使用量化精度 Q3_K_M;70B 模型需要多卡并采用 Q4 量化,硬件成本显著增加,建议评估性价比后再决策。

模块二:方案 —— 完整安装配置全流程

2.1 系统环境检查:NVIDIA 驱动与 CUDA

在开始部署之前,需要确认服务器具备 NVIDIA 显卡且驱动正常。这是本地 RAG 的硬件前提。

# 检查 NVIDIA 驱动是否正常加载
nvidia-smi

# 正常输出示例:
# +------------------------------------------------------------------+
# | NVIDIA-SMI 550.127.05   Driver Version: 550.127.05  CUDA Version: 12.4 |
# |-------------------------------+--------------+----------------------+
# | GPU  Name   TCC/WDDM         | Bus-Id       | Memory Usage         |
# +-------------------------------+--------------+----------------------+
# |   0  NVIDIA GeForce ...      | 00000000:01:00.0 |  1820 MiB / 24576 MiB |
# +-------------------------------+--------------+----------------------+

# 查看 CUDA 编译器版本(确定可支持的最高 CUDA 版本)
nvcc --version

# 输出示例:
# nvcc: NVIDIA (R) Cuda compiler driver
# Cuda compilation tools, release 12.4, V12.4.131
⚠️ 注意:CUDA 版本必须与 Ollama 编译时使用的 CUDA 版本匹配。如果使用预编译的 Ollama Docker 镜像,默认需要 CUDA 12.1 或更高版本。可通过 nvidia-smi 输出的 "CUDA Version" 字段确认驱动支持的最高 CUDA 版本,而非 nvcc --version 显示的版本(后者是本机编译工具链版本)。
# 检查当前已安装的 CUDA 运行时库版本(驱动兼容的 CUDA 版本)
nvidia-smi | grep "CUDA Version"
# 显示 12.4,表示驱动支持 CUDA 12.4 以下的任何版本

# 如果 Ollama 报错找不到 CUDA 库,需要确认:
cat /usr/local/cuda/version.txt 2>/dev/null || cat /usr/lib/cuda/version.txt 2>/dev/null || echo "CUDA 未安装在标准路径"

# 确认容器能否正确访问 NVIDIA 设备(Docker 层面)
docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi
# 如果此命令失败,说明 Docker NVIDIA Container Toolkit 配置有问题

2.2 Docker 与 Docker Compose 安装

# 安装 Docker(若未安装)
curl -fsSL https://get.docker.com | sh

# 安装 Docker Compose v2(推荐使用 v2 内置插件,无需独立安装)
docker compose version
# Docker Compose version v2.24.0 或更高版本即可

# 确认 Docker 服务正常运行
systemctl status docker

2.3 Ollama 部署:本地 LLM 运行时

Ollama 是本地大语言模型的核心运行引擎,提供简洁的 API 接口,支持热加载模型、管理模型版本,并内置了与 OpenAI API 兼容的接口规范,让 Dify 可以无缝接入。

# 安装 Ollama(Linux x86_64 自动脚本)
curl -fsSL https://ollama.com/install.sh | sh

# 验证安装成功
ollama --version
# 输出类似:ollama version 0.5.11

# 启动 Ollama 服务(默认端口 11434)
ollama serve

# 在另一个终端下载第一个模型(7B Q4_K_M 量化版,约 4GB)
# qwen2.5:7b-instruct-q4_K_M — Qwen2.5 7B 量化版,兼顾质量与显存占用
ollama pull qwen2.5:7b-instruct-q4_K_M

# 拉取 Embedding 模型(用于文档向量化)
ollama pull nomic-embed-text

# 验证模型已下载
ollama list

# 测试 API 调用(与 OpenAI 接口兼容)
curl http://localhost:11434/api/chat -d '{
  "model": "qwen2.5:7b-instruct-q4_K_M",
  "messages": [{"role": "user", "content": "你好,介绍你自己"}],
  "stream": false
}'
💡 常用模型推荐:中文场景推荐 qwen2.5:7b-instruct-q4_K_M;英文为主可用 llama3.1:8b-instruct-q4_K_M;需要更强推理能力可选 deepseek-r1:7b-q4_K_M(需要 ollama pull deepseek-r1:7b-q4_K_M)。

2.4 Docker Compose 完整配置:Dify + Ollama + Qdrant

本配置使用 Dify 作为 RAG 应用编排平台,Qdrant 作为向量数据库(轻量、易用、支持 Rust 原生运行),Ollama 作为本地 LLM 推理引擎。

# 创建项目目录
mkdir -p ~/dify-rag && cd ~/dify-rag

# 创建 docker-compose.yaml
cat > docker-compose.yaml << 'EOF'
# ============================================================
# Dify + Ollama + Qdrant 本地 RAG 知识库完整部署
# 适用于:Ubuntu 20.04+ / CUDA 12.1+ / NVIDIA 显卡 16GB+
# ============================================================

version: '3.8'

services:
  # ----------------------------------------------------------
  # Ollama:本地大语言模型推理引擎
  # 作用:托管 qwen2.5 等开源模型,提供 /api/chat 接口
  # 端口:11434(host模式直接映射,避免容器内网络开销)
  # ----------------------------------------------------------
  ollama:
    image: ollama/ollama:latest
    container_name: dify-ollama
    restart: unless-stopped
    # 将主机 NVIDIA GPU 透传给容器
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all          # 映射所有可用 GPU
              capabilities: [gpu]
    ports:
      - "11434:11434"             # Ollama API 端口(HTTP)
    volumes:
      # 模型存储路径:OLLAMA_MODELS 主机目录 -> /root/.ollama 容器目录
      # 避免容器重建后模型丢失
      - ollama-models:/root/.ollama
    environment:
      # 设置 Ollama 服务地址(供同一主机上的 Dify 访问)
      OLLAMA_HOST: 0.0.0.0
      # 关闭 GPU 资源自动检测日志(减少噪音)
      OLLAMA_DEBUG: "0"
    networks:
      - dify-network

  # ----------------------------------------------------------
  # Qdrant:向量数据库
  # 作用:存储文档的向量 Embedding,支持相似度检索
  # 端口:6333(Qdrant 控制台) + 6334(gRPC,高性能检索)
  # ----------------------------------------------------------
  qdrant:
    image: qdrant/qdrant:v1.12.0
    container_name: dify-qdrant
    restart: unless-stopped
    ports:
      - "6333:6333"              # Qdrant Dashboard HTTP 端口
      - "6334:6334"              # Qdrant gRPC 端口(高性能向量检索)
    volumes:
      # 向量数据持久化存储
      - qdrant-data:/qdrant/storage
    networks:
      - dify-network

  # ----------------------------------------------------------
  # Dify 后端服务:Redis(缓存)
  # ----------------------------------------------------------
  redis:
    image: redis:7-alpine
    container_name: dify-redis
    restart: unless-stopped
    volumes:
      - redis-data:/data
    networks:
      - dify-network

  # ----------------------------------------------------------
  # Dify 后端服务:PostgreSQL(主数据库)
  # ----------------------------------------------------------
  postgres:
    image: postgres:15-alpine
    container_name: dify-postgres
    restart: unless-stopped
    environment:
      # Dify 数据库名称
      POSTGRES_DB: dify
      # Dify 数据库用户
      POSTGRES_USER: dify
      # Dify 数据库密码(生产环境请使用更复杂的密码)
      POSTGRES_PASSWORD: dify123456
    volumes:
      # 数据库文件持久化
      - postgres-data:/var/lib/postgresql/data
    networks:
      - dify-network

  # ----------------------------------------------------------
  # Dify 后端服务:Weaviate(向量检索,也可选 Qdrant)
  # 注意:当前使用 Qdrant 作为主向量库,此处作为备用保留
  # ----------------------------------------------------------
  weaviate:
    image: semitechnologies/weaviate:1.28.0
    container_name: dify-weaviate
    restart: unless-stopped
    environment:
      # 启用 CUDA 加速向量运算
      ENABLE_CUDA: "1"
      # CUDA 设备编号(0 = 第一块 GPU)
      CUDA_DEVICE: "0"
      # 默认向量维度(基于 text2vec-base-chinese 模型)
      DEFAULT_VECTORIZER_MODULE: text2vec-transformers
      # 禁用所有云端模块(纯本地运行)
      DISABLE_MODULES: generative-openai,generative-anthropic,reranker-openai
      # 向量维度(BGE-large-zh 支持 1024 维)
      QUERY_MAXIMUM_RESULTS: 10000
      PERSISTENCE_DATA_PATH: /var/lib/weaviate
      AUTHENTICATION_ANONYMOUS_ACCESS: "true"
      DISABLE_RESTRICTIVE_ACCESS_CHECK: "1"
    volumes:
      - weaviate-data:/var/lib/weaviate
    networks:
      - dify-network

  # ----------------------------------------------------------
  # Dify 后端 API 服务
  # ----------------------------------------------------------
  api:
    image: langgenius/dify:0.14.1
    container_name: dify-api
    restart: unless-stopped
    environment:
      # 数据库连接(使用 postgres service)
      DB_USERNAME: dify
      DB_PASSWORD: dify123456
      DB_HOST: postgres
      DB_PORT: 5432
      DB_DATABASE: dify
      # Redis 连接(使用 redis service)
      REDIS_HOST: redis
      REDIS_PORT: 6379
      REDIS_PASSWORD: ""
      # Ollama 配置(Dify 通过 Ollama 接口调用本地模型)
      OLLAMA_BASE_URL: http://ollama:11434
      # Qdrant 向量库配置
      QDRANT_HOST: http://qdrant:6333
      # Dify 内部密钥(生产环境请修改)
      SECRET_KEY: dify-secret-key-2026-change-this-in-production
      # 初始化管理员账户
      INIT_PASSWORD: dify123456
      # 发送邮件配置(可选,可先注释掉)
      MAIL_TYPE: ""       # 设为空可禁用邮件发送功能
    depends_on:
      - postgres
      - redis
      - qdrant
    volumes:
      # Dify 存储卷(上传文件、日志等)
      - uploads:/app/api/storage
    networks:
      - dify-network

  # ----------------------------------------------------------
  # Dify 前端 Web 服务
  # ----------------------------------------------------------
  web:
    image: langgenius/dify-web:0.14.1
    container_name: dify-web
    restart: unless-stopped
    depends_on:
      - api
    networks:
      - dify-network

  # ----------------------------------------------------------
  # Dify Worker(异步任务处理:知识库文档解析、向量生成等)
  # ----------------------------------------------------------
  worker:
    image: langgenius/dify:0.14.1
    container_name: dify-worker
    restart: unless-stopped
    command: python worker.py
    environment:
      DB_USERNAME: dify
      DB_PASSWORD: dify123456
      DB_HOST: postgres
      DB_PORT: 5432
      DB_DATABASE: dify
      REDIS_HOST: redis
      REDIS_PORT: 6379
      REDIS_PASSWORD: ""
      OLLAMA_BASE_URL: http://ollama:11434
      QDRANT_HOST: http://qdrant:6333
      SECRET_KEY: dify-secret-key-2026-change-this-in-production
    depends_on:
      - postgres
      - redis
      - api
    volumes:
      - uploads:/app/api/storage
    networks:
      - dify-network

# 定义持久化存储卷(Docker 管理,防止数据丢失)
volumes:
  ollama-models:                  # Ollama 模型文件存储卷
  qdrant-data:                    # Qdrant 向量数据存储卷
  redis-data:                     # Redis 缓存数据存储卷
  postgres-data:                  # PostgreSQL 数据库文件存储卷
  weaviate-data:                  # Weaviate 向量数据存储卷(备用)
  uploads:                        # Dify 上传文件存储卷

# 定义网络(所有容器在同一网络中通过服务名互相访问)
networks:
  dify-network:
    driver: bridge
EOF

# 启动所有服务(后台运行)
docker compose up -d

# 查看所有容器状态
docker compose ps

# 查看日志(确认启动无误)
docker compose logs -f --tail=100
💡 启动后访问地址:Dify 控制台 http://服务器IP:8080,Qdrant 控制台 http://服务器IP:6333/dashboard,Ollama API http://服务器IP:11434

2.5 Dify 控制台配置:从零到知识库上线

以下是在 Dify 控制台中完成 RAG 知识库配置的标准流程,以文字描述每一步操作。

第一步:初始化 Dify 并配置 Ollama 模型供应商

# Dify 控制台访问地址
# 首次访问需要设置管理员账户
# 管理员邮箱:admin@dify.local
# 初始密码:在 docker-compose.yaml 中设置的 INIT_PASSWORD

操作步骤(文字描述):

  1. 浏览器打开 http://服务器IP:8080,首次访问进入初始化引导页
  2. 填写管理员邮箱、密码(建议至少 12 位),点击"创建账户"
  3. 登录后进入 Dify 主界面,左侧边栏点击"设置"(齿轮图标)
  4. 在设置页面点击"模型供应商"标签页
  5. 找到"Ollama"卡片,点击右侧"启用"按钮
  6. 填写 Ollama 连接信息:
    • 模型名称(Model Name):qwen2.5:7b-instruct-q4_K_M
    • 基础 URL(Base URL):http://服务器IP:11434(注意不是 localhost)
  7. 点击"保存",系统会自动验证连接是否可用

第二步:创建知识库并上传文档

  1. 主界面左侧点击"知识库"(📚 图标),进入知识库管理页面
  2. 点击右上角"创建知识库"按钮
  3. 填写知识库基本信息:
    • 名称:如"投肯技术文档库"
    • 描述:如"公司内部技术文档与产品说明"
  4. 向量数据库选择:选择"Qdrant"(已通过 Docker Compose 部署)
  5. Embedding 模型选择:点击"选择模型",从下拉列表选择 nomic-embed-text(需先通过 Ollama 拉取)
  6. 文本分段设置(关键参数):
    • 分段标识(Chunk Size):建议 512 字符(中文文档)
    • 重叠长度(Overlap):建议 64 字符(避免上下文断裂)
  7. 点击"创建",进入知识库详情页
  8. 点击"上传文档"按钮,支持 PDF、Word、TXT、Markdown 等格式
  9. 上传完成后,点击右上角"开始索引"按钮,Dify Worker 会自动完成分块、向量化、存入 Qdrant 的全流程
💡 分段策略建议:技术文档建议 512 字符分段 + 64 字符重叠;FAQ 类文档可用 256 字符分段以提高检索精度;长文档(>10MB)建议先预处理为纯文本格式再上传,提升解析成功率。

第三步:创建 RAG 应用并接入知识库

  1. 主界面点击"创建应用"按钮
  2. 选择应用模板:点击"从头开始创建",选择"聊天助手"类型
  3. 填写基本信息:
    • 应用名称:如"智能问答助手"
    • 应用描述:如"基于本地知识库的 RAG 问答系统"
  4. 进入应用编排页面,找到"模型"设置区域,从下拉框选择刚才配置的 Ollama 模型
  5. 找到"上下文"设置区域,点击"添加知识库",将第二步创建的知识库关联进来
  6. 在"提示词编排"区域配置系统提示词(System Prompt),示例:
    你是一个专业的技术问答助手,基于提供的上下文知识库内容回答用户问题。
    请严格根据提供的上下文内容进行回答,如果上下文中没有相关信息,请明确告知用户"根据当前知识库内容,无法回答此问题",
    不要编造或猜测答案。
    回答时需标注信息来源,具体说明该答案来自哪份文档的第几段。
  7. 点击右上角"发布"按钮,应用即上线
  8. 点击"预览"按钮,可直接与知识库对话测试效果

2.6 Python 直接调用 Ollama API 实现 RAG 检索

如果需要在自有系统中直接调用 Ollama 实现 RAG 流程(绕过 Dify),以下是一套完整的 Python 实现代码,包含文档加载、向量化、向量存储、相似度检索与答案生成:

# ============================================================
# Ollama RAG 检索系统(纯 Python 实现,无需 Dify)
# 依赖:pip install requests langchain-community sentence-transformers
# ============================================================

import requests
import json
from typing import List, Optional

# ================== 配置区域 ==================
OLLAMA_BASE_URL = "http://localhost:11434"
EMBEDDING_MODEL = "nomic-embed-text"   # 向量化模型(需提前 ollama pull)
LLM_MODEL = "qwen2.5:7b-instruct-q4_K_M"  # LLM 模型
CHUNK_SIZE = 512                        # 分块大小(字符数)
CHUNK_OVERLAP = 64                      # 相邻块重叠字符数
TOP_K = 5                               # 召回的最相似文档块数量

# ================== 向量化服务 ==================
def get_embedding(text: str, ollama_url: str = OLLAMA_BASE_URL) -> List[float]:
    """
    调用 Ollama 的 Embedding 接口,将文本转换为向量
    参数:
        text: 待向量化的文本
    返回:向量列表(浮点数)
    """
    url = f"{ollama_url}/api/embeddings"
    payload = {
        "model": EMBEDDING_MODEL,
        "prompt": text
    }
    response = requests.post(url, json=payload, timeout=30)
    response.raise_for_status()
    return response.json()["embedding"]


def embed_documents(texts: List[str]) -> List[List[float]]:
    """
    批量向量化多个文本片段
    内部自动调用 get_embedding() 逐条处理
    """
    return [get_embedding(text) for text in texts]


# ================== 文本分块(简单滑动窗口) ==================
def chunk_text(text: str, chunk_size: int = CHUNK_SIZE, overlap: int = CHUNK_OVERLAP) -> List[str]:
    """
    使用滑动窗口将长文本切分为重叠的块
    作用:保证上下文连续性,避免块边界处语义断裂
    """
    chunks = []
    start = 0
    text_length = len(text)
    while start < text_length:
        end = start + chunk_size
        chunk = text[start:end]
        chunks.append(chunk)
        start += (chunk_size - overlap)  # 移动窗口步长 = 块大小 - 重叠长度
    return chunks


# ================== Ollama LLM 对话接口 ==================
def chat_with_ollama(
    prompt: str,
    system_prompt: Optional[str] = None,
    context_chunks: Optional[List[str]] = None,
    model: str = LLM_MODEL,
    temperature: float = 0.3
) -> str:
    """
    调用 Ollama /api/chat 接口进行 RAG 增强的对话生成
    参数:
        prompt: 用户问题
        system_prompt: 系统提示词
        context_chunks: 从向量数据库召回的相关文档块
        model: Ollama 模型名称
        temperature: 采样温度(0.3 推荐 RAG 场景,降低幻觉)
    返回:LLM 生成的文本回答
    """
    # 组装上下文:如果有召回的文档块,将其拼接到 system prompt 中
    if context_chunks:
        context_text = "\n\n---\n\n以下是与问题相关的知识库内容:\n"
        context_text += "\n\n".join(
            f"[文档{i+1}] {chunk}" for i, chunk in enumerate(context_chunks)
        )
        context_text += "\n\n请基于以上内容回答用户问题。如果内容不相关,请如实说明。"
    else:
        context_text = "没有找到相关背景知识,请基于自身能力回答。"

    messages = []
    if system_prompt:
        messages.append({"role": "system", "content": system_prompt})
    messages.append({"role": "user", "content": f"{context_text}\n\n---\n\n用户问题:{prompt}"})

    payload = {
        "model": model,
        "messages": messages,
        "temperature": temperature,    # 0.3 温度保持回答事实性,减少幻觉
        "stream": False               # 关闭流式输出,直接返回完整结果
    }

    url = f"{OLLAMA_BASE_URL}/api/chat"
    response = requests.post(url, json=payload, timeout=120)
    response.raise_for_status()
    result = response.json()
    return result["message"]["content"]


# ================== 简单向量相似度计算(余弦相似度) ==================
def cosine_similarity(vec_a: List[float], vec_b: List[float]) -> float:
    """计算两个向量的余弦相似度"""
    dot_product = sum(a * b for a, b in zip(vec_a, vec_b))
    norm_a = sum(a * a for a in vec_a) ** 0.5
    norm_b = sum(b * b for b in vec_b) ** 0.5
    if norm_a == 0 or norm_b == 0:
        return 0.0
    return dot_product / (norm_a * norm_b)


def retrieve_relevant_chunks(query: str, document_chunks: List[str], top_k: int = TOP_K) -> List[str]:
    """
    从文档块中检索与用户问题最相关的 top_k 个块
    流程:
        1. 将用户问题向量化
        2. 遍历所有文档块,计算与问题的余弦相似度
        3. 排序后返回 top_k 个最高相似度块
    """
    query_vector = get_embedding(query)
    similarities = []
    for chunk in document_chunks:
        chunk_vector = get_embedding(chunk)
        sim = cosine_similarity(query_vector, chunk_vector)
        similarities.append((chunk, sim))
    # 按相似度降序排列,保留 top_k
    similarities.sort(key=lambda x: x[1], reverse=True)
    return [chunk for chunk, _ in similarities[:top_k]]


# ================== 端到端 RAG 问答 ==================
def rag_answer(user_question: str, full_document: str) -> dict:
    """
    完整的 RAG 流程:
        1. 分块 → 2. 向量化 → 3. 检索 → 4. 生成
    参数:
        user_question: 用户问题
        full_document: 完整文档内容(示例用,生产中从数据库加载)
    返回:包含答案、相关文档块的字典
    """
    # Step 1: 文本分块
    chunks = chunk_text(full_document)
    print(f"[RAG] 文档分块完成,共 {len(chunks)} 个块")

    # Step 2: 从知识库检索相关块
    relevant_chunks = retrieve_relevant_chunks(user_question, chunks, top_k=TOP_K)
    print(f"[RAG] 检索到 {len(relevant_chunks)} 个相关块")

    # Step 3: LLM 生成答案
    answer = chat_with_ollama(
        prompt=user_question,
        context_chunks=relevant_chunks,
        temperature=0.3
    )

    return {
        "answer": answer,
        "relevant_chunks": relevant_chunks,
        "total_chunks": len(chunks)
    }


# ================== 测试示例 ==================
if __name__ == "__main__":
    sample_doc = """
    重庆投肯智能科技有限公司成立于 2021 年,专注于人工智能技术研发与应用。
    公司主要产品包括:智能客服系统、RAG 知识库平台、智能文档分析工具。
    公司位于重庆市渝中区,核心技术团队来自国内外知名高校和科技企业。
    公司愿景:让每个企业都能用上安全、可靠的 AI 技术。
    公司使命:降低 AI 应用门槛,推动人工智能普惠化。
    产品已在金融、医疗、法律等多个行业落地,累计服务企业超过 200 家。
    """

    question = "投肯智能的核心产品有哪些?"

    result = rag_answer(question, sample_doc)
    print("\n========== RAG 问答结果 ==========")
    print(f"问题:{question}")
    print(f"答案:{result['answer']}")
    print(f"参考块数:{len(result['relevant_chunks'])}")

模块三:效果 —— 性能测试与质量对比

3.1 测试环境说明

组件规格备注
CPUAMD Ryzen 9 7950X(16核32线程)主机处理文档解析
内存64GB DDR5主要承载 Docker 容器与系统开销
显卡NVIDIA RTX 4090 24GBOllama 模型推理 + Qdrant 向量运算
系统Ubuntu 22.04 LTSCUDA 12.4 + NVIDIA Driver 550
测试模型Qwen2.5-7B-Q4_K_M vs GPT-4o同为 7B 级别量化模型
测试文档50篇技术文档,合计约 8 万字涵盖产品规格、技术文档、FAQ

3.2 性能对比表格

维度GPT-4o(云端 API)Qwen2.5-7B-Ollama(本地 RTX 4090)对比结论
回答质量
(知识库相关问题)
★★★★★
推理能力强,长上下文理解好
★★★★☆
中文知识库场景质量接近 GPT-4o,复杂推理略弱
日常知识库问答无明显差距
回答质量
(数学/逻辑推理)
★★★★★ ★★★☆☆
复杂数学推导容易出错
RAG 场景以检索为主,差距不大
首次响应延迟
(TTFT,首字延迟)
1.5~3 秒(依赖网络) 0.8~2 秒(本地直接推理) 本地略有优势,无网络波动
完整回复时间
(500字回答)
8~15 秒(含网络延迟) 12~20 秒(LLM 生成速度) 云端更快(专线优化),本地差距可接受
显存占用 0 GB(云端处理) 约 4.5 GB(Q4_K_M 量化) RTX 4090 还有约 19GB 冗余
并发能力 受 API 限速(50~200 RPM) 受显存限制(约 3~5 并发) 并发数相近,本地无超额费用
月均成本 约 800~3000 元(视请求量) ≈ 0 元(电费约 50 元/月) 本地节省 >95% 费用
数据安全性 ⚠️ 数据经第三方服务器 ✅ 全程本地,不出防火墙 敏感行业必须本地部署
离线可用性 ❌ 必须联网 ✅ 完全离线可用 工厂/内网场景刚需

3.3 显存与响应速度实测数据

# 使用 nvidia-smi 监测显存占用(Ollama 加载 Qwen2.5-7B-Q4_K_M 时)
watch -n 1 nvidia-smi

# 监控 Ollama 日志(查看模型加载状态)
tail -f ~/.ollama/logs/server.log

# 测试 Ollama API 响应延迟(本地)
time curl -s http://localhost:11434/api/chat \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen2.5:7b-instruct-q4_K_M","messages":[{"role":"user","content":"介绍一下RAG技术的原理"}],"stream":false}' \
  | jq '.total_duration'
# total_duration 单位为纳秒,/1000000 即为毫.ms

# RTX 4090 实测数据:
# 模型加载时间:约 8-12 秒(冷启动)
# 模型切换(如换用 deepseek-r1):约 5-8 秒
# 显存占用(Qwen2.5-7B-Q4_K_M):约 4.5 GB
# 显存占用(DeepSeek-R1-7B-Q4_K_M):约 4.8 GB
# 显存空闲余量:约 19 GB(可继续加载 Embedding 模型等其他任务)
💡 性能优化结论:在 RTX 4090 上,Qwen2.5-7B-Q4_K_M 完全可以在 5 秒内完成完整回复,显存占用仅 4.5GB,余量充足。如需更高质量回答,可将模型升级为 qwen2.5:14b-instruct-q3_K_M(约 9GB 显存),质量显著提升。

模块四:总结 —— 常见错误排错与调优参数

4.1 常见错误代码与解决方案(8+ 条)

错误现象解决方案
Error: no GPU detected
Docker 容器内找不到 NVIDIA 显卡
Docker NVIDIA Container Toolkit 未正确安装;--gpus all 参数未传递 1. 确认宿主机已安装 nvidia-container-toolkit
2. 重新安装:apt install nvidia-container-toolkit
3. 重启 Docker:systemctl restart docker
4. 启动容器时加参数:docker run --gpus all ...
5. 验证:docker run --rm --gpus all nvidia/cuda:12.4.0-base nvidia-smi
CUDA out of memory
显存不足导致模型无法加载
模型量化级别太高(如 FP16);同时运行多个模型;其他进程占用了 GPU 显存 1. 降低量化级别:将 qwen2.5:7b 改为 qwen2.5:7b-q4_K_M
2. 检查显存占用:nvidia-smi
3. 关闭其他占用显存的进程(如 TensorBoard、训练任务等)
4. 使用更小的 Embedding 模型:nomic-embed-text(约 274MB)
connection refused: 127.0.0.1:11434
Ollama 端口拒绝连接
Ollama 服务未启动;端口配置错误;防火墙阻断 1. 确认 Ollama 服务运行:curl http://localhost:11434
2. 如未运行,执行:ollama serve
3. 检查防火墙:ufw allow 11434
4. Docker 网络问题:确认容器在 dify-network 中,Ollama 访问地址用服务名 http://ollama:11434
model not found, try ollama pull xxx
模型未下载
指定的模型名称拼写错误;未在 Ollama 中拉取模型 1. 查看已下载模型:ollama list
2. 拉取模型:ollama pull qwen2.5:7b-instruct-q4_K_M
3. 确认模型名称格式:模型名:标签,冒号不能漏
file too large
上传文档时文件过大被拒绝
Dify 默认单文件上传大小限制(通常 15MB);文档格式不受支持 1. 拆分大文档:将 >15MB 的文档拆分为多个小文件
2. 转换为支持格式:优先使用 TXT、Markdown、PDF
3. 修改 Dify 上传限制(在 docker-compose.yaml 的 api 服务中配置 UPLOAD_SIZE_LIMIT=50
4. 复杂 PDF 使用 pdfplumber 预处理提取文本
Qdrant 控制台能访问,但 Dify 无法写入向量 Qdrant collection 未创建;Dify 与 Qdrant 网络不通;端口映射错误 1. 访问 http://IP:6333/dashboard,检查 Collections
2. 确认 Dify 中选择的向量库名称与实际一致
3. 检查 docker-compose 中 Qdrant 端口映射:"6333:6333"
4. 测试连通性:docker exec -it dify-api curl http://qdrant:6333
NVIDIA-SMI: command not found
nvidia-smi 命令失效
NVIDIA 驱动未安装;驱动版本与 CUDA 版本不匹配;PATH 环境变量缺失 1. 重新安装 NVIDIA 驱动:ubuntu-drivers autoinstall
2. 或从 NVIDIA 官网下载 .run 文件手动安装
3. 确认驱动版本:cat /proc/driver/nvidia/version
4. 添加到 PATH:export PATH=/usr/bin:$PATH
5. 驱动要求:CUDA 12.x 需要 ≥ 535 版本的驱动
Dify 知识库检索结果为空(检索失败) Embedding 模型未加载;文档分块大小不适合;向量维度不匹配 1. 确认 Ollama 中已拉取 nomic-embed-text
2. 在 Dify 知识库设置中确认选择的 Embedding 模型与实际部署一致
3. 尝试调大分段重叠(Overlap 从 64 提升到 128)
4. 检查知识库状态:确保文档已完成"索引"(不是"处理中"状态)
ollama run 卡住 / 无响应
模型运行无响应
模型文件损坏;磁盘 I/O 瓶颈;内存不足导致 OOM 1. 删除并重新拉取模型:ollama rm qwen2.5:7b-instruct-q4_K_M && ollama pull qwen2.5:7b-instruct-q4_K_M
2. 确认磁盘空间充足:df -h(模型文件约 4GB)
3. 检查内存:free -m(确保 ≥ 16GB 可用)
4. 如内存紧张,增加 swap:sudo fallocate -l 16G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile
Web 界面加载慢或无法访问(:8080 Dify Web/Api 容器未启动;端口冲突;内存不足 1. 检查容器状态:docker compose ps
2. 查看 Web 容器日志:docker compose logs web --tail=50
3. 检查端口占用:ss -tlnp | grep 8080
4. 重启服务:docker compose restart
5. 确认内存:free -m(Dify 全套服务建议 ≥ 32GB)

4.2 关键调优参数汇总

Ollama 服务端调优

# 在 docker-compose.yaml 中为 Ollama 服务添加环境变量进行调优
environment:
  OLLAMA_NUM_PARALLEL: "4"       # 并行推理数量(根据 GPU 核心数调整,RTX 4090 建议 4)
  OLLAMA_MAX_LOADED_MODELS: "2"  # 最多同时加载 2 个模型(避免显存溢出)
  OLLAMA_GPU_OVERHEAD: "0"       # GPU 显存预留(字节),设为 0 表示不预留

Dify 应用层调优(提示词参数)

# 在 Dify 应用编排中调整高级参数

# Temperature(采样温度):推荐 0.3~0.5
# 0.3:事实性强,幻觉少,适合 RAG 知识库问答
# 0.7:创意性回答,适合文案生成场景

# Top_P(核采样):推荐 0.9
# 控制模型输出的词汇多样性

# Max Tokens(最大输出长度):推荐 2048
# 单次回答的最大 token 数,防止无限输出

# Retrieval parameters(检索参数):
# Top_K: 5      # 召回的候选文档块数量(知识库越大可适当增大)
# Top_N: 3      # 最终输入给 LLM 的块数量(建议 2~5,过多会稀释上下文)

Qdrant 向量库调优

# 在 Qdrant 控制台(http://IP:6333/dashboard)中调整 collection 设置

# HNSW 参数(向量索引算法):
# m: 16          # 每个节点连接数(值越大精度越高,占用内存越大)
# ef_construct: 128  # 构建时的搜索深度(值越大精度越高,速度越慢)
# full_scan_threshold: 10000  # 超过此数量的向量时强制使用索引

# 生产环境建议:m=16, ef_construct=128,可获得 95%+ 的召回率

4.3 硬件选型建议(不同场景)

场景推荐配置可运行模型预估成本
个人开发者 / 学习研究 RTX 3060 12GB / RTX 4060 Ti 16GB Qwen2.5-7B-Q4_K_M, Llama3-8B-Q4 3000~5000 元
小团队(5~10人) RTX 4090 24GB Qwen2.5-14B-Q3_K_M, DeepSeek-R1-7B 16000~20000 元
中型企业 / 生产环境 RTX 4090 × 2(双卡) 或 RTX 6000 Ada 48GB Qwen2.5-14B-Q4, DeepSeek-R1-14B 40000~70000 元
大规模部署 NVIDIA A100 80GB SXM Qwen2.5-72B-Q4, Llama3.1-70B 15 万~30 万元

4.4 完整部署检查清单

# ============================================================
# 部署完成后,按以下顺序执行验证检查
# ============================================================

# 1. 检查所有容器是否正常运行
docker compose ps
# 预期:所有容器状态为 "Up"

# 2. 验证 NVIDIA 驱动和 CUDA
nvidia-smi
# 预期:显示 GPU 型号、驱动版本、显存使用情况

# 3. 验证 Ollama 服务可访问
curl http://localhost:11434
# 预期:返回 {"status":"ok"}

# 4. 验证 Ollama 能正常调用模型
curl http://localhost:11434/api/chat \
  -d '{"model":"qwen2.5:7b-instruct-q4_K_M","messages":[{"role":"user","content":"test"}],"stream":false}'
# 预期:返回 JSON 包含 "message" 字段

# 5. 验证 Qdrant 服务可访问
curl http://localhost:6333/collections
# 预期:返回 {"result":{"collections":[]},"status":"ok"}

# 6. 验证 Dify 控制台可访问
curl http://localhost:8080
# 预期:返回 HTML 页面(非 502 或 404)

# 7. 验证 Dify 与 Ollama 连接(通过 Dify 控制台)
# 在 Dify 设置 → 模型供应商 → 点击 "Ollama" 的测试按钮
# 预期:显示绿色"可用"提示

# 8. 上传测试文档验证完整 RAG 流程
# 在 Dify 知识库中上传一个 TXT 文件 → 开始索引 → 等待索引完成
# 在应用预览中提问与文档相关的问题
# 预期:回答内容与上传文档高度相关

4.5 结论与展望

通过 Dify + Ollama + Qdrant 的组合,我们构建了一套完全本地化、零 API 费用、支持离线运行的生产级 RAG 知识库系统。RTX 4090 级别的硬件即可支撑中小规模团队日常使用,硬件投入在 3~6 个月内即可通过节省 API 费用回收。

本地 RAG 的局限在于:模型规模受显存约束,复杂推理任务不如 GPT-4o。对于以知识检索为核心的场景,本地方案已完全满足需求,是兼顾成本与效益的最优解。

后续可探索的方向包括:多模型路由(根据问题类型自动切换本地/云端模型)、知识库增量自动更新(结合 Cron 定时抓取)、以及多语言文档的专项优化。