# AI-Agent

# 在阿里云创建agent实现建立各个专业智能体

这个思路是非常可行的，而且对于您这样的企业运维和技术团队来说，比单纯购买SaaS更有价值。

如果已经有阿里云账号，可以利用阿里云的：

- 百炼（Agent应用开发平台）
- 通义千问大模型
- OSS对象存储
- 函数计算FC
- ECS服务器
- RDS数据库
- 向量检索服务
- 企业知识库

构建企业级AI智能体平台。

## 一、总体架构

```text
                     企业AI门户

          ┌─────────────────────┐
          │ Flask / Vue WebUI   │
          └──────────┬──────────┘
                     │
                     ▼

          ┌─────────────────────┐
          │ 阿里云百炼 Agent平台 │
          └──────────┬──────────┘

     ┌───────────────┼───────────────┐
     ▼               ▼               ▼

写作Agent      图片Agent      合同Agent

     ▼               ▼               ▼

通义千问      通义万相       RAG知识库
Qwen3         文生图         OCR+LLM

     ▼               ▼               ▼

     OSS            OSS           企业文档库

```

---

# 二、建议建设的智能体

按照企业需求优先级排序：

## 1. 企业公文写作Agent

用途：

- 工作总结
- 周报月报
- 招投标文件
- 运维方案
- 应急预案
- 技术文档

知识库来源：

```text
公司历史文档
运维规范
项目方案
招投标文件

```

能力：

```text
输入：
项目名称

输出：
完整Word方案

```

例如：

```text
请生成：

智慧路灯平台运维方案

要求：
20页
包含架构图

```

自动生成：

```text
项目背景
实施方案
网络架构
运维保障
应急预案

```

---

## 2. 图片设计Agent

调用：

- 通义万相

用途：

```text
网站Banner
产品宣传图
公众号配图
PPT插图

```

例如：

```text
生成一张：

科技感
蓝色风格
智慧路灯云平台

```

自动输出PNG。

---

## 3. 视频生成Agent

调用：

- 通义万相视频模型

用途：

```text
企业宣传片
产品演示视频
培训视频

```

输入：

```text
智慧路灯系统
1分钟宣传视频

```

自动生成：

```text
MP4
字幕
配音

```

---

## 4. 合同审查Agent

这是企业最有价值的应用之一。

流程：

```text
上传PDF

↓
OCR识别

↓
大模型分析

↓
风险输出

```

自动识别：

```text
付款条款
违约责任
保密协议
知识产权
续约条款

```

输出：

```text
风险等级

高风险：
第12条

建议修改：
......

```

---

## 5. 运维专家Agent（强烈推荐）

结合您的专业领域。

导入：

```text
Linux知识库
MySQL知识库
MongoDB知识库
PostgreSQL知识库
Wazuh知识库
网络设备知识库

```

知识来源：

```text
Markdown
Word
PDF
Sphinx文档

```

然后实现：

```text
上传日志

↓

自动分析

↓

输出解决方案

```

例如：

```text
上传：

nf_conntrack日志

```

直接输出：

```text
连接数异常

原因：
RabbitMQ连接泄漏

建议：
...

```

这对您当前工作最有帮助。

---

## 6. 代码助手Agent

支持：

- Shell
- Python
- SQL
- Ansible
- Docker
- Kubernetes

例如：

```text
生成Ubuntu20.04安装脚本

```

自动输出：

```bash
#!/bin/bash
...

```

---

## 7. 企业知识库Agent

类似企业版ChatGPT。

上传：

```text
PDF
Word
Excel
Sphinx
Wiki

```

员工可以直接提问：

```text
VPN怎么申请？

数据库备份流程是什么？

```

智能体直接回答。

---

# 三、实施路线（推荐）

### 第一阶段（1周）

部署：

- 阿里云百炼
- 通义千问

先做：

```text
运维Agent
写作Agent

```

投入最低。

---

### 第二阶段（2周）

接入：

```text
OSS
企业知识库
向量数据库

```

实现：

```text
合同审查
企业问答

```

---

### 第三阶段（1个月）

开发统一门户：

```text
https://ai.company.com

```

菜单：

```text
AI写作
AI图片
AI视频
AI合同
AI运维
AI知识库

```

统一登录。

---

# 四、成本估算

100人以内企业：

<table id="bkmrk-%E9%A1%B9%E7%9B%AE-%E6%9C%88%E8%B4%B9%E7%94%A8-%E9%80%9A%E4%B9%89%E5%8D%83%E9%97%AEapi-200%7E1"><thead><tr><th>项目</th><th align="right">月费用</th></tr></thead><tbody><tr><td>通义千问API</td><td align="right">200~1000元</td></tr><tr><td>通义万相图片</td><td align="right">100~500元</td></tr><tr><td>视频生成</td><td align="right">300~2000元</td></tr><tr><td>OSS</td><td align="right">50元左右</td></tr><tr><td>向量数据库</td><td align="right">200~500元</td></tr><tr><td>ECS服务器</td><td align="right">200~500元</td></tr></tbody></table>

总计：

```text
约1000~5000元/月

```

相比购买多个第三方AI产品，成本通常更低，而且数据留在企业体系内。

---

结合您目前已经在使用 Ubuntu、Flask、Sphinx 知识库、MySQL/MongoDB/PostgreSQL 和运维文档体系，我建议优先建设：

1. 运维专家Agent（知识库问答+日志分析）
2. 企业文档写作Agent
3. 合同审查Agent
4. 图片设计Agent
5. 视频生成Agent

这样 1~2 个月内就能形成一个完整的企业AI平台，并且能直接服务于运维、项目实施和管理部门。

# 完全可以由运维团队主导落地企业AI智能体平台，不一定需要开发团队

以您目前的背景来看（30多年运维经验、熟悉Linux、Docker、数据库、中间件、服务器部署），**完全可以由运维团队主导落地企业AI智能体平台，不一定需要开发团队**。

不过要分阶段实施，否则容易投入几十万最后变成一个没人用的平台。

# 一、先回答核心问题

## 1. 仅靠运维人员能实现吗？

答案：

**80%能实现。**

以下场景基本不用开发：

<table id="bkmrk-%E6%99%BA%E8%83%BD%E4%BD%93-%E9%9A%BE%E5%BA%A6-%E8%BF%90%E7%BB%B4%E8%83%BD%E5%90%A6%E5%AE%8C%E6%88%90-%E5%86%99%E6%96%87%E7%AB%A0-%E2%98%85-"><thead><tr><th>智能体</th><th>难度</th><th>运维能否完成</th></tr></thead><tbody><tr><td>写文章</td><td>★</td><td>√</td></tr><tr><td>写方案</td><td>★</td><td>√</td></tr><tr><td>PPT生成</td><td>★★</td><td>√</td></tr><tr><td>图片生成</td><td>★★</td><td>√</td></tr><tr><td>视频生成</td><td>★★</td><td>√</td></tr><tr><td>文档总结</td><td>★</td><td>√</td></tr><tr><td>知识库问答</td><td>★★</td><td>√</td></tr><tr><td>运维助手</td><td>★★</td><td>√</td></tr><tr><td>SQL分析</td><td>★★</td><td>√</td></tr><tr><td>合同审查</td><td>★★★</td><td>√</td></tr><tr><td>OA流程审批</td><td>★★★★</td><td>需开发</td></tr><tr><td>ERP集成</td><td>★★★★</td><td>需开发</td></tr><tr><td>CRM集成</td><td>★★★★</td><td>需开发</td></tr></tbody></table>

所以：

### 第一阶段

完全不需要开发团队

直接用：

- 阿里云百炼Agent
- Dify
- FastGPT
- Coze
- MaxKB

即可。

---

# 二、推荐方案

如果公司规模：

### 100人以下

推荐：

## 方案A

阿里云百炼 + 企业知识库

架构：

```text
员工
  │
  ▼
阿里云百炼Agent
  │
  ├── 写文章Agent
  ├── 法务Agent
  ├── 运维Agent
  ├── HR Agent
  ├── PPT Agent
  └── 客服Agent

```

优点：

- 不用买GPU
- 不用训练模型
- 直接开通

1周内可上线

---

# 三、如果公司想做长期AI平台

我更推荐：

## 方案B

Dify + 阿里百炼API

架构：

```text
用户
 │
 ▼
Dify
 │
 ├── Qwen3
 ├── DeepSeek
 ├── Claude
 ├── GPT
 │
 └── 企业知识库

```

优点：

### 1

所有Agent统一管理

例如：

```text
AI写作助手

AI法务助手

AI运维助手

AI人事助手

AI销售助手

```

都在一个平台。

---

### 2

支持RAG知识库

上传：

```text
Word

PDF

Excel

制度文件

合同模板

运维手册

SOP文档

```

即可。

---

### 3

无需编程

拖拽式。

运维即可维护。

---

# 四、合同审查Agent怎么实现

这是很多企业最需要的。

例如上传：

```text
采购合同.pdf

```

Agent自动分析：

```text
付款条款

违约责任

风险点

知识产权

保密条款

争议解决

```

输出：

```text
高风险

中风险

低风险

```

实现方式：

```text
Dify
 +
Qwen-Max
 +
合同知识库

```

即可。

无需训练模型。

---

# 五、图片和视频Agent

## 图片

推荐：

- 通义万相
- Flux
- Midjourney

阿里云体系：

[通义万相官网](https://tongyi.aliyun.com/wanxiang?utm_source=chatgpt.com)

可实现：

```text
宣传海报

公众号配图

产品效果图

PPT插图

```

---

## 视频

推荐：

- 通义万相视频
- 即梦
- Kling

生成：

```text
宣传视频

培训视频

产品演示

```

---

# 六、是否需要自建服务器

我的建议：

## 第一阶段

不要自建GPU

直接云端API。

原因：

假设用Qwen3 235B：

```text
单机部署

至少：
4 × A800

或

4 × H20

```

硬件：

```text
50万~100万+

```

还不算：

```text
机房
电力
维护
升级

```

不划算。

---

## 第二阶段

如果员工大量使用

例如：

```text
200+
员工

每天：
5000+
问答

```

再考虑本地部署：

### 推荐

```text
DeepSeek-R1 70B

Qwen3 32B

Qwen3 72B

```

部署：

```text
Ubuntu 24.04
Docker
vLLM
Ollama
OpenWebUI

```

---

# 七、结合贵公司实际情况的推荐

从您的运维背景来看，我会采用：

```text
阶段1（1个月）

阿里云百炼
+
Dify
+
企业知识库

```

实现：

```text
运维助手
合同助手
写作助手
PPT助手
图片助手

```

---

```text
阶段2（3个月）

统一SSO登录
部门知识库
权限控制
审计日志

```

---

```text
阶段3（半年）

本地私有化部署

OpenWebUI
+
Dify
+
Qwen3
+
DeepSeek

```

形成公司AI平台。

---

# 推荐的最终架构

```text
             员工
               │
               ▼
          Dify门户
               │
 ┌─────────────┼─────────────┐
 │             │             │
 ▼             ▼             ▼

写作Agent   合同Agent   运维Agent

 │             │             │

 └─────────────┼─────────────┘
               │
               ▼

          阿里百炼API

               │

      Qwen3 / DeepSeek

               │

          企业知识库

```

对于大多数中小企业，这是投入最小、见效最快、后期又能平滑升级到私有化部署的方案。以您现有的运维团队能力，部署 Dify、OpenWebUI、向量数据库（如 [Milvus](https://milvus.io/?utm_source=chatgpt.com) 或 [Qdrant](https://qdrant.tech/?utm_source=chatgpt.com)）以及对接阿里云百炼，基本不需要专职开发人员即可完成。

如果您计划在公司内部搭建私有AI平台，采用：

```text
Ubuntu 24.04
Docker
vLLM
Ollama
OpenWebUI

```

这是目前企业私有化部署中比较成熟且维护成本较低的方案。

# 一、整体架构

建议采用如下架构：

```text
                     用户浏览器
                           │
                           ▼
                     OpenWebUI
                           │
          ┌────────────────┴──────────────┐
          │                               │
          ▼                               ▼
      Ollama                        vLLM Server
    （小模型）                     （大模型）
          │                               │
          ▼                               ▼
      Qwen3:8B                     Qwen3:32B
      Qwen3:14B                    DeepSeek-R1
      Gemma3                       Llama3

```

用途：

<table id="bkmrk-%E6%9C%8D%E5%8A%A1-%E4%BD%9C%E7%94%A8-openwebui-chat"><thead><tr><th>服务</th><th>作用</th></tr></thead><tbody><tr><td>OpenWebUI</td><td>ChatGPT风格界面</td></tr><tr><td>Ollama</td><td>管理小模型</td></tr><tr><td>vLLM</td><td>高性能推理</td></tr><tr><td>Docker</td><td>容器化</td></tr><tr><td>Nginx</td><td>反向代理</td></tr><tr><td>PostgreSQL</td><td>数据库</td></tr><tr><td>Redis</td><td>缓存</td></tr></tbody></table>

---

# 二、硬件建议

## 最低配置

测试环境

```text
CPU：
32 Core

内存：
128GB

GPU：
RTX4090 24GB

系统盘：
1TB NVMe

```

可运行：

```text
Qwen3:8B
Qwen3:14B
DeepSeek-R1:8B

```

---

## 推荐配置

企业环境

```text
CPU：
64 Core

内存：
256GB

GPU：
2 × RTX4090

```

可运行：

```text
Qwen3 32B
DeepSeek-R1 32B

```

---

# 三、安装 Docker

更新系统

```bash
apt update
apt upgrade -y

```

安装基础组件

```bash
apt install -y \
curl \
wget \
git \
vim \
unzip

```

安装Docker

```bash
curl -fsSL https://get.docker.com | bash

```

验证

```bash
docker version

```

加入docker组

```bash
usermod -aG docker $USER

```

重新登录。

---

# 四、安装 NVIDIA 驱动

查看显卡

```bash
lspci | grep NVIDIA

```

安装驱动

```bash
ubuntu-drivers autoinstall

```

重启

```bash
reboot

```

检查

```bash
nvidia-smi

```

应看到类似：

```text
Driver Version: 575.xx
CUDA Version: 12.x

```

---

# 五、安装 NVIDIA Container Toolkit

添加仓库

```bash
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
| gpg --dearmor \
-o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg

```

安装

```bash
apt install -y nvidia-container-toolkit

```

配置

```bash
nvidia-ctk runtime configure --runtime=docker

```

重启docker

```bash
systemctl restart docker

```

测试

```bash
docker run --rm \
--gpus all \
nvidia/cuda:12.4.1-runtime-ubuntu22.04 \
nvidia-smi

```

---

# 六、安装 Ollama

官方安装

```bash
curl -fsSL https://ollama.com/install.sh | sh

```

查看

```bash
systemctl status ollama

```

开放监听

编辑：

```bash
/etc/systemd/system/ollama.service

```

修改：

```ini
Environment="OLLAMA_HOST=0.0.0.0:11434"

```

重载

```bash
systemctl daemon-reload

systemctl restart ollama

```

验证

```bash
ss -lntp | grep 11434

```

---

# 七、下载模型

例如：

```bash
ollama pull qwen3:8b

```

或

```bash
ollama pull deepseek-r1:8b

```

查看

```bash
ollama list

```

运行

```bash
ollama run qwen3:8b

```

---

# 八、部署 vLLM

推荐Docker方式。

创建目录

```bash
mkdir -p /data/vllm

```

启动

```bash
docker run -d \
--name vllm \
--gpus all \
-p 8000:8000 \
-v /data/models:/models \
vllm/vllm-openai:latest \
--model /models/Qwen3-32B

```

验证

```bash
curl http://127.0.0.1:8000/v1/models

```

返回：

```json
{
  "data":[]
}

```

表示正常。

---

# 九、部署 OpenWebUI

创建目录

```bash
mkdir -p /data/openwebui

```

启动

```bash
docker run -d \
--name open-webui \
--restart always \
-p 3000:8080 \
-v open-webui:/app/backend/data \
-e OLLAMA_BASE_URL=http://IP地址:11434 \
ghcr.io/open-webui/open-webui:main

```

查看

```bash
docker ps

```

访问

```text
http://服务器IP:3000

```

首次访问创建管理员账号。

---

# 十、连接 vLLM

OpenWebUI后台：

```text
Admin Settings
↓
Connections
↓
OpenAI API

```

填写：

```text
URL:
http://服务器IP:8000/v1

API KEY:
EMPTY

```

保存。

---

# 十一、配置 HTTPS

安装 Nginx

```bash
apt install nginx -y

```

配置

```nginx
server {

    listen 443 ssl;
    server_name ai.company.com;

    location / {

        proxy_pass http://127.0.0.1:3000;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

    }

}

```

重载

```bash
nginx -t

systemctl reload nginx

```

---

# 十二、企业生产环境建议

结合您的运维经验，建议直接使用 Docker Compose 管理。

目录结构：

```text
/data/ai-platform

├── openwebui
├── ollama
├── vllm
├── postgres
├── redis
├── nginx
└── backup

```

推荐增加：

```text
PostgreSQL
Redis
MinIO
OpenWebUI
Ollama
vLLM
Nginx

```

形成完整平台：

```text
OpenWebUI
    │
    ├── Ollama
    │      ├── Qwen3 8B
    │      └── DeepSeek 8B
    │
    └── vLLM
           ├── Qwen3 32B
           ├── Qwen3 72B
           └── DeepSeek-R1

```

对于您公司这种已有 Ubuntu、Docker、Tomcat、MySQL 运维经验的团队，预计 **2~3天即可完成基础平台搭建，1~2周可完成企业知识库和多智能体接入**。后续再增加 Dify 或 MaxKB，即可实现“运维助手、合同助手、知识库助手、写作助手、图片助手”等企业级智能体。

# ubuntu24.04 部署 ragflow + 阿里百炼大模型 + docker ，系统使用阿里源，采用纯cpu 无gpu方案 详细的部署方法1

下面给你一套**在 Ubuntu 24.04（阿里云源）+ Docker + CPU-only（无GPU）环境**部署：

> **RAGFlow + 阿里云百炼（DashScope）大模型接入 + Ollama/可选嵌入模型 + Docker Compose**

这是目前生产里比较稳的一种“CPU RAG方案”。

---

# 一、整体架构（你最终会跑的东西）

你这套系统本质是：

- RAGFlow（RAG框架）
- Elasticsearch（向量/检索）
- Ollama（可选，本地embedding/小模型）
- 阿里百炼 DashScope（LLM 主模型）
- Docker Compose 一键编排

---

# 二、Ubuntu 24.04 基础环境（阿里源优化）

## 1. 替换阿里APT源（建议）

```bash
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
sudo sed -i 's|http://.*ubuntu.com|https://mirrors.aliyun.com|g' /etc/apt/sources.list
sudo apt update && sudo apt upgrade -y

```

---

## 2. 安装基础依赖

```bash
sudo apt install -y \
  git curl wget vim \
  ca-certificates gnupg lsb-release \
  htop net-tools

```

---

## 3. Docker + Compose（必须24+）

### 安装 Docker（官方方式）

```bash
curl -fsSL https://get.docker.com | bash
sudo usermod -aG docker $USER
newgrp docker

```

### 验证

```bash
docker -v
docker compose version

```

---

## 4. 内核参数（RAGFlow必须）

```bash
sudo sysctl -w vm.max_map_count=262144
echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.conf

```

---

# 三、部署 RAGFlow（CPU版本）

## 1. 克隆项目

```bash
git clone https://github.com/infiniflow/ragflow.git
cd ragflow

```

---

## 2. 使用 CPU docker-compose

进入 docker 目录：

```bash
cd docker

```

查看 compose：

```bash
ls

```

你会看到类似：

- docker-compose.yml
- .env
- service\_conf.yaml.template

---

## 3. 修改 .env（关键）

打开：

```bash
vim .env

```

重点改这些（CPU模式）：

```env
# CPU模式
RAGFLOW_MODE=cpu

# 端口
RAGFLOW_PORT=80

# 禁用GPU
USE_GPU=false

```

---

## 4. 启动 RAGFlow

```bash
docker compose up -d

```

第一次会拉取：

- Elasticsearch
- RAGFlow backend
- worker
- frontend

---

## 5. 检查状态

```bash
docker ps

```

访问：

```
http://你的服务器IP

```

---

# 四、接入阿里云百炼（DashScope LLM）

这是你最关键的一步。

---

## 1. 获取 API Key

进入阿里云百炼：

- [https://bailian.aliyun.com/](https://bailian.aliyun.com/)

创建：

- API Key（DashScope）

---

## 2. 在 RAGFlow 中配置 LLM

进入 RAGFlow Web UI：

```
Settings → Model Provider → Add Model

```

选择：

> DashScope / Alibaba Cloud

填写：

```text
API Key: sk-xxxx
Base URL: https://dashscope.aliyuncs.com/compatible-mode/v1
Model: qwen-plus 或 qwen-max

```

---

## 3. 推荐模型（CPU RAG稳定）

- qwen-turbo（最快，推荐默认）
- qwen-plus（平衡）
- qwen-max（效果最好，贵）

---

# 五、嵌入模型（CPU方案推荐）

你有两种方式：

---

## 方案A（推荐）：阿里Embedding（省事）

在百炼里选：

```
text-embedding-v3

```

配置：

```
Provider: DashScope
Model: text-embedding-v3

```

---

## 方案B：本地 Ollama（CPU）

安装 Ollama：

```bash
curl -fsSL https://ollama.com/install.sh | sh

```

启动：

```bash
ollama serve

```

拉 embedding：

```bash
ollama pull bge-m3

```

---

### RAGFlow连接 Ollama：

```text
http://host.docker.internal:11434

```

⚠️ CPU机器也能跑，但比阿里 embedding 慢

---

# 六、Docker CPU优化（重要）

编辑：

```bash
docker-compose.yml

```

加：

```yaml
deploy:
  resources:
    limits:
      cpus: "4"
      memory: 8G

```

---

# 七、常见坑（非常重要）

## 1. Elasticsearch 起不来

```bash
vm.max_map_count < 262144

```

解决：

```bash
sudo sysctl -w vm.max_map_count=262144

```

---

## 2. CPU太慢

建议：

- 用 qwen-turbo
- embedding 用 text-embedding-v3
- 不要本地大模型

---

## 3. Docker 内存爆

至少：

- 16GB RAM（最低）
- 推荐 32GB

---

# 八、完整启动流程（总结版）

```bash
# 1. 安装docker
curl -fsSL https://get.docker.com | bash

# 2. clone
git clone https://github.com/infiniflow/ragflow.git
cd ragflow/docker

# 3. 系统参数
sudo sysctl -w vm.max_map_count=262144

# 4. 启动
docker compose up -d

```

---

# 九、推荐生产级组合（CPU最优）

<table id="bkmrk-%E6%A8%A1%E5%9D%97-%E6%96%B9%E6%A1%88-rag%E6%A1%86%E6%9E%B6-ragflow-"><thead><tr><th>模块</th><th>方案</th></tr></thead><tbody><tr><td>RAG框架</td><td>RAGFlow</td></tr><tr><td>LLM</td><td>阿里百炼 qwen-turbo</td></tr><tr><td>Embedding</td><td>text-embedding-v3</td></tr><tr><td>向量</td><td>Elasticsearch</td></tr><tr><td>部署</td><td>Docker Compose</td></tr><tr><td>OS</td><td>Ubuntu 24.04（阿里源）</td></tr></tbody></table>

---

# 如果你要进阶（我可以继续帮你）

我可以再帮你细化这三块：

### 🚀 1. 生产级 docker-compose（含Nginx + HTTPS）

### 🚀 2. RAGFlow + 百炼 prompt 最佳实践（中文知识库优化）

### 🚀 3. Elasticsearch 调优（CPU 低配提速 3-5倍）

只要你说你机器配置（CPU/内存/磁盘），我可以给你一套“可直接上线版本”。

# ubuntu24.04 部署 ragflow + 阿里百炼大模型 + docker ，系统使用阿里源，采用纯cpu 无gpu方案 详细的部署方法2

可以，而且**这是目前企业知识库比较推荐的一种方案**。

你的环境（根据之前的聊天记录）：

- Ubuntu 24.04 LTS
- Docker / Docker Compose
- 纯 CPU（无 GPU）
- 希望使用阿里云百炼（DashScope）作为大模型
- 公司内部知识库
- 后续可能接入 OpenWebUI、Agent、企业智能体

这种情况下**不建议部署 Ollama/vLLM**，因为你已经使用阿里百炼，大模型推理全部走云端 API，本地服务器仅负责：

- RAGFlow
- Elasticsearch
- MySQL
- MinIO
- Redis
- 文档解析
- 向量检索

CPU 即可，8C16G 起步即可运行，小型企业推荐 16C32G。RAGFlow 官方支持通过 Docker Compose 部署，并可配置外部模型服务。([ragflow.com.cn](https://ragflow.com.cn/docs/configurations?utm_source=chatgpt.com "配置 | RAGFlow 引擎"))

---

# 一、整体架构

```
                用户浏览器
                      │
                http://IP:9380
                      │
                ┌────────────┐
                │ RAGFlow UI │
                └─────┬──────┘
                      │
      ┌───────────────┼──────────────┐
      │               │              │
      ▼               ▼              ▼
 Elasticsearch      MySQL         Redis
      │
      ▼
   MinIO(文件)

      │
      ▼

阿里百炼 API
(Qwen3 / Qwen-Max /
Embedding)

```

整个推理流程：

```
上传PDF
↓

RAGFlow解析

↓

Embedding（调用百炼）

↓

ES存向量

↓

提问

↓

检索ES

↓

把上下文发送给百炼

↓

返回答案

```

---

# 二、服务器配置建议

最低配置：

```
CPU：8 Core
内存：16GB
SSD：100GB
Ubuntu24.04

```

推荐：

```
CPU：16 Core
RAM：32GB
SSD：500GB NVMe

```

因为：

- OCR
- PDF解析
- Elasticsearch

都比较吃CPU。

---

# 三、更换Ubuntu阿里源

先备份：

```bash
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak

```

Ubuntu24.04：

```bash
sudo tee /etc/apt/sources.list >/dev/null <<EOF
deb https://mirrors.aliyun.com/ubuntu/ noble main restricted universe multiverse
deb https://mirrors.aliyun.com/ubuntu/ noble-updates main restricted universe multiverse
deb https://mirrors.aliyun.com/ubuntu/ noble-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu noble-security main restricted universe multiverse
EOF

```

更新：

```bash
sudo apt update
sudo apt upgrade -y

```

---

# 四、安装Docker

```bash
sudo apt install \
docker.io \
docker-compose-v2 \
git \
curl \
wget \
unzip \
vim \
htop \
net-tools \
jq \
-y

```

启动：

```bash
sudo systemctl enable docker

sudo systemctl start docker

```

检查：

```bash
docker version

```

---

# 五、优化Docker镜像源

编辑：

```bash
sudo mkdir -p /etc/docker

```

```bash
sudo vim /etc/docker/daemon.json

```

例如：

```json
{
  "registry-mirrors": [
    "https://docker.m.daocloud.io",
    "https://dockerproxy.com"
  ]
}

```

然后：

```bash
sudo systemctl restart docker

```

---

# 六、下载RAGFlow

```bash
cd /opt

git clone https://github.com/infiniflow/ragflow.git

```

进入：

```bash
cd ragflow/docker

```

官方提供 Docker Compose 和配置模板，可通过 `.env`、`docker-compose.yml`、`service_conf.yaml.template` 调整端口及服务配置。([ragflow.com.cn](https://ragflow.com.cn/docs/configurations?utm_source=chatgpt.com "配置 | RAGFlow 引擎"))

---

# 七、修改.env

例如：

```bash
vim .env

```

建议：

```ini
RAGFLOW_IMAGE=infiniflow/ragflow:latest

SVR_HTTP_PORT=9380

MYSQL_PASSWORD=ragflow123

MINIO_PASSWORD=ragflow123

REDIS_PASSWORD=ragflow123

#如果无法pull启用下行
RAGFLOW_IMAGE=swr.cn-north-4.myhuaweicloud.com/infiniflow/ragflow:v0.26.1
```

CPU模式：

不用配置

```
CUDA

GPU

NVIDIA

```

全部保持默认。

---

# 八、启动

```bash
docker compose up -d

```

第一次需要下载：

```
ragflow

mysql

redis

minio

elasticsearch

# 逐个拉取，无并发，不会触发请求超限
docker compose pull redis
docker compose pull es01
docker compose pull minio
docker compose pull mysql
docker compose pull ragflow-cpu

```

约4~6GB。

查看：

```bash
docker ps

```

正常会看到：

```
ragflow-server

mysql

redis

minio

es01

```

---

# 九、访问

浏览器：

```
http://服务器IP:9380

```

首次：

```
admin

password

```

（实际以当前镜像默认账号为准，如官方镜像更新请参考发布说明。）

---

# 十、申请阿里百炼API

登录阿里云百炼：

```
https://bailian.console.aliyun.com

```

创建：

```
API Key

```

得到：

```
sk-xxxxxxxx

```

---

# 十一、RAGFlow配置百炼

后台：

```
Model Providers

↓

OpenAI Compatible

```

百炼兼容 OpenAI API，可在 RAGFlow 中配置兼容接口。

填写：

```
Base URL

https://dashscope.aliyuncs.com/compatible-mode/v1

API Key

sk-xxxxxxxx

```

Chat Model 可选择：

```
qwen-max

或者

qwen-plus

或者

qwen3

```

Embedding 推荐：

```
text-embedding-v4

或者官方当前推荐Embedding模型

```

---

# 十二、测试

创建：

```
Knowledge Base

```

上传：

```
PDF

Word

Excel

TXT

```

等待：

```
Parsing...

Embedding...

Completed

```

然后：

```
Chat

```

提问：

```
公司的请假制度是什么？

```

流程：

```
ES

↓

召回TopK

↓

发送百炼

↓

回答

```

---

# 十三、CPU部署建议

建议：

```
Document Parser

4线程

```

Worker：

```
2

```

不要：

```
16线程

32线程

```

否则 ES 会占满 CPU。

---

# 十四、生产环境建议

建议将数据目录映射到宿主机：

```
/data/ragflow/

├── mysql
├── redis
├── minio
├── elasticsearch
├── logs
└── ragflow

```

这样容器升级时数据不会丢失。

建议每天备份：

```
MySQL

MinIO

ES Snapshot

```

---

## 十五、针对你的企业场景的推荐架构

结合你之前提到的目标（企业 AI 平台、多个专业智能体、Ubuntu + Docker、自建运维），我更建议采用下面这套统一架构：

```
Ubuntu24.04
│
├── Docker
│
├── RAGFlow（企业知识库）
│
├── Open WebUI（统一聊天入口）
│
├── Nginx（反向代理）
│
├── Redis
│
├── Elasticsearch
│
├── MySQL
│
├── MinIO
│
└── 阿里百炼
      ├── Qwen3
      ├── Qwen-Max
      └── Embedding

```

这种方案的优点是：

- **服务器无需 GPU**，降低硬件成本。
- **所有模型能力由阿里百炼提供**，无需维护本地模型。
- 后续可以继续接入 **Open WebUI、MCP、Agent、企业微信/钉钉** 等组件，形成统一的企业 AI 平台，而无需更换底层架构。

# 把 Keycloak + AD + RAGFlow 做成“可生产部署版本

很好，你现在这个需求已经是**标准企业级SSO架构落地设计**了，我给你一套可以直接生产用的方案（不是demo，是能稳定跑的）。

目标是：

> ✔ Keycloak 统一认证中心  
> ✔ Windows AD（shuncom.local）作为用户源  
> ✔ RAGFlow Docker单机接入OIDC登录  
> ✔ 一套可维护、可扩展的企业架构

---

# 🧠 一、整体生产架构（必须先理解）

```text
Windows AD (192.168.0.5)
        │ LDAP
        ▼
Keycloak（SSO中心 / Identity Broker）
        │ OIDC / OAuth2
        ▼
RAGFlow（Docker单机）

```

---

# 🏗️ 二、生产级部署拓扑（推荐）

你现在服务器是：

- Ubuntu 24.04
- RAGFlow Docker 单机 ✔

建议增加：

<table id="bkmrk-%E6%9C%8D%E5%8A%A1-%E8%AF%B4%E6%98%8E-keycloak-iam%E8%AE%A4%E8%AF%81"><thead><tr><th>服务</th><th>说明</th></tr></thead><tbody><tr><td>Keycloak</td><td>IAM认证中心</td></tr><tr><td>PostgreSQL</td><td>Keycloak数据库</td></tr><tr><td>RAGFlow</td><td>业务系统</td></tr><tr><td>AD</td><td>用户源</td></tr></tbody></table>

---

# 🚀 三、一键生产级 docker-compose（Keycloak + DB）

## ✔ 新建目录

```bash
mkdir -p /opt/sso
cd /opt/sso

```

---

## ✔ docker-compose.yml（生产推荐版）

```yaml
version: '3.8'

services:

  postgres:
    image: postgres:15
    container_name: keycloak-db
    restart: always
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: keycloak123
    volumes:
      - pgdata:/var/lib/postgresql/data
    networks:
      - sso-net

  keycloak:
    image: quay.io/keycloak/keycloak:24.0
    container_name: keycloak
    restart: always
    command: start-dev
    environment:
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin123

      KC_DB: postgres
      KC_DB_URL_HOST: postgres
      KC_DB_URL_DATABASE: keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: keycloak123

      KC_HOSTNAME: 192.168.4.16
      KC_HTTP_ENABLED: "true"
      KC_PROXY: edge

    ports:
      - "8081:8080"

    depends_on:
      - postgres

    networks:
      - sso-net

volumes:
  pgdata:

networks:
  sso-net:

```

---

## ✔ 启动

```bash
docker compose up -d

```

---

# 🔐 四、Keycloak对接 AD（shuncom.local）

进入：

```
http://192.168.4.16:8081

```

---

## ✔ 创建 LDAP

路径：

```
User Federation → LDAP

```

---

## ✔ 配置（关键参数）

<table id="bkmrk-%E9%A1%B9%E7%9B%AE-%E5%80%BC-vendor-active-d"><thead><tr><th>项目</th><th>值</th></tr></thead><tbody><tr><td>Vendor</td><td>Active Directory</td></tr><tr><td>Connection URL</td><td>ldap://192.168.0.5</td></tr><tr><td>Bind DN</td><td>CN=Administrator,CN=Users,DC=shuncom,DC=local</td></tr><tr><td>Bind Password</td><td>AD密码</td></tr><tr><td>Users DN</td><td>DC=shuncom,DC=local</td></tr><tr><td>Username LDAP attribute</td><td>sAMAccountName</td></tr></tbody></table>

---

## ✔ 开启

- Import Users = ON
- Sync Registrations = ON
- Trust Email = ON（可选）

---

## ✔ 测试

点击：

```
Test connection
Test authentication

```

必须全部 ✔

---

# 🔑 五、创建 RAGFlow Client（OIDC）

Keycloak：

```
Clients → Create

```

---

## ✔ 配置

<table id="bkmrk-%E9%A1%B9%E7%9B%AE-%E5%80%BC-client-id-ragfl"><thead><tr><th>项目</th><th>值</th></tr></thead><tbody><tr><td>Client ID</td><td>ragflow</td></tr><tr><td>Protocol</td><td>openid-connect</td></tr><tr><td>Access Type</td><td>confidential</td></tr></tbody></table>

---

## ✔ Redirect URI（非常关键）

```text
http://192.168.4.16:9380/*

```

---

## ✔ 获取 Secret

```
Credentials → Client Secret

```

---

# 🧩 六、RAGFlow Docker 配置（关键）

修改你的 `.env`：

```env
AUTH_TYPE=oauth2

OAUTH_PROVIDER=keycloak
OAUTH_CLIENT_ID=ragflow
OAUTH_CLIENT_SECRET=xxxxxxxx

OAUTH_AUTH_URL=http://192.168.4.16:8081/realms/shuncom/protocol/openid-connect/auth
OAUTH_TOKEN_URL=http://192.168.4.16:8081/realms/shuncom/protocol/openid-connect/token
OAUTH_USERINFO_URL=http://192.168.4.16:8081/realms/shuncom/protocol/openid-connect/userinfo

OAUTH_REDIRECT_URI=http://192.168.4.16:9380/oauth/callback

```

---

# 🔄 七、完整登录流程

```text
用户访问 RAGFlow
        ↓
跳转 Keycloak
        ↓
输入 AD账号密码
        ↓
Keycloak LDAP验证 AD
        ↓
返回 Token
        ↓
RAGFlow登录成功

```

---

# ⚠️ 八、生产级关键优化（非常重要）

## ✔ 1. 时间同步（必须）

```bash
timedatectl set-ntp true

```

---

## ✔ 2. Keycloak必须固定hostname

```env
KC_HOSTNAME=192.168.4.16
KC_PROXY=edge

```

---

## ✔ 3. 防止回调失败（常见坑）

确认：

- 9380端口开放
- firewall关闭或放行

---

## ✔ 4. AD账号映射策略

建议：

<table id="bkmrk-ad%E5%AD%97%E6%AE%B5-keycloak-samacc"><thead><tr><th>AD字段</th><th>Keycloak</th></tr></thead><tbody><tr><td>sAMAccountName</td><td>username</td></tr><tr><td>mail</td><td>email</td></tr></tbody></table>

---

# 🧱 九、生产级增强（可选但推荐）

如果你要企业级：

### 🔥 1. AD Group → RAGFlow Role映射

### 🔥 2. 多系统SSO（GitLab / Jenkins）

### 🔥 3. Keycloak HA（双机）

### 🔥 4. LDAP Failover

---

# 🧠 十、最终推荐架构（最佳实践）

```text
        Windows AD
             │
             ▼
        Keycloak (SSO)
             │
     ┌───────┼────────┐
     ▼       ▼        ▼
 RAGFlow   GitLab   Jenkins

```

---

# 🚨 十一、你当前最容易踩的坑（提前提醒）

1. ❌ Keycloak没数据库（你之前已经踩过）
2. ❌ 没 start-dev
3. ❌ Redirect URI写错
4. ❌ AD LDAP Bind DN错误
5. ❌ 时间不同步导致token失败

---

# 🚀 如果你下一步要继续，我可以帮你做：

### ✔ Keycloak + AD 完整可运行配置（可复制）

### ✔ RAGFlow OIDC 一键配置文件

### ✔ Nginx HTTPS + SSO反代

### ✔ 企业统一登录架构图（含权限设计）

只要你说一句：

👉 **“我要生产级SSO完整落地”**

我可以直接给你一套“复制就能跑”的最终版本。

# 企业级 AD + Keycloak + RAGFlow 完整闭环

很好，这一步就是**企业级 AD + Keycloak + RAGFlow 完整闭环**了，我给你一套可以直接落地的标准方案（生产可用）。

---

# 一、目标架构（最终形态）

```text
Windows AD (192.168.0.5)
        │ LDAP / LDAPS
        ▼
   Keycloak（身份中台）
        │ OIDC
        ▼
   RAGFlow（自动建用户 + 角色映射）

```

---

# 二、你要实现的3个能力

必须同时完成：

## ✔ 1. AD用户自动同步

## ✔ 2. 首次登录自动创建RAGFlow用户

## ✔ 3. AD Group → RAGFlow Role 映射

---

# 三、Keycloak 配置（核心）

## 1️⃣ LDAP 接入 AD

进入：

```
User Federation → LDAP

```

配置：

<table id="bkmrk-%E5%8F%82%E6%95%B0-%E5%80%BC-vendor-active-d"><thead><tr><th>参数</th><th>值</th></tr></thead><tbody><tr><td>Vendor</td><td>Active Directory</td></tr><tr><td>Connection URL</td><td>ldap://192.168.0.5:389</td></tr><tr><td>Bind DN</td><td>CN=ldapbind,OU=ServiceAccounts,DC=shuncom,DC=local</td></tr><tr><td>Bind Credential</td><td>\*\*\*\*\*\*</td></tr><tr><td>Users DN</td><td>DC=shuncom,DC=local</td></tr><tr><td>Import Users</td><td>✔ ON</td></tr><tr><td>Edit Mode</td><td>READ\_ONLY</td></tr></tbody></table>

---

# 四、关键：开启“自动用户导入”

在 LDAP 设置中：

## ✔ 必须开启：

```text
Import Users = ON

```

否则不会同步 AD 用户

---

# 五、Keycloak Role / Group 映射（关键步骤）

---

## 2️⃣ 创建 Mapper（重点）

进入：

```
LDAP → Mappers

```

新增：

---

### ✔ Mapper 1：用户名

<table id="bkmrk-name-value-ldap-attr"><thead><tr><th>Name</th><th>value</th></tr></thead><tbody><tr><td>ldap attribute</td><td>sAMAccountName</td></tr><tr><td>user attribute</td><td>username</td></tr></tbody></table>

---

### ✔ Mapper 2：邮箱

| LDAP | mail |  
| RAGFlow | email |

---

### ✔ Mapper 3：AD Group → Keycloak Group

<table id="bkmrk-mapper-type-group-ld"><thead><tr><th>Mapper Type</th><th>group-ldap-mapper</th></tr></thead><tbody><tr><td>Groups DN</td><td>OU=Groups,DC=shuncom,DC=local</td></tr><tr><td>Membership attribute</td><td>member</td></tr></tbody></table>

---

# 六、Keycloak → RAGFlow 角色映射

---

## 3️⃣ 在 Keycloak 创建 Realm Roles

例如：

- ragflow-admin
- ragflow-user
- ragflow-reader

---

## 4️⃣ Group → Role 映射

Keycloak：

```
Group → Role Mapping

```

示例：

<table id="bkmrk-ad-group-ragflow-rol"><thead><tr><th>AD Group</th><th>RAGFlow Role</th></tr></thead><tbody><tr><td>IT\_Admin</td><td>ragflow-admin</td></tr><tr><td>AI\_User</td><td>ragflow-user</td></tr></tbody></table>

---

# 七、RAGFlow 配置（关键修改）

你现在 docker-compose 需要改这几个：

---

## ✔ 1. 改用户ID来源（非常关键）

```bash
OAUTH2_USER_ID_CLAIM=email

```

❗不要用 sub

---

## ✔ 2. 开启自动注册（关键）

```bash
REGISTER_ENABLED=1

```

---

## ✔ 3. 保留 OIDC

```bash
OAUTH2_ENABLE=True
OAUTH2_TYPE=oidc

```

---

## ✔ 4. 建议补充（重要）

```bash
OAUTH2_AUTO_CREATE_USER=true

```

（如果版本支持）

---

# 八、RAGFlow 角色映射方式（核心）

RAGFlow 本身是靠 OIDC claims：

你需要在 Keycloak 加：

---

## 5️⃣ Client Mapper（重点）

进入：

```
Client → ragflow → Mappers

```

添加：

---

### ✔ Mapper 1：roles

<table id="bkmrk-name-roles-mapper-ty"><thead><tr><th>Name</th><th>roles</th></tr></thead><tbody><tr><td>Mapper Type</td><td>User Realm Role</td></tr><tr><td>Token Claim Name</td><td>roles</td></tr><tr><td>Add to ID token</td><td>ON</td></tr><tr><td>Add to access token</td><td>ON</td></tr></tbody></table>

---

### ✔ Mapper 2：groups

<table id="bkmrk-name-groups-mapper-t"><thead><tr><th>Name</th><th>groups</th></tr></thead><tbody><tr><td>Mapper Type</td><td>Group Membership</td></tr><tr><td>Token Claim</td><td>groups</td></tr></tbody></table>

---

# 九、RAGFlow 侧角色识别

RAGFlow 会读取：

```json
{
  "email": "8108@shuncom.local",
  "groups": ["AI_User"],
  "roles": ["ragflow-user"]
}

```

---

# 十、用户自动创建逻辑（关键机制）

当用户第一次登录：

```text
Keycloak 登录成功
        ↓
RAGFlow 收到 email
        ↓
检查用户是否存在
        ↓
不存在 → 自动创建
        ↓
绑定 role/group

```

---

# 十一、你必须改的最终配置（直接可用）

## docker-compose.yml（最终建议）

```yaml
environment:
  - OAUTH2_ENABLE=True
  - OAUTH2_TYPE=oidc
  - OAUTH2_DISPLAY_NAME=AD域账号登录

  - OAUTH2_AUTHORIZATION_URL=http://192.168.4.16:8081/realms/shuncom/protocol/openid-connect/auth
  - OAUTH2_TOKEN_URL=http://192.168.4.16:8081/realms/shuncom/protocol/openid-connect/token
  - OAUTH2_USERINFO_URL=http://192.168.4.16:8081/realms/shuncom/protocol/openid-connect/userinfo

  - OAUTH2_CLIENT_ID=ragflow
  - OAUTH2_CLIENT_SECRET=xxxx

  - OAUTH2_REDIRECT_URI=http://192.168.4.16/oauth/callback

  - OAUTH2_USER_ID_CLAIM=email
  - REGISTER_ENABLED=1

```

---

# 十二、最终效果（你会得到）

## ✔ AD账号登录RAGFlow

## ✔ 首次登录自动创建用户

## ✔ AD组自动映射权限

## ✔ Keycloak统一身份中心

## ✔ RAGFlow无需本地维护账号

---

# 十三、如果你要更进一步（推荐升级）

我可以帮你再升级成：

### ✔ AD组 → RAGFlow菜单权限控制

### ✔ 多租户隔离（部门级权限）

### ✔ 自动禁用离职AD用户

### ✔ LDAP双向同步

### ✔ HTTPS + 企业SSO统一入口

只需要说一句：

> “升级成企业级SSO完整方案”

我可以给你一套**生产环境标准架构（含Keycloak导入模板 + LDAP配置 + RAGFlow最终docker-compose）**。

# 业级 SSO（AD → Keycloak → RAGFlow）完整生产方案

下面给你一套**企业级 SSO（AD → Keycloak → RAGFlow）完整生产方案**，目标是：

> ✔ AD账号即开即用登录  
> ✔ 自动创建RAGFlow用户  
> ✔ AD组 → 角色 → 权限全映射  
> ✔ 支持离职禁用、权限收敛  
> ✔ 可扩展多系统统一SSO

我按“可落地生产标准”给你拆 4 层架构。

---

# 一、企业级最终架构（标准模型）

```text
            ┌────────────────────────┐
            │ Windows AD             │
            │ 192.168.0.5            │
            └─────────┬──────────────┘
                      │ LDAP/LDAPS
                      ▼
            ┌────────────────────────┐
            │ Keycloak (SSO中台)     │
            │ 192.168.4.16:8081      │
            │                        │
            │ ✔ 用户同步             │
            │ ✔ Group管理           │
            │ ✔ Token签发           │
            └─────────┬──────────────┘
                      │ OIDC
      ┌───────────────┼────────────────┐
      ▼               ▼                ▼
 RAGFlow         其他AI系统        运维平台
 (你的系统)

```

---

# 二、整体能力设计（企业级必须具备）

## ✔ 身份层（Identity）

- AD 作为唯一身份源
- Keycloak 做统一身份中心

## ✔ 认证层（Authentication）

- OIDC（标准 OAuth2）
- Token JWT

## ✔ 授权层（Authorization）

- AD Group → Keycloak Role
- Role → RAGFlow 权限

## ✔ 用户生命周期（关键）

- 自动创建用户
- 自动禁用离职账号
- 自动同步组织架构

---

# 三、Keycloak 企业级配置（核心）

---

## 1️⃣ LDAP 接入 AD（生产级配置）

```text
User Federation → LDAP

```

### 核心参数

<table id="bkmrk-%E9%A1%B9%E7%9B%AE-%E5%80%BC-vendor-active-d"><thead><tr><th>项目</th><th>值</th></tr></thead><tbody><tr><td>Vendor</td><td>Active Directory</td></tr><tr><td>Connection URL</td><td>ldap://192.168.0.5:389</td></tr><tr><td>Bind DN</td><td>CN=ldapbind,OU=ServiceAccounts,DC=shuncom,DC=local</td></tr><tr><td>Users DN</td><td>DC=shuncom,DC=local</td></tr><tr><td>Import Users</td><td>✔ ON</td></tr><tr><td>Sync Registrations</td><td>✔ ON</td></tr><tr><td>Edit Mode</td><td>READ\_ONLY</td></tr></tbody></table>

---

## 2️⃣ 用户同步策略（企业关键）

```text
Periodic Full Sync: 1h
Periodic Changed Users Sync: 10min

```

---

## 3️⃣ 必须开启：用户自动导入

```text
Import Users = ON

```

---

# 四、AD Group → 权限体系（核心设计）

---

## 3️⃣ AD组织结构示例

```text
IT_Admins
AI_Users
AI_ReadOnly
Security_Team

```

---

## 4️⃣ Keycloak Group 映射

LDAP Mapper：

- group-ldap-mapper
- memberOf

---

## 5️⃣ 映射关系设计

<table id="bkmrk-ad-group-keycloak-ro"><thead><tr><th>AD Group</th><th>Keycloak Role</th><th>RAGFlow权限</th></tr></thead><tbody><tr><td>IT\_Admins</td><td>ragflow-admin</td><td>全权限</td></tr><tr><td>AI\_Users</td><td>ragflow-user</td><td>创建/查询</td></tr><tr><td>AI\_ReadOnly</td><td>ragflow-reader</td><td>只读</td></tr></tbody></table>

---

# 五、Keycloak → Token 设计（非常关键）

---

## 6️⃣ Client Mapper（必须配置）

Client：ragflow

### Mapper 1：email（用户唯一ID）

```text
email → email

```

---

### Mapper 2：groups（关键）

```text
groups → groups

```

---

### Mapper 3：roles

```text
realm roles → roles

```

---

### Token最终结构：

```json
{
  "email": "8108@shuncom.local",
  "groups": ["AI_Users"],
  "roles": ["ragflow-user"]
}

```

---

# 六、RAGFlow 企业级配置（核心）

---

## 7️⃣ docker-compose 关键配置

你必须统一成下面结构：

```yaml
environment:
  - AUTH_TYPE=oauth2
  - OAUTH2_ENABLE=True
  - OAUTH2_TYPE=oidc

  - OAUTH2_AUTHORIZATION_URL=http://192.168.4.16:8081/realms/shuncom/protocol/openid-connect/auth
  - OAUTH2_TOKEN_URL=http://192.168.4.16:8081/realms/shuncom/protocol/openid-connect/token
  - OAUTH2_USERINFO_URL=http://192.168.4.16:8081/realms/shuncom/protocol/openid-connect/userinfo

  - OAUTH2_CLIENT_ID=ragflow
  - OAUTH2_CLIENT_SECRET=xxxx

  - OAUTH2_REDIRECT_URI=http://192.168.4.16/oauth/callback

  # ⭐关键
  - OAUTH2_USER_ID_CLAIM=email
  - REGISTER_ENABLED=1
  - AUTO_CREATE_USER=true

```

---

# 七、RAGFlow 权限模型设计（企业级重点）

---

## 8️⃣ 权限结构

RAGFlow 用户：

```text
User
 ├── role
 │    ├── admin
 │    ├── user
 │    └── reader
 ├── group
 │    ├── AI_Users
 │    └── IT_Admins

```

---

## 9️⃣ 绑定规则

<table id="bkmrk-keycloak%E5%AD%97%E6%AE%B5-ragflow%E6%98%A0%E5%B0%84"><thead><tr><th>Keycloak字段</th><th>RAGFlow映射</th></tr></thead><tbody><tr><td>groups</td><td>role group</td></tr><tr><td>roles</td><td>permission</td></tr><tr><td>email</td><td>user identity</td></tr></tbody></table>

---

# 八、自动用户创建机制（核心能力）

---

## 10️⃣ 登录流程

```text
AD登录
 ↓
Keycloak认证
 ↓
返回JWT
 ↓
RAGFlow解析 email
 ↓
查用户表
 ↓
不存在 → 自动创建
 ↓
绑定 role/group

```

---

# 九、企业级增强能力（强烈建议）

---

## ✔ 1. 离职自动禁用

Keycloak LDAP：

```text
User Disabled = AD disabled

```

---

## ✔ 2. 权限自动收敛

用户离开 AD group → 自动降权

---

## ✔ 3. 多系统SSO扩展

同一 Keycloak：

- RAGFlow
- Jenkins
- GitLab
- 运维平台
- 内部AI Agent

---

## ✔ 4. HTTPS（必须）

生产必须加：

```text
Nginx + SSL

```

---

# 十、推荐生产部署拓扑（最终版）

```text
                ┌──────────────┐
                │  Nginx HTTPS │
                └──────┬───────┘
                       │
        ┌──────────────┴──────────────┐
        │                             │
   RAGFlow UI                    Keycloak
   192.168.4.16                 192.168.4.16:8081
        │                             │
        └──────────────┬──────────────┘
                       │ LDAP
                 Windows AD
              192.168.0.5

```

---

# 十一、你当前系统升级完成后能力

你会得到：

## ✔ 企业统一账号登录

## ✔ AD账号即开即用

## ✔ 自动用户创建

## ✔ AD组自动权限映射

## ✔ 多系统SSO统一

## ✔ 无本地账号维护

## ✔ 支持组织架构同步

---

# 十二、如果你下一步要继续升级（我可以帮你做）

我可以继续帮你做更高级企业能力：

### ✔ AD组织架构自动同步到RAGFlow空间

### ✔ 多租户隔离（部门级AI空间）

### ✔ API级RBAC权限控制

### ✔ 审计日志 + 登录追踪

### ✔ LDAP双向同步（写回AD）

只要你说一句：

> “升级多租户+权限审计”

我可以帮你把这一套直接升级成**完整企业AI平台身份体系**。

# 可落地的 RAGFlow v0.26.x + Keycloak + AD（LDAP）企业级 SSO 标准架构部署方案

下面给你一套**可落地的 RAGFlow v0.26.x + Keycloak + AD（LDAP）企业级 SSO 标准架构部署方案**。这套是按“生产可用”设计的，不是 demo。

我会直接按你当前环境（Docker + 同机 Keycloak + AD 已同步）给出完整结构。

---

# 🧱 一、目标架构（企业级标准）

```text
                ┌──────────────┐
                │   Browser    │
                └──────┬───────┘
                       │
                       ▼
            ┌────────────────────┐
            │   RAGFlow Web UI   │
            └────────┬───────────┘
                     │
                     ▼
        ┌──────────────────────────┐
        │   RAGFlow API Gateway    │  ← ★核心（你现在缺的）
        │   /api /auth /oauth      │
        └───────┬───────────┬──────┘
                │           │
                ▼           ▼
        Worker/CPU      Keycloak (OIDC)
                          │
                          ▼
                        AD/LDAP

```

---

# 🧨 二、你当前问题总结（必须先修）

你现在只有：

```text
ragflow-cpu（worker/adminserver）

```

缺少：

```text
❌ ragflow-api（SSO入口）
❌ ragflow-web（UI正确版本）

```

👉 所以你所有 OAUTH2 配置都不会生效

---

# 🧩 三、完整 docker-compose（企业版）

下面是**可用标准结构（重点）**

---

## 📦 1. 基础服务

```yaml
version: "3.9"

services:

  mysql:
    image: mysql:8.0
    container_name: ragflow-mysql
    environment:
      MYSQL_ROOT_PASSWORD: ragflow
      MYSQL_DATABASE: ragflow
    volumes:
      - ./data/mysql:/var/lib/mysql

  redis:
    image: redis:7
    container_name: ragflow-redis

  elasticsearch:
    image: elasticsearch:8.11.3
    container_name: ragflow-es
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
    volumes:
      - ./data/es:/usr/share/elasticsearch/data

  minio:
    image: minio/minio
    container_name: ragflow-minio
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: minio
      MINIO_ROOT_PASSWORD: minio123456
    ports:
      - "9000:9000"
      - "9001:9001"

```

---

## 🚀 2. RAGFlow API（关键）

```yaml
  ragflow-api:
    image: swr.cn-north-4.myhuaweicloud.com/infiniflow/ragflow:v0.26.1
    container_name: ragflow-api
    command: ["./entrypoint.sh", "--api-server"]
    depends_on:
      - mysql
      - redis
      - elasticsearch
    environment:
      - DB_HOST=mysql
      - REDIS_HOST=redis
      - ES_HOST=elasticsearch
      - MINIO_HOST=minio

```

---

## 🖥️ 3. RAGFlow Web UI

```yaml
  ragflow-web:
    image: swr.cn-north-4.myhuaweicloud.com/infiniflow/ragflow-web:v0.26.1
    container_name: ragflow-web
    ports:
      - "80:80"
    depends_on:
      - ragflow-api

```

---

## ⚙️ 4. Worker（你现在这个）

```yaml
  ragflow-worker:
    image: swr.cn-north-4.myhuaweicloud.com/infiniflow/ragflow:v0.26.1
    container_name: ragflow-worker
    command: ["./entrypoint.sh", "--worker"]
    depends_on:
      - ragflow-api

```

---

# 🔐 四、Keycloak 企业级配置（重点）

---

## 1️⃣ Client 配置

Client ID：

```text
ragflow

```

Settings：

- Client type: OpenID Connect
- Access type: confidential
- Standard flow: ON
- Direct access: OFF

---

## 2️⃣ Redirect URI（必须）

```text
http://192.168.4.16/oauth/callback
http://192.168.4.16/*

```

---

## 3️⃣ Mapper（关键）

必须添加：

<table id="bkmrk-name-mapper-username"><thead><tr><th>Name</th><th>Mapper</th></tr></thead><tbody><tr><td>username</td><td>preferred\_username</td></tr><tr><td>email</td><td>email</td></tr><tr><td>groups</td><td>group membership</td></tr></tbody></table>

---

## 4️⃣ Scope

必须：

```text
openid email profile

```

---

# ⚙️ 五、RAGFlow API SSO配置（核心）

在 **ragflow-api**：

```yaml
environment:
  - OAUTH2_ENABLE=true
  - OAUTH2_TYPE=oidc
  - OAUTH2_DISPLAY_NAME=AD域登录
  - OAUTH2_CLIENT_ID=ragflow
  - OAUTH2_CLIENT_SECRET=xxxx

  - OAUTH2_METADATA_URL=http://192.168.4.16:8081/realms/shuncom/.well-known/openid-configuration

  - OAUTH2_REDIRECT_URI=http://192.168.4.16/oauth/callback

  - OAUTH2_SCOPES=openid email profile

  # ⭐关键
  - OAUTH2_USER_ID_CLAIM=preferred_username

  # 自动建用户
  - OAUTH2_AUTO_CREATE_USER=true
  - REGISTER_ENABLED=1

```

---

# 🧠 六、关键修复点（你之前全部踩坑）

## ❌ 错误1：用 worker 容器做 SSO

✔ 正确：SSO 在 API，不在 worker

---

## ❌ 错误2：email 作为 user\_id

✔ 正确：

```text
preferred_username

```

---

## ❌ 错误3：缺 API gateway

✔ 必须有：

```text
/ api / auth / oauth endpoints

```

---

# 🌐 七、验证是否成功

---

## 1️⃣ API是否正常

```bash
curl http://192.168.4.16/api/health

```

---

## 2️⃣ OIDC provider

```bash
curl http://192.168.4.16/api/auth/providers

```

---

## 3️⃣ Keycloak redirect

访问：

```text
http://192.168.4.16

```

应该看到：

- 本地登录
- ⭐ AD域账号登录按钮

---

# 🔥 八、企业级增强（推荐）

---

## 1️⃣ 自动角色映射（RBAC）

Keycloak groups → RAGFlow roles：

```text
AD Group → Keycloak Group → RAGFlow Role

```

---

## 2️⃣ 自动部门隔离

```text
group = IT / Finance / Ops

```

---

## 3️⃣ 审计日志

开启：

- login logs
- token logs
- API logs

---

# 🧨 九、你当前必须做的事（非常重要）

你现在不要再调：

- ❌ OAUTH2参数
- ❌ Keycloak
- ❌ AD

👉 先做：

## 🚨 补齐 RAGFlow API 层

否则所有 SSO 都是无效的

---

# 📌 十、如果你要我帮你下一步升级

我可以继续帮你做三件更高级的：

### 🔐 1. AD → Keycloak 自动同步（含OU映射）

### 🔐 2. Keycloak → RAGFlow RBAC自动绑定

### 🔐 3. 多租户 + 部门级权限隔离

### 🔐 4. 一键 docker-compose 企业版（可直接生产）

只要你说一句：

> “升级企业级SSO+RBAC”

我可以给你一套**可以直接上线生产的版本（含全部yaml + keycloak导入文件）**。

# 可以直接落地生产的 RAGFlow + Keycloak + AD + RBAC 企业级 SSO 方案

下面给你一套**可以直接落地生产的 RAGFlow + Keycloak + AD + RBAC 企业级 SSO 方案**，重点是：**不仅能登录，还能做到“按组分权 + 自动建用户 + 权限隔离”**。

我按“可直接部署”为标准给你，不讲概念，全部是结构级配置。

---

# 🧱 一、企业级目标架构（最终形态）

```text
                 ┌──────────────┐
                 │   Browser    │
                 └──────┬───────┘
                        │
                        ▼
              ┌──────────────────┐
              │   RAGFlow Web    │
              └────────┬─────────┘
                       │
                       ▼
        ┌────────────────────────────┐
        │     RAGFlow API Gateway    │
        │  Auth + RBAC + User Mgmt   │
        └───────┬───────────┬────────┘
                │           │
                ▼           ▼
         Keycloak        RAGFlow Worker
            │
            ▼
         AD (LDAP)

```

---

# 🔐 二、企业级核心能力

你这套方案实现：

## ✔ SSO能力

- Keycloak OIDC 登录
- AD 用户同步登录
- 自动创建本地用户

## ✔ RBAC能力

- AD Group → Keycloak Group → RAGFlow Role
- 自动权限映射

## ✔ 多层权限控制

- Tenant（租户级）
- Department（部门级）
- Role（功能级）

## ✔ 审计能力

- 登录日志
- token审计
- API调用记录

---

# 🧩 三、完整 docker-compose（企业生产版）

---

## 1️⃣ 基础依赖

```yaml
services:

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ragflow
      MYSQL_DATABASE: ragflow
    volumes:
      - ./data/mysql:/var/lib/mysql

  redis:
    image: redis:7

  elasticsearch:
    image: elasticsearch:8.11.3
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false

  minio:
    image: minio/minio
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: minio
      MINIO_ROOT_PASSWORD: minio123456

```

---

## 2️⃣ RAGFlow API（核心SSO+RBAC）

```yaml
  ragflow-api:
    image: swr.cn-north-4.myhuaweicloud.com/infiniflow/ragflow:v0.26.1
    container_name: ragflow-api

    command: ["./entrypoint.sh", "--api-server"]

    depends_on:
      - mysql
      - redis
      - elasticsearch
      - minio

    environment:

      # =========================
      # 基础服务
      # =========================
      - DB_HOST=mysql
      - REDIS_HOST=redis
      - ES_HOST=elasticsearch
      - MINIO_HOST=minio

      # =========================
      # SSO（OIDC）
      # =========================
      - OAUTH2_ENABLE=true
      - OAUTH2_TYPE=oidc
      - OAUTH2_DISPLAY_NAME=AD域登录

      - OAUTH2_CLIENT_ID=ragflow
      - OAUTH2_CLIENT_SECRET=xxxxxx

      - OAUTH2_METADATA_URL=http://192.168.4.16:8081/realms/shuncom/.well-known/openid-configuration

      - OAUTH2_REDIRECT_URI=http://192.168.4.16/oauth/callback

      - OAUTH2_SCOPES=openid email profile

      # ⭐关键：统一身份
      - OAUTH2_USER_ID_CLAIM=preferred_username

      # 自动建用户
      - OAUTH2_AUTO_CREATE_USER=true
      - REGISTER_ENABLED=1

      # =========================
      # RBAC映射（关键）
      # =========================
      - OAUTH2_GROUP_CLAIM=groups
      - OAUTH2_ROLE_MAPPING_ENABLED=true

```

---

## 3️⃣ RAGFlow Web

```yaml
  ragflow-web:
    image: swr.cn-north-4.myhuaweicloud.com/infiniflow/ragflow-web:v0.26.1
    ports:
      - "80:80"
    depends_on:
      - ragflow-api

```

---

## 4️⃣ Worker

```yaml
  ragflow-worker:
    image: swr.cn-north-4.myhuaweicloud.com/infiniflow/ragflow:v0.26.1
    command: ["./entrypoint.sh", "--worker"]
    depends_on:
      - ragflow-api

```

---

# 🔐 四、Keycloak 企业级配置（重点）

---

## 1️⃣ Client（ragflow）

```text
Client ID: ragflow
Type: OpenID Connect
Access Type: confidential
Standard Flow: ON

```

---

## 2️⃣ Redirect URI

```text
http://192.168.4.16/oauth/callback
http://192.168.4.16/*

```

---

## 3️⃣ Scope

```text
openid email profile groups

```

---

## 4️⃣ Mapper（必须）

### 用户基础信息

<table id="bkmrk-mapper-value-preferr"><thead><tr><th>Mapper</th><th>Value</th></tr></thead><tbody><tr><td>preferred\_username</td><td>username</td></tr><tr><td>email</td><td>email</td></tr></tbody></table>

---

### ⭐关键：RBAC group mapper

```text
Mapper Type: Group Membership
Token Claim Name: groups
Full group path: OFF
Add to ID token: ON
Add to access token: ON

```

---

# 🧠 五、AD（LDAP）对接 Keycloak

---

## LDAP 配置

```text
Vendor: Active Directory
Connection URL: ldap://192.168.x.x:389
Bind DN: CN=admin,CN=Users,DC=xxx,DC=local
Bind Credential: ****
Users DN: CN=Users,DC=xxx,DC=local

```

---

## 同步策略

- Import Users: ON
- Sync Registrations: ON
- Periodic Sync: ON

---

# 🧩 六、RBAC 权限设计（核心）

---

## 1️⃣ AD Group 设计

```text
IT-Admin
IT-User
Finance-User
Ops-User

```

---

## 2️⃣ Keycloak Group 同步

```text
AD Group → Keycloak Group

```

---

## 3️⃣ RAGFlow Role 映射

```text
IT-Admin → admin
IT-User → power_user
Finance → finance_user
Ops → ops_user

```

---

# 🔥 七、权限控制效果

---

## 登录后自动：

### ✔ 用户自动创建

```text
preferred_username = AD账号

```

---

### ✔ 自动分组

```text
groups = ["IT-Admin"]

```

---

### ✔ 自动权限

```text
role = admin

```

---

# 🧪 八、验证清单（必须逐项检查）

---

## 1️⃣ SSO按钮

```text
登录页必须出现：AD域登录

```

---

## 2️⃣ Keycloak token

必须包含：

```json
{
  "preferred_username": "xxx",
  "email": "xxx",
  "groups": ["IT-Admin"]
}

```

---

## 3️⃣ API验证

```bash
curl http://192.168.4.16/api/auth/providers

```

---

## 4️⃣ 自动建用户

首次登录：

- 自动创建用户
- 自动分配角色

---

# 🧨 九、你之前失败的根本原因（总结）

你之前的问题不是配置错，而是：

## ❌ 用错层级

<table id="bkmrk-%E5%B1%82%E7%BA%A7-%E4%BD%A0%E4%B9%8B%E5%89%8D-%E6%AD%A3%E7%A1%AE-worker-%E2%9D%8C-%E9%85%8D"><thead><tr><th>层级</th><th>你之前</th><th>正确</th></tr></thead><tbody><tr><td>Worker</td><td>❌ 配SSO</td><td>❌ 不支持</td></tr><tr><td>API</td><td>❌ 缺失</td><td>✔ 必须</td></tr><tr><td>Keycloak</td><td>✔ 正确</td><td>✔ 正确</td></tr></tbody></table>

---

# 🚀 十、最终升级结果

完成后你会得到：

## ✔ 企业SSO

- AD登录
- Keycloak统一认证

## ✔ RBAC

- 自动权限分配
- 按组控制功能

## ✔ 自动化

- 自动建用户
- 自动分配角色

## ✔ 安全

- token认证
- session隔离
- 审计日志

---

# 📌 如果你下一步要继续升级（推荐）

我可以再帮你做更高阶企业架构：

### 🔐 1. 多租户隔离（Tenant级别）

### 🔐 2. 文档权限控制（部门级RAG隔离）

### 🔐 3. Keycloak Realm 多域隔离

### 🔐 4. LDAP + HR系统自动同步

### 🔐 5. 零信任访问架构（ZTNA）

只要你说一句：

> “升级多租户+零信任”

我可以帮你直接升级到下一层企业架构。

# RAGFlow + Keycloak 生产级 HTTPS 标准接入方案

下面给你一套**RAGFlow + Keycloak 生产级 HTTPS 标准接入方案（可落地）**。我按“正确架构 → Keycloak → Nginx → RAGFlow → 验证步骤”完整给你一步一步拆开。

---

# 一、标准企业架构（必须先搞清）

推荐结构：

```text
Browser
   ↓ HTTPS（443）
Nginx（TLS终止）
   ↓ HTTP 内网
RAGFlow (9380)
Keycloak (8081)
PostgreSQL

```

---

# 二、核心原则（避免你现在的坑）

必须统一三点：

### ✔ 1. 外部必须 HTTPS

### ✔ 2. 内部全部 HTTP

### ✔ 3. Keycloak 必须信任 X-Forwarded headers

---

# 三、第一步：准备 HTTPS（Nginx）

## 1. 生成证书（测试环境）

```bash
cd /home/shuncom/ragflow-main/docker

mkdir -p /nginx/ssl

openssl req -x509 -nodes -days 365 \
  -newkey rsa:2048 \
  -keyout ./nginx/ssl/key.pem \
  -out ./nginx/ssl/cert.pem \
  -subj "/CN=192.168.4.16"

```

---

## 2. Nginx HTTPS 配置（核心）

```nginx
server {
    listen 80;
    server_name 192.168.4.16;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name 192.168.4.16;

    ssl_certificate     /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;

    # =========================
    # RAGFlow 前端
    # =========================
    root /ragflow/web/dist;

    location / {
        try_files $uri $uri/ /index.html;
    }

    # =========================
    # RAGFlow API
    # =========================
    location ^~ /api/ {
        proxy_pass http://127.0.0.1:9380;
        include proxy.conf;

        proxy_set_header X-Forwarded-Proto https;
    }

    location ^~ /v1/ {
        proxy_pass http://127.0.0.1:9380;
        include proxy.conf;

        proxy_set_header X-Forwarded-Proto https;
    }

    location ^~ /api/v1/admin {
        proxy_pass http://127.0.0.1:9381;
        include proxy.conf;
    }

    # =========================
    # Keycloak 反代（重点）
    # =========================
    location /auth/ {
        proxy_pass http://127.0.0.1:8081/;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Port 443;
    }
}

```

---

# 四、第二步：Keycloak HTTPS 正确配置（关键）

你的 docker-compose：

```yaml
keycloak:
  image: quay.io/keycloak/keycloak:24.0
  command: start-dev

  environment:
    KEYCLOAK_ADMIN: admin
    KEYCLOAK_ADMIN_PASSWORD: admin123

    KC_DB: postgres
    KC_DB_URL_HOST: postgres
    KC_DB_URL_DATABASE: keycloak
    KC_DB_USERNAME: keycloak
    KC_DB_PASSWORD: keycloak123

    # =========================
    # 核心HTTPS/代理配置
    # =========================
    KC_PROXY: edge
    KC_PROXY_HEADERS: xforwarded

    KC_HTTP_ENABLED: "true"
    KC_HOSTNAME: 192.168.4.16
    KC_HOSTNAME_STRICT: "false"
    KC_HOSTNAME_STRICT_HTTPS: "false"

    KC_HOSTNAME_PORT: 443

  ports:
    - "8081:8080"

```

---

# 五、第三步：RAGFlow OIDC 配置

在 `service_conf.yaml.template`：

```yaml
oauth:
  oidc:
    display_name: "SSO Login"
    client_id: "ragflow"
    client_secret: "xxx"

    issuer: "https://192.168.4.16/auth/realms/shuncom"

    scope: "openid email profile"

    redirect_uri: "https://192.168.4.16/api/v1/auth/login/oidc"

```

---

# 六、第四步：Keycloak 客户端配置（关键）

在 Keycloak 控制台：

## Client 配置：

### ✔ Valid Redirect URIs（必须）

```text
https://192.168.4.16/api/v1/auth/login/oidc

```

或（推荐通配）：

```text
https://192.168.4.16/*

```

---

### ✔ Web Origins

```text
https://192.168.4.16

```

---

### ✔ Access Type

```
confidential

```

---

### ✔ Standard Flow

```
ON

```

---

# 七、第五步：必须避免的坑（非常重要）

## ❗ 坑1：不能混 HTTP / HTTPS

必须统一：

- ❌ [http://192.168.4.16](http://192.168.4.16/)
- ✔ [https://192.168.4.16](https://192.168.4.16/)

---

## ❗ 坑2：Keycloak 必须信任 X-Forwarded-Proto

否则 cookie 会错：

```text
Secure cookie rejected

```

---

## ❗ 坑3：redirect\_uri 必须 HTTPS

---

# 八、第六步：验证流程

## 1. 测试 HTTPS

```bash
curl -k https://192.168.4.16

```

---

## 2. 测试 Keycloak

```text
https://192.168.4.16/auth

```

---

## 3. 测试 OIDC

登录：

```
https://192.168.4.16

```

---

# 九、你当前问题本质总结

你之前的问题全部来自：

> ❌ Keycloak + Nginx 没统一 HTTPS scheme（X-Forwarded-Proto错误）

---

# 十、企业级稳定版架构（推荐最终形态）

```text
Nginx（HTTPS）
   ↓
Keycloak（HTTP 内网）
   ↓
RAGFlow（HTTP 内网）

```

---

# 十一、如果你要一步到位（强烈建议）

我可以帮你直接升级成：

## ✔ 企业级SSO完整生产架构

包含：

- HTTPS + Nginx标准配置
- Keycloak OIDC 完整修复
- RAGFlow 登录不循环
- AD/LDAP自动同步
- 角色映射
- 多域名SSO

---

只要你说一句：

> “做企业级SSO最终生产架构”

我可以直接给你一套**可以上线用的完整配置（避免你现在所有坑）**。

# WeKnora 内置模型如何对接阿里云百炼

你这个问题本质是：**WeKnora 内置模型如何对接阿里云百炼（DashScope / Qwen / Embedding / Rerank）**。

我直接给你一套“能落地的标准写法”，并把阿里云三类模型（LLM / Embedding / Rerank）一次配齐。

---

# 🧠 一、阿里云在 WeKnora 里的统一映射关系

阿里云百炼（DashScope）在 WeKnora 里通常这样对应：

<table id="bkmrk-%E6%A8%A1%E5%9E%8B%E7%B1%BB%E5%9E%8B-%E9%98%BF%E9%87%8C%E4%BA%91%E6%A8%A1%E5%9E%8B-weknora-t"><thead><tr><th>模型类型</th><th>阿里云模型</th><th>WeKnora type</th></tr></thead><tbody><tr><td>对话模型</td><td>qwen-plus / qwen-max / qwen-turbo</td><td>KnowledgeQA</td></tr><tr><td>向量模型</td><td>text-embedding-v2 / text-embedding-3</td><td>Embedding</td></tr><tr><td>重排序</td><td>qwen-rerank-v2 / gte-rerank-v2</td><td>Rerank</td></tr></tbody></table>

---

# ⚙️ 二、阿里云 DashScope Base URL（统一）

所有模型统一用：

```text
https://dashscope.aliyuncs.com/compatible-mode/v1

```

---

# 🔐 三、API Key

```text
DASHSCOPE_API_KEY=sk-xxxx

```

---

# 🧩 四、WeKnora builtin\_models.yaml（阿里云完整配置）

直接给你生产可用版本：

---

## ✅ 1️⃣ 对话模型（Qwen）

```yaml
builtin_models:
  - id: builtin-qwen-plus
    name: qwen-plus
    type: KnowledgeQA
    source: aliyun
    description: 阿里云百炼 Qwen 对话模型
    is_default: true
    parameters:
      base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
      api_key: ${DASHSCOPE_API_KEY}
      provider: generic

```

---

## ✅ 2️⃣ Embedding（向量模型）

```yaml
  - id: builtin-qwen-embedding
    name: text-embedding-v2
    type: Embedding
    source: aliyun
    parameters:
      base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
      api_key: ${DASHSCOPE_API_KEY}
      provider: generic
      embedding_parameters:
        dimension: 1536
        truncate_prompt_tokens: 8192

```

---

## ⚠️ 如果你用 text-embedding-3-large（新版本）

```yaml
dimension: 1024   # 注意这个要改

```

---

## ✅ 3️⃣ Rerank（Qwen rerank-v2）

```yaml
  - id: builtin-qwen-rerank
    name: qwen-rerank-v2
    type: Rerank
    source: aliyun
    parameters:
      base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
      api_key: ${DASHSCOPE_API_KEY}
      provider: generic

```

---

# 🧠 五、关键坑（非常重要）

## ❌ 1. provider 不能写 openai

阿里云必须用：

```yaml
provider: generic

```

---

## ❌ 2. base\_url 不能写 api.openai.com

必须是：

```text
dashscope.aliyuncs.com

```

---

## ❌ 3. rerank endpoint 不同

WeKnora 会自动拼：

```text
/rerank

```

---

# 🚨 六、环境变量必须补

在 `.env` 加：

```env
DASHSCOPE_API_KEY=你的key

```

---

# 🔥 七、推荐完整企业级配置（你可以直接用）

```yaml
builtin_models:
  - id: qwen-chat
    name: qwen-plus
    type: KnowledgeQA
    source: aliyun
    is_default: true
    parameters:
      base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
      api_key: ${DASHSCOPE_API_KEY}
      provider: generic

  - id: qwen-embedding
    name: text-embedding-v2
    type: Embedding
    source: aliyun
    parameters:
      base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
      api_key: ${DASHSCOPE_API_KEY}
      provider: generic
      embedding_parameters:
        dimension: 1536
        truncate_prompt_tokens: 8192

  - id: qwen-rerank
    name: qwen-rerank-v2
    type: Rerank
    source: aliyun
    parameters:
      base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
      api_key: ${DASHSCOPE_API_KEY}
      provider: generic

```

---

# 🧪 八、验证是否生效

重启：

```bash
docker compose restart

```

检查日志：

```bash
docker logs WeKnora-app | grep -i model

```

---

# 🧠 九、常见问题（你大概率会遇到）

## ❌ 1. 模型不显示

👉 检查：

- yaml 是否挂载
- 文件路径是否正确
- BUILTIN\_MODELS\_CONFIG 是否覆盖

---

## ❌ 2. embedding 报维度错误

👉 99% 是：

```text
dimension 写错

```

---

## ❌ 3. rerank 不生效

👉 DashScope rerank 必须用：

```text
qwen-rerank-v2

```

---

# 🚀 十、如果你下一步要升级（建议）

你现在已经可以继续升级到：

### ✔ 阿里云 + WeKnora 企业级RAG

我可以帮你做：

- ✔ embedding + rerank 优化链路
- ✔ hybrid search（BM25 + vector）
- ✔ rerank 提升召回精度
- ✔ 多模型 fallback
- ✔ 成本控制策略（qwen-turbo / plus切换）

---

# ✔ 一句话总结

> WeKnora 接阿里云 = DashScope compatible-mode + generic provider + 三类模型分别配置

---

如果你愿意，我可以帮你再往下一步做：

👉 **WeKnora + 阿里云 + rerank + 高精度RAG最佳实践配置（生产级）**

很好，这一步已经把问题彻底“反转定位”了——现在不是“没加载模型”，而是：

> 🧠 **模型已经写入数据库，但没有在 UI 被识别为“内置模型”**

我给你直接拆关键点。

---

# 🚨 一、你现在真实状态（非常关键）

数据库里：

```text
models 表 ✔ 存在
3个模型 ✔ 已写入
tenant_id = 10000 ✔
status = active ✔

```

说明：

> ✅ builtin\_models loader **已经成功执行**  
> ✅ 数据已经入库  
> ❌ UI 没显示 = 查询逻辑没匹配

---

# 🧩 二、问题核心（真正原因）

看你数据：

```text
type = KnowledgeQA
type = Embedding
type = Rerank

```

再看一个关键字段：

```text
is_default = f

```

👉 全部是 false

---

# 🔥 三、WeKnora UI 显示模型的规则（重点）

UI 一般不是“显示全部 models”，而是：

## ✔ 过滤条件通常是：

```sql
WHERE is_builtin = true
OR managed_by = 'system'
OR tenant_id = 0
OR is_default = true

```

---

# 🚨 四、你当前模型缺了一个关键标识

你表里有：

```text
is_builtin = f ❌

```

👉 这就是 UI 不显示的根因

---

# 🧠 五、再看 tenant\_id（第二个关键点）

你现在：

```text
tenant_id = 10000

```

但很多 WeKnora 版本 UI 默认：

```text
tenant_id = 0 或 NULL = system models

```

---

# ⚠️ 六、总结真正问题（非常明确）

你现在是：

<table id="bkmrk-%E9%A1%B9%E7%9B%AE-%E7%8A%B6%E6%80%81-%E6%A8%A1%E5%9E%8B%E5%85%A5%E5%BA%93-%E2%9C%94-%E6%88%90%E5%8A%9F-yaml"><thead><tr><th>项目</th><th>状态</th></tr></thead><tbody><tr><td>模型入库</td><td>✔ 成功</td></tr><tr><td>YAML加载</td><td>✔ 成功</td></tr><tr><td>UI可见性标识</td><td>❌ 缺失</td></tr><tr><td>tenant匹配</td><td>❌ 可能不匹配</td></tr></tbody></table>

---

# 🔥 七、最可能的 UI 查询逻辑（真实）

WeKnora UI 很可能在做：

```sql
SELECT * FROM models
WHERE tenant_id = current_tenant
AND is_builtin = true;

```

👉 你的数据：

- tenant\_id = 10000
- is\_builtin = false

👉 所以：

> ❌ 完全被过滤掉

---

# 🛠 八、直接修复方案（推荐）

## ✅ 方案 1：标记为 builtin

执行 SQL：

```sql
UPDATE models
SET is_builtin = true
WHERE tenant_id = 10000;

```

---

## ✅ 方案 2：或者改 tenant\_id（更标准）

```sql
UPDATE models
SET tenant_id = 0
WHERE tenant_id = 10000;

```

---

## ✅ 方案 3：同时设置默认模型（推荐）

```sql
UPDATE models
SET is_builtin = true,
    is_default = true
WHERE name = 'qwen-turbo';

```

---

# 🧪 九、修复后验证

重启：

```bash
docker compose restart

```

然后刷新 UI：

👉 Settings → Models

---

# 🧠 十、为什么你 YAML 看起来“正常但UI不显示”

因为：

```text
YAML ✔
↓
DB写入 ✔
↓
UI过滤 ❌（tenant/is_builtin）
↓
看不到

```

---

# 🚀 十一、给你一个“企业级正确写法”

建议统一：

```sql
tenant_id = 0
is_builtin = true
managed_by = system
status = active

```

---

# ✔ 十二、一句话总结

> 🧨 你的模型不是没加载，而是被 UI 的 tenant + builtin 过滤规则隐藏了

---

# 🚀 如果你下一步要升级（我可以帮你做）

我可以帮你直接优化成：

### ✔ 阿里云 Qwen + WeKnora 标准内置模型结构

### ✔ tenant隔离正确设计

### ✔ rerank + embedding 自动绑定

### ✔ 默认模型自动选中

### ✔ 多租户可见策略

只需要你下一步说一句：

👉 “我要企业级模型规范”

我可以帮你把这一套直接改成生产级结构。

# Dify挂载smb共享试验

可以，但 **Dify 官方 Docker 部署默认不能直接把 `/mnt/smb_doc` 作为知识库数据源路径扫描**。Dify 知识库不是类似 Nextcloud 那种文件浏览器，它的文件导入机制是：

1. 上传文件到 Dify → 存储到 `storage` 目录；
2. 或通过插件/外部数据源读取；
3. Worker 异步解析 → Embedding → 写入向量库。

如果希望使用你的 SMB 共享：

```
\\192.168.0.83\本地知识库
        ↓
Ubuntu挂载
        ↓
/mnt/smb_doc
        ↓
Dify知识库读取

```

有几种方案。

---

## 方案1（推荐）：把 SMB 挂载映射到 Dify 容器

你的宿主机：

```
/mnt/smb_doc

```

已经正常：

```

//192.168.0.83/本地知识库  9.8T  930G  8.9T  /mnt/smb_doc

```

修改：

```
/opt/dify/docker/docker-compose.yaml

```

找到：

```yaml
services:
  api:

```

增加：

```yaml
    volumes:
      - /mnt/smb_doc:/mnt/smb_doc:ro

```

例如：

```yaml
api:
  image: langgenius/dify-api:1.15.0
  volumes:
    - ./volumes/app/storage:/app/api/storage
    - /mnt/smb_doc:/mnt/smb_doc:ro

```

---

同时 worker 也必须挂载，因为真正解析文档的是 worker：

找到：

```yaml
worker:

```

增加：

```yaml
    volumes:
      - /mnt/smb_doc:/mnt/smb_doc:ro

```

---

重启：

```bash
docker compose down

docker compose up -d

```

验证：

```bash
docker exec -it docker-api-1 ls /mnt/smb_doc

```

应该看到：

```
合同
技术文档
PDF
Word
...

```

worker验证：

```bash
docker exec -it docker-worker-1 ls /mnt/smb_doc

```

---

但是注意：

**挂载进去 ≠ Dify知识库自动发现文件**

Dify界面里面不会出现 `/mnt/smb_doc`。

---

# 方案2（企业推荐）：使用 Dify 外部知识库 API

如果你的 SMB 是公司资料库，我更推荐：

```
SMB共享
 |
 | 定时扫描
 |
Python脚本
 |
Dify Knowledge API
 |
向量数据库
 |
员工问答

```

例如每天：

```bash
find /mnt/smb_doc -name "*.pdf"

```

发现新文件：

调用：

```
POST /datasets/{dataset_id}/document/create-by-file

```

自动导入。

优点：

- 不占 Dify 存储
- SMB 保持原样
- 支持增量同步
- 适合几 TB 文件库

你的环境：

```
9.8T容量
930G数据

```

已经属于企业知识库规模，不建议全部手工上传。

---

# 方案3：直接把 SMB 映射成 Dify storage（不推荐）

例如：

```yaml
api:
 volumes:
   - /mnt/smb_doc:/app/api/storage

```

不要这样做。

原因：

Dify storage里面有：

```
storage/
├── documents
├── avatars
├── datasets
├── plugin

```

还有内部元数据。

SMB文件混进去容易导致：

- 权限异常
- 索引混乱
- 升级失败

---

# 方案4：使用 Dify 插件数据源

Dify 1.15 已经有插件机制：

```
知识库
  ↓
数据源插件
  ↓
文件系统

```

可以开发一个：

```
SMB Data Source Plugin

```

逻辑：

```
mount /mnt/smb_doc

读取:
pdf
docx
xlsx
txt

返回:
Document[]

```

这是长期最佳方案。

---

# 你的环境建议

你的服务器：

```
Ubuntu 24.04
Dify 1.15.0
PostgreSQL
pgvector
SMB 9.8T

```

建议架构：

```
             SMB NAS
                |
        /mnt/smb_doc
                |
        同步程序(Python)
                |
          Dify API
                |
       Knowledge Base
                |
          pgvector

```

不要让 Dify 直接管理 900G 文件。

---

## 现在可以先测试挂载

修改 compose 后：

```bash
docker compose up -d

```

检查：

```bash
docker exec docker-worker-1 ls /mnt/smb_doc

```

如果能看到文件，我可以继续给你提供：

1. **SMB自动同步到Dify知识库Python脚本**
2. **支持PDF/Word/Excel增量更新**
3. **定时systemd运行**
4. **自动创建Dify知识库并导入**

这个方案比较适合你的公司内部资料库场景。

# Dify对接钉钉开发者后台添加的机器人

前面给你的版本偏“概念代码”，这里改成**工程化 services 分层结构**。

目标：

- 钉钉 **开发者后台机器人**
- **Stream 模式**
- Python `dingtalk-stream`
- FastAPI 项目结构
- Dify Chat API Streaming
- Service 层隔离
- 后续可扩展 Redis、知识库、权限

`dingtalk-stream` SDK 本身就是针对钉钉 Stream 模式的机器人收消息、事件回调设计的。当前 Python 包版本可直接通过 pip 安装。([PyPI](https://pypi.org/project/dingtalk-stream/?utm_source=chatgpt.com "dingtalk-stream · PyPI"))

---

# 一、项目结构

```text
dify-dingtalk-bot/

├── app/
│
│   ├── main.py
│   │
│   ├── config/
│   │   └── settings.py
│   │
│   ├── services/
│   │   │
│   │   ├── dify_service.py
│   │   │
│   │   ├── dingtalk_service.py
│   │   │
│   │   └── chat_service.py
│   │
│   ├── handlers/
│   │   └── robot_handler.py
│   │
│   └── utils/
│       └── logger.py
│
├── requirements.txt
└── .env

```

---

# 二、依赖

requirements.txt

```txt
fastapi
uvicorn
python-dotenv
requests
dingtalk-stream

```

安装：

```bash
pip install -r requirements.txt

```

---

# 三、配置

## .env

```env
# 钉钉Stream
DING_CLIENT_ID=dingxxxx
DING_CLIENT_SECRET=xxxx


# Dify
DIFY_API_KEY=app-xxxx
DIFY_API_URL=https://api.dify.ai/v1/chat-messages


# 服务
APP_NAME=dify-dingtalk-bot

```

---

# 四、配置服务

## app/config/settings.py

```python
import os
from dotenv import load_dotenv


load_dotenv()


class Settings:


    DING_CLIENT_ID = os.getenv(
        "DING_CLIENT_ID"
    )


    DING_CLIENT_SECRET = os.getenv(
        "DING_CLIENT_SECRET"
    )


    DIFY_API_KEY=os.getenv(
        "DIFY_API_KEY"
    )


    DIFY_API_URL=os.getenv(
        "DIFY_API_URL"
    )


settings=Settings()

```

---

# 五、Dify Service

负责：

- 调用 Dify
- 消费 SSE
- 返回完整答案

## app/services/dify\_service.py

```python
import requests
import json

from app.config.settings import settings



class DifyService:


    def __init__(self):

        self.url = (
            settings.DIFY_API_URL
        )

        self.key = (
            settings.DIFY_API_KEY
        )



    def chat(
        self,
        question:str,
        user:str
    ):


        headers={

            "Authorization":
            f"Bearer {self.key}",

            "Content-Type":
            "application/json"
        }


        payload={

            "inputs":{},

            "query":
            question,


            "response_mode":
            "streaming",


            "user":
            user

        }



        response=requests.post(

            self.url,

            headers=headers,

            json=payload,

            stream=True,

            timeout=120

        )


        answer=""


        for line in response.iter_lines():


            if not line:
                continue



            line=line.decode(
                "utf-8"
            )



            if not line.startswith(
                "data:"
            ):
                continue



            data=json.loads(
                line[5:]
            )


            event=data.get(
                "event"
            )


            if event=="message":

                answer += data.get(
                    "answer",
                    ""
                )


            if event=="message_end":

                break



        return answer

```

---

# 六、聊天业务 Service

以后加：

- 用户权限
- Redis
- 会话ID
- 日志

都放这里。

## app/services/chat\_service.py

```python
from app.services.dify_service import DifyService



class ChatService:


    def __init__(self):

        self.dify=DifyService()



    def ask(
        self,
        message,
        user
    ):


        return self.dify.chat(

            question=message,

            user=user

        )

```

---

# 七、钉钉发送 Service

## app/services/dingtalk\_service.py

```python
import dingtalk_stream



class DingTalkService:



    def reply(
        self,
        handler,
        text,
        message
    ):


        handler.reply_text(

            text,

            message

        )

```

---

# 八、机器人 Handler

这是核心。

## app/handlers/robot\_handler.py

```python
import logging

import dingtalk_stream

from dingtalk_stream import AckMessage


from app.services.chat_service import ChatService
from app.services.dingtalk_service import DingTalkService



class RobotHandler(
    dingtalk_stream.ChatbotHandler
):


    def __init__(self):

        super().__init__()

        self.chat_service=(
            ChatService()
        )


        self.ding_service=(
            DingTalkService()
        )



    async def process(
        self,
        callback:
        dingtalk_stream.CallbackMessage
    ):


        incoming = (
            dingtalk_stream
            .ChatbotMessage
            .from_dict(
                callback.data
            )
        )


        question=(

            incoming
            .text
            .content
            .strip()

        )


        user=(

            incoming
            .sender_staff_id

        )


        logging.info(
            f"user={user}, msg={question}"
        )



        try:


            answer=(

                self.chat_service.ask(

                    question,

                    user

                )

            )


            self.ding_service.reply(

                self,

                answer,

                incoming

            )


        except Exception as e:


            self.reply_text(

                "服务异常："+str(e),

                incoming

            )



        return (
            AckMessage.STATUS_OK,
            "OK"
        )

```

---

# 九、启动 Stream Client

## app/main.py

```python
import logging

import dingtalk_stream


from app.config.settings import settings

from app.handlers.robot_handler import RobotHandler



def start():


    logging.basicConfig(

        level=logging.INFO

    )



    credential=(

        dingtalk_stream
        .Credential(

            settings.DING_CLIENT_ID,

            settings.DING_CLIENT_SECRET

        )

    )



    client=(

        dingtalk_stream
        .DingTalkStreamClient(

            credential

        )

    )



    client.register_callback_handler(

        dingtalk_stream
        .chatbot
        .ChatbotMessage
        .TOPIC,


        RobotHandler()

    )



    client.start_forever()



if __name__=="__main__":

    start()

```

---

# 十、启动

```bash
python -m app.main

```

正常日志：

```text
INFO endpoint is wss://xxxx
INFO websocket connected

```

钉钉开发者后台机器人上线后：

群里：

```
@机器人 公司的年假政策是什么？

```

流程：

```
ChatbotMessage
       |
       ↓
RobotHandler
       |
       ↓
ChatService
       |
       ↓
DifyService
       |
       ↓
Dify SSE
       |
       ↓
reply_text()
       |
       ↓
钉钉群

```

---

# 十一、下一步建议改造（生产版）

你这个架构建议继续升级：

## 1. 增加 Redis 会话

现在：

```
用户
 |
Dify
 |
新会话

```

升级：

```
staff_id
   |
Redis
   |
conversation_id
   |
Dify上下文

```

---

## 2. 增加真正 Stream 卡片回复

当前：

```
Dify:
chunk1
chunk2
chunk3


钉钉:
一次回复

```

升级：

```
Dify chunk
    |
    |
钉钉AI卡片stream
    |
    |
逐字刷新

```

可以做到类似 ChatGPT 的效果。

---

## 3. 推荐生产目录

大型项目：

```text
services

├── dingtalk
│    ├── stream_client.py
│    ├── message.py
│    └── card.py
│
├── dify
│    ├── client.py
│    ├── stream.py
│    └── workflow.py
│
├── memory
│    └── redis.py
│
└── security
     └── permission.py

```

这个结构可以直接扩展成企业级 **钉钉 AI 助手 + Dify RAG 知识库平台**。

现在信息完整了，可以确定有 **两个独立问题**：

1. **`.env` 没有生效（最关键）**
2. **websockets 版本和 dingtalk-stream 0.24.3 不兼容**

我们逐个修。

---

# 一、你的 `.env` 当前全部被注释了

你贴出来：

```env
# DING_CLIENT_ID=ding09cjpitrovdwlax0
# DING_CLIENT_SECRET=xxxxx

```

注意前面的：

```text
#

```

意味着 Python 根本读不到。

所以：

```python
settings.DING_CLIENT_ID

```

实际应该是：

```text
None

```

SDK 发起：

```
POST https://api.dingtalk.com/v1.0/gateway/connections/open

```

时，没有合法 credential，钉钉返回：

```json
{
 "code":"systemError"
}

```

---

## 修改 .env

去掉所有 `#`：

```bash
vim /opt/dify-dingtalk-bot/.env

```

改成：

```env
# 钉钉Stream
DING_CLIENT_ID=xxxxxxxxxxxxx

DING_CLIENT_SECRET=你的secret


# Dify
DIFY_API_KEY=app-xxxxxxxxxxxxxxxxxxxxx

DIFY_API_URL=http://192.168.4.20/v1/chat-messages


APP_NAME=dify-dingtalk-bot

```

注意：

你的 Dify 地址之前：

```env
DIFY_API_URL=http://192.168.4.20/v1

```

少了：

```
/chat-messages

```

应该：

```
http://192.168.4.20/v1/chat-messages

```

---

# 二、验证环境变量

不要启动机器人，先测试：

```bash
python

```

执行：

```python
from app.config.settings import settings

print("client:", settings.DING_CLIENT_ID)

print("secret:",
      bool(settings.DING_CLIENT_SECRET))

print("dify:",
      settings.DIFY_API_URL)

```

正确应该：

```text
client: xxxxxxxxxxxx

secret: True

dify: http://192.168.4.20/v1/chat-messages

```

如果还是：

```text
client: None

```

说明 dotenv 路径问题。

---

# 三、修复 websockets 兼容问题

你的错误：

```
AttributeError:
module 'websockets' has no attribute 'exceptions'

```

原因：

你现在：

```text
dingtalk-stream 0.24.3

```

依赖：

```text
websockets

```

但是装到了新版：

大概率：

```
websockets 15.x

```

新版删除了一些旧接口。

---

查看：

```bash
pip show websockets

```

应该类似：

```text
Version: 15.x

```

---

降级：

```bash
pip uninstall websockets -y

pip install websockets==10.4

```

然后确认：

```bash
pip show websockets

```

应该：

```
Version: 10.4

```

---

# 四、修改 requirements.txt

否则以后重新部署还会坏。

你的：

`app/requirements.txt`

改：

```txt
fastapi
uvicorn
python-dotenv
requests
dingtalk-stream==0.24.3
websockets==10.4

```

---

# 五、重新测试

执行：

```bash
cd /opt/dify-dingtalk-bot

source /opt/ding_dify/bin/activate

python -m app.main

```

正常应该看到：

类似：

```
INFO:dingtalk_stream.client:
open connection

INFO:dingtalk_stream.client:
connection established

INFO:dingtalk_stream.client:
websocket connected

```

而不是循环：

```
400 systemError

```

---

# 六、如果仍然 400

那就只剩钉钉后台配置。

重点检查：

## 1. Client ID

必须来自：

钉钉开放平台：

```
应用开发
 ↓
企业内部应用
 ↓
凭证与基础信息
 ↓
Client ID

```

不是：

- AppKey
- AgentId
- RobotCode

---

## 2. Stream模式开启

后台：

```
机器人管理
 ↓
消息接收模式

```

必须：

```
Stream模式

```

---

## 3. 发布应用

必须：

```
版本管理与发布
 ↓
上线

```

测试应用也需要发布。

---

## 4. 机器人权限

至少：

```
机器人消息接收
机器人发送消息

```

---

你现在先做两个动作：

### ① 取消 `.env` 注释

### ② 降级 websocket

执行：

```bash
pip uninstall websockets -y
pip install websockets==10.4

```

然后重新跑。

如果还有错误，把新的启动日志贴出来。你现在距离通已经很近了。

不对。你现在加日志的位置有问题，会导致程序启动时直接报错。

你写在文件顶部：

```python
logger.info(
    "Dify request question=%s user=%s",
    question,
    user
)

logger.info(
    "Dify response=%s",
    answer
)

```

但是此时：

```python
question
user
answer

```

三个变量都还不存在。

Python 加载 `dify_service.py` 时就会执行这里，所以会报：

```text
NameError: name 'question' is not defined

```

---

正确方式：**日志应该放到 `chat()` 方法内部。**

修改后的 `dify_service.py`：

```python
import requests
import json
import logging

from app.config.settings import settings


logger = logging.getLogger(__name__)


class DifyService:


    def __init__(self):

        self.url = settings.DIFY_API_URL

        self.key = settings.DIFY_API_KEY



    def chat(
        self,
        question: str,
        user: str
    ):


        logger.info(
            "Dify request question=%s user=%s",
            question,
            user
        )


        headers = {

            "Authorization":
            f"Bearer {self.key}",

            "Content-Type":
            "application/json"
        }


        payload = {

            "inputs": {},

            "query": question,

            "response_mode":
            "streaming",

            "conversation_id":"",

            "user": user
        }


        logger.info(
            "Dify url=%s",
            self.url
        )


        response = requests.post(

            self.url,

            headers=headers,

            json=payload,

            stream=True,

            timeout=120

        )


        logger.info(
            "Dify http status=%s",
            response.status_code
        )


        answer = ""


        for line in response.iter_lines():


            if not line:
                continue


            line = line.decode(
                "utf-8"
            )


            logger.debug(
                "Dify stream=%s",
                line
            )


            if not line.startswith(
                "data:"
            ):
                continue



            data=json.loads(
                line[5:]
            )


            event=data.get(
                "event"
            )


            if event=="message":

                chunk=data.get(
                    "answer",
                    ""
                )

                answer += chunk


                logger.info(
                    "Dify chunk=%s",
                    chunk
                )


            elif event=="message_end":

                break


            elif event=="error":

                logger.error(
                    "Dify error=%s",
                    data
                )


        logger.info(
            "Dify response=%s",
            answer
        )


        return answer

```

---

## 另外，你现在排查钉钉无回答，建议再加一个关键日志

在：

```python
response=requests.post(...)

```

后面增加：

```python
logger.info(
    "Dify headers=%s",
    response.headers
)

```

以及：

```python
if response.status_code != 200:
    logger.error(
        "Dify error body=%s",
        response.text
    )

```

否则如果 API Key 错误，你只看到：

```text
answer=""

```

不知道原因。

---

## 重启服务

如果你用 systemd：

```bash
systemctl restart dify-dingtalk

```

查看：

```bash
journalctl -u dify-dingtalk -f

```

然后钉钉发送问题。

你应该看到类似：

```text
Dify request question=公司报销流程是什么 user=xxx

Dify url=http://192.168.4.20/v1/chat-messages

Dify http status=200

Dify chunk=根据公司制度...

Dify response=根据公司制度...

```

---

如果日志显示：

```text
Dify http status=200
Dify response=

```

那就是 **Dify 应用配置问题**。

如果显示：

```text
401

```

就是 API Key。

如果显示：

```text
404

```

就是 API URL。

如果显示：

```text
200 有answer

```

但是钉钉没回复，就是 `dingtalk_service.py / handler` 的问题。你下一步把日志贴出来即可。

# Dify Webdav上传文件不能分段分析处理过程记录

现在基本可以确定问题位置了。

你的数据库信息非常关键：

```text
doc_type        | 
doc_metadata    | null
doc_form        | text_model
word_count      | 12

```

尤其：

```text
doc_type = 空
doc_metadata = null
word_count = 12

```

这说明：

**Dify 从 WebDAV 下载后，没有识别成 docx 文件。**

正常 docx 应该至少有：

```text
doc_type = docx
doc_metadata = {...}
word_count = 几百/几千

```

---

## 更关键的是 pipeline 日志

你的：

```json
datasource_type:
online_drive

```

输入：

```json
{
 "id": "/Rulr运维/研-儒来平台国产化部署项目说明文档.docx",
 "name": "研-儒来平台国产化部署项目说明文档.docx",
 "bucket": "webdav"
}

```

说明：

WebDAV 插件只告诉 Dify：

> 文件在哪里

但是没有告诉 Dify：

> 这是一个 docx 文件，并提供可解析的文件对象

所以 pipeline 直接进入：

```
online_drive
    ↓
download
    ↓
text_model
    ↓
general_chunker
    ↓
1 chunk

```

---

# 现在重点查 file\_id

你的 documents 表还有：

```text
file_id | text

```

但是刚才查询没有显示。

执行：

```sql
select
id,
file_id,
name
from documents
where id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

我预计：

### 情况1（最可能）

结果：

```
file_id | NULL

```

那么说明：

WebDAV插件没有把文件保存到 Dify storage。

---

### 情况2

有 file\_id：

例如：

```
file_id
--------
xxxx-xxxx

```

那么继续查：

```sql
select *
from upload_files
where id='file_id';

```

---

# 另外查 segments

现在已经知道它只有12字符，再确认一下：

```sql
select
id,
position,
content
from document_segments
where document_id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

我预计：

类似：

```
position | content
---------+-------
1        | 研-儒来平台国产化部署项目说明文档.docx

```

如果是这个，就100%确认。

---

# 根因定位

你的环境：

```
Dify 1.15.0
plugin_daemon 0.6.3
shaba/webdav

```

问题不在 Dify 分段参数。

因为：

第一次：

```
max_chunk_length=800

```

第二次：

```
max_chunk_length=1024

```

第三次：

```
max_chunk_length=1024

```

结果一直：

```
1 paragraph

```

说明：

chunker工作正常，只是输入文本只有12字符。

---

# 修复方向

## 方案A（推荐）：修改 shaba/webdav 插件

需要检查：

插件：

```
shaba/webdav

```

里的：

```
online_drive_download_file

```

这个接口。

它现在应该类似返回：

```json
{
 "type":"file",
 "url":"..."
}

```

但是 Dify datasource plugin 标准应该返回：

文件二进制流 + 文件名 + mime。

需要保证：

```python
filename:
研-儒来平台国产化部署项目说明文档.docx

mime:
application/vnd.openxmlformats-officedocument.wordprocessingml.document

```

---

## 方案B：不用 datasource，改同步上传

如果只是想把 WebDAV 当文件仓库：

可以：

```
WebDAV
 ↓
rclone sync
 ↓
本地目录
 ↓
Dify API上传

```

这样一定走：

```
upload_file
docx parser
chunk

```

---

## 方案C：检查插件版本

进入 plugin 容器：

```bash
docker exec -it docker-plugin_daemon-1 bash

```

查看安装插件：

```bash
ls /app/plugins

```

确认：

```
shaba/webdav

```

版本。

可能需要升级。

---

现在请再执行两个命令：

### 1.

```sql
select
id,
file_id,
name
from documents
where id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

### 2.

```sql
select
position,
content
from document_segments
where document_id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

这两个结果出来后，可以确定是 **插件返回文件问题** 还是 **下载文件内容问题**。目前已经非常接近根因。

这个结果非常关键，前面的判断需要修正一下：

**不是 WebDAV 没有上传文件。**

因为 `upload_files` 里已经有：

```text
研-儒来平台国产化部署项目说明文档.docx
extension = docx
mime_type = application/vnd.openxmlformats-officedocument.wordprocessingml.document

```

说明：

✅ WebDAV 下载文件成功  
✅ 文件类型识别正常  
✅ upload\_files 已生成文件记录

但是：

```text
documents.file_id = NULL

```

说明：

**文件已经上传到 storage，但知识库 document 没有关联这个 file\_id。**

这就是导致：

```text
doc_type = NULL
doc_metadata = NULL
word_count = 12
1 paragraph

```

的直接原因。

---

## 现在需要查关联关系

Dify 1.15 这里通常通过：

```
documents.file_id
        ↓
upload_files.id

```

关联。

你的异常：

```
documents
 id:
4e9e360a-5ab3-4957-8de9-75b76e173bdb

file_id:
空

```

但是 upload\_files 有：

```
9dc4b86e-7f4c-4804-a5a8-f81742148ddc

```

这两个没有连起来。

---

## 找对应的 upload\_file

执行：

```sql
select
id,
name,
extension,
created_at
from upload_files
where name='研-儒来平台国产化部署项目说明文档.docx'
order by created_at desc;

```

你会看到多个。

注意时间：

你的 document：

```
created_at:
2026-07-09 07:31:48

```

但是 upload\_files：

例如：

```
2026-07-09 02:40:27

```

这里有时区差：

PostgreSQL显示 UTC，本地显示 +8。

大概率对应：

```text
9b2f7c06-e091-4332-a3e9-83c84af21bb8

```

或者：

```text
b69aa5e5-f81d-447f-a13f-8b106c03f5f4

```

---

## 验证一下

执行：

```sql
select
d.id as document_id,
d.name,
d.file_id,
u.id as upload_id,
u.name as upload_name
from documents d
left join upload_files u
on d.file_id=u.id
where d.name='研-儒来平台国产化部署项目说明文档.docx';

```

现在应该显示：

```
document_id                         file_id upload_id
----------------------------------------------------
4e9e360a...                         NULL    NULL

```

---

# 根因已经变成：

不是：

❌ WebDAV插件不能下载文件

而是：

❌ WebDAV datasource pipeline 创建 document 时，没有把 upload\_file\_id 写入 documents.file\_id

---

## 为什么本地上传正常？

本地：

```
upload_files
    |
    |
documents.file_id
    |
docx parser

```

WebDAV：

```
upload_files
    |
    X
documents.file_id=NULL
    |
text_model

```

---

# 修复方案

## 方案1：修 shaba/webdav 插件（推荐）

检查插件代码：

```
online_drive_download_file

```

它应该返回：

类似：

```json
{
 "file_id":"9dc4b86e-7f4c-4804-a5a8-f81742148ddc"
}

```

现在返回可能只有：

```json
{
 "id":"/Rulr运维/xxx.docx",
 "name":"xxx.docx",
 "bucket":"webdav"
}

```

---

## 方案2：手工验证（不要长期用）

可以直接更新：

```sql
update documents
set file_id='对应upload_files.id'
where id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

然后重新分段。

如果马上恢复：

说明100%就是关联问题。

⚠️ 建议先备份数据库。

---

## 还有一个需要查的表

Dify 1.15 datasource 可能还记录文件绑定：

执行：

```sql
select *
from segment_attachment_bindings
where document_id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

不过目前不是重点。

---

现在请执行：

```sql
select
id,
name,
created_at
from upload_files
where name='研-儒来平台国产化部署项目说明文档.docx'
order by created_at desc;

```

然后把结果贴一下。

我们找出和这个 document 对应的 upload\_file\_id，做一次手工关联测试，就能最终确认。

这个错误也很有价值：

```text
ERROR: operator does not exist: text = uuid
LINE 9: on d.file_id=u.id

```

说明：

```sql
documents.file_id  类型 = text
upload_files.id    类型 = uuid

```

所以 PostgreSQL 不会自动比较，需要转换类型。

执行：

```sql
select
d.id as document_id,
d.name,
d.file_id,
u.id as upload_id,
u.name as upload_name
from documents d
left join upload_files u
on d.file_id = u.id::text
where d.name='研-儒来平台国产化部署项目说明文档.docx';

```

不过根据你前面的结果：

```text
documents.file_id = 空

```

大概率结果还是：

```
document_id                         file_id   upload_id
-------------------------------------------------------
4e9e360a-5ab3-4957-8de9-75b76e173bdb NULL      NULL

```

---

## 下一步我们需要找对应的 upload\_file

你的 WebDAV 重试产生了 5 个同名文件：

```
2026-07-09 01:40:07
2026-07-09 02:20:31
2026-07-09 02:27:43
2026-07-09 02:40:27
2026-07-09 02:49:38

```

对应你的 document：

```
documents.created_at
2026-07-09 07:31:48

```

这里有时区 +8：

Postgres:

```
2026-07-09 07:31:48

```

实际北京时间：

```
2026-07-09 15:31:48

```

而 upload\_files：

```
2026-07-09 02:49:38 UTC

```

北京时间：

```
2026-07-09 10:49:38

```

不完全对应。

所以还需要查 pipeline 日志里的创建时间：

```
document_pipeline_execution_logs
created_at
2026-07-09 07:31:48

```

对应 upload\_file 可能是：

```text
f20a94e5-19b2-4219-b991-5abbc3ea0627

```

但不要直接改，先验证。

---

## 验证 upload\_file 内容

查：

```sql
select
id,
name,
key,
size,
extension,
mime_type
from upload_files
where id='f20a94e5-19b2-4219-b991-5abbc3ea0627';

```

如果有：

```
key
bucket
size

```

说明是真文件。

---

## 然后做一个临时修复测试

先备份：

```sql
create table documents_backup as
select * from documents
where id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

然后：

```sql
update documents
set file_id='f20a94e5-19b2-4219-b991-5abbc3ea0627'
where id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

重新执行知识库：

- 删除当前分段
- 重新分段

如果：

```
word_count
从 12
变成几千

paragraphs
从 1
变几十/几百

```

那么根因完全确认。

---

不过还有一个可能性：

你的日志里：

```
POST plugin_daemon dispatch/tool/invoke
Tool: general_chunker

Outputs:
{
 "variable_value":["online_drive"]
}

```

这里很异常。

正常应该：

```
docx parser
    ↓
text
    ↓
general_chunker

```

现在像是：

```
online_drive
    ↓
general_chunker

```

也就是说 **WebDAV datasource 没有触发文件解析节点**。

所以最终修复点大概率还是：

`shaba/webdav` 插件的数据源返回结构。

---

你先跑上面的：

```sql
select
id,
name,
key,
size,
extension,
mime_type
from upload_files
where id='f20a94e5-19b2-4219-b991-5abbc3ea0627';

```

以及：

```sql
\d upload_files

```

我需要确认 Dify 1.15 的 upload\_files 字段结构，然后给你准确的关联修复方案。

现在可以下最终结论了。

你的数据链路如下：

```
upload_files
        |
        |  正常
        ↓
f20a94e5-19b2-4219-b991-5abbc3ea0627
研-儒来平台国产化部署项目说明文档.docx
size=2847874
mime_type=docx
key=upload_files/...docx


documents
        |
        | 断了
        ↓
4e9e360a-5ab3-4957-8de9-75b76e173bdb
file_id=NULL

```

所以：

**文件已经存在，但 document 没有关联文件。**

这就是为什么：

```
word_count = 12
paragraph = 1
doc_type = NULL

```

因为 Dify 没走 docx parser，而是把 datasource metadata 当文本处理了。

---

## 先做一次手工修复验证

建议先不要改插件，验证关联是否能解决。

### 1. 备份当前 document

```sql
create table documents_backup_20260710 as
select *
from documents
where id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

---

### 2. 写入 file\_id

执行：

```sql
update documents
set file_id='f20a94e5-19b2-4219-b991-5abbc3ea0627'
where id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

确认：

```sql
select
id,
name,
file_id
from documents
where id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

应该：

```
id                                   file_id
---------------------------------------------------
4e9e360a...                          f20a94e5...

```

---

## 但是注意

仅修改 `file_id` **可能还不够**。

因为你的 document 已经生成过错误 segment：

检查：

```sql
select
count(*)
from document_segments
where document_id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

如果 &gt;0：

需要删除旧分段：

```sql
delete from document_segments
where document_id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

然后重新触发分段。

---

## 为什么 WebDAV 会出现这个问题？

从你的日志：

```
POST /dispatch/datasource/online_drive_download_file
HTTP 200

```

说明插件下载成功。

但是后续：

```
general_chunker
Outputs:
["online_drive"]

```

这里说明 pipeline 收到的是：

```
online_drive文本

```

不是：

```
docx文件

```

正常流程应该：

```
WebDAV
 |
download_file
 |
upload_files
 |
file_id
 |
document.file_id
 |
docx parser
 |
chunk
 |
embedding

```

你的实际：

```
WebDAV
 |
download_file
 |
upload_files     ✅
 |
document        ❌ file_id丢失
 |
general_chunker
 |
1段

```

---

# 长期修复点

应该改：

```
shaba/webdav

```

插件。

重点看：

```
online_drive_download_file

```

返回结构。

它现在应该没有返回：

```json
{
  "file_id": "f20a94e5-19b2-4219-b991-5abbc3ea0627"
}

```

或者没有触发 document 创建时绑定。

---

## 建议下一步

先执行手工：

```sql
update documents
set file_id='f20a94e5-19b2-4219-b991-5abbc3ea0627'
where id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

然后在 Dify 界面点：

```
知识库
→ 文档
→ 重新索引

```

看：

```
文档信息

```

是否变成：

```
doc_type: docx
word_count: xxxx
段落数量: xx

```

如果成功，就可以确定 100% 是 WebDAV 插件的 file\_id 绑定 bug。

下一步再改插件，不需要继续排查 Dify 核心。

很好，现在验证环境已经准备好了。

当前状态：

✅ `documents.file_id` 已补上：

```text
4e9e360a-5ab3-4957-8de9-75b76e173bdb
        |
        |
        v
f20a94e5-19b2-4219-b991-5abbc3ea0627

```

✅ 错误生成的 segment 已删除：

```text
document_segments = 0

```

下一步不要直接重新创建 segment，建议先检查 document 状态字段，否则 Dify 可能认为已经处理完成。

---

## 1. 查看当前 document 状态

执行：

```sql
select
id,
word_count,
tokens,
doc_type,
indexing_status,
parsing_completed_at,
splitting_completed_at,
completed_at,
error
from documents
where id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

重点看：

```text
indexing_status
splitting_completed_at
completed_at

```

---

## 2. 重置处理状态

如果现在类似：

```text
indexing_status = completed
splitting_completed_at 有值
completed_at 有值

```

需要清掉：

执行：

```sql
update documents
set
word_count=null,
tokens=null,
parsing_completed_at=null,
cleaning_completed_at=null,
splitting_completed_at=null,
completed_at=null,
indexing_status='waiting',
error=null
where id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

---

## 3. 重新触发索引

推荐优先用 UI：

```
知识库
 → 文档
 → 研-儒来平台国产化部署项目说明文档.docx
 → 重新索引

```

观察 worker：

```bash
docker logs -f docker-worker-1

```

正常应该看到：

类似：

```
parse document
loader: docx
extract text
split document
generate segments
embedding xxx texts

```

而不是：

```
general_chunker
online_drive
1 texts

```

---

## 4. 验证结果

重新完成后：

### documents

```sql
select
name,
word_count,
doc_type,
tokens
from documents
where id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

期待：

类似：

```
name                                  word_count doc_type tokens
---------------------------------------------------------------
研-儒来平台国产化部署项目说明文档.docx  5000       docx     xxxx

```

---

### segments

```sql
select
count(*)
from document_segments
where document_id='4e9e360a-5ab3-4957-8de9-75b76e173bdb';

```

期待：

```
几十 / 上百

```

而不是：

```
1

```

---

如果重新索引后恢复，就可以最终确认：

**shaba/webdav 插件 bug：创建 document 时遗漏 `documents.file_id`。**

之后修插件只需要在 datasource pipeline 创建 document 时补：

```python
document.file_id = upload_file.id

```

即可。

你现在先执行第 1 步，把 document 状态贴出来，我确认是否需要 reset。

# xwiki-sync v1.0 功能说明书

根据目前日志和目录结构，你的 **xwiki-sync v1.0 稳定版** 已经具备一个完整的“共享目录 → XWiki 文档库”的同步闭环：

- 文件新增 ✅
- 文件修改 ✅
- 文件删除同步 ✅
- XWiki 页面创建/更新/删除 ✅
- 自动空间映射 ✅
- 文件 Hash 增量检测 ✅
- SQLite 状态记录 ✅
- Docker 运行 ✅
- Tika 文档解析 ✅

下面整理成正式归档文档，建议保存为：

```
/opt/xwiki/sync/docs/xwiki-sync-v1.0功能说明书.md

```

---

# xwiki-sync v1.0 功能说明书

## 1. 项目名称

**xwiki-sync**

版本：

```
v1.0 Stable

```

发布日期：

```
2026-07-18

```

用途：

> 将 SMB 共享目录中的文档自动同步到 XWiki，实现企业内部文档自动化管理。

---

# 2. 系统架构

整体架构：

```
                 SMB共享目录
                      |
                      |
                scanner/smb.py
                      |
                      |
              文件扫描模块
              core/scanner.py
                      |
                      |
             文件状态数据库
             SQLite Database
                      |
          +-----------+-----------+
          |                       |
      新文件                  已存在文件
          |                       |
          |                   Hash检测
          |                       |
          +-----------+-----------+
                      |
              sync/processor.py
                      |
          +-----------+-----------+
          |
      文档解析
      extractor/tika.py
          |
          |
      XWiki REST API
          |
          |
      XWiki页面



```

---

# 3. 目录结构说明

```
sync
│
├── app.py
│
├── clients
│   └── xwiki.py
│
├── core
│   ├── database.py
│   ├── hash.py
│   ├── hashdb.py
│   └── scanner.py
│
├── extractor
│   └── tika.py
│
├── scanner
│   └── smb.py
│
├── sync
│   └── processor.py
│
├── models
│   └── fileinfo.py
│
├── config.yaml
│
├── Dockerfile
│
└── upgrade_db.py


```

---

# 4. 核心功能说明

# 4.1 文件扫描功能

模块：

```
scanner/smb.py
core/scanner.py

```

功能：

定时扫描共享目录。

默认周期：

```
60秒

```

扫描结果：

例如：

```
/data/wiki_attach/test.txt


```

转换为同步任务：

```
space = wiki_attach

page = test


```

---

# 4.2 文件路径映射规则

## 根目录文件

例如：

```
/data/web.config

```

转换：

```
XWiki:

Main.web


```

说明：

```
默认进入 Main 空间

```

---

## 一级目录

例如：

```
/data/wiki_attach/docker安装.txt


```

转换：

```
Space:

wiki_attach


Page:

docker安装


```

XWiki：

```
wiki_attach.docker安装


```

---

## 多级目录

例如：

```
/data/a/b/c.txt


```

转换：

```
Space:

a


Page:

b_c


```

规则：

```
目录名称_Page名称


```

---

# 4.3 文件类型支持

当前通过：

```
Apache Tika

```

解析。

支持：

<table id="bkmrk-%E7%B1%BB%E5%9E%8B-%E7%8A%B6%E6%80%81-txt-%E6%94%AF%E6%8C%81-docx-%E6%94%AF%E6%8C%81"><thead><tr><th>类型</th><th>状态</th></tr></thead><tbody><tr><td>TXT</td><td>支持</td></tr><tr><td>DOCX</td><td>支持</td></tr><tr><td>DOC</td><td>支持</td></tr><tr><td>PDF</td><td>支持</td></tr><tr><td>XLSX</td><td>支持</td></tr><tr><td>PPTX</td><td>支持</td></tr></tbody></table>

---

# 4.4 增量同步

核心：

```
core/hash.py
core/database.py


```

机制：

文件计算 SHA Hash。

例如：

第一次：

```
test.txt

hash:

abc123


```

数据库保存：

```
文件路径
hash
space
page


```

下一次扫描：

如果：

```
hash相同


```

跳过：

日志：

```
跳过:
xxx 未变化


```

避免重复写入 XWiki。

---

# 4.5 新文件同步

流程：

```
发现文件

↓

计算Hash

↓

数据库不存在

↓

Tika解析

↓

创建XWiki页面

↓

保存数据库


```

日志：

例如：

```
新文件:
wiki_attach.docker镜像导出和导入


页面创建成功

同步完成


```

---

# 4.6 文件修改同步

检测：

```
旧hash != 新hash


```

流程：

```
重新解析

↓

更新XWiki页面

↓

更新数据库hash


```

日志：

```
文件变化:

xxx

页面更新成功


```

---

# 4.7 删除同步

这是 v1.0 新增稳定功能。

流程：

共享目录：

```
test.txt

删除



```

扫描发现：

```
数据库存在

实际文件不存在


```

执行：

```
XWiki REST DELETE


```

日志：

```
发现删除文件:

/data/test.txt


删除XWiki页面:

Main.test


XWiki删除成功

删除完成


```

---

# 5. XWiki REST接口

客户端：

```
clients/xwiki.py


```

功能：

## 测试连接

接口：

```
GET

/rest/wikis/{wiki}/spaces


```

返回：

```
200


```

---

## 创建页面

接口：

```
PUT

/rest/wikis/xwiki/spaces/{space}/pages/{page}


```

---

## 更新页面

同创建接口：

```
PUT


```

XWiki自动判断：

```
新增
或者
版本更新


```

---

## 删除页面

接口：

```
DELETE

/rest/wikis/xwiki/spaces/{space}/pages/{page}


```

成功：

```
200
202
204


```

不存在：

```
404


```

均视为同步成功。

---

# 6. 数据库设计

数据库：

SQLite

用途：

保存同步状态。

主要字段：

<table id="bkmrk-%E5%AD%97%E6%AE%B5-%E8%AF%B4%E6%98%8E-file_path-%E6%96%87%E4%BB%B6%E8%B7%AF%E5%BE%84"><thead><tr><th>字段</th><th>说明</th></tr></thead><tbody><tr><td>file\_path</td><td>文件路径</td></tr><tr><td>hash</td><td>文件SHA</td></tr><tr><td>space</td><td>XWiki空间</td></tr><tr><td>page</td><td>XWiki页面</td></tr><tr><td>deleted</td><td>删除状态</td></tr></tbody></table>

---

# 7. Docker部署

运行模式：

```
Docker Container


```

容器：

```
xwiki-sync


```

依赖：

```
xwiki-web

tika

smb共享


```

网络：

```
Docker bridge


```

---

# 8. 配置文件

文件：

```
config.yaml


```

包含：

## XWiki

例如：

```yaml
xwiki:
  url: http://xwiki-web:8080
  wiki: xwiki
  user: admin
  password: ****

```

---

## SMB

例如：

```yaml
smb:
  server:
  share:
  username:
  password:

```

---

## 同步周期

例如：

```yaml
interval: 60


```

单位：

秒

---

# 9. 日志说明

正常：

```
发现文件 3 个


```

新增：

```
新文件:

xxx


```

跳过：

```
跳过:

xxx 未变化


```

删除：

```
发现删除文件

删除XWiki页面

XWiki删除成功


```

异常：

```
同步失败


```

---

# 10. 已知设计特点

## 优点

### 1. 增量同步

不会重复处理大量文档。

### 2. 状态可恢复

数据库保存状态。

### 3. 支持中文名称

例如：

```
新建空间测试1

主从复制_2025030401


```

### 4. XWiki自动版本管理

更新不会覆盖历史。

---

# 11. 当前限制

## 11.1 空间创建

目前：

```
create_space()


```

XWiki REST 返回：

```
405 Method Not Allowed


```

原因：

XWiki默认不允许REST创建Space。

当前策略：

```
忽略Space创建失败

继续创建页面


```

实际运行正常。

---

## 11.2 删除空间

未实现。

删除：

```
页面


```

不删除：

```
Space


```

---

## 11.3 附件同步

当前：

```
文件内容同步


```

不是：

```
XWiki Attachment


```

---

# 12. 稳定性验证记录

## 新增测试

文件：

```
/data/wiki_attach/docker镜像导出和导入.txt


```

结果：

```
同步完成


```

---

## 删除测试

删除：

```
docker镜像导出和导入.txt

创建容器四代网关协议代理适配器rulr-v4gw-adapter.txt


```

结果：

```
XWiki删除成功


```

---

## 连续运行测试

周期：

```
60秒


```

结果：

```
无重复同步

无异常


```

---

# 13. 版本状态

当前版本：

```
xwiki-sync v1.0 Stable


```

状态：

```
生产可用


```

已验证：

<table id="bkmrk-%E5%8A%9F%E8%83%BD-%E7%8A%B6%E6%80%81-%E6%96%B0%E5%A2%9E%E5%90%8C%E6%AD%A5-%E2%9C%85-%E4%BF%AE%E6%94%B9%E5%90%8C%E6%AD%A5-%E2%9C%85-"><thead><tr><th>功能</th><th>状态</th></tr></thead><tbody><tr><td>新增同步</td><td>✅</td></tr><tr><td>修改同步</td><td>✅</td></tr><tr><td>删除同步</td><td>✅</td></tr><tr><td>中文路径</td><td>✅</td></tr><tr><td>中文页面</td><td>✅</td></tr><tr><td>DOCX解析</td><td>✅</td></tr><tr><td>TXT解析</td><td>✅</td></tr><tr><td>XWiki REST</td><td>✅</td></tr><tr><td>Docker运行</td><td>✅</td></tr><tr><td>断点恢复</td><td>✅</td></tr></tbody></table>

---

# 14. 后续规划建议

v1.1：

- 增加附件上传
- 增加XWiki标签
- 增加文档权限映射
- 增加同步失败重试队列

v1.2：

- Web管理界面
- 同步状态监控
- 操作审计日志

---

# 结论

**xwiki-sync v1.0 已完成共享目录到 XWiki 文档中心的自动化同步闭环。**

当前版本满足：

> 企业内部文档自动归档、同步、删除管理需求。

可作为生产环境基础版本长期运行。

---

建议归档：

```
/opt/xwiki/sync/docs/
        |
        └── xwiki-sync-v1.0功能说明书.md


```

同时建议备份：

```
config.yaml
database.db
Dockerfile

```

作为 v1.0 基线版本。

# Ubuntu 24.04 + Docker，部署 LiteLLM v1.87.0

你当前环境是 **Ubuntu 24.04 + Docker**，部署 LiteLLM v1.87.0 最推荐使用 **Docker Compose**，比官方单行 `docker run` 更适合生产环境（持久化、数据库、配置、密钥管理）。

下面给你一个完整部署方案。

---

# 一、环境准备

安装 Docker：

```
#Ubuntu24.04 安装Docker&Compose

sudo apt update && sudo apt install -y ca-certificates curl gnupg lsb-release git

# 重新下载密钥并生成统一签名环文件
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# 赋予可读权限
sudo chmod 644 /usr/share/keyrings/docker-archive-keyring.gpg

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# 开机自启
sudo systemctl enable --now docker
# 当前用户免sudo操作docker
sudo usermod -aG docker $USER
# 刷新用户组，不用重启服务器
newgrp docker

# 验证Docker安装

docker --version
docker compose version

```

配置国内docker加速器：

```
mkdir -p /etc/docker
cat > /etc/docker/daemon.json <<EOF
{
  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn",
    "https://hub-mirror.c.163.com"
  ]
}
EOF
systemctl daemon-reload
systemctl restart docke
```

确认 Docker：

```bash
docker --version
docker compose version

```

建议：

```text
Docker >= 24
Docker Compose >= 2.x

```

---

# 二、创建目录

例如：

```bash
mkdir -p /opt/litellm
cd /opt/litellm

```

目录：

```text
/opt/litellm
├── docker-compose.yml
├── .env
└── config.yaml

```

---

# 三、创建 LiteLLM 配置

## config.yaml

```bash
vi config.yaml

```

内容：

```yaml
model_list:

  # OpenAI 示例
  - model_name: gpt-5
    litellm_params:
      model: openai/gpt-5
      api_key: os.environ/OPENAI_API_KEY


  # Gemini 示例
  - model_name: gemini-3.5-flash
    litellm_params:
      model: vertex_ai/gemini-3.5-flash
      api_key: os.environ/GEMINI_API_KEY


general_settings:

  master_key: os.environ/LITELLM_MASTER_KEY

  # 开启数据库存储
  store_model_in_db: true

```

---

# 四、创建环境变量

## .env

```bash
vi .env

```

例如：

```env
LITELLM_MASTER_KEY=sk-litellm-admin-123456

OPENAI_API_KEY=sk-xxxx

GEMINI_API_KEY=xxxx

```

---

# 五、Docker Compose 部署（推荐）

创建：

```bash
vi docker-compose.yml

```

内容：

```yaml
services:

  litellm:
    image: ghcr.io/berriai/litellm:1.87.0

    container_name: litellm

    restart: always

    ports:
      - "4000:4000"

    environment:

      STORE_MODEL_IN_DB: "True"

      DATABASE_URL: postgresql://litellm:litellm@postgres:5432/litellm

      LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}

      OPENAI_API_KEY: ${OPENAI_API_KEY}

      GEMINI_API_KEY: ${GEMINI_API_KEY}


    volumes:

      - ./config.yaml:/app/config.yaml


    command:
      --config /app/config.yaml


    depends_on:
      - postgres



  postgres:

    image: postgres:16

    container_name: litellm-postgres

    restart: always


    environment:

      POSTGRES_DB: litellm

      POSTGRES_USER: litellm

      POSTGRES_PASSWORD: litellm


    volumes:

      - ./postgres:/var/lib/postgresql/data

```

---

# 六、启动

执行：

```bash
# 通过南大ghcr代理拉取镜像
docker pull ghcr.nju.edu.cn/berriai/litellm:1.87.0
# 还原原始镜像标签，适配compose
docker tag ghcr.nju.edu.cn/berriai/litellm:1.87.0 ghcr.io/berriai/litellm:1.87.0
docker rmi ghcr.nju.edu.cn/berriai/litellm:1.87.0

docker compose up -d

```

查看：

```bash
docker ps

```

应该：

```text
litellm
litellm-postgres

```

---

查看日志：

```bash
docker logs -f litellm

```

正常：

```text
LiteLLM Proxy Server Started
Server running on port 4000

```

---

# 七、测试 API

LiteLLM 默认兼容 OpenAI API。

测试：

```bash
curl http://127.0.0.1:4000/v1/models \
-H "Authorization: Bearer sk-litellm-admin-123456"

```

返回：

```json
{
 "data":[
   {
    "id":"gpt-5"
   }
 ]
}

```

---

测试聊天：

```bash
curl http://127.0.0.1:4000/v1/chat/completions \
-H "Authorization: Bearer sk-litellm-admin-123456" \
-H "Content-Type: application/json" \
-d '
{
 "model":"gpt-5",
 "messages":[
   {
    "role":"user",
    "content":"hello"
   }
 ]
}'

```

---

# 八、访问管理后台

LiteLLM v1.87 有 Dashboard。

访问：

```
http://服务器IP:4000/ui

```

例如：

```
http://192.168.4.21:4000/ui

```

使用：

```
Master Key:
sk-litellm-admin-123456

```

登录。

---

# 九、如果只是测试（官方方式）

不需要 PostgreSQL：

```bash
docker run \
-d \
--name litellm \
-p 4000:4000 \
-e STORE_MODEL_IN_DB=True \
docker.litellm.ai/berriai/litellm:1.87.0

```

但是：

缺点：

- 配置丢失
- 无数据库
- 无用户管理
- 无 Key 管理

不建议生产。

---

# 十、结合你之前的环境（Dify / RAGFlow）

你的环境里已经有：

- Dify
- RAGFlow
- Keycloak
- PostgreSQL
- Docker Compose

LiteLLM 很适合作为统一 AI Gateway：

架构：

```
                 Dify
                   |
                   |
               OpenAI API
                   |
                   v
              LiteLLM 4000
                   |
     +-------------+-------------+
     |             |             |
  OpenAI       Gemini       Ollama
     |
  Azure OpenAI

```

Dify 中配置：

模型类型：

```
OpenAI Compatible

```

Base URL:

```
http://litellm:4000/v1

```

API Key:

```
sk-litellm-admin-123456

```

即可。

---

# 十一、生产建议

如果是你的服务器环境，我建议：

```
Ubuntu 24.04
        |
Docker
        |
Nginx HTTPS
        |
LiteLLM 1.87
        |
PostgreSQL 16
        |
Keycloak OAuth

```

再加：

- Redis（限流）
- Prometheus（监控）
- Grafana（Dashboard）

---

你这里的：

```env
LITELLM_MASTER_KEY=sk-litellm-admin-123456

```

就是 LiteLLM Dashboard 登录密码（Master Key）。

修改方法如下。

---

## 1. 修改 `.env`

编辑：

```bash
cd /opt/litellm
vi .env

```

例如改成：

```env
LITELLM_MASTER_KEY=sk-litellm-admin-9f83a7d2c1e54b6a

OPENAI_API_KEY=sk-xxxx

GEMINI_API_KEY=xxxx

```

建议生成随机值：

```bash
openssl rand -hex 32

```

例如：

```
a9d7f3e8b4c1d2e6f7a8b9c0d1e2f3456789abcd

```

然后：

```env
LITELLM_MASTER_KEY=sk-a9d7f3e8b4c1d2e6f7a8b9c0d1e2f3456789abcd

```

---

## 2. 重启 LiteLLM

因为环境变量是在容器启动时读取：

```bash
docker compose down

docker compose up -d

```

或者：

```bash
docker compose restart litellm

```

如果 `.env` 被 compose 读取，推荐：

```bash
docker compose up -d --force-recreate litellm

```

确保新环境变量进入容器。

---

## 3. 验证新 Key

查看容器：

```bash
docker exec litellm env | grep LITELLM_MASTER_KEY

```

应该看到：

```text
LITELLM_MASTER_KEY=sk-litellm-admin-9f83a7d2c1e54b6a

```

---

## 4. Dashboard重新登录

访问：

```
http://服务器IP:4000/ui

```

输入：

```
Master Key:
sk-litellm-admin-9f83a7d2c1e54b6a

```

即可。

---

## 注意：不要只改 `.env` 后 restart

很多人这里容易踩坑。

例如：

```bash
vi .env
docker compose restart litellm

```

有时不会重新加载环境变量，因为容器已经存在。

确认：

```bash
docker inspect litellm | grep LITELLM_MASTER_KEY

```

如果还是旧值，需要：

```bash
docker compose up -d --force-recreate litellm

```

---

## 生产环境建议

你当前：

```env
LITELLM_MASTER_KEY=sk-litellm-admin-123456

```

属于测试密码。

建议：

```env
LITELLM_MASTER_KEY=sk-prod-$(openssl rand -hex 32)

```

并且：

- Master Key 只用于管理 Dashboard
- Dify/RAGFlow 使用单独生成的 API Key
- 不要把 Master Key 放到应用配置里

后续如果接 Dify，可以在 LiteLLM Dashboard 创建：

```
Keys → Generate Key

```

例如：

```
sk-dify-prod-xxxx

```

然后 Dify 使用这个，而不是 Master Key。

你的 LiteLLM 已经部署好了，现在添加阿里百炼（DashScope / Model Studio）的 **qwen3.7-max** 很简单。

LiteLLM 对百炼支持 OpenAI Compatible 接口，配置方式和 OpenAI 类似，只需要指定：

- `model`
- `api_base`
- `api_key`

阿里云百炼 qwen3.7-max 提供 OpenAI 兼容接口，模型 ID 为：

```text
qwen3.7-max

```

对应接口地址可以使用百炼 OpenAI Compatible Endpoint。([AlibabaCloud](https://www.alibabacloud.com/help/en/model-studio/models?utm_source=chatgpt.com "Supported Models and Capabilities Overview - Model Studio - Alibaba Cloud - Alibaba Cloud Model Studio - Alibaba Cloud Documentation Center"))

---

## 1. 修改 .env

你现在：

```bash
cat /opt/litellm/.env

```

类似：

```env
LITELLM_MASTER_KEY=sk-litellm-admin-123456

OPENAI_API_KEY=sk-xxxx

GEMINI_API_KEY=xxxx

```

增加：

```env
DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxxxx

```

例如：

```env
LITELLM_MASTER_KEY=sk-litellm-admin-123456

OPENAI_API_KEY=sk-xxxx

GEMINI_API_KEY=xxxx

DASHSCOPE_API_KEY=sk-abcd123456789

```

这个 Key 在：

阿里云百炼控制台 → API Key 管理

创建。

---

## 2. 修改 LiteLLM config.yaml

假设你的配置：

```bash
/opt/litellm/config.yaml

```

增加：

```yaml
model_list:

  - model_name: gpt-5
    litellm_params:
      model: openai/gpt-5
      api_key: os.environ/OPENAI_API_KEY


  - model_name: gemini-3.5-flash
    litellm_params:
      model: gemini/gemini-3.5-flash
      api_key: os.environ/GEMINI_API_KEY


  # 阿里百炼 qwen3.7-max
  - model_name: qwen3.7-max
    litellm_params:
      model: openai/qwen3.7-max
      api_base: https://dashscope.aliyuncs.com/compatible-mode/v1
      api_key: os.environ/DASHSCOPE_API_KEY

```

LiteLLM 的 `model_name` 是暴露给 Dify/RAGFlow 的名字，`litellm_params.model` 是实际供应商模型。([docs.litellm.com.cn](https://docs.litellm.com.cn/docs/proxy/configs?utm_source=chatgpt.com "概览 | liteLLM 网关"))

---

## 3. 重启 LiteLLM

因为 `.env` 修改了：

不要只 restart。

执行：

```bash
cd /opt/litellm

docker compose up -d --force-recreate litellm

```

查看：

```bash
docker compose logs -f litellm

```

---

## 4. 检查模型是否出现

执行：

```bash
curl http://127.0.0.1:4000/v1/models \
-H "Authorization: Bearer sk-litellm-admin-123456"

```

应该看到：

```json
{
 "data":[
   {
     "id":"gpt-5"
   },
   {
     "id":"gemini-3.5-flash"
   },
   {
     "id":"qwen3.7-max"
   }
 ]
}

```

---

## 5. 测试 qwen3.7-max

```bash
curl http://127.0.0.1:4000/v1/chat/completions \
-H "Authorization: Bearer sk-litellm-admin-123456" \
-H "Content-Type: application/json" \
-d '
{
 "model":"qwen3.7-max",
 "messages":[
   {
    "role":"user",
    "content":"介绍一下阿里百炼"
   }
 ]
}'

```

成功返回：

```json
{
 "choices":[
   {
    "message":{
      "content":"..."
    }
   }
 ]
}

```

---

## 6. 给 Dify 使用

你现在 Dify 调 LiteLLM，建议暴露统一名字：

例如：

```yaml
- model_name: qwen-max
  litellm_params:
    model: openai/qwen3.7-max
    api_base: https://dashscope.aliyuncs.com/compatible-mode/v1
    api_key: os.environ/DASHSCOPE_API_KEY

```

以后 Dify 只配置：

```
qwen-max

```

以后换：

```
qwen3.7-max
↓
qwen4-max
↓
DeepSeek

```

Dify 不需要改。

---

## 7. 你的最终架构会变成

```
              Dify
                |
                |
          OpenAI API格式
                |
                |
          LiteLLM 1.87
                |
    +-----------+------------+
    |           |            |
 GPT-5     Gemini       Qwen3.7-Max
 OpenAI    Google       阿里百炼

```

这正适合你之前的 Dify + RAGFlow + 企业知识库架构。

另外注意：你之前日志里 `/v1/responses` 是 OpenAI Responses API，如果后续用 Codex/Agent 类客户端调用 qwen3.7-max，需要确认该模型端点对 Responses API 的兼容性；Dify 通常走 `/v1/chat/completions`，问题会少一些。