Ragflow是目前团队化部署大模型+RAG的优质方案,不过其仍不适合直接部署使用,本文将从实际使用的角度,对其进行二次开发。
1. Ragflow 存在问题
Ragflow 开源仓库地址:https://github.com/infiniflow/ragflow
Ragflow 当前版本: v0.17.0
Ragflow 目前主要存在以下问题:
-
登录页开放注册
当前版本,在login页面,用户可直接进行注册,在小规模私有化部署中,开放注册接口,易对用户产生困扰,甚至存在被频繁调用攻击的风险。 -
知识库共享问题
当前版本,团队成员进行知识库共享需要知识库的创建者邀请其它成员进团队,发出邀请时,需要其它成员点击接受才行,较为繁琐。 -
模型设置问题
当前版本,所有团队成员的模型设置是独立的,如需共用同一套模型配置,需要每个用户单独进行设置,不利于团队化协作。 -
可视化管理
当前版本,未存在超级管理员的后台界面,无法直观的对用户账户进行可视化管理。
本文将围绕以上四点问题,对Ragflow进行二次开发解决。
2. 开源协议
Ragflow采用的是较为宽松的Apache License 2.0
,这意味着其允许进行二次开发和商用,且修改后可无需开源。
3. 技术栈分析
3.1 容器组件分析
通过docker启动该服务时,docker-compose-base.yml
包含了部分基础配置参数,可以看到整个服务共包含5个容器组件:
services:es01:container_name: ragflow-es-01infinity:container_name: ragflow-infinitymysql:container_name: ragflow-mysqlminio:container_name: ragflow-minioredis:container_name: ragflow-redis
各组件功能如下:
-
Elasticsearch
主要用作文档引擎,负责存储和检索文本及向量数据,作为系统的知识库存储后端,用以支持向量存储和相似度搜索 -
ragflow-infinity
前端系统,包含基本的界面显示、数据交互、路由跳转等功能 -
MySQL
关系型数据库,存储系统的结构化数据,包括管理用户账户、权限等基础信息、
存储知识库的元数据信息等 -
MinIO
对象存储服务,用于存储原始文档及文档切片图像信息 -
Redis
内存数据库, 采用Valkey版本,缓存大模型的响应结果,处理异步任务,临时保存对话上下文
3.2 前后端框架分析
该系统前端框架使用React+Typescript,代码统一在web
文件夹。
后端框架使用Flask+Python,代码分好几部分,具体内容如下:
- agent:对应前端agent相关模块功能
- agentic_reasoning:对应前端搜索相关模块功能
- api:核心后端程序,用来与前端进行数据对接,并提供后端服务和其它各组件连接及数据交互功能
- deepdoc:提供文件ocr等解析相关功能
- graphrag:知识图谱相关功能
- rag:主要用以和大模型相关接口进行交互
- sdk:拓展型功能,用来提供系统的外部调用,目前不太完善,可忽略。
3.3 前后端可视化分析
3.3.1 前端可视化分析
前端代码全部集成在web文件夹下,因此可直接在web路径下直接启动查看。
先安装依赖:
yarn instsall
依赖安装完成,生成node_modules
再启动:
yarn start
访问http://localhost:9222
即可进入登录界面。
考虑到登录需要和后端交互,密码验证通过后,才能进入主界面。如需直接进入主界面预览,可修改web/src/utils/request.ts
:
这里的逻辑是:本来响应结果为504,弹出error,这里直接改成成功响应。
request.interceptors.response.use(async (response: Response, options) => {if (response?.status === 413 || response?.status === 504) {// 原代码// message.error(RetcodeMessage[response?.status as ResultCode]);// 修改return new Response(JSON.stringify({code: 0,message: 'Success',data: {}}));}
3.3.2 mysql可视化分析
下面再可视化查看其它组件信息,在docker/.env
文件中,包含了各组件的密码。
启动docker服务,首先查看mysql数据库信息。
使用DBeaver
进行连接,连接参数如下,默认用户名为root
,密码为infini_rag_flow
。
可以看到,该数据库中包含多张数据表,后续在解决实际问题时,会进一步分析。
3.3.3 Elasticsearch可视化分析
Elasticsearch需要通过Kibana
进行可视化分析。虽然.env
文件中写了Kibana的初始用户名和密码,但实际服务中,并未启用Kibana。看到仓库中有人提出过该问题,具体可参考该pr:https://github.com/infiniflow/ragflow/pull/548/files
3.3.4 MinIO可视化分析
MinIO自带了可视化管理的控制台,访问http://localhost:9001/
即可进入,默认用户名为rag_flow
,默认密码为infini_rag_flow
。
进入可看见,里面的容器包含了原始上传的pdf文件和切块分页的图像数据。
3.3.5 Redis可视化分析
使用Rdis insight
连接Redis数据库,默认地址为127.0.0.1:6379
,默认用户名为default
,默认密码为infini_rag_flow
。
进入可看见,里面存储了一些缓存数据。
4. 问题解决方法
分析完了,开始解决开头提到的一些具体问题。
4.1 关闭注册通道
关闭注册通道,可直接将前端界面上的注册元素注释掉。
具体方法是修改src\pages\login\index.tsx
文件,注释掉以下内容:
{ <div>{title === 'login' && (<div>{t('signInTip')}<Button type="link" onClick={changeTitle}>{t('signUp')}</Button></div>)}{title === 'register' && (<div>{t('signUpTip')}<Button type="link" onClick={changeTitle}>{t('login')}</Button></div>)}
</div>}
这样修改,用户直接通过浏览器访问/register
也是无法注册的,因为注册功能并不是一个单独界面,而是在login中,post到后端进行处理。
4.2 后台添加用户
阻止用户自己注册之后,管理员还需要为用户进行注册。可直接采用修改数据库内容的方式进行实现。
连接mysql数据库,用户信息存储在user
表中。该表包含以下字段,核心字段是email
和password
。
email比较容易理解,存储的就是登陆明文邮箱,但密码是哈希字符串,为了防止被人攻击泄露,不能存储明文密码,因此,要解决注册问题,首先需要理清楚密码的加密逻辑。
通过仔细阅读源代码,我理清楚了注册阶段,密码的加密存储过程:
首先,前端获取到用户原始输入密码后,先进行Base64编码,防止特殊字符造成解析失败,编码后利用公钥进行RSA加密;
后端接收到加密密码后,利用私钥进行RSA解密,最后通过Hash处理,存储到数据库。
A[原始密码] --> B[前端Base64编码] --> C[RSA加密] --> D[后端RSA解密] --> E[hash存储]
为了让这个过程更容易理解,我写了个python代码,模拟了该过程,其中,私钥数据可以在文件中找到,路径为 conf/private.pem
:
import base64
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_v1_5
from werkzeug.security import generate_password_hash, check_password_hashdef rsa_decrypt(encrypted_password: str, private_key_path: str, passphrase: str) -> str:# 从文件中读取私钥with open(private_key_path, "r") as key_file:private_key = key_file.read()# 导入私钥rsa_key = RSA