企业级私有化部署方案

🏢 企业级私有化部署方案

将当前基于 Dify 云服务的 AI 应用改造为完全私有化的企业级解决方案


📋 目录

  1. 现状分析
  2. 私有化部署架构
  3. 核心组件私有化方案
  4. 部署实施步骤
  5. 成本与资源评估
  6. 安全与合规
  7. 监控与运维
  8. 常见问题

1. 现状分析

1.1 当前架构依赖

┌─────────────────────────────────────────────────┐
│           当前架构(云服务依赖)                   │
├─────────────────────────────────────────────────┤
│                                                 │
│  前端应用 (React)                               │
│       ↓                                         │
│  后端服务 (Node.js + Express)                   │
│       ↓                                         │
│  ┌──────────────┐      ┌─────────────────┐    │
│  │  Dify 云平台  │      │  云端 LLM API   │    │
│  │  (工作流)     │      │  (SiliconFlow)  │    │
│  └──────────────┘      └─────────────────┘    │
│       ↓                        ↓               │
│  ┌──────────────────────────────────────┐     │
│  │      外部云服务(数据离开内网)        │     │
│  └──────────────────────────────────────┘     │
│                                                 │
└─────────────────────────────────────────────────┘

1.2 存在的问题

问题类型 具体表现 业务影响
数据安全 企业数据通过 API 发送到云端 ⚠️ 数据泄露风险
合规性 无法满足数据本地化要求 ❌ 无法通过审计
可控性 依赖第三方服务可用性 ⚠️ 业务连续性风险
成本 按调用量计费,无法控制 💰 成本不可预测
定制化 模型和工作流受限于平台 🚫 功能扩展受限

2. 私有化部署架构

2.1 目标架构

┌─────────────────────────────────────────────────────────────┐
│               企业私有化部署架构(内网环境)                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌──────────────────────────────────────────────┐         │
│  │         负载均衡层 (Nginx / HAProxy)          │         │
│  └──────────────────────────────────────────────┘         │
│                        ↓                                    │
│  ┌──────────────────────────────────────────────┐         │
│  │           前端服务 (React + Vite)             │         │
│  └──────────────────────────────────────────────┘         │
│                        ↓                                    │
│  ┌──────────────────────────────────────────────┐         │
│  │        应用服务层 (Node.js + Express)         │         │
│  │                                               │         │
│  │  - REST API                                   │         │
│  │  - SSE 流式输出                                │         │
│  │  - LangChain 工作流引擎                        │         │
│  └──────────────────────────────────────────────┘         │
│           ↓               ↓              ↓                  │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐       │
│  │  Dify 私有版 │  │ 大模型服务   │  │  向量数据库  │       │
│  │  (Docker)   │  │  (Ollama)   │  │  (Qdrant)   │       │
│  └─────────────┘  └─────────────┘  └─────────────┘       │
│                                                             │
│  ┌──────────────────────────────────────────────┐         │
│  │           数据持久化层 (PostgreSQL)           │         │
│  └──────────────────────────────────────────────┘         │
│                                                             │
│  ┌──────────────────────────────────────────────┐         │
│  │       监控与日志 (Prometheus + Grafana)        │         │
│  └──────────────────────────────────────────────┘         │
│                                                             │
│  🏢 企业内网环境 - 数据不出内网                             │
└─────────────────────────────────────────────────────────────┘

2.2 核心组件说明

组件 作用 私有化方案 备选方案
工作流编排 替代 Dify 云平台 Dify 社区版(Docker) LangChain(已集成)
大语言模型 替代云端 API Ollama + 开源模型 vLLM / FastChat / LocalAI
向量数据库 RAG 文档检索 Qdrant(Docker) Milvus / Weaviate / Chroma
数据库 存储会话、用户数据 PostgreSQL MySQL / MongoDB
文件存储 存储上传的文档、图片 MinIO(对象存储) 本地文件系统 / NFS
缓存 提升响应速度 Redis -
负载均衡 流量分发 Nginx HAProxy / Traefik
监控 系统健康监控 Prometheus + Grafana -

3. 核心组件私有化方案

3.1 Dify 私有化部署

方案 A:Dify 社区版(推荐)

优点

  • ✅ 提供完整的 Web UI 和工作流编辑器
  • ✅ 支持所有云平台功能
  • ✅ 开源免费,社区活跃
  • ✅ 支持多种模型和向量数据库

部署方式

# 1. 克隆 Dify 仓库
git clone https://github.com/langgenius/dify.git
cd dify/docker

# 2. 复制环境配置
cp .env.example .env

# 3. 修改 .env 配置(关键配置)
cat > .env << EOF
# API 服务配置
CONSOLE_WEB_URL=http://your-internal-ip:3000
CONSOLE_API_URL=http://your-internal-ip:5001
SERVICE_API_URL=http://your-internal-ip:5001

# 数据库配置(使用 Docker Compose 自带的 PostgreSQL)
DB_USERNAME=postgres
DB_PASSWORD=dify_strong_password
DB_HOST=db
DB_PORT=5432
DB_DATABASE=dify

# Redis 配置
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=dify_redis_password

# 向量数据库配置(使用 Qdrant)
VECTOR_STORE=qdrant
QDRANT_URL=http://qdrant:6333
QDRANT_API_KEY=your-qdrant-api-key

# 存储配置
STORAGE_TYPE=local
STORAGE_LOCAL_PATH=/app/storage

# 模型配置(指向本地 Ollama)
# 在 Web UI 中配置,不需要在这里设置
EOF

# 4. 启动 Dify
docker compose up -d

# 5. 查看日志
docker compose logs -f

# 访问地址:
# - Web UI: http://your-ip:3000
# - API: http://your-ip:5001

资源需求

  • CPU: 4 核以上
  • 内存: 8GB 以上
  • 磁盘: 50GB 以上(数据、向量索引)

方案 B:纯 LangChain 方案(已实现)

优点

  • ✅ 项目已集成 LangChain 工作流引擎
  • ✅ 完全自主可控
  • ✅ 无需额外部署 Dify

实施步骤

  1. 启用 LangChain 路由(已完成)
// backend/src/server.js 已集成
app.use('/api/langchain', langchainRoutes);
  1. 使用 LangChain 工作流引擎
// 参考 backend/src/langchain-version/workflows/workflowEngine.js
import { WorkflowEngine, createSequentialWorkflow } from './workflowEngine.js';

const workflow = createSequentialWorkflow([
  {
    type: 'llm',
    prompt: '分析这个问题:{{input}}',
    systemPrompt: '你是一个专业的分析师',
  },
  {
    type: 'code',
    code: 'return { result: inputs.output.toUpperCase() }',
  },
]);

const engine = new WorkflowEngine(workflow);
const result = await engine.execute({ input: '用户问题' });

对比

特性 Dify 社区版 LangChain 方案
可视化编辑器
学习成本
定制化程度
运维复杂度
推荐场景 快速上线、业务人员可用 开发团队主导、深度定制

3.2 大模型私有化部署

方案 A:Ollama(推荐)

优点

  • ✅ 安装简单,一键部署
  • ✅ 支持 60+ 主流开源模型
  • ✅ 自动处理模型下载和量化
  • ✅ 提供 OpenAI 兼容 API
  • ✅ GPU/CPU 自动适配

部署步骤

# 1. 安装 Ollama(Linux)
curl -fsSL https://ollama.com/install.sh | sh

# 2. 下载并运行模型
# 中文对话模型(推荐)
ollama pull qwen2.5:7b           # 7B 参数,显存需求 ~6GB
ollama pull qwen2.5:14b          # 14B 参数,显存需求 ~12GB
ollama pull qwen2.5:32b          # 32B 参数,显存需求 ~24GB

# 代码生成模型
ollama pull deepseek-coder-v2:16b  # 代码专用模型

# 小型快速模型
ollama pull qwen2.5:3b           # 3B 参数,显存需求 ~3GB

# 3. 测试模型
ollama run qwen2.5:7b

# 4. 作为 API 服务运行(默认端口 11434)
# Ollama 自动在后台运行,提供 OpenAI 兼容 API

Docker 部署(生产环境推荐)

# docker-compose.ollama.yml
version: '3.8'

services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    ports:
      - "11434:11434"
    volumes:
      - ollama_data:/root/.ollama
    environment:
      - OLLAMA_KEEP_ALIVE=24h          # 模型保持加载时间
      - OLLAMA_NUM_PARALLEL=4          # 并行请求数
      - OLLAMA_MAX_LOADED_MODELS=2     # 同时加载的模型数
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    restart: unless-stopped

volumes:
  ollama_data:
# 启动 Ollama
docker compose -f docker-compose.ollama.yml up -d

# 下载模型(在容器内执行)
docker exec -it ollama ollama pull qwen2.5:7b

配置后端连接 Ollama

// backend/src/langchain-version/config.js
export const config = {
  model: {
    provider: 'ollama',
    apiKey: 'ollama',  // Ollama 不需要真实的 API Key
    baseURL: 'http://localhost:11434/v1',  // Ollama OpenAI 兼容接口
    modelName: 'qwen2.5:7b',
    temperature: 0.7,
  },
  
  embedding: {
    provider: 'ollama',
    baseURL: 'http://localhost:11434/v1',
    modelName: 'bge-m3:latest',  // 向量化模型
  },
};

推荐模型列表

模型 参数量 显存需求 适用场景 中文能力
qwen2.5:3b 3B ~3GB 轻量级对话、快速响应 ⭐⭐⭐⭐
qwen2.5:7b 7B ~6GB 通用对话、知识问答 ⭐⭐⭐⭐⭐
qwen2.5:14b 14B ~12GB 复杂推理、专业领域 ⭐⭐⭐⭐⭐
qwen2.5:32b 32B ~24GB 高难度任务、创意写作 ⭐⭐⭐⭐⭐
deepseek-coder-v2:16b 16B ~14GB 代码生成、代码审查 ⭐⭐⭐⭐
llama3.1:8b 8B ~7GB 英文对话(中文一般) ⭐⭐⭐
mistral:7b 7B ~6GB 英文推理 ⭐⭐

方案 B:vLLM(高性能方案)

优点

  • ✅ 推理速度快 20-30 倍(相比 HuggingFace)
  • ✅ 支持连续批处理
  • ✅ 更高的并发能力
  • ✅ 适合生产环境高负载场景

部署步骤

# 1. 安装 vLLM
pip install vllm

# 2. 下载模型
# 从 HuggingFace 或 ModelScope 下载
# 例如:Qwen2.5-7B-Instruct
git clone https://www.modelscope.cn/Qwen/Qwen2.5-7B-Instruct.git

# 3. 启动 vLLM 服务(OpenAI 兼容 API)
python -m vllm.entrypoints.openai.api_server \
    --model /path/to/Qwen2.5-7B-Instruct \
    --host 0.0.0.0 \
    --port 8000 \
    --trust-remote-code \
    --tensor-parallel-size 1  # 单 GPU

Docker 部署

# docker-compose.vllm.yml
version: '3.8'

services:
  vllm:
    image: vllm/vllm-openai:latest
    container_name: vllm-server
    ports:
      - "8000:8000"
    volumes:
      - ./models:/models  # 模型存储路径
    environment:
      - MODEL_NAME=/models/Qwen2.5-7B-Instruct
      - TENSOR_PARALLEL_SIZE=1
      - MAX_MODEL_LEN=4096
    command: >
      --model /models/Qwen2.5-7B-Instruct
      --host 0.0.0.0
      --port 8000
      --trust-remote-code
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    restart: unless-stopped

方案 C:FastChat(多模型管理)

优点

  • ✅ 支持多模型同时部署
  • ✅ 提供 Web UI 管理界面
  • ✅ 支持模型热切换
# 1. 安装 FastChat
pip install "fschat[model_worker,webui]"

# 2. 启动控制器
python -m fastchat.serve.controller --host 0.0.0.0

# 3. 启动模型 Worker(可启动多个)
python -m fastchat.serve.model_worker \
    --model-path /path/to/qwen2.5-7b \
    --controller http://localhost:21001 \
    --worker-address http://localhost:31000

# 4. 启动 OpenAI 兼容 API 服务
python -m fastchat.serve.openai_api_server \
    --controller-address http://localhost:21001 \
    --host 0.0.0.0 \
    --port 8000

对比选择

场景 推荐方案 原因
快速部署、小团队 Ollama 简单易用,开箱即用
高并发、生产环境 vLLM 性能最优,吞吐量高
多模型管理 FastChat 支持模型切换和管理
开发测试 Ollama 快速迭代,便于调试

3.3 向量数据库私有化

方案 A:Qdrant(推荐)

优点

  • ✅ 高性能,支持 HNSW 索引
  • ✅ 提供 REST API 和 gRPC
  • ✅ 支持过滤、多向量搜索
  • ✅ 内置集群和副本功能
  • ✅ 开源免费

Docker 部署

# docker-compose.qdrant.yml
version: '3.8'

services:
  qdrant:
    image: qdrant/qdrant:latest
    container_name: qdrant
    ports:
      - "6333:6333"  # REST API
      - "6334:6334"  # gRPC
    volumes:
      - qdrant_data:/qdrant/storage
    environment:
      - QDRANT__SERVICE__HTTP_PORT=6333
      - QDRANT__SERVICE__GRPC_PORT=6334
      # 性能优化配置
      - QDRANT__STORAGE__WAL__WAL_CAPACITY_MB=256
      - QDRANT__STORAGE__PERFORMANCE__MAX_SEARCH_THREADS=4
    restart: unless-stopped

volumes:
  qdrant_data:
# 启动 Qdrant
docker compose -f docker-compose.qdrant.yml up -d

# 访问 Web UI: http://localhost:6333/dashboard

配置后端连接 Qdrant

// backend/src/utils/vectorDB/qdrant.js(已存在)
import { QdrantClient } from '@qdrant/js-client-rest';

const client = new QdrantClient({
  url: process.env.QDRANT_URL || 'http://localhost:6333',
  apiKey: process.env.QDRANT_API_KEY, // 可选
});

// 创建集合
await client.createCollection('documents', {
  vectors: {
    size: 1024,  // 向量维度(根据 Embedding 模型)
    distance: 'Cosine',
  },
  optimizers_config: {
    default_segment_number: 2,
  },
  replication_factor: 2,  // 副本数(生产环境推荐)
});

// 插入向量
await client.upsert('documents', {
  points: [
    {
      id: 1,
      vector: [...],  // 向量数据
      payload: {
        text: '文档内容',
        metadata: { source: 'doc1.pdf' },
      },
    },
  ],
});

// 搜索
const results = await client.search('documents', {
  vector: [...],  // 查询向量
  limit: 5,
  with_payload: true,
});

方案 B:Milvus(企业级方案)

优点

  • ✅ 功能最全面
  • ✅ 支持 GPU 加速
  • ✅ 企业级特性(备份、监控)
  • ✅ 支持超大规模数据(10亿+)

Docker 部署

# 下载 Milvus docker-compose 配置
wget https://github.com/milvus-io/milvus/releases/download/v2.3.0/milvus-standalone-docker-compose.yml -O docker-compose.milvus.yml

# 启动 Milvus
docker compose -f docker-compose.milvus.yml up -d

方案 C:Weaviate

优点

  • ✅ 内置多种向量化模块
  • ✅ 支持 GraphQL 查询
  • ✅ 语义搜索能力强
# docker-compose.weaviate.yml
version: '3.8'
services:
  weaviate:
    image: semitechnologies/weaviate:latest
    ports:
      - "8080:8080"
    environment:
      - AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=true
      - PERSISTENCE_DATA_PATH=/var/lib/weaviate
    volumes:
      - weaviate_data:/var/lib/weaviate
volumes:
  weaviate_data:

对比选择

特性 Qdrant Milvus Weaviate
部署简单度 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐
性能 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
功能丰富度 ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
资源占用
推荐场景 中小规模 大规模生产 语义搜索

3.4 Embedding 模型私有化

RAG 系统需要将文本转换为向量,推荐使用:

# Ollama 安装 Embedding 模型
ollama pull bge-m3:latest           # 通用中文 Embedding(推荐)
ollama pull nomic-embed-text        # 英文 Embedding
ollama pull mxbai-embed-large       # 高精度多语言

# 或者使用本地 Sentence Transformers
pip install sentence-transformers

python
>>> from sentence_transformers import SentenceTransformer
>>> model = SentenceTransformer('BAAI/bge-large-zh-v1.5')
>>> embeddings = model.encode(['文本1', '文本2'])

后端配置

// backend/src/langchain-version/config.js
export const config = {
  embedding: {
    provider: 'ollama',
    baseURL: 'http://localhost:11434/v1',
    modelName: 'bge-m3',  // Ollama Embedding 模型
  },
};

4. 部署实施步骤

4.1 阶段一:基础环境准备(1-2 天)

4.1.1 硬件资源准备

最低配置(开发/测试环境)

- CPU: 8 核
- 内存: 32GB
- GPU: NVIDIA RTX 3090 (24GB) 或同等算力
- 磁盘: 500GB SSD
- 网络: 千兆内网

推荐配置(生产环境)

- CPU: 16 核 × 2(双路)
- 内存: 128GB
- GPU: NVIDIA A100 (40GB) × 2 或 H100
- 磁盘: 2TB NVMe SSD(系统) + 10TB HDD(数据)
- 网络: 万兆内网 + 负载均衡

4.1.2 软件环境安装

# 1. 安装 Docker 和 Docker Compose
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

# 2. 安装 NVIDIA Docker(GPU 支持)
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | \
  sudo tee /etc/apt/sources.list.d/nvidia-docker.list

sudo apt update
sudo apt install -y nvidia-docker2
sudo systemctl restart docker

# 3. 验证 GPU 支持
docker run --rm --gpus all nvidia/cuda:11.8.0-base-ubuntu22.04 nvidia-smi

# 4. 安装 Nginx
sudo apt install -y nginx

# 5. 安装 PostgreSQL(或使用 Docker)
sudo apt install -y postgresql postgresql-contrib

4.2 阶段二:核心组件部署(3-5 天)

完整 Docker Compose 配置

创建 docker-compose.yml

version: '3.8'

services:
  # ===== 数据库层 =====
  
  # PostgreSQL(存储应用数据)
  postgres:
    image: postgres:15-alpine
    container_name: ai-postgres
    environment:
      POSTGRES_USER: aiuser
      POSTGRES_PASSWORD: strong_password_123
      POSTGRES_DB: ai_platform
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U aiuser"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  # Redis(缓存)
  redis:
    image: redis:7-alpine
    container_name: ai-redis
    command: redis-server --requirepass redis_password_123
    volumes:
      - redis_data:/data
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 5
    restart: unless-stopped

  # ===== 向量数据库 =====
  
  qdrant:
    image: qdrant/qdrant:latest
    container_name: ai-qdrant
    ports:
      - "6333:6333"
      - "6334:6334"
    volumes:
      - qdrant_data:/qdrant/storage
    environment:
      - QDRANT__SERVICE__HTTP_PORT=6333
      - QDRANT__SERVICE__GRPC_PORT=6334
    restart: unless-stopped

  # ===== 大模型服务 =====
  
  ollama:
    image: ollama/ollama:latest
    container_name: ai-ollama
    ports:
      - "11434:11434"
    volumes:
      - ollama_data:/root/.ollama
    environment:
      - OLLAMA_KEEP_ALIVE=24h
      - OLLAMA_NUM_PARALLEL=4
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    restart: unless-stopped

  # ===== Dify 平台(可选)=====
  
  # Dify API 服务
  dify-api:
    image: langgenius/dify-api:latest
    container_name: dify-api
    environment:
      - MODE=api
      - LOG_LEVEL=INFO
      - DB_USERNAME=aiuser
      - DB_PASSWORD=strong_password_123
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_DATABASE=dify
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - REDIS_PASSWORD=redis_password_123
      - CELERY_BROKER_URL=redis://:redis_password_123@redis:6379/1
      - VECTOR_STORE=qdrant
      - QDRANT_URL=http://qdrant:6333
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    volumes:
      - dify_storage:/app/storage
    ports:
      - "5001:5001"
    restart: unless-stopped

  # Dify Worker(后台任务)
  dify-worker:
    image: langgenius/dify-api:latest
    container_name: dify-worker
    environment:
      - MODE=worker
      - DB_USERNAME=aiuser
      - DB_PASSWORD=strong_password_123
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_DATABASE=dify
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - REDIS_PASSWORD=redis_password_123
      - CELERY_BROKER_URL=redis://:redis_password_123@redis:6379/1
    depends_on:
      - dify-api
    volumes:
      - dify_storage:/app/storage
    restart: unless-stopped

  # Dify Web UI
  dify-web:
    image: langgenius/dify-web:latest
    container_name: dify-web
    environment:
      - CONSOLE_API_URL=http://your-internal-ip:5001
      - APP_API_URL=http://your-internal-ip:5001
    ports:
      - "3000:3000"
    depends_on:
      - dify-api
    restart: unless-stopped

  # ===== 应用服务层 =====
  
  # 后端服务
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: ai-backend
    environment:
      - NODE_ENV=production
      - PORT=3001
      
      # Ollama 配置
      - MODEL_PROVIDER=ollama
      - MODEL_BASE_URL=http://ollama:11434/v1
      - MODEL_NAME=qwen2.5:7b
      - EMBEDDING_BASE_URL=http://ollama:11434/v1
      - EMBEDDING_MODEL=bge-m3
      
      # 数据库配置
      - DATABASE_URL=postgresql://aiuser:strong_password_123@postgres:5432/ai_platform
      - REDIS_URL=redis://:redis_password_123@redis:6379/0
      
      # 向量数据库配置
      - QDRANT_URL=http://qdrant:6333
      
      # Dify 配置(可选)
      - DIFY_API_URL=http://dify-api:5001
    depends_on:
      - postgres
      - redis
      - qdrant
      - ollama
    volumes:
      - ./backend/uploads:/app/uploads
    ports:
      - "3001:3001"
    restart: unless-stopped

  # 前端服务
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: ai-frontend
    environment:
      - VITE_API_BASE_URL=http://your-internal-ip:3001/api
    ports:
      - "5173:80"
    depends_on:
      - backend
    restart: unless-stopped

  # ===== 监控层 =====
  
  # Prometheus(指标收集)
  prometheus:
    image: prom/prometheus:latest
    container_name: ai-prometheus
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
    ports:
      - "9090:9090"
    restart: unless-stopped

  # Grafana(可视化)
  grafana:
    image: grafana/grafana:latest
    container_name: ai-grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin123
      - GF_INSTALL_PLUGINS=redis-datasource
    volumes:
      - grafana_data:/var/lib/grafana
      - ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards
      - ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources
    ports:
      - "3005:3000"
    depends_on:
      - prometheus
    restart: unless-stopped

  # ===== 负载均衡 =====
  
  nginx:
    image: nginx:alpine
    container_name: ai-nginx
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      - frontend
      - backend
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:
  qdrant_data:
  ollama_data:
  dify_storage:
  prometheus_data:
  grafana_data:

networks:
  default:
    name: ai-platform
    driver: bridge

启动服务

# 1. 创建必要的目录
mkdir -p monitoring/grafana/{dashboards,datasources}
mkdir -p nginx

# 2. 启动所有服务
docker compose up -d

# 3. 查看服务状态
docker compose ps

# 4. 下载 Ollama 模型
docker exec -it ai-ollama ollama pull qwen2.5:7b
docker exec -it ai-ollama ollama pull bge-m3

# 5. 初始化数据库(Dify)
docker exec -it dify-api flask db upgrade

# 6. 查看日志
docker compose logs -f backend

4.3 阶段三:应用配置(1-2 天)

4.3.1 后端环境配置

创建 backend/.env.production

# ===== 运行环境 =====
NODE_ENV=production
PORT=3001

# ===== 大模型配置 =====
MODEL_PROVIDER=ollama
MODEL_BASE_URL=http://ollama:11434/v1
MODEL_NAME=qwen2.5:7b
MODEL_TEMPERATURE=0.7
MODEL_MAX_TOKENS=2000

# ===== Embedding 配置 =====
EMBEDDING_PROVIDER=ollama
EMBEDDING_BASE_URL=http://ollama:11434/v1
EMBEDDING_MODEL=bge-m3

# ===== 数据库配置 =====
DATABASE_URL=postgresql://aiuser:strong_password_123@postgres:5432/ai_platform

# ===== Redis 配置 =====
REDIS_URL=redis://:redis_password_123@redis:6379/0

# ===== 向量数据库配置 =====
VECTOR_DB_TYPE=qdrant
QDRANT_URL=http://qdrant:6333
QDRANT_API_KEY=  # 可选

# ===== Dify 配置(可选)=====
DIFY_API_URL=http://dify-api:5001

# ===== 文件存储配置 =====
UPLOAD_DIR=./uploads
MAX_FILE_SIZE=50mb

# ===== 安全配置 =====
JWT_SECRET=your-jwt-secret-key-change-this
SESSION_SECRET=your-session-secret-change-this

# ===== 监控配置 =====
ENABLE_METRICS=true
METRICS_PORT=9091

4.3.2 Nginx 配置

创建 nginx/nginx.conf

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    # Gzip 压缩
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;

    # 上游服务
    upstream backend {
        server backend:3001 max_fails=3 fail_timeout=30s;
        keepalive 32;
    }

    upstream frontend {
        server frontend:80 max_fails=3 fail_timeout=30s;
        keepalive 32;
    }

    upstream dify_web {
        server dify-web:3000 max_fails=3 fail_timeout=30s;
        keepalive 32;
    }

    # 后端 API 服务
    server {
        listen 80;
        server_name api.your-company.internal;  # 修改为你的内部域名

        client_max_body_size 50M;

        # API 路由
        location /api/ {
            proxy_pass http://backend;
            proxy_http_version 1.1;
            
            # 保持连接
            proxy_set_header Connection "";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            # SSE 流式输出配置
            proxy_buffering off;
            proxy_cache off;
            proxy_read_timeout 300s;
            proxy_connect_timeout 75s;

            # 添加 CORS 头(如果需要)
            add_header Access-Control-Allow-Origin * always;
            add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
            add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;

            if ($request_method = OPTIONS) {
                return 204;
            }
        }

        # 健康检查
        location /health {
            proxy_pass http://backend;
            access_log off;
        }
    }

    # 前端服务
    server {
        listen 80;
        server_name ai.your-company.internal;  # 修改为你的内部域名

        location / {
            proxy_pass http://frontend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            # 缓存静态资源
            location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
                expires 1y;
                add_header Cache-Control "public, immutable";
            }
        }
    }

    # Dify Web UI(可选)
    server {
        listen 80;
        server_name dify.your-company.internal;

        location / {
            proxy_pass http://dify_web;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }

    # Grafana 监控(可选)
    server {
        listen 80;
        server_name monitoring.your-company.internal;

        location / {
            proxy_pass http://grafana:3000;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

4.3.3 创建 Dockerfile

后端 Dockerfile

# backend/Dockerfile
FROM node:18-alpine

WORKDIR /app

# 安装依赖
COPY package*.json ./
RUN npm ci --only=production

# 复制源代码
COPY . .

# 创建上传目录
RUN mkdir -p uploads

# 暴露端口
EXPOSE 3001

# 启动应用
CMD ["node", "src/server.js"]

前端 Dockerfile

# frontend/Dockerfile
FROM node:18-alpine AS builder

WORKDIR /app

# 安装依赖
COPY package*.json ./
RUN npm ci

# 复制源代码并构建
COPY . .
RUN npm run build

# 生产镜像
FROM nginx:alpine

# 复制构建产物
COPY --from=builder /app/dist /usr/share/nginx/html

# 复制 Nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

前端 nginx.conf

# frontend/nginx.conf
server {
    listen 80;
    server_name localhost;
    root /usr/share/nginx/html;
    index index.html;

    # SPA 路由支持
    location / {
        try_files $uri $uri/ /index.html;
    }

    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # 禁用缓存 HTML
    location ~* \.html$ {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
    }

    # Gzip 压缩
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
}

4.4 阶段四:测试验证(2-3 天)

4.4.1 健康检查脚本

创建 scripts/health-check.sh

#!/bin/bash

# 颜色定义
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

echo "======================================"
echo "   AI 平台健康检查"
echo "======================================"

# 检查 Docker 服务
check_service() {
    local service_name=$1
    local port=$2
    local endpoint=$3

    echo -n "检查 $service_name... "
    
    if curl -sf "http://localhost:$port$endpoint" > /dev/null 2>&1; then
        echo -e "${GREEN}✓ 正常${NC}"
        return 0
    else
        echo -e "${RED}✗ 异常${NC}"
        return 1
    fi
}

# 检查各服务
check_service "PostgreSQL" 5432 ""  # TCP 端口检查
check_service "Redis" 6379 ""
check_service "Qdrant" 6333 "/healthz"
check_service "Ollama" 11434 "/api/tags"
check_service "后端 API" 3001 "/health"
check_service "Dify API" 5001 "/health"  # 可选
check_service "前端服务" 5173 "/"

echo ""
echo "======================================"
echo "   模型检查"
echo "======================================"

# 检查 Ollama 模型
echo "已安装的 Ollama 模型:"
docker exec ai-ollama ollama list

echo ""
echo "======================================"
echo "   访问地址"
echo "======================================"
echo "前端应用: http://localhost:5173"
echo "后端 API: http://localhost:3001"
echo "Dify UI:  http://localhost:3000"
echo "Grafana:  http://localhost:3005 (admin/admin123)"
echo "Prometheus: http://localhost:9090"
echo "Qdrant Dashboard: http://localhost:6333/dashboard"
echo "======================================"
# 赋予执行权限
chmod +x scripts/health-check.sh

# 执行健康检查
./scripts/health-check.sh

4.4.2 功能测试

测试 1:基础对话

# 测试后端 API
curl -X POST http://localhost:3001/api/langchain/chat \
  -H "Content-Type: application/json" \
  -d '{
    "message": "你好,请介绍一下你自己",
    "sessionId": "test-session-1"
  }'

测试 2:RAG 文档检索

# 1. 上传文档
curl -X POST http://localhost:3001/api/rag/upload \
  -F "file=@test-document.pdf"

# 2. 查询文档
curl -X POST http://localhost:3001/api/rag/query \
  -H "Content-Type: application/json" \
  -d '{
    "query": "文档的主要内容是什么?"
  }'

测试 3:工作流执行

curl -X POST http://localhost:3001/api/langchain/workflow \
  -H "Content-Type: application/json" \
  -d '{
    "workflowId": "test-workflow",
    "inputs": {
      "topic": "人工智能"
    }
  }'

4.5 阶段五:生产优化(持续)

4.5.1 性能优化

1. Ollama 性能调优

# 增加并行处理能力
docker exec -it ai-ollama sh -c 'echo "export OLLAMA_NUM_PARALLEL=8" >> ~/.bashrc'

# 调整上下文长度(根据显存)
docker exec -it ai-ollama sh -c 'echo "export OLLAMA_MAX_LOADED_MODELS=3" >> ~/.bashrc'

# 重启 Ollama
docker restart ai-ollama

2. Qdrant 性能调优

// 优化索引参数
await client.updateCollection('documents', {
  optimizers_config: {
    default_segment_number: 4,      // 增加段数提升并发
    max_segment_size: 200000,        // 单段最大向量数
    memmap_threshold: 50000,         // 内存映射阈值
    indexing_threshold: 20000,       // 索引阈值
    flush_interval_sec: 5,           // 刷新间隔
  },
  hnsw_config: {
    m: 16,                            // HNSW 参数 M(连接数)
    ef_construct: 100,                // 构建时的 ef
    full_scan_threshold: 10000,       // 全扫描阈值
  },
});

3. PostgreSQL 优化

-- 修改 PostgreSQL 配置(在容器中执行)
ALTER SYSTEM SET shared_buffers = '4GB';
ALTER SYSTEM SET effective_cache_size = '12GB';
ALTER SYSTEM SET maintenance_work_mem = '1GB';
ALTER SYSTEM SET checkpoint_completion_target = 0.9;
ALTER SYSTEM SET wal_buffers = '16MB';
ALTER SYSTEM SET default_statistics_target = 100;
ALTER SYSTEM SET random_page_cost = 1.1;
ALTER SYSTEM SET effective_io_concurrency = 200;
ALTER SYSTEM SET work_mem = '64MB';
ALTER SYSTEM SET min_wal_size = '1GB';
ALTER SYSTEM SET max_wal_size = '4GB';

-- 重启 PostgreSQL

4.5.2 监控配置

Prometheus 配置monitoring/prometheus.yml):

global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  # Node Exporter(系统指标)
  - job_name: 'node'
    static_configs:
      - targets: ['node-exporter:9100']

  # 后端服务指标
  - job_name: 'backend'
    static_configs:
      - targets: ['backend:9091']

  # Ollama 指标(如果支持)
  - job_name: 'ollama'
    static_configs:
      - targets: ['ollama:11434']

  # Qdrant 指标
  - job_name: 'qdrant'
    static_configs:
      - targets: ['qdrant:6333']
    metrics_path: '/metrics'

  # PostgreSQL Exporter
  - job_name: 'postgres'
    static_configs:
      - targets: ['postgres-exporter:9187']

  # Redis Exporter
  - job_name: 'redis'
    static_configs:
      - targets: ['redis-exporter:9121']

Grafana 监控面板monitoring/grafana/dashboards/ai-platform.json):

主要监控指标:

  • 系统指标:CPU、内存、磁盘、网络
  • 应用指标:QPS、响应时间、错误率
  • 模型指标:推理耗时、Token 使用量
  • 数据库指标:连接数、慢查询、缓存命中率
  • 向量库指标:搜索延迟、索引大小

5. 成本与资源评估

5.1 硬件成本

配置等级 适用场景 硬件成本(一次性) 月度成本
入门级 10 并发,7B 模型 ¥30,000 ¥500(电费)
标准级 50 并发,14B 模型 ¥80,000 ¥1,500
企业级 200 并发,32B 模型 ¥200,000 ¥5,000
旗舰级 1000+ 并发,多模型 ¥500,000+ ¥15,000+

5.2 软件成本

组件 开源方案 商业方案 年度成本
Dify 社区版(免费) 企业版 ¥0 / ¥100,000+
Ollama 开源免费 - ¥0
Qdrant 开源免费 云托管版 ¥0 / ¥50,000+
监控系统 Prometheus + Grafana Datadog / New Relic ¥0 / ¥80,000+

总结:采用全开源方案,软件成本为 ¥0

5.3 人力成本

角色 人数 职责 月薪估算
架构师 1 系统设计、技术选型 ¥40,000
后端工程师 2 API 开发、模型集成 ¥25,000 × 2
前端工程师 1 UI/UX 开发 ¥20,000
运维工程师 1 部署、监控、运维 ¥22,000
算法工程师 1 模型调优、RAG 优化 ¥35,000

总计:约 ¥167,000/月(开发期)

运维期:1-2 人即可维护,约 ¥30,000-60,000/月

5.4 与云服务对比

假设:每天 10,000 次对话,平均每次 1000 tokens

方案 月度成本 年度成本 备注
云端 API(OpenAI / SiliconFlow) ¥50,000+ ¥600,000+ 按调用量计费
私有化部署(硬件 + 运维) ¥10,000 ¥120,000 一次性硬件投入后成本低

结论

  • 小规模(< 1000 次/天):云端 API 更划算
  • 中等规模(1000-10000 次/天):6-12 个月回本
  • 大规模(> 10000 次/天):私有化部署节省 80%+ 成本

6. 安全与合规

6.1 数据安全

6.1.1 网络隔离

┌─────────────────────────────────────────┐
│            企业内网                       │
│  ┌──────────────────────────────────┐   │
│  │     DMZ 区(对外服务)             │   │
│  │  - Nginx 反向代理                 │   │
│  │  - 前端服务                       │   │
│  └──────────┬───────────────────────┘   │
│             │                            │
│  ┌──────────▼───────────────────────┐   │
│  │     应用区(核心业务)             │   │
│  │  - 后端 API                       │   │
│  │  - LangChain 引擎                 │   │
│  └──────────┬───────────────────────┘   │
│             │                            │
│  ┌──────────▼───────────────────────┐   │
│  │     数据区(敏感数据)             │   │
│  │  - PostgreSQL                     │   │
│  │  - Qdrant                         │   │
│  │  - Ollama(模型)                  │   │
│  │  ⚠️  禁止外网访问                  │   │
│  └──────────────────────────────────┘   │
│                                          │
│  🔒 防火墙规则:                          │
│  - DMZ → 应用:允许                      │
│  - 应用 → 数据:允许                     │
│  - 外网 → 数据:禁止                     │
└─────────────────────────────────────────┘

6.1.2 访问控制

Nginx 配置

# 限制 IP 访问(仅允许内网)
location /api/ {
    allow 10.0.0.0/8;       # 内网 A 类地址
    allow 172.16.0.0/12;    # 内网 B 类地址
    allow 192.168.0.0/16;   # 内网 C 类地址
    deny all;

    proxy_pass http://backend;
}

# 限制请求频率(防止滥用)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;

location /api/chat {
    limit_req zone=api_limit burst=20 nodelay;
    proxy_pass http://backend;
}

应用层鉴权

// backend/src/middleware/auth.js
import jwt from 'jsonwebtoken';

export const authenticate = (req, res, next) => {
  const token = req.headers.authorization?.replace('Bearer ', '');
  
  if (!token) {
    return res.status(401).json({ error: 'Unauthorized' });
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    return res.status(401).json({ error: 'Invalid token' });
  }
};

// 使用鉴权中间件
app.use('/api/chat', authenticate, chatRoutes);

6.1.3 数据加密

传输加密(TLS/SSL):

# 生成自签名证书(测试用)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout nginx/ssl/private.key \
  -out nginx/ssl/certificate.crt \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=YourCompany/CN=ai.your-company.internal"

# 生产环境使用正式证书(Let's Encrypt 或购买)

Nginx HTTPS 配置

server {
    listen 443 ssl http2;
    server_name ai.your-company.internal;

    ssl_certificate /etc/nginx/ssl/certificate.crt;
    ssl_certificate_key /etc/nginx/ssl/private.key;
    
    # SSL 优化
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # HSTS(强制 HTTPS)
    add_header Strict-Transport-Security "max-age=31536000" always;

    location / {
        proxy_pass http://frontend;
    }
}

# HTTP 自动跳转 HTTPS
server {
    listen 80;
    server_name ai.your-company.internal;
    return 301 https://$server_name$request_uri;
}

数据库加密

# PostgreSQL 配置(docker-compose.yml)
postgres:
  image: postgres:15-alpine
  environment:
    # 启用数据加密
    - POSTGRES_INITDB_ARGS=--data-checksums
  volumes:
    # 使用加密卷
    - type: volume
      source: postgres_data
      target: /var/lib/postgresql/data
      volume:
        nocopy: true
  command:
    - postgres
    - -c
    - ssl=on
    - -c
    - ssl_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
    - -c
    - ssl_key_file=/etc/ssl/private/ssl-cert-snakeoil.key

6.1.4 敏感数据脱敏

// backend/src/utils/security.js

/**
 * 脱敏手机号
 */
export function maskPhone(phone) {
  return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
}

/**
 * 脱敏身份证号
 */
export function maskIdCard(idCard) {
  return idCard.replace(/(\d{6})\d{8}(\d{4})/, '$1********$2');
}

/**
 * 脱敏邮箱
 */
export function maskEmail(email) {
  return email.replace(/(.{2}).+(@.+)/, '$1***$2');
}

/**
 * 日志脱敏(自动识别敏感字段)
 */
export function maskSensitiveData(data) {
  const sensitiveFields = ['password', 'apiKey', 'token', 'secret', 'phone', 'idCard', 'email'];
  
  if (typeof data !== 'object') return data;
  
  const masked = { ...data };
  for (const key of Object.keys(masked)) {
    if (sensitiveFields.some(field => key.toLowerCase().includes(field))) {
      masked[key] = '***';
    }
  }
  return masked;
}

6.2 合规性

6.2.1 审计日志

// backend/src/middleware/auditLog.js
import fs from 'fs';
import path from 'path';

export const auditLogger = (req, res, next) => {
  const logEntry = {
    timestamp: new Date().toISOString(),
    user: req.user?.id || 'anonymous',
    ip: req.ip,
    method: req.method,
    path: req.path,
    userAgent: req.get('user-agent'),
    body: maskSensitiveData(req.body),
  };

  // 写入审计日志文件
  const logFile = path.join(__dirname, '../../logs/audit.log');
  fs.appendFileSync(logFile, JSON.stringify(logEntry) + '\n');

  next();
};

// 使用审计日志
app.use('/api/*', auditLogger);

6.2.2 数据留存策略

-- 定期清理过期数据(PostgreSQL)
CREATE OR REPLACE FUNCTION cleanup_old_data()
RETURNS void AS $$
BEGIN
  -- 删除 90 天前的对话记录
  DELETE FROM conversations WHERE created_at < NOW() - INTERVAL '90 days';
  
  -- 删除 30 天前的审计日志
  DELETE FROM audit_logs WHERE created_at < NOW() - INTERVAL '30 days';
  
  -- 删除 7 天前的临时文件记录
  DELETE FROM temp_files WHERE created_at < NOW() - INTERVAL '7 days';
END;
$$ LANGUAGE plpgsql;

-- 定时任务(每天凌晨 2 点执行)
CREATE EXTENSION IF NOT EXISTS pg_cron;
SELECT cron.schedule('cleanup-old-data', '0 2 * * *', 'SELECT cleanup_old_data()');

6.3 模型安全

6.3.1 输入验证

// backend/src/middleware/validation.js

/**
 * 聊天输入验证
 */
export const validateChatInput = (req, res, next) => {
  const { message } = req.body;

  // 1. 检查输入长度
  if (!message || message.length === 0) {
    return res.status(400).json({ error: '消息不能为空' });
  }
  if (message.length > 10000) {
    return res.status(400).json({ error: '消息长度不能超过 10000 字符' });
  }

  // 2. 检查恶意内容(SQL 注入、XSS)
  const sqlInjectionPattern = /(\bDROP\b|\bDELETE\b|\bINSERT\b|\bUPDATE\b|\bEXEC\b)/i;
  const xssPattern = /<script[^>]*>.*?<\/script>/gi;
  
  if (sqlInjectionPattern.test(message) || xssPattern.test(message)) {
    return res.status(400).json({ error: '检测到非法输入' });
  }

  // 3. 敏感词过滤(根据企业需求)
  const bannedWords = ['敏感词1', '敏感词2'];  // 实际使用时从配置文件加载
  for (const word of bannedWords) {
    if (message.includes(word)) {
      return res.status(400).json({ error: '输入包含敏感内容' });
    }
  }

  next();
};

app.post('/api/chat', validateChatInput, chatController);

6.3.2 输出过滤

// backend/src/utils/contentFilter.js

/**
 * 过滤模型输出中的敏感信息
 */
export function filterOutput(text) {
  // 1. 脱敏手机号
  text = text.replace(/1[3-9]\d{9}/g, (match) => maskPhone(match));

  // 2. 脱敏身份证号
  text = text.replace(/\d{17}[\dXx]/g, (match) => maskIdCard(match));

  // 3. 脱敏邮箱
  text = text.replace(/[\w.-]+@[\w.-]+\.\w+/g, (match) => maskEmail(match));

  // 4. 移除可能的 API Key
  text = text.replace(/\b(sk-[a-zA-Z0-9]{32,})\b/g, '***API_KEY***');

  return text;
}

7. 监控与运维

7.1 监控指标

7.1.1 系统指标

指标类型 监控项 告警阈值
CPU 使用率 > 80%
内存 使用率 > 85%
磁盘 使用率、IOPS > 90%, < 100 IOPS
网络 带宽、连接数 > 80% 带宽
GPU 显存、利用率 > 90% 显存

7.1.2 应用指标

指标类型 监控项 告警阈值
QPS 每秒请求数 -
延迟 P50/P95/P99 响应时间 P95 > 5s
错误率 5xx 错误比例 > 1%
并发 活跃连接数 -

7.1.3 业务指标

指标类型 监控项 告警阈值
模型推理 平均耗时、Token 使用量 -
RAG 检索 检索耗时、命中率 P95 > 1s
会话 活跃会话数、平均长度 -
文件上传 成功率、平均大小 成功率 < 95%

7.2 日志管理

7.2.1 日志收集(ELK Stack)

# docker-compose.logging.yml
version: '3.8'

services:
  # Elasticsearch(日志存储)
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.9.0
    container_name: elasticsearch
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms2g -Xmx2g"
    volumes:
      - es_data:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"
    restart: unless-stopped

  # Logstash(日志处理)
  logstash:
    image: docker.elastic.co/logstash/logstash:8.9.0
    container_name: logstash
    volumes:
      - ./logstash/pipeline:/usr/share/logstash/pipeline
      - ./logs:/logs:ro
    ports:
      - "5044:5044"
    depends_on:
      - elasticsearch
    restart: unless-stopped

  # Kibana(日志可视化)
  kibana:
    image: docker.elastic.co/kibana/kibana:8.9.0
    container_name: kibana
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch
    restart: unless-stopped

volumes:
  es_data:

Logstash 配置logstash/pipeline/logstash.conf):

input {
  file {
    path => "/logs/*.log"
    start_position => "beginning"
    codec => json
  }
}

filter {
  # 解析应用日志
  if [type] == "app" {
    grok {
      match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:message}" }
    }
    date {
      match => [ "timestamp", "ISO8601" ]
    }
  }

  # 解析审计日志
  if [type] == "audit" {
    json {
      source => "message"
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "ai-platform-%{+YYYY.MM.dd}"
  }
  stdout {
    codec => rubydebug
  }
}

7.2.2 日志轮转

# backend/logrotate.conf
/app/logs/*.log {
  daily                    # 每天轮转
  missingok                # 文件丢失不报错
  rotate 30                # 保留 30 天
  compress                 # 压缩旧日志
  delaycompress            # 延迟压缩
  notifempty               # 空文件不轮转
  create 0640 node node    # 创建新文件的权限
  sharedscripts
  postrotate
    docker exec ai-backend kill -USR1 $(cat /var/run/backend.pid) 2>/dev/null || true
  endscript
}

7.3 备份策略

7.3.1 数据库备份

#!/bin/bash
# scripts/backup-database.sh

BACKUP_DIR="/backup/postgres"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/backup_$DATE.sql.gz"

# 创建备份目录
mkdir -p $BACKUP_DIR

# 备份 PostgreSQL
docker exec ai-postgres pg_dumpall -U aiuser | gzip > $BACKUP_FILE

# 上传到对象存储(可选)
# aws s3 cp $BACKUP_FILE s3://your-backup-bucket/postgres/

# 删除 30 天前的备份
find $BACKUP_DIR -name "backup_*.sql.gz" -mtime +30 -delete

echo "数据库备份完成: $BACKUP_FILE"

7.3.2 向量数据库备份

#!/bin/bash
# scripts/backup-qdrant.sh

BACKUP_DIR="/backup/qdrant"
DATE=$(date +%Y%m%d_%H%M%S)

# 创建快照
curl -X POST 'http://localhost:6333/collections/documents/snapshots' \
  -H 'Content-Type: application/json'

# 下载快照
SNAPSHOT_NAME=$(curl 'http://localhost:6333/collections/documents/snapshots' | jq -r '.result[0].name')
curl "http://localhost:6333/collections/documents/snapshots/$SNAPSHOT_NAME" \
  --output "$BACKUP_DIR/snapshot_$DATE.snapshot"

echo "Qdrant 备份完成: $BACKUP_DIR/snapshot_$DATE.snapshot"

7.3.3 定时备份(Cron)

# 编辑 crontab
crontab -e

# 添加定时任务
# 每天凌晨 1 点备份数据库
0 1 * * * /path/to/scripts/backup-database.sh >> /var/log/backup.log 2>&1

# 每天凌晨 2 点备份 Qdrant
0 2 * * * /path/to/scripts/backup-qdrant.sh >> /var/log/backup.log 2>&1

# 每周日凌晨 3 点备份整个 Docker 卷
0 3 * * 0 /path/to/scripts/backup-volumes.sh >> /var/log/backup.log 2>&1

7.4 故障恢复

7.4.1 数据库恢复

# 恢复 PostgreSQL
docker exec -i ai-postgres psql -U aiuser < backup_20240126.sql

# 或者使用 pg_restore
docker exec -i ai-postgres pg_restore -U aiuser -d ai_platform < backup.dump

7.4.2 Qdrant 恢复

# 上传快照
curl -X POST 'http://localhost:6333/collections/documents/snapshots/upload' \
  --form 'snapshot=@snapshot_20240126.snapshot'

# 从快照恢复
curl -X PUT 'http://localhost:6333/collections/documents/snapshots/snapshot_20240126/recover'

7.4.3 灾难恢复计划

故障类型 恢复时间目标(RTO) 恢复点目标(RPO) 恢复步骤
单容器故障 < 5 分钟 0 Docker 自动重启
数据库故障 < 30 分钟 24 小时 从备份恢复
整体服务故障 < 2 小时 24 小时 重新部署 + 数据恢复
物理服务器故障 < 4 小时 24 小时 切换到备用服务器

8. 常见问题

8.1 部署相关

Q: Ollama 模型下载失败怎么办?

A: 如果网络不稳定,可以使用镜像或手动下载:

# 方法 1:使用国内镜像(如果有)
export OLLAMA_MIRRORS="https://your-mirror.com"
ollama pull qwen2.5:7b

# 方法 2:手动下载模型文件
# 1. 从 ModelScope 下载模型
# 2. 创建 Ollama Modelfile
cat > Modelfile << EOF
FROM /path/to/model
TEMPLATE """{{ .System }}
{{ .Prompt }}"""
PARAMETER stop "<|im_end|>"
EOF

# 3. 导入模型
ollama create qwen2.5:7b -f Modelfile

Q: GPU 显存不足怎么办?

A: 可以使用量化模型或调整参数:

# 方法 1:使用量化模型(Q4、Q5)
ollama pull qwen2.5:7b-q4_0  # 4-bit 量化,显存需求减半

# 方法 2:限制上下文长度
export OLLAMA_MAX_LOADED_MODELS=1
export OLLAMA_NUM_PARALLEL=2

# 方法 3:使用 CPU(较慢)
docker run -d -v ollama:/root/.ollama -p 11434:11434 \
  --name ollama ollama/ollama

Q: Qdrant 内存占用过高?

A: 调整配置以使用磁盘存储:

qdrant:
  environment:
    - QDRANT__STORAGE__STORAGE_PATH=/qdrant/storage
    - QDRANT__STORAGE__PERFORMANCE__MAX_SEGMENT_SIZE_KB=200000
    - QDRANT__STORAGE__OPTIMIZERS__MEMMAP_THRESHOLD_KB=50000

8.2 性能相关

Q: 推理速度慢怎么优化?

A: 多方面优化:

  1. 模型层面

    • 使用更小的模型(7B → 3B)
    • 使用量化模型(Q4_0、Q4_K_M)
    • 启用 GPU 加速
  2. 系统层面

    • 增加 OLLAMA_NUM_PARALLEL
    • 使用 vLLM 替代 Ollama
    • 启用批处理
  3. 架构层面

    • 添加 Redis 缓存常见问题
    • 使用负载均衡分发请求
    • 部署多个模型实例

Q: RAG 检索准确率低?

A: 优化检索策略:

// 1. 优化 Chunk 大小
const chunkSize = 500;  // 调整为 300-800 字符

// 2. 增加重叠窗口
const chunkOverlap = 100;

// 3. 混合检索(向量 + 关键词)
const results = await Promise.all([
  vectorSearch(query, { limit: 10 }),
  keywordSearch(query, { limit: 5 }),
]);

// 4. 重排序(Reranking)
const reranked = await rerankModel.rank(query, results);

// 5. 调整检索参数
const searchResults = await qdrant.search('documents', {
  vector: queryEmbedding,
  limit: 20,              // 增加初始检索数量
  score_threshold: 0.7,   // 设置相似度阈值
});

8.3 安全相关

Q: 如何防止数据泄露?

A: 多层防护:

  1. 网络隔离:数据库仅内网访问
  2. 访问控制:JWT 鉴权 + IP 白名单
  3. 数据脱敏:日志和输出自动脱敏
  4. 审计日志:记录所有敏感操作
  5. 加密传输:全站 HTTPS
  6. 定期审计:安全扫描和渗透测试

Q: 如何防止模型被滥用?

A: 限流和监控:

// 1. 基于用户的限流
const rateLimiter = new RateLimiter({
  tokensPerInterval: 100,  // 每小时 100 次
  interval: 'hour',
});

// 2. 基于 IP 的限流(Nginx)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

// 3. 异常检测
if (requestCount > threshold) {
  sendAlert('可能存在滥用行为');
}

// 4. 内容审核
const auditResult = await contentAudit(userInput);
if (auditResult.risk > 0.8) {
  rejectRequest();
}

8.4 运维相关

Q: 如何滚动更新服务?

A: 使用 Docker Compose 滚动更新:

# 1. 拉取最新镜像
docker compose pull backend frontend

# 2. 滚动更新(不中断服务)
docker compose up -d --no-deps --build backend
docker compose up -d --no-deps --build frontend

# 3. 验证新版本
curl http://localhost:3001/health

# 4. 如果有问题,回滚到旧版本
docker compose up -d --no-deps backend:old-tag

Q: 如何扩容?

A: 水平扩展:

# docker-compose.yml
services:
  backend:
    image: ai-backend:latest
    deploy:
      replicas: 3  # 部署 3 个实例
    # ...

  ollama:
    image: ollama/ollama:latest
    deploy:
      replicas: 2  # 部署 2 个模型实例
    # ...

配置 Nginx 负载均衡:

upstream backend {
    least_conn;  # 最少连接算法
    server backend-1:3001 max_fails=3 fail_timeout=30s;
    server backend-2:3001 max_fails=3 fail_timeout=30s;
    server backend-3:3001 max_fails=3 fail_timeout=30s;
}

upstream ollama {
    ip_hash;  # 同一用户路由到同一实例(保持会话)
    server ollama-1:11434;
    server ollama-2:11434;
}

9. 总结与建议

9.1 推荐部署方案

根据不同规模的企业,推荐以下方案:

小型企业(< 50 人)

方案:轻量级部署
- 大模型:Ollama + Qwen2.5:7b
- 工作流:LangChain(已集成)
- 向量库:Qdrant
- 部署方式:单机 Docker Compose
- 硬件:1 台服务器(8核 32GB + RTX 3090)
- 成本:硬件 ¥30,000 + 运维 1 人

中型企业(50-500 人)

方案:标准化部署
- 大模型:Ollama + Qwen2.5:14b
- 工作流:Dify 社区版 + LangChain
- 向量库:Qdrant(集群模式)
- 部署方式:Docker Compose + Nginx
- 硬件:2 台服务器(主备)
- 成本:硬件 ¥80,000 + 运维 1-2 人

大型企业(> 500 人)

方案:企业级高可用部署
- 大模型:vLLM + Qwen2.5:32b(多实例)
- 工作流:Dify 企业版 / 自研
- 向量库:Milvus(分布式)
- 部署方式:Kubernetes + Helm
- 硬件:5+ 台服务器(集群)
- 成本:硬件 ¥200,000+ + 运维团队

9.2 实施路线图

第 1 周:基础环境搭建

  • ✅ 硬件采购和机房部署
  • ✅ 操作系统和 Docker 安装
  • ✅ 网络配置和防火墙设置

第 2-3 周:核心组件部署

  • ✅ Ollama 模型部署和测试
  • ✅ Qdrant 向量数据库配置
  • ✅ PostgreSQL 数据库初始化
  • ✅ Dify 平台部署(可选)

第 4 周:应用迁移

  • ✅ 后端配置迁移到私有化环境
  • ✅ 前端 API 地址更新
  • ✅ 功能测试和性能调优

第 5-6 周:监控和安全

  • ✅ Prometheus + Grafana 监控部署
  • ✅ 日志收集和分析系统
  • ✅ 安全加固和渗透测试
  • ✅ 备份和恢复演练

第 7 周:试运行

  • ✅ 小范围用户试用
  • ✅ 收集反馈和问题修复
  • ✅ 性能优化和容量规划

第 8 周:正式上线

  • ✅ 全员开放使用
  • ✅ 培训和文档交付
  • ✅ 运维交接

9.3 关键成功因素

  1. 选择合适的模型

    • 根据业务需求选择模型大小
    • 中文业务优先选择 Qwen 系列
    • 平衡性能和资源消耗
  2. 充分的测试

    • 压力测试(并发、长时间运行)
    • 功能测试(各场景覆盖)
    • 安全测试(渗透、数据泄露)
  3. 完善的监控

    • 实时监控系统和应用指标
    • 及时发现和处理异常
    • 定期生成运营报告
  4. 持续优化

    • 根据使用情况调整资源配置
    • 定期更新模型和组件
    • 收集用户反馈持续改进

9.4 下一步行动

  1. 立即可做

    • ✅ 评估现有硬件资源
    • ✅ 确定部署规模和方案
    • ✅ 申请预算和采购硬件
  2. 短期规划(1-2 个月):

    • ✅ 搭建测试环境
    • ✅ 完成核心功能迁移
    • ✅ 小范围试用验证
  3. 中期规划(3-6 个月):

    • ✅ 正式上线投产
    • ✅ 优化性能和成本
    • ✅ 扩展高级功能
  4. 长期规划(6-12 个月):

    • ✅ 模型微调和定制化
    • ✅ 多模态能力扩展
    • ✅ 智能体和自动化工作流

10. 附录

10.1 相关文档

10.2 社区资源

10.3 技术支持

如需专业技术支持,可联系:

  • Dify 企业版支持:support@dify.ai
  • 本地部署咨询:(根据实际情况填写)

文档版本:v1.0
最后更新:2026-01-26
维护者:马年行大运


🎉 祝您部署顺利!

posted @ 2026-01-26 15:20  XiaoZhengTou  阅读(23)  评论(0)    收藏  举报