|
如何在 RAGFlow 或者 dify 的问答中显示原始文档的图片? 这是我最近一直被问到的一个问题。前面一篇文章给大家演示了,如何在 Gradio 这个轻量框架下,实现渲染本地图片路径的做法。RAG维保案例分享:如何实现"文+图"的答案呈现 在回答中显示原始文档中的相关图片,从而提供更丰富的信息呈现确实在很多需求场景下有比较大的实际意义。 这篇以 RAGFlow 为例,尝试说清楚如何通过让RAGFlow搭配图片服务器独立容器化部署,实现本地图片的访问功能。 Dify 中实现方法类似,各位可以对照参考测试。 视频和文章搭配更佳 以下,enjoy: 1 Gradio本地图片渲染的两个机制 1.1 静态文化服务挂载 在代码中,FastAPI 应用挂载了静态文件目录: app.mount("/static",StaticFiles(directory="static"),name="static")这行代码将本地的"static"目录暴露为 HTTP 服务的"/static"路径,使得静态目录中的所有文件(包括图片)可以通过 HTTP URL 访问。 注:上一期源码在知识星球内 1.2 Markdown 渲染功能 聊天组件开启了 Markdown 渲染: chatbot=gr.Chatbot(label="Chatbot",height=750,avatar_images=("images/user.jpeg","images/tongyi.png"),render_markdown=True)render_markdown=True 这个参数使 Chatbot 组件能够将 Markdown 格式的文本(包括图片链接)渲染为 HTML。 1.3 工作流程说明 当 RAG 系统生成回答时,如果回答中包含指向静态目录的图片 Markdown 链接(如!图片描述 (/static/images/example.jpg)),Gradio 会自动将其渲染为 HTML 图片元素,然后浏览器通过 HTTP 请求获取并显示图片。 1.4 关键点总结 文件系统到 HTTP 的转换:
Gradio/FastAPI 将本地文件系统路径转换为 HTTP URL Markdown 渲染:
Chatbot 组件能够解析并渲染 Markdown 中的图片引用 浏览器请求机制:
浏览器根据渲染后的 HTML 中的图片 URL 发起 HTTP 请求获取图片 从用例区别上来说,Gradio 主要还是设计用于原型开发和演示,追求快速开发体验,之前发布的这个 Demo 也是希望大家能先能理解基础的实现原理。 因为RAGFlow 缺少这种自动将本地路径转换为 HTTP 路径并提供静态文件服务的机制,我们需要另辟蹊径。 2 RAGFlow 这类框架的特点 在讨论 RAGFlow 实现本地图片渲染的几种选择之前,我们先来再复习下几个 RAGFlow 这类框架的几个特点,从而更好对症下药: 2.1 Docker 容器隔离设计 Dify 和 RAGFlow 这类框架通常设计为在容器化环境中运行,出于安全考虑限制了对主机文件系统的直接访问。Docker 容器与主机文件系统是隔离的,这也是容器化技术的核心安全特性之一。 2.2 企业级架构考虑 这些框架设计时考虑了更复杂的生产环境需求: 前后端分离架构:更严格的前后端分离,适合分布式部署 专业存储方案:预期使用专门的对象存储服务而非本地文件系统 多节点部署支持:在分布式系统中,直接访问本地文件系统会导致数据一致性问题 2.3 安全性考虑 自动暴露本地文件系统作为 HTTP 服务可能带来安全风险: 目录遍历攻击:如果配置不当,可能导致敏感文件被访问 文件上传安全:需要严格控制什么类型的文件可以被上传和访问 权限控制:企业环境中需要精细的文件访问权限控制 个人猜测这些平台是希望用户在生产环境中使用专门的对象存储服务(如 S3、OSS 等)来处理静态资源,而非依赖于简单的本地文件服务机制,不过这些也确实是在大规模部署环境中是更合理的架构选择。 3 四种实现方案对比 总体来说,理论上有四种方案可行,其中前两种实测不靠谱,后两种都可行,但最后一种性价比最高。 3.1 云存储方案 这个方案也是我最开始测试的方案,好处就是短平快,但局限也很明显,就是要把原始文档预处理的图片变成所有人可访问的 URL,这个在生产环境显然不现实。 此外,如果使用阿里云OSS配置公域访问的过程也很繁琐,相信会劝退大部分人。Anyway,这种方法知道就成,没必要去试。 3.2 Base64 方式嵌入 将图片以 base64 方式嵌入到文档中进行向量化也是一种看似更直接的做法,毕竟可以绕开图片地址访问方式的问题,但这种做法有几个明显的硬伤无法突破: 上下文长度限制 这是最严重的问题,Base64 编码会显著增加数据体积。例如,一张中等质量的图片可能就占用数千甚至上万 tokens,很容易耗尽模型的上下文窗口。我测试下来往往在 embedding 这个步骤就会报错无法进行。 向量化效率降低 文本嵌入模型不是为处理大块的 base64 编码设计的,这些看似随机的字符序列对于语义向量化没有实际意义,可能导致检索精度下降以及向量空间被无意义数据污染等问题。 存储和计算开销 Base64 编码显著增加了存储需求和处理负担,检索变慢不说,对于基于 token 的计费模式,成本也会明显上升。 考虑一个具体例子,一张普通的300KB的图片转换为Base64后大约会变成400KB的文本,可能占用超过5000个tokens。所以如果文档包含10张这样的图片,就已经超过了许多模型的上下文限制。Anyway, 这种做法各位也不用试了。3.3 开发框架插件 需要说明的是,在开发插件这个方案里,RAGFlow 暂时还不是一个选项,其 Agent Studio 中截止 v0.17.2 还不支持自定义 Python 组件。 Dify 插件开发官方提供了详细的文档说明,涉及初始化开发工具、调用预定义模型或集成自定义模型,以及封装业务代码为插件等。这部分在本篇也不做展开,有想了解的盆友可以留言,人多的话我后续考虑专门出一期。 Dify插件文档:https://docs.dify.ai/zh-hans/plugins/quick-start/develop-plugins
此处说个题外话,前几天知识星球内有个提问是 RAGFlow 智能体流程中的对话生成回答组件如何引入 MCP 而有不影响原有功能? 因为 RAGFlow 最新版本的 Agent Studio 中还不支持添加自定义 Python 节点,我原本计划创建一个简单的 API 服务作为 MCP 和 RAGFlow 的桥接层,后来简单测试了下发现使用 Dify 作为主框架,在 Dify 中创建自定义工具,通过 HTTP 调用 RAGFlow 的 API 或许是更好的做法,这部分教程预计 4月中旬前发出(又给自己挖了个坑)。 3.4 图片服务器独立容器化(推荐方案) 工作原理 |