
1. 为什么要做“RuoYi RAGFlow”这套组合1.1 从企业实际痛点说起前阵子帮一家做工程项目管理的客户做内部系统改造对方提了个很实在的需求公司积累了几百份制度文档、验收规范、历史项目总结和供应商名录平时员工找资料全靠“问行政”、“翻共享盘”、“在群里喊一嗓子”。效率低不说很多关键信息散落在个人电脑里新人入职想快速了解项目背景基本靠老员工口口相传。他们一开始想直接用大模型产品把文档丢进去聊天但试下来有几个硬伤一是通用大模型对内部专业术语的理解很浅二是数据要出网合规这关就过不了三是没有权限体系谁都能问、什么都能答管理层根本不敢放开用。那时候我就在想企业缺的其实不是“一个AI”而是“一个长在业务系统里的AI助手”。它要有正经的登录认证和权限控制要知道用户是谁、能看什么、不能看什么还要能真正吃透企业内部那些排版混乱、表格嵌套、扫描件居多的文档。这正好对应了两样东西RuoYi负责业务系统的壳和权限RAGFlow负责文档理解和检索问答。1.2 为什么是RAGFlow而不是Dify或WeKnow很多人一上来就问Dify不是更火吗WeKnow不是也主打私有化吗我简单说说我的选型理由。先明确一点这三者定位其实有区别。Dify更像一个“AI应用开发平台”可视化编排工作流、Agent、插件体系都很强适合做复杂的AI应用WeKnow在轻量级知识库问答上做得挺简洁部署也容易RAGFlow的核心差异化在“深度文档理解”这几个字上它把PDF、Word、Excel里那些复杂的版面、表格、多栏排版先做版面解析再做切片和向量化而不是简单粗暴地按字符截断。我们手头有大量带表格的验收单、带扫描签章的合同附件、双栏排版的旧版规范实测下来RAGFlow的解析效果确实能打。补充一点RAGFlow的Agent功能这两年也追得很快支持多种意图识别和工具调用跟Dify的差距在缩小。但你要说“拿来搞知识库问答和私有化Agent部署”RAGFlow的文档解析底子更稳对国内企业那些“格式乱七八糟的文档”容忍度高很多。再加上它对中文检索的优化和可自定义的提示词模板我最终定了它。1.3 与“RuoYi全家桶”的契合方式再聊RuoYi。这个框架在国内企业级项目里的占有率不用多说前后端分离版、微服务版、移动端版都有现成的脚手架。它自带用户管理、角色权限、菜单管理、操作日志、数据权限这些基础能力。如果企业已经用RuoYi搭好了OA或项目管理系统直接在它上面加一个“智能问答”模块是最平滑的路径——用户体系不用重新建权限不用重新设计界面风格可以保持一致。我们这次集成的整体思路是RuoYi负责一切业务入口用户登录、菜单权限、会话管理、前端页面。RAGFlow负责一切文档能力知识库创建、文档解析、向量检索、对话生成。两者通过后端API对接RuoYi的后端持有RAGFlow的API密钥前端永远接触不到密钥本身。这套架构的好处是边界清晰RuoYi不碰文档解析的脏活RAGFlow不管业务权限。后面我会把每一层的实现细节拆开讲。2. RAGFlow私有化部署的完整前置准备2.1 硬件选型8C16G起步磁盘能多给就多给先说结论如果你只是想跑通Demo8核16G内存、50G磁盘的机器也能凑合如果真要给企业用建议至少16核32G内存、200G以上SSD。为什么RAGFlow的docker compose会拉起Elasticsearch、MySQL、Redis、MinIO、推理服务等一堆组件光是ES的内存占用就不小。文档解析时服务对CPU的消耗也很明显多栏PDF解析时CPU直接拉满都是正常的。磁盘这块我吃过亏。一开始图省事给机器分了60G结果测试阶段上传了一堆扫描件和Word文档MinIO里存的原始文件和解析产物加起来非常快。ES的索引段文件也是一路涨。后来扩容到200G才算踏实。如果你预算允许直接上500G SSD一年半载不用操心存储。内存不足的典型表现是服务起半天起不来或者起来之后ES疯狂报错再或者文档解析任务积压。如果你的机器内存只有16G建议把ES的堆内存调小一点RAGFlow自带的配置文件里可以通过环境变量控制别让它默认吃满。另外部署前先确认Docker能正常使用磁盘分区格式没问题避免后续排查环境问题浪费时间。2.2 Docker环境与Docker Compose方案RAGFlow官方主推的部署方式就是Docker Compose社区里“ragflow docker部署”和“ragflow本地化部署”的教程很多我这边再梳理一次关键步骤和容易出错的点。环境要求先列一下Linux系统Ubuntu 22.04 / CentOS 7.9以上都行Windows下玩建议用WSL2Docker 20.10 和 Docker Compose v2能访问外部网络用于拉取镜像和配置模型API防火墙放行必要端口具体步骤# 1. 下载项目源码官方仓库里包含docker目录 git clone https://github.com/infiniflow/ragflow.git # 2. 进到docker目录复制环境变量模板 cd ragflow/docker cp .env.example .env # 3. 按需编辑.env重点配模型API和多个服务端口 vim .env.env文件里最关键的有这几项# RAGFlow服务的端口 RAGFLOW_API_PORT9380 RAGFLOW_SERVER_PORT80 # Elasticsearch相关配置 ES_HOSTelasticsearch ES_PORT9200 # MinIO存储最小分片大小 MINIO_ACCESS_KEYQjFjM2VhZDQ4ZmU0 MINIO_SECRET_KEYYzI4MjJiMzgwYTdk # 模型API的base地址和key以OpenAI兼容接口为例 LLM_BASE_URLhttps://api.deepseek.com/v1 LLM_API_KEYsk-xxxxxxxxxxxxxxxx LLM_MODELdeepseek-chat这里要注意每个镜像版本的配套关系。RAGFlow版本升级后docker-compose.yml里的镜像tag可能会变直接拉最新的可能和你本机已有的数据不兼容尤其是ES的索引格式变化。所以生产环境不要轻易用latest锁定一个发布版本更稳。以我们目前使用的版本为例各核心镜像如下组件镜像说明RAGFlow服务infiniflow/ragflow:v0.17.0-slim包含API和Web服务Elasticsearchdocker.elastic.co/elasticsearch/elasticsearch:8.14.3向量和全文混合检索MySQLmysql:8.0.33元数据存储Redisredis:7.2.4缓存和会话管理MinIOminio/minio:RELEASE.2025.04.22T18.12.36Z对象存储启动命令很简单docker compose -f docker/docker-compose.yml up -d首次启动会拉很多镜像时间取决于网络。启动完成后访问http://服务器IP默认账号是admin密码infini_rag_flow登录后建议立刻改掉。2.3 大模型接入云端API与本地Ollama两条路线RAGFlow本身不提供大模型推理能力它依赖外部模型API来完成“根据检索到的内容生成回答”这一步。部署前必须先想好模型从哪里来因为后面的测试问答马上要用。两条主流路线路线一云端API最适合快速落地国内可选的包括DeepSeek、通义千问、智谱等。DeepSeek因为便宜大碗最近用来跑RAG的非常多。在.env里配置好base_url和api_key然后在RAGFlow界面“模型提供商”里把对应模型添加进去再设置成聊天模型和Embedding模型即可。这里有个细节容易被忽略RAGFlow的对话服务有的模型还需要单独配置“函数调用”能力如果你的Agent功能或意图识别需要工具调用得选支持function calling的模型。DeepSeek-chat是支持的但一些小众模型可能不支持配完会报错。路线二本地Ollama数据完全不出内网如果企业有硬性要求数据一步都不允许出内网那就用Ollama在本地拉起模型。Ollama部署很简单装完之后拉模型# 安装ollama之后拉一个适合中文问答的模型 ollama pull qwen2.5:7b ollama pull bge-m3 # 让Ollama监听所有网卡否则容器里访问不到 ollama serve --host 0.0.0.0然后在RAGFlow的模型提供商里添加“Ollama”类型填上Ollama服务的地址比如http://本机IP:11434配置好聊天模型和Embedding模型。选型提示如果企业确实打算长期用本地模型做知识库问答和私有化Agent我个人的建议是至少上7B以上参数的中文模型llama这类英文底子模型直接拿来答中文效果一般尤其涉及专业术语时比较弱。qwen2.5系列、GLM系列在中文上的表现更友好。Embedding模型建议用bge-m3或同级别的中文向量模型检索质量差别真的很大。2.4 部署完成后的首轮验证服务起来后别急着马上对接RuoYi先在RAGFlow管理界面把整条链路验证通了再说。路径是创建知识库 → 上传一份测试文档 → 发起对话 → 确认能引用文档内容回答。这一步的目的是把变量隔离如果RuoYi对接后问答效果不对你要能判断是RAGFlow侧的问题还是RuoYi对接侧的问题。我见过不少同事跳过这一步直接开始写代码结果RuoYi界面里问不出东西查了半天发现是RAGFlow的模型API没配好。首轮验证时打个比方判断标准其实就一句话系统答出来的内容确实是来自你传的那份文档而不是模型凭记忆编的。确认这件事之后集成工作才算有了可靠底座。3. RuoYi侧的统一身份链路与接口封装3.1 登录态打通JWT与Redis双验证RuoYi的前后端分离版用的是JWT Redis的登录方案用户登录后后端生成一个Token返回给前端Token的有效性状态存在Redis里。每次请求后端从请求头里取出Token解析出用户信息再去Redis比对一下。这样用户在RuoYi侧的会话状态是完整可控的。集成RAGFlow时一个常见疑问是要不要把RAGFlow的用户体系也接过来我的答案是不要。企业里真正面对用户的是RuoYiRAGFlow只作为一个“知识服务引擎”在后台工作不应该让它掌握业务用户的账号体系。否则两套密码、两套权限、两套日志维护成本成倍增长。所以我的做法是在RuoYi后端单独配置一个RAGFlow的“系统级账号”用这个账号去调用RAGFlow API。业务用户登录RuoYi后RuoYi后端拿到当前登录用户的部门、角色信息在调用RAGFlow之前先做一次业务侧权限判断判断通过了再用系统级账号去请求RAGFlow。用户身份不直接暴露给RAGFlowRAGFlow只知道“有个合法的系统账号在提问”具体是谁在问、能不能问由RuoYi说了算。这样设计有几个好处业务用户量再大也不需要在RAGFlow里维护一份账号。权限逻辑都写在RuoYi侧审计时只看RuoYi的操作日志就够了。RAGFlow的API密钥只保存在RuoYi后端前端拿不到。3.2 RAGFlow API的封装策略RAGFlow提供的API是标准的RESTful接口核心调用链路由三部分组成登录获取Token也可以直接用API Key方式创建/获取会话ID对应一个问答对话窗口发送消息、接收回答在RuoYi这边我建议不要在前端直接调RAGFlow接口而是通过RuoYi的自定义Controller做一层统一封装。这样前端只需要关心业务接口不用处理RAGFlow的鉴权细节后续如果RAGFlow升级、换了API地址或改参数结构只动后端封装层就行。我设计的接口大致是这样的// RuoYi侧新增的Controller路径为 /ai/chat RestController RequestMapping(/ai/chat) public class AiChatController extends BaseController { Autowired private RagFlowChatService ragFlowChatService; /** * 创建会话窗口 */ PostMapping(/session) public AjaxResult createSession(RequestBody CreateSessionRequest request) { // 1. 校验当前登录用户的文档权限 // 2. 调用RAGFlow创建会话 // 3. 返回会话ID给前端 } /** * 发送消息流式返回 */ PostMapping(/message) public void sendMessage(RequestBody SendMessageRequest request, HttpServletResponse response) { // 1. 校验权限 // 2. 校验会话归属 // 3. 设置SSE响应头 // 4. 调用RAGFlow将流式内容转发给前端 } }封装层里要处理的细节有几个我展开说说。RAGFlow创建会话的接口需要指定知识库IDKB ID。但我并不建议在前端URL里直接暴露这个ID因为知识库的ID是RAGFlow内部的资源标识业务用户根本不需要关心。我倾向在RuoYi的系统配置表里维护“知识库ID”这个参数后端封装时从配置中心读出来前端只传“用户问的是什么”这样权限和资源解耦得更干净。再看API调用的方式RAGFlow的问答接口支持阻塞式响应也支持SSE流式响应。直接在RuoYi后端做一次完整的HTTP调用、拿到全部文本再返回给前端在浏览器里表现为“转圈N秒才一次性蹦出答案”。用户体验不好。尤其答案比较长的时候用户会以为系统卡死了。看社区里“ragflow 教程 批量处理文件”相关的经验帖有不少用户在对接前端时也提到流式输出的问题说明这是个普遍关注点。3.3 创建问答会话与流式输出的处理流式输出这块很多RuoYi开发者第一次接触会有点懵因为RuoYi默认的AjaxResult是一个整体响应体没法做增量推送。解决方案是改用SSEServer-Sent Events后端一边接收RAGFlow的流式数据流一边通过HttpServletResponse的OutputStream写给浏览器。RuoYi后端负责转发流的核心代码逻辑大致思路如下// RagFlowChatService中的核心方法伪代码 public void streamChat(String sessionId, String question, HttpServletResponse response) { response.setContentType(text/event-stream); response.setCharacterEncoding(UTF-8); PrintWriter writer response.getWriter(); // 构造RAGFlow的HTTP请求 HttpURLConnection conn (HttpURLConnection) new URL(ragflowApiUrl).openConnection(); conn.setRequestMethod(POST); conn.setRequestProperty(Authorization, Bearer ragflowApiKey); // ... 设置请求体和各种头 // 读取RAGFlow返回的流式数据 BufferedReader reader new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); String line; while ((line reader.readLine()) ! null) { // SSE格式要求每行数据前加 data: writer.write(data: line \n\n); writer.flush(); } // 结束标记 writer.write(data: [DONE]\n\n); writer.flush(); writer.close(); }前端用EventSource或fetch配合ReadableStream解析就行。Vue项目里我习惯用fetch ReadableStream灵活性更高因为EventSource只能发GET请求而我们的接口是POST。前端解析SSE流这块不复杂但要注意断流重连和超时问题。另一个容易踩坑的点SSE的Content-Type必须设置对响应头不能随意加Content-Disposition之类的东西否则浏览器会当成文件下载而不是流式接收。前端也不要对SSE接口走RuoYi默认的响应拦截器因为拦截器会尝试解析JSON直接给你把一个正常的流式响应干成报错。4. 知识库初始化的完整链路从脏文档到可用问答4.1 数据准备清洗、去重、合规检查知识库好不好用40%看解析60%看源头数据质量。这句话我反复跟客户强调。很多团队兴致勃勃把上千份文档一股脑传上去结果一问一个胡话就开始怀疑系统不行。实际上问题往往出在文档本身。我总结了一套通用的清洗流程按重要性排第一去掉“不可对外”的内容。在RuoYi这类系统里知识库默认所有人都能问但企业内部总有合同价格、未公开的经营数据、个人信息等高敏内容。做知识库之前先让业务部门自己标一遍敏感文档范围拿不准的一律不传。RAGFlow没有“自动判断文档是否保密”的能力这步只能靠人工把关。第二处理旧版本文档。项目方案经常有V1、V2、V3只保留最新版本。不然问同一个问题系统可能从老版本文档里检索出早已废弃的流程答出来的东西直接误导人。上架之前按“文档名最后修改时间”做一轮去重筛选。第三格式统一。PDF、Word、Excel、PPT都支持但尽量以PDF为主。原因是PDF在跨设备展示时格式最稳定RAGFlow对PDF的版面解析支持也最成熟。Word文档如果里面有嵌入对象、宏、批注解析时可能会产生奇怪的切片。如果手头有扫描版PDF确认清晰度太模糊的扫描件连OCR都会跪。第四敏感信息脱敏。比如身份证号、手机号、银行卡号正则替换成占位符。这一步既是合规要求也是防止知识库被滥用时出问题。这套流程走完产出的文档集才算“可以进知识库”。4.2 知识库创建与文档解析的参数调优在RAGFlow管理界面创建知识库时几个关键参数要提前想好知识库名称与描述命名用中文就行但描述建议写成“知识库用途说明”因为RAGFlow在Agent模式下会根据描述判断从哪里检索。嵌入模型选一个固定的中文Embedding模型。这里要提醒建库之后不要轻易换Embedding模型否则所有已生成的向量全部作废需要重新建立索引。解析模式RAGFlow支持不同的解析模式比如“版面分析”“通用”“OCR增强”等。对多栏PDF、复杂表格优先用版面分析模式对扫描件开OCR增强。文档切分策略可以自定义切分规则。RAGFlow默认的切分效果已经不错但如果你有“全段落不可拆”的需求可以调大切片长度并设置重叠区间。说说我踩过的一个解析坑。一开始用默认模式解析一份带横向表格的工程验收单结果表格内容被拦腰截断检索时怎么都查不全。后来切成带OCR增强的版面分析模式表格被完整识别成一个整体问答准确率立刻上来了。所以建议在正式全量导入前先挑10份“最难啃”的文档做解析测试把参数调到满意再批量上传这一步能省掉很多返工。批量上传这块RAGFlow支持拖拽上传整个文件夹也支持通过API上传。我看到不少人在找“ragflow 教程 批量处理文件”说明批量操作的需求很集中。实际上管理界面里直接选多文件或者拖文件夹都能批量执行但解析任务会排队CPU如果不够会拖得很慢耐心等就行。4.3 提示词模板与检索参数的首轮配置文档进库之后还得配置对话侧的“出厂参数”。在RAGFlow里新建“聊天助理”或“Agent”时可以自定义系统提示词模板。我实测下来对中文企业内部知识库提示词里至少应该包含这几层意思你的角色是“企业内部知识库助手”。只依据提供的资料片段回答。如果资料里没有相关信息直接说“没找到”不要编造。回答尽量引用原文关键句并给出文档来源。内置的模板有些用词偏通用对具体企业可以再微调。比如工程类企业可以在提示词里加一句“回答要区分规范要求和建议做法并注明出处”对产品型企业可以加“回答时优先说明适用版本和产品系列”。检索参数里重点看两个地方检索条数TopK默认值通常够用但如果企业内部文档里同一主题的重复内容很多TopK太大反而会把相似片段都捞出来让模型回答变得啰嗦。一般先设5看效果再调。相似度阈值阈值设低了不相关内容也能进上下文模型容易被带偏设高了又可能捞不到东西回答频繁变成“没找到”。我的做法是先设0.2左右然后拿一批典型问题回归测试统计回答置信度再微调。这轮配置不需要一次做到完美搭好之后再根据实际问答效果迭代。4.4 测试问答与结果校验知识库正式启用之前一定要做一轮“验收式问答”。我会从业务部门收集三批问题第一批是“有标准答案”的问题比如“报销流程中发票金额超过多少需要线下审批”用来验证检索准确性和引用正确性。第二批是“跨文档综合题”比如“项目验收需要准备哪些材料”正确答案散落在多个文档里用来验证RAGFlow的多路召回能力。第三批是“文档里没有答案的问题”比如“公司食堂周六是否营业”用来验证模型会不会老老实实说不知道。这里特别强调第三批因为“AI乱编”是企业最忌讳的问题。如果在测试阶段发现模型对未知问题强行回答就要回头调整提示词把“不知道就承认不知道”写得更硬核同时可以把相似度阈值往上提一点。问答校验时还要对比查看RAGFlow返回的“引用来源”。RuoYi前端展示的每个回答最好附带“查看来源”的链接用户点开能看到是哪个文档的哪一段支撑了这个回答。这个功能对建立信任感非常重要工程人员对AI本来就半信半疑你让他能点开原文核对他会更愿意用。5. 权限收敛、数据隔离与几个容易被忽视的细节5.1 知识库权限与RuoYi数据权限的双层控制私有化知识库绕不开的问题就是“谁能问什么”。RuoYi自带的数据权限按部门、按自定义已经很成熟我们可以直接用这套逻辑来限定每个业务用户能访问的知识库列表。一个可落地的做法是在RuoYi系统配置里维护一张“知识库授权表”RuoYi角色可见的知识库ID说明管理员全部运维人员可管理知识库内容项目经理kb_project, kb_contract项目文档和合同规范普通员工kb_policy, kb_manual规章制度和操作手册RuoYi后端在调用RAGFlow之前先查这张表拿到“当前用户可访问的知识库ID集合”再把这些ID作为检索范围传给RAGFlow。RAGFlow侧也要把知识库设为“仅自己可见”或私有类型双重保险。这里有一个实践心得千万别把RAGFlow的API密钥直接写在前端配置文件里那样等于把整个知识库脱光了挂网上。密钥只留在RuoYi服务端所有请求走后端转发。我见过有团队图省事前端直接用API Key调RAGFlow的Web SDK结果浏览器F12一开就能看到密钥这是低级但很普遍的错误。5.2 引用来源与不可靠回答的处理有了权限接下来就是回答可信度的问题。前面测试阶段强调过引用来源正式上线也一样。RuoYi前端展示回答的界面我会给每条回答底部放几个可折叠的“来源文档”标签点击后能看到文档名、页码和命中的原文片段。如果问答是流式输出的引用可以放在流结束后批量返回避免解析过程中打断用户的阅读。对“不确定”的答案还有一招在RuoYi后台加一个“人工反馈”按钮。用户如果觉得某条回答不靠谱可以一键标记。这些标记数据存到RuoYi数据库后台定期汇总由知识库管理员去优化文档或调整检索参数。知识库不是建完就完事的它和业务系统一样需要持续运营。5.3 日常维护与文档更新机制最后一个多数项目都会忽略的问题就是知识库的更新。RAGFlow里的文档一旦上传解析就形成一个快照。如果业务部门更新了流程文件RAGFlow不会自动感知你得手动把新文档传上去、把旧文档删掉或者停用。企业规模化运营时这一步必须有流程约束不然知识库过了几个月回答里全是旧规则比没有知识库还危险。我的建议是在RuoYi的管理后台加一个“知识库文档管理”入口把文档的“上传、解析状态、生效时间、失效时间”管起来。文档变更走固定的审批流程审批通过后才允许同步到RAGFlow。这听起来有点重但对中大型企业来说知识库的权威性必须靠管理流程来保障技术上再强也替代不了操作规范。再分享一个日常维护的实用技巧RAGFlow支持按文档维度查看解析状态和检索命中次数。每隔一段时间把“从未被命中”的文档拉出来看看要么是没人关心要么是内容太陈旧。定期清理低价值文档不仅能让检索更精准也能让知识库的体积保持在健康范围。这套“RuoYi管业务和权限、RAGFlow管文档和问答”的组合在跑通之后还有一个很方便的扩展方向RuoYi里所有业务流程的节点比如任务审批、项目周报、客户回访记录都可以通过API把结构化数据推给RAGFlow做增量索引。知识库会慢慢从“静态文档库”进化成“业务记忆库”那时候它就不再只是回答问题而是真正参与业务决策辅助了。我们目前正在做的第二阶段就是把RuoYi的工单历史数据接进知识库让AI回答基于真实业务过程而非只有制度文档。这也是我比较推荐后续尝试的方向。