ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

WorkBuddy+RAGFlow打通飞书内部问答:从乱答到精准回复

WorkBuddy+RAGFlow打通飞书内部问答:从乱答到精准回复 从机器人胡乱回答到知识库精准回复我用 WorkBuddy 打通飞书内部问答的完整记录先说说我为什么要搭这条链路。我们团队有个挺常见的问题新员工入职之后每天在飞书群里问报销流程是什么服务器地址在哪某某项目的 Wiki 入口在哪儿老员工回答同一个问题回答了几十遍烦不胜烦。市面上不是没有现成的智能问答机器人但企业内部的文档有保密要求不可能直接扔到公网 SaaS 服务去索引。所以我的诉求很明确做一个飞书机器人它背后连的是我们本地部署的知识库引擎问什么答什么答案要有据可查索引的文档全部留在内网。这个项目我全程用的是 AI 智能体平台 WorkBuddy 来做编排知识库引擎选择的是开源项目 RAGFlow最后通过飞书开放平台把机器人挂到群里。整套链路跑通之后我最大的感受是三分靠模型七分靠链路设计。很多看起来机器人答得不准的问题根子根本不在模型而在文档解析、检索参数、回调超时这类细节。这篇文章我尽量把所有步骤、关键参数和我踩过的坑都写清楚想复现的可以直接照着操作。1. 方案选型的思考为什么是 RAGFlow WorkBuddy而不是别的组合刚开始做技术选型时我身边的朋友推荐了两个方向一个是直接用 Dify 的工作流另一个是用 LangChain 自己写服务。我最后没有走这两条路原因挺实际的。1.1 为什么知识库引擎选 RAGFlowRAGFlow 最大的优势是它对文档解析这件事做得很重。传统 RAG 方案里最影响回答质量的就是文档切片如果切片切坏了后面无论模型多强检索出来的片段都是碎片化的。RAGFlow 内置了 DeepDoc 这样的解析工具对 PDF、Word、表格的处理能力强很多它能识别版面结构把段落、标题、表格相对完整地切出来。这一点在做企业内部文档时非常关键因为我们的文件里有大量表格和分栏排版按固定字符数硬切的效果很烂。另外 RAGFlow 有完整的开源版本可以直接用 Docker Compose 部署到内网服务器不需要把文档传到外部服务。它自带一个还算友好的管理后台我可以在 Web 界面上维护知识库、查看文档解析状态、手动触发重新解析。这些能力如果全部自己用代码撸工作量会大很多。1.2 为什么中间要加一个 WorkBuddy 编排层其实飞书机器人可以直接调用 RAGFlow 的 API 来回答问题最开始我也是这么想的后来发现事情没那么简单。RAGFlow 是检索问答引擎但它不会帮你做意图判断。比如说群聊里有人问今天午饭吃什么检索系统会强行去知识库里找匹配内容最后给出一段莫名其妙的答案。真实场景下飞书群里既有正经的知识问题也有闲聊还有可能是让机器人发个表格、拉个列表之类的指令操作。不能让所有消息都走知识库检索。WorkBuddy 在这里的角色是一个智能体编排平台。它允许我定义一个助手这个助手可以配置多个能力Skill每个 Skill 负责一类事情一个绑定 RAGFlow 做知识问答另一个绑定飞书 API 做消息发送或表格生成。助手本身有一个大模型来理解用户意图决定把问题路由给哪个能力。这样整个链路就变成了飞书 - 飞书机器人回调 - WorkBuddy 智能体 - RAGFlow每一层只管自己擅长的事。1.3 我最终确定的整体架构画成文字就是三层交互层飞书应用负责接收群里的 机器人 消息异步回调到后台服务编排层WorkBuddy 工作台接收飞书推送的消息用智能体判断意图按需调用多个 Skill知识层RAGFlow 本地实例提供文档解析、向量化、检索、引用标注能力这里有一个容易踩的坑飞书的回调服务不能直接指向 WorkBuddy 的云端地址尤其像我们这样机器人和知识库都在内网的情况必须有一个中转服务来做消息格式的转换和转发。这个中转服务我写在后面第 3 节是我花了最多时间调试的部分。2. 本地知识库底座RAGFlow 的部署、文档导入和检索调优记录RAGFlow 的部署本身不难官方文档写得很清楚但实际跑起来的时候有几个坑是文档里不会告诉你的。我按顺序讲。2.1 Docker Compose 快速部署的硬件要求我用的机器是 16 核 CPU、32GB 内存、一张 8GB 显存的 GPU 卡。这个配置跑 RAGFlow 官方默认的 docker-compose.yml 是够用的但如果你只有 8GB 内存的机器建议先把一些非必要的组件关掉否则 Elasticsearch 和 MySQL 会把内存吃满服务直接卡死。部署步骤大致是git clone https://github.com/infiniflow/ragflow.git cd ragflow/docker # 确认 .env 文件里的 SVR_HTTP_PORT 和 SVR_HTTPS_PORT 端口没有冲突 docker compose up -d启动完成后浏览器访问服务器的 80 端口或者你改的端口默认账号密码是 admin / infini_rag_flow第一次登录之后尽快改掉。这个默认密码在公网环境下非常危险我们因为是在内网所以暂时没太在意但如果你有外网访问需求一定要改。2.2 配置模型接入时的三个经典坑RAGFlow 本身不内置大模型它需要对接一个 LLM 来做向量化、重排序和生成回答。官方支持 OpenAI 接口、Ollama、DeepSeek、通义千问等一堆渠道。我们内网环境不允许出公网所以我选了 Ollama 跑本地模型但这一路踩了不少坑。第一个坑是 embedding 模型必须单独指定而且要和 LLM 分开选。我刚开始以为只需要配置一个对话模型就够了结果创建知识库的时候死活不让我选文档提示embedding 模型未配置。后来才搞清楚RAGFlow 的模型提供商里要配置两类模型一个是 chat 模型负责生成回答一个是 embedding 模型负责把文档切片转成向量。如果都用 Ollama要在 Ollama 先拉好对应模型比如ollama pull bge-m3来跑 embeddingollama pull qwen2.5:14b来跑生成。第二个坑是 embedding 模型的维度会影响向量检索的召回效果。我一开始图省事直接用了一个很小的 embedding 模型维度只有 384 维跑出来的结果就是答非所问。后来换成了 bge-m3它的维度是 1024召回效果明显好了很多。这里要注意的是同一个知识库里所有文档必须用同一个 embedding 模型而且这个模型中途不要换否则向量空间不一致检索结果会变成灾难。第三个坑是 RAGFlow 里配置 Ollama 的地址。如果 RAGFlow 是用 Docker 跑的那容器内访问宿主机的 Ollama 不能写 localhost必须写宿主机 IP。这个坑我查了很久才发现docker-compose 里配置文件填的地址是http://localhost:11434但容器里根本访问不到宿主机。正确写法是查一下宿主机的内网 IP填成http://192.168.1.100:11434之类的地址。2.3 创建知识库、批量导入文档和解析策略RAGFlow 的管理后台里创建知识库很简单点一下新建起个名字选 embedding 模型就行。导入文档时支持 PDF、Word、Markdown、Excel 等格式。我这边把部门常用的操作手册、项目文档、FAQ 整理成了几十个文件一次性拖进去后台会自动解析。解析策略这里我建议不要用默认的通用策略而是根据文档类型微调。比如说我们的制度文件有大量条款列表用paper或者manual模式会比通用模式切得更合理。RAGFlow 解析完会后在文件列表里显示每个文档的状态我遇到过解析失败的情况点击进去能看到具体的错误日志大多数是因为 PDF 是扫描件没有文字层这种需要先用 OCR 工具把文字层补上再导入否则检索出来的全是乱码。解析完成后可以在测试页面输入一句查询词看检索返回的片段和相似度分数。我实测下来的经验是如果返回的片段里标题和上下文被切开了说明解析模板没选对如果相似度整体都很低很可能是 embedding 模型太弱和任务不匹配。3. 飞书机器人接入应用创建、事件订阅回调和服务端中转飞书机器人接入是整条链路里文档最碎、最容易迷糊的部分。飞书开放平台和微信、钉钉的逻辑差异不小我简单梳理一下我操作时的要点。3.1 创建企业自建应用并启用机器人登录飞书开放平台进到开发者后台创建一个企业自建应用。创建完成后在应用能力里添加机器人能力这样应用就有了机器人的身份可以出现在群聊里被 。这一步没什么难度但要注意一个细节机器人发布版本时需要走创建版本-申请发布-管理员审核的流程如果你自己就是管理员那审核那一步点自己通过就行。如果跳过发布步骤机器人不会真正出现在组织中很多人卡在这里。3.2 事件订阅的坑回调地址校验和消息事件订阅飞书机器人的消息接收方式有两种一种是长连接模式不用配置公网回调另一种是 Webhook 事件订阅模式。我之前因为在公网上没有固定的 HTTPS 域名所以选了长连接模式用飞书官方提供的 SDK 在本地起了一个长连接服务。这个模式的好处是不需要公网回调地址适合内网部署缺点是服务必须常驻运行。事件订阅这里最坑的是订阅事件和事件回调不是一回事。飞书开放平台要求你在应用配置里订阅接收消息事件即im.message.receive_v1然后才可以把消息推送过来。如果你只在后台配了回调地址但没有订阅对应事件消息是根本不会推过来的控制台也不会报错只会静默失败。另外长连接模式下飞书官方文档里说可以直接用 SDK 里面的WSClient但它对 Python 和 Go 的支持程度不一样。我用 Python 的时候遇到一个坑SDK 默认的事件处理函数签名非常严格我必须要把PydanticEvent对象解析出来才能拿到消息内容而且消息内容里文本在最里面一层嵌套我一开始直接拿event.message.content去解析结果拿到的是 JSON 字符串而不是字典白白排查了半天。3.3 服务端中转把飞书消息转发给 WorkBuddy我在第 1 节提到不能直接把飞书回调指到 WorkBuddy。原因一方面是 WorkBuddy 通常不提供飞书事件格式的对接入口另一方面是飞书要求回调地址必须是 HTTPS而我们在内网很难给 WorkBuddy 单独配一个 HTTPS 域名。所以我在中间加了一个轻量级 Python 服务它的职责很简单接收飞书长连接推送的消息事件提取消息里的文本和发送者信息调用 WorkBuddy 的智能体 API把文本传过去拿到返回结果后再调飞书 API 发送消息回群聊飞书长连接 - [Python中转服务] - WorkBuddy智能体API - [Python中转服务] - 飞书消息API这个 Python 服务大概 200 行代码就搞定了但它同时承担了消息格式适配和异步防超时两个职责。飞书的机器人发消息接口是独立的不需要和事件回调一一对应所以我们可以先把消息交给 WorkBuddy 慢慢处理等处理完再主动发到群聊里。这一点很重要因为飞书的事件回调有超时限制如果同步等待知识库检索完再返回大概率会超时。4. WorkBuddy 智能体编排Skill 设计、上下文拼接和问题路由WorkBuddy 的使用方式我总结下来就是定义一个助手给它配技能然后在对话里跑起来。它和直接用代码写 Agent 的区别是大部分框架层面的东西模型调用、上下文管理、工具注册都已经封装好了我需要做的主要是定义清楚每个 Skill 的职责和参数。4.1 为什么不直接让大模型自由发挥最初的版本里我尝试让 WorkBuddy 直接充当所有对话的模型问什么答什么。结果就是前面说的闲聊也走知识库检索或者知识库没检索到的内容模型开始编。后来我在 WorkBuddy 里创建了两个 SkillKnowledgeQA Skill绑定 RAGFlow 的检索 API专门处理知识问答FeishuSender Skill绑定飞书消息发送 API负责以机器人身份发消息然后通过 WorkBuddy 的意图识别让助手自动判断该调用哪个 Skill。这样今天天气怎么样这种消息就不会触发知识库检索了而是会走默认的闲聊回复或者不回。4.2 用 Skill 包装 RAGFlow 检索接口RAGFlow 官方提供了一套 API但调用起来有几个环节检索知识库retrieve、再按会话方式生成回答chat。我在 WorkBuddy 里定义 KnowledgeQA 这个 Skill 时把这两个步骤合成了一步Skill 的输入参数就是用户问题输出就是最终回答加引用来源。实际调用时我需要在 WorkBuddy 的 Skill 里配置RAGFlow 的 API 地址内网 IPAPI Key在 RAGFlow 后台的个人中心生成知识库 ID检索时返回的 top k我设的是 8相似度阈值我设的是 0.25这里有个经验RAGFlow 的相似度分数和其他向量库的算法不太一样分数越低表示越相似它的评分逻辑是距离型。我第一次没意识到这一点把阈值设成了 0.7结果所有文档都被过滤了机器人永远回答未找到相关信息。后来查了检索日志才明白这个阈值应该设成一个比较低的值一般 0.2 到 0.3 之间比较合适。4.3 上下文拼接让回答带上引用企业内部问答有一个硬需求回答必须能追溯不能是模型凭空生成的。RAGFlow 的 chat API 本身支持返回引用referencesWorkBuddy 拿到的引用是一个包含文档名、页码、原文片段的数组。我在中转服务里把这些引用格式化成了 Markdown 引用块附在回答后面发给飞书。这样同事看到答案后能直接翻开文档原文核对。这个细节虽然简单但实际体验差别很大没有引用的话给大家的感觉就是一个飘着的 AI有了引用可信度立刻就不一样了。5. 联调排错实录从飞书消息到知识库回答的完整排查链路整条链路搭好之后联调过程是我预期到的但真正跑起来问题还是一大堆。我不按时间顺序讲按消息链路从上到下的排查顺序讲这样以后你们遇到类似问题也知道从哪下手。5.1 飞书侧消息收到了但机器人不回话如果你发现群里 机器人没反应先别急着怀疑 WorkBuddy。按照我的排查顺序第一步看飞书开放平台后台的事件订阅日志。在这里能看到飞书有没有成功推送事件给你。如果连事件都没有说明是订阅配置问题检查事件是否订阅、应用是否发布。第二步看你的回调服务日志。如果事件收到了但处理报错了飞书会按重试策略推几次。常见报错是消息格式解析失败我前面提到过飞书消息内容content字段是 JSON 字符串要先json.loads再来取text。还有一个坑是图片消息和文本消息的 content 结构不一样别默认所有消息都带 text 字段。第三步确认消息是不是被机器人吃掉了。飞书机器人默认有权限设置有些消息类型比如合并转发、富文本默认不接收需要在权限配置里额外打开。5.2 WorkBuddy 侧智能体收到消息但答非所问如果飞书方面没问题问题就会下沉到 WorkBuddy。最常见的现象是机器人答了但回答完全和知识库无关像是在瞎聊。这时候我会先去 WorkBuddy 的会话日志里看用户原始输入是什么、智能体判断出的意图是什么、调用了哪个 Skill、传给 Skill 的参数是什么。我遇到过这样一个案例用户问怎么申请服务器智能体把这句话路由到了闲聊 Skill回复了申请服务器需要联系管理员哦这种通用废话。后来我在 Prompt 里加了强约束要求智能体在不确定意图时优先走 KnowledgeQA而不是走默认回复并且把服务器申请报销请假这些关键词作为优先级提示放进去。微调之后这类问题基本都能正确路由。另一个典型案例是用户问了一句特别长的问题超过了我设定的最大长度限制消息直接被截断知识库检索到的片段完全不相关。WorkBuddy 对输入长度是有限制的我一开始没注意后来把所有对话消息的最长长度调大才解决。5.3 RAGFlow 侧知识库检索出来了但回答内容不对再往下沉就是 RAGFlow 的检索质量问题。我在第 2 节讲过 embedding 模型、解析策略对检索的影响这里再补两个我在联调中发现的高频问题。第一个是知识库混存导致检索互相干扰。我最初把操作手册和项目文档放在同一个知识库结果问项目上线流程时检索结果混进了大量操作手册里的上线环境配置片段回答变得又长又乱。后来我把知识库按文档类型拆分成运维手册库和项目文档库然后让 WorkBuddy 根据问题里的关键词决定查哪个库效果立刻提升。第二个是相似度阈值设置不合理导致检索结果被误过滤。前面讲过 RAGFlow 分数越低表示越相似如果你发现检索结果经常为空或者回答里频繁出现未找到答案不要急着换模型先去调整知识库设置里的相似度阈值试几个值看看返回的片段变化。我最终用的 0.25 是基于几十条真实问题的测试得到的你们可以直接参考但不建议直接照抄因为不同文档集合的向量分布不一样。5.4 一个容易被忽略的坑异步重试导致重复回复飞书事件订阅是有重试机制的如果你的回调服务处理消息耗时太长飞书会认为处理失败然后重发同一条事件。如果我们的服务和 WorkBuddy 处理时间超过了 3 秒飞书就会重试这时候群里会出现同一个问题被机器人回答两遍的情况。我的解决方案是在中转服务里加一个简单的幂等表用 message_id 做去重。收到飞书事件时先查一下这个 message_id 是否已经处理过如果处理过就直接返回成功不重复调用 WorkBuddy。这个表我用 Redis 做过期时间设 10 分钟够覆盖飞书的重试窗口了。6. 跑通之后我做的几项优化批量文件处理、表格消息和权限控制链路基本稳定后我陆续做了一些体验优化。这里挑三个比较实用、对你们也有参考价值的说一下。6.1 处理批量文件和超长文档时的分库思路我们团队的文档每天都在增长把新文件不断往同一个知识库里塞最终检索质量一定会下降。我现在制订了一个规则每周把新增文档统一导入 RAGFlow导入后触发一次重新解析然后抽查 5 到 10 条代表性问题确认检索结果没有变差才通知团队知识库已更新。对于特别长的文档比如几百页的产品手册我会在导入前先用脚本按章节拆分多个文件而不是把一个巨型 PDF 直接扔进去。因为 RAGFlow 对超大文件的解析时间很长中途任何一个页面解析异常会导致整个文件标记失败拆开后至少能精确定位问题页而且检索时命中更精准。6.2 让机器人直接发送表格消息有同事经常问帮我把这个排期表发出来这类需求靠知识库检索搞不定因为它属于指令操作。我在 WorkBuddy 里加了一个 FeishuSender Skill它可以接收结构化数据比如二维数组然后调飞书的消息接口发送富文本表格卡片。飞书发送表格卡片稍微有点繁琐需要构造消息卡片 JSON里面用column_set之类的组件。刚开始我很抗拒写这个格式后来发现 WorkBuddy 里可以让 Skill 直接输出待发送的数据内容然后在 Python 中转服务里统一转换成飞书卡片 JSON这样 Skill 只管生成数据格式转换交给中转层。这个分层逻辑和整个架构是一脉相承的非常好用。6.3 权限控制哪些人能问、哪些内容能查企业内部知识库不是所有人都有权限查所有文档。我在 WorkBuddy 里做了一个简单的用户维度控制飞书消息里带上用户的 open_id中转服务先查一下这个 open_id 在不在知识库白名单里不在就直接回复抱歉你暂无权限使用该功能。更细粒度一点的权限控制比如 A 组只能查 A 组文档则是靠 RAGFlow 的多知识库做到的。每个知识库绑定一组人的 open_id 列表WorkBuddy 在调用检索前根据提问人决定查哪个库。这个方案谈不上精致但胜在实现简单对于内部几十人的团队完全够用。7. 最后分享一点我的个人经验和踩坑心得整套链路从规划到稳定运行我大概花了两个周末。如果让我重新做一次我会在第一天就把日志这件事做扎实而不是等到联调出问题才开始补。日志是大问题排查的王道尤其是飞书事件订阅、WorkBuddy 会话、RAGFlow 检索这三个环节每个环节的日志格式最好统一带上 message_id这样一条消息从头到尾可追踪。再说说稳定运行期间的发现RAGFlow 的检索响应速度是我们整条链路里最慢的一环长文档库的检索有时需要 3 到 5 秒。飞书侧的体验上用户 机器人之后 5 秒内没有回复就有人会再发一遍在吗。我的应对方案是在中转服务里做先秒回一个正在查资料稍等然后再异步发送最终答案。这个处理在群里体验非常自然也不会因为慢而被飞书判定超时重试。整个项目验证了我开头那句话机器人好不好用不取决于模型多聪明而取决于你有没有把链路里的每一个细节都处理好。文档解析的质量、意图路由的准确性、引用信息的呈现、超时重试的幂等这些才是决定成败的关键。希望这篇教程能帮你们少走几步弯路尤其是 RAGFlow 相似度阈值和飞书事件订阅这两个坑如果你也正在搭类似的东西祝一次跑通。
返回列表