☰
RuoYi集成RAGFlow实战:私有化知识库问答与智能体搭建
2026/9/29 5:20:06 网站建设 项目流程

上一篇把 RuoYi 和 RAGFlow 的底座搭起来了,Docker Compose 部署、内网登录简化这些基础工作做完后,很多朋友在问同一个问题:底座有了,接下来数据到底怎么进去?怎么在 RuoYi 后台里让用户上传文档、触发解析、最后还能像聊天一样问出自己的私有知识库?这篇就是冲着这些问题来的。

这篇实践覆盖的范围比较明确:RuoYi 后端怎么封装 RAGFlow 的 OpenAPI、登录用户信息从哪里拿、文档解析怎么选模板、批量导入怎么不把流程写死、本地大模型选 Llama 还是国产开源模型、知识库问答怎么进一步做成智能体,最后附上这段时间踩过的坑汇总。适合已经部署完 RAGFlow、想在 RuoYi 框架里做私有化 AI 应用的后端开发,也适合正在做开源 RAG 方案选型的朋友参考。

1. 这一篇要补上什么

1.1 为什么单独把集成拿出来写

RuoYi 本身的定位是后台管理框架,用户、角色、菜单、权限这一套很成熟,但里面没有“知识库”这种东西。RAGFlow 定位是 RAG 引擎,文档解析和向量检索很强,但它自己的账号体系、权限模型比较弱,也不适合让业务人员直接用它的界面去管理企业知识库。两者拼在一起,本质上就是让 RuoYi 当入口和权限层,把 RAGFlow 当被调用的服务,所有文档上传、解析、问答操作都在 RuoYi 的菜单和按钮权限控制下完成。

第一次做这个集成时容易犯一个错:直接在 RAGFlow 界面里建数据集、传文件,然后 RuoYi 只负责跳个链接。这种做法的后果是权限失控,任何人都能进 RAGFlow 后台看到所有知识库,审计也没法做。这次我写的所有操作都走 RuoYi 后端转发,RAGFlow 的 API Key 只保存在后端,前端永远接触不到,这是私有化知识库最基本的安全底线。

1.2 目标架构和这次实践的范围

先明确整个集成的目标架构,方便后面理解每一步在干什么。

  • 展示层:RuoYi-Vue 前端,新增“知识库管理”和“知识问答”两个菜单。
  • 控制层:RuoYi 后端(Spring Boot),负责鉴权、日志、数据落库,同时封装 RAGFlow 的 HTTP 接口。
  • RAG 引擎:RAGFlow 服务,负责文档解析、切片、向量化、检索和问答补全。
  • 模型层:本地 Ollama 或 vLLM 提供的开源大模型,也可以是公司采购的 API,但私有化场景我默认用内网部署。

这篇默认你已经把 RAGFlow 容器跑起来了,RuoYi 后端也能正常登录。如果还没有,先回看系列第一篇,Docker 部署那部分已经写得很详细,这里不再重复。

2. RuoYi 后端接入 RAGFlow:接口和数据流设计

2.1 先搞清楚 RAGFlow 的 OpenAPI 到底能干什么

RAGFlow 从很早期版本就提供了 OpenAPI,前缀一般是/api/v1,用 Bearer Token 鉴权。不同小版本之间参数有过调整,所以真实联调时以你们部署版本的 OpenAPI 文档为准,但常用接口的形态基本稳定。

我平时高频用到的就这么几个:

  • 创建数据集:传数据集名称、权限级别、关联的 Embedding 模型。
  • 上传文档:往指定数据集里传文件,支持 PDF、DOCX、Excel、TXT、Markdown、图片等。
  • 触发解析:对已上传的文档发起异步解析任务。
  • 查询解析状态:返回文档的进度、状态、报错信息。
  • 执行检索:给定问题,从指定数据集里召回相关片段。
  • Chat 对话:调用已配置好的 Chat Agent,直接返回答案和引用。

在做 RuoYi 封装之前,建议先用 curl 把这几个接口全部通一遍。这一步能帮你提前区分是 RAGFlow 本身的问题还是后面 RuoYi 代码的问题。我在本地调试时最常用的一条命令大概长这样:

curl -X POST "http://localhost:9380/api/v1/datasets" \ -H "Authorization: Bearer ragflow_xxxxxxxx" \ -H "Content-Type: application/json" \ -d '{"name":"测试数据集","embedding_model":"BAAI/bge-zh-v1.5"}'

2.2 登录用户信息从哪里来,怎么和操作记录串起来

标题里追问的“RuoYi 在哪里写入登录用户的信息”是后端开发绕不开的点。RuoYi 登录成功后,用户信息会在SysLoginService里被封装成LoginUser对象,通过TokenService.createToken()生成 Token 并写入 Redis。之后每个请求进来,拦截器根据 Header 里的 Authorization 从 Redis 取回LoginUser,再放到 Spring Security 上下文里。

业务代码里拿当前用户,标准姿势是调SecurityUtils.getLoginUser().getUserId()和SecurityUtils.getUsername()。后面所有和 RAGFlow 相关的操作,我都会把这些信息写入业务表。

以文档上传为例,RuoYi 里新建一张rag_file表,字段包括文件ID、所属数据集ID、RAGFlow 文档ID、文件名、解析状态、创建人。创建人字段不是手填的,直接从SecurityUtils.getLoginUser()拿。这样做的好处是所有操作都能追溯到人,也符合企业做审计的要求。

2.3 核心代码:RuoYi 后端 RAGFlow 客户端的封装思路

RuoYi 本身没有专门的 RAGFlow 客户端,我建议新建一个RagFlowClientService,统一封装 HTTP 调用,避免业务 Controller 里到处重复写 RestTemplate。

先定义一个配置类,把 RAGFlow 的地址和 API Key 放到application.yml里,不要硬编码:

ragflow: base-url: http://localhost:9380 api-key: ragflow_xxxxxxxx

然后是创建数据集的核心代码:

@Service public class RagFlowClient { private final RestTemplate restTemplate; private final RagFlowProperties props; public RagFlowClient(RestTemplate restTemplate, RagFlowProperties props) { this.restTemplate = restTemplate; this.props = props; } public String createDataset(String name, String embeddingModel) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(props.getApiKey()); Map<String, Object> body = new HashMap<>(); body.put("name", name); body.put("embedding_model", embeddingModel); body.put("permission", "team"); HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers); ResponseEntity<String> response = restTemplate.postForEntity( props.getBaseUrl() + "/api/v1/datasets", request, String.class); if (!response.getStatusCode().is2xxSuccessful()) { throw new ServiceException("RAGFlow 创建数据集失败"); } return response.getBody(); } }

上传文件时 RestTemplate 处理 multipart 比较啰嗦,我实际项目里用的是 OkHttp。核心逻辑就是构造一个MultipartBody,附上file字段和 Bearer Token,把文件流发给 RAGFlow。解析触发则是 POST 到/api/v1/datasets/{datasetId}/documents/{docId}/parse,这个接口是异步的,返回后不代表解析完成,需要轮询状态。

2.4 权限控制和操作日志不能省

RuoYi 的价值就在这。每个和 RAGFlow 相关的接口,都给配上按钮权限点,比如rag:dataset:add、rag:file:upload、rag:chat:send。Controller 上直接加@PreAuthorize("@ss.hasPermi('rag:file:upload')"),只有被分配了权限的用户才能上传文件。

同时加上@Log注解,记录操作人、操作模块、操作内容。我第一次做的版本没加日志,后来出了个问题:有人删了某个数据集,整个业务线的知识库都没了,但完全查不到是谁删的。从那以后我定了个规矩,RAGFlow 相关的写操作一律记日志,而且日志里必须带数据集名称。

3. 文件解析:最容易拉胯的环节

3.1 RAGFlow 解析模板怎么选

RAGFlow 让我觉得最值钱的就是解析模板,这也是“ragflow 解析技巧”里最值得先说的一条。它支持通用、问答、书籍、论文、手册、表格、法律、财报、代码等多种模板,不同模板影响的是切分策略。

实际选择时,我的经验是:

  • 全电子版 PDF、Word,用通用模板,最稳。
  • 扫描版 PDF,必须开 OCR,建议用深度解析类模板,不然出来的 chunk 全是乱码。
  • 表格密集的 Excel 或单据,用表格模板,RAGFlow 对表格结构还原做得比较细。
  • 代码文件,用代码模板,它会按函数或逻辑块切分,而不是死板按字符数切。
  • FAQ 类的文档,用问答模板,检索时直接返回问题和答案对,效果比通用模板好很多。

很多人忽略一个关键动作:解析完一定要抽查 chunk。RAGFlow 管理界面里能看到文档切出来的所有片段,如果发现某个 PDF 解析出来的是乱码或者内容顺序错乱,别急着调提示词,先换模板或者开 OCR。

3.2 批量处理文件时怎么设计流程

企业知识库一开始导入就是成百上千个文件,不可能让用户一个个在 RAGFlow 界面里操作。RuoYi 这边的批量流程,我建议做成这样:

  1. 前端支持多选文件,逐个上传到 RuoYi 临时目录。
  2. RuoYi 后端完成格式校验后,调用 RAGFlow 上传文档接口,把文件流转发过去。
  3. 上传成功后,再统一触发解析。
  4. 前端通过轮询接口查看每个文件的解析状态,展示“排队中、解析中、已完成、失败”。

这里有三个容易踩的坑。第一,不要等 RAGFlow 解析完成后再传下一个文件,RAGFlow 有异步任务队列,批量提交没问题,串行反而慢。第二,轮询间隔不要低于 10 秒,解析任务在队列里和实际执行中变化很快,太频繁只会增加后端压力。第三,解析不是实时完成的,大 PDF 可能要好几分钟,同步接口会很容易超时。

我在项目里用 Spring 的@Scheduled写了一个定时任务,每 15 秒扫描状态为“解析中”的记录,调用 RAGFlow 查询接口更新进度,超过 30 分钟还没完成的直接标记异常并重试一次。这个方案实测下来稳定,也不需要在 RAGFlow 那边额外配置 Webhook。

3.3 解析失败的三种典型场景和处理办法

解析失败在私有化部署里非常常见,不要慌,按状态排查就行。

  • 一直处于“待解析”:说明任务没被消费,重启 RAGFlow 的 server 容器或者检查 Redis 是否正常。
  • 状态变成了失败,报错信息和 PDF 内容相关:多半是文件本身的问题,比如扫描版 PDF 没开 OCR、文件后缀名和真实格式不一致。建议上传时不要只按扩展名判断,用 magic bytes 校验真实文件类型。
  • 解析成功但检索不到内容:检查数据集捆绑的 Embedding 模型是否配置成功,索引构建有延迟。

某次客户现场导入一批合同扫描件,解析失败率高达 40%,后来发现是这批 PDF 没有文本层,OCR 又没开。处理办法是统一走深度解析模板加 OCR,失败率降到 3% 以下。扫描版文档千万别图省事用通用模板。

4. LLM 选型:国内私有化部署到底怎么选

4.1 Llama 适合国内企业吗

“Llama 适合国内企业拿来搞知识库问答和私有化 Agent 部署吗”这个问题我一次次被问到,直接说结论:不太适合。

Llama 3.1 的英文能力确实强,但中文语料占比低,直接拿来做中文知识库问答,经常出现表达生硬、理解偏差的问题。更关键的是 Llama 的中文指令遵循能力一般,做 Agent 工具调用时更容易出幺蛾子。国产模型里,Qwen 系列、DeepSeek 系列、智谱 GLM 系列的中文底子和社区生态都更合适。

如果团队的 GPU 资源紧张,首选 Qwen2.5-7B 的 4bit 量化版,一张 12G 显存的卡就能跑起来;显存有 24G 的两张,直接上 Qwen2.5-14B 或 DeepSeek-R1-Distill-Qwen-14B。真要追求深度推理,可以在回答链路外面套一层大模型反思,但这不是私有化知识库的第一步。

4.2 RAGFlow 本地化部署时怎么接模型

RAGFlow 支持的模型接入方式很多,最省事的是在它的管理后台配置一个 OpenAI 兼容地址,指向本地部署的 vLLM 或 Ollama 服务。填 Base URL 的时候注意,要填到/v1这一级,比如http://10.0.0.15:8000/v1,API Key 填一个自定义值就行了,本地服务一般不校验。

Embedding 模型我建议用BAAI/bge-zh-v1.5或bge-m3,中文场景比text-embedding-ada-002稳。如果内网完全隔离,Embedding 模型文件需要在部署阶段提前拉取到本地,否则容器启动后连不上外网就会一直失败。这个属于 RAGFlow 本地化部署里最容易被忽略的细节。

4.3 开源方案对比:Dify、RAGFlow、FastGPT 怎么选

很多人在 RuoYi 集成前会纠结选 Dify 还是 RAGFlow 还是 FastGPT,我从企业落地的角度做个对比,方便按场景选。

产品最强项明显短板适合场景
RAGFlowDeepDoc 解析能力强,RAG 调参细,召回效果好Agent 编排和业务流程能力相对弱企业文档多且杂,核心诉求是私有知识库问答
Dify工作流和 Agent 编排丰富,插件生态多,做成产品很快深度解析和召回调优不如 RAGFlow 细,部分企业功能在商业版要快速搭建对话应用、Agent 流程
FastGPT中文产品体验好,知识库加工作流一体,部署简单大规模知识库要额外维护 ES,解析能力中规中矩中小团队快速上线知识库助手

如果主应用是 RuoYi,我更建议把 RAGFlow 当纯 RAG 引擎来用,不要纠结它的 Agent 编排够不够强。上层业务编排完全可以由 RuoYi 自己来,这样权限、审批、流程都可以用自己的逻辑,不冗余。

5. 从知识库问答到智能体:RAGFlow 在 RuoYi 里的落地方式

5.1 在 RAGFlow 里配置一个可用的 Chat Agent

RAGFlow 新版里应用管理区分了 Chat Assistant 和 Agent 两类。Chat Assistant 适合直接做知识库问答,配置起来最快:选一个数据集、选一个大模型、写一段 Prompt,保存后就有 chat_id。

Prompt 不要写得太大而空,就按企业知识库客服的标准写,明确“只根据提供的知识库片段回答,不要编造事实,无法回答时直接说明不知道”。示例 Prompt 里最好带上引用要求,让流程后面的引用解析有据可依。

5.2 RuoYi 后端封装问答接口

RuoYi 后端封装问答接口,核心就是转发 RAGFlow 的 Chat 接口。我提供一个简化示例,Java 里用 RestTemplate 发起一片问答请求:

public String chat(String question, String sessionId) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(props.getApiKey()); Map<String, Object> body = new HashMap<>(); body.put("question", question); body.put("stream", false); if (sessionId != null) { body.put("session_id", sessionId); } HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers); ResponseEntity<String> response = restTemplate.postForEntity( props.getBaseUrl() + "/api/v1/chats/" + props.getChatId() + "/completions", request, String.class); return response.getBody(); }

返回结果里除了答案,还有引用文件列表。前端拿到引用后要展示出来,因为企业内部知识库问答,用户会追问“这句话出自哪份文档”,没有引用,回答的可信度会大打折扣。我建议把引用解析成结构化数据再传给前端,别直接扔原始 JSON。

5.3 更进一步:在 RuoYi 里做 Function Calling 式智能体

如果 RAGFlow 自带 Agent 编排满足不了需求,可以在 RuoYi 后端自己做一套轻量智能体。思路是:RuoYi 后端直接对接支持 Function Calling 的大模型,把查询知识库注册成一个工具函数,模型决定需要检索时,后端调 RAGFlow 的检索接口,把召回片段作为上下文拼进 Prompt,最后再由模型生成答复。

这样做的最大好处是,知识的检索变成了业务系统里的一个能力,可以和 RuoYi 自己的业务接口连起来。比如用户在问“最近项目的进度”时,模型可以同时检索 RAGFlow 里的项目文档,再调 RuoYi 的业务接口拿实时进度数据,答案来自两套系统。

这种方案对模型的要求是必须支持工具调用。实测下来 Qwen 和 DeepSeek 系列都比较稳,太小的 7B 模型可能在复杂工具调用上不稳定,可以考虑先用 14B,后面再根据实际效果做量化压缩。

6. 常见问题与排查实录

6.1 Windows 11 下跑 RAGFlow 的注意事项

很多开发者的本机是 Windows 11,RAGFlow 用 Docker Desktop 跑起来没问题,但有几个坑必须先说。WSL2 的内存一定要给足,我推荐至少给 6GB 到 8GB,否则容器启动中途会被直接杀掉。Docker Desktop 的配置不只是调界面里的内存,还要检查.wslconfig文件,否则改完不生效。

另一个坑是路径别带中文和空格。Windows 下把项目放到“桌面/新建文件夹”这种路径,Docker 挂载目录很容易出问题,日志里报的错误有时候还很难看懂。我自己的开发目录直接放在D:\dev\ragflow,全程没踩路径问题。

最后就是性能,Windows 11 下解析大批量 PDF 的速度确实慢,适合做调试,不适合做生产环境。生产环境老老实实放 Linux 服务器。

6.2 RuoYi-Vue 去掉验证码的坑

私有化内网系统想把验证码去掉是很正常的诉求,RuoYi 的配置项是数据库参数sys.account.captchaEnabled。但很多人在改造时只改前端,把登录页的验证码组件隐藏了,结果后端还在校验验证码,登录请求带着空的 code 过去,直接报“验证码错误”。

正确的做法是前后端一起改。后端在登录逻辑里读取SysConfig的captchaEnabled,如果关闭了就跳过校验;前端登录页不再加载验证码组件。改完一定要测两件事:一是登录成功后用户信息能正常写入 Redis,二是退出登录后 Token 失效逻辑不受影响。

必须提醒一点:验证码是防暴力破解的重要防线,去掉只适合可信内网环境。一旦系统暴露在公网,强烈建议保留或者至少做登录频率限制。RuoYi 的登录日志接口也能帮我们快速发现异常尝试,别把这条道堵死。

6.3 集成排查三板斧

RuoYi 集成 RAGFlow 出问题时,我惯用的排查顺序是这样的。

先看 RuoYi 后端日志,找不到明显报错就去翻 RAGFlow 的容器日志,命令是docker logs ragflow-server -f。这两条加起来能解决大部分问题。

如果还不明确,直接用 curl 测 RAGFlow 原始接口。比如手动触发一次解析再查询状态,如果原始接口都失败,问题基本就在 RAGFlow 侧,别再折腾 RuoYi 的代码。

最后看数据库里的状态记录。RuoYi 侧维护的解析状态表和 RAGFlow 的文档状态可能不一致,这种数据对不上是集成时最常见的问题。我用一句话总结:先确保 RAGFlow 自己能完成全流程,再去查 RuoYi 的封装逻辑。

一点个人经验收尾

这套 RuoYi 加 RAGFlow 的集成做到能稳定问答之后,我最大的体会是:知识库项目真正的成败点不在大模型,而在文档解析和检索调优。模型再强,解析出来一堆乱码 chunk,回答质量也不可能好。我建议所有刚开始做私有化知识库的团队,先用真实业务文档跑一轮全链路测试,仔细看召回片段和引用来源,再逐步调解析模板、chunk 大小和提示词。如果后续有机会做第三篇,我会重点写召回阶段的具体调优:chunk 切多长、重排序加不加、混合检索怎么配。先把解析和检索的地基打牢,剩下的模型选型反而不用太焦虑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询