究极方案:油猴脚本实现RAG问答前端图片流式体验


一个月前发了篇文章介绍了基于 MiniO 存储的 RAGFlow+Dfiy 图片处理方案,之后有几个知识星球内的星友提问,如何优化上一版方案中不能流式输出的问题。

究极方案:油猴脚本实现RAG问答前端图片流式体验

这篇试图说清楚,三种 RAG 图片问答的方案迭代过程,油猴脚本 (Tampermonkey)的具体实现方式,以及项目架构梳理。

以下,enjoy:

1

   

多阶段方案演进

在现有开源框架基础上,不改动核心代码,又要达到较好的流式图文体验,这自然会引入一些“变通方案”。本着“规避 LLM 弱点”、“模块化处理”、“逐步优化体验”的工程实践思路,我前后进行了以下三种方案的探索,其中前两个历史文章已经详细介绍过,这篇先大致回顾下前两种的核心理念,后续主要就第三种方案展开。

究极方案:油猴脚本实现RAG问答前端图片流式体验

1.1

   

直接嵌入 HTML 的局限性

RAGFlow如何实现图片问答:原理分析+详细步骤(附源码)

为了能在 RAGFlow、dify 这类封装度较高的框架中,回答显示原文相关图片,富文本处理 (图片转 URL)是基础,也是让图片有被浏览器渲染的可能。我在最开始的工程化尝试里,采用了最简单直接的方式,就是通过图片服务器容器化方案,把图片直接使用 <img> 标签和其公开的 HTTP URL 放处理后的富文本中,这样只要在回答中输出图片 URL,浏览器就可以直接渲染。

究极方案:油猴脚本实现RAG问答前端图片流式体验

但 LLM 在处理包含复杂 HTML 结构或 URL 的文本时,可能会“创造性地”修改它们。尤其明显的就是 LLM 会想当然的按照 Next token prediction 的套路修改 URL 中的图片名称,从而导致加载失败。

1.2

   

后端占位符替换

Dify+RAGFLow:基于占位符的图片问答升级方案(最佳实践)

为了解决“LLM 可能会改动富文本中的 URL”这个问题,把不稳定的 URL 替换为 LLM 更难篡改的、有特定格式的占位符,并把替换逻辑后置,这样可以尽可能的确保数据在经过 LLM 后的完整性和可解析性。

究极方案:油猴脚本实现RAG问答前端图片流式体验

具体来说,本地 python 脚本针对原文档进行预处理后,提取图片和 map.json 存入 MinIO,文本中插入占位符上传至 RAGFlow 的知识库。紧接着 Dify 工作流中,通过 HTTP 节点获取 map.json,Code 节点进行后端替换。

究极方案:油猴脚本实现RAG问答前端图片流式体验

这个迭代方案的优点是最大限度的解决了 LLM 修改图片链接的问题,保证了图片引用的准确性。但也带来了新的痛点,就是 Dify Code 节点的批处理特性,导致图片无法随 LLM 的流式回答实时显示,用户体验有明显的延迟,尤其是针对选择了带显示思维链的 LLM 而言,提问后的等待就变得更加反人性。

1.3

   

前端实时渲染

先做下定义扫盲,简单来说油猴脚本就是个浏览器扩展插件,在授权前提下允许用户运行自定义 JavaScript 脚本来修改网页。通俗解释就是给网页打“补丁”,增强或改变其功能。选择这种做法的核心原因是可以非侵入式的修改 Dify 前端行为,免去了改动 Dify/RAGFlow 源码的风险。(直接增强后端流式处理能力或前端渲染逻辑。成本高,且影响框架升级。)

究极方案:油猴脚本实现RAG问答前端图片流式体验

经过了前两个阶段的试错之后,就很自然的过渡到这个前端实时渲染的方案。引入油猴脚本后,就是把图片占位符的替换工作从 Dify 后端转移到用户浏览器前端,从而实现图片随 LLM 文本流式输出同步显示,优化用户的使用体验。

究极方案:油猴脚本实现RAG问答前端图片流式体验

总结来说,目前主流的、主要基于文本处理的 LLM(即使是某些声称支持多模态的,在处理嵌入式 HTML 时也不完美),都不擅长精确保持和输出复杂的结构化数据如 HTML。上述后两种方案讨论的“占位符+映射替换”策略,本质上是将“结构化内容渲染”这个任务从 LLM 的职责中剥离出来,交给了后续更可控的程序(Code 节点或前端脚本),这是一种有效的规避 LLM 短板的思路。

如果使用 Langchain、LlamaIndex 这些自主开发 RAG 系统,在回答中显示图片的效果会简化很多,但图片提取、存储和 URL 管理的复杂性依然存在,而且需要编写更多的代码来搭建整个应用。

2

   

项目架构图

究极方案:油猴脚本实现RAG问答前端图片流式体验

为了让大家看的清楚些,整了张流程图再还原下整个数据流和处理环节:从 PDF 到 MinIO,再到 RAGFlow 知识库,Dify 调用,LLM 处理,最终油猴脚本渲染。

3

   

油猴脚本的具体实现

3.1

   

@match 指令

这个指令位于油猴脚本的元数据头部(// ==UserScript== 块内),它告诉 Tampermonkey 扩展只有当用户访问的网址符合我列出的这些模式时,才需要激活并运行我这个脚本。

究极方案:油猴脚本实现RAG问答前端图片流式体验

在这个本项目中, 需要将 @match 指令精确配置为 Dify 应用的聊天页面 URL 模式,例如 http://localhost/chat/* 或 ://<你的 Dify 域名>/app//chat/*。这样,只有当用户进入 Dify 聊天界面时,图片替换脚本才会被激活。

3.2

   

map.json 的 URL 配置

我们的脚本需要知道哪个图片占位符(如 [IMG::image1.png])对应哪张真实的图片(如 http://minio-server/bucket/image1.png)。 这个对应关系存储在由 process_pdf.py 脚本生成并上传到 MinIO 的 map.json 文件中。脚本内部通常会有一个变量(例如 mappingFileUrl)来存储这个 map.json 文件的完整 HTTP URL。脚本在初始化时,会通过这个 URL 异步下载并解析 map.json 的内容。

究极方案:油猴脚本实现RAG问答前端图片流式体验
究极方案:油猴脚本实现RAG问答前端图片流式体验

在这个本项目中: 这个 URL(例如 http://localhost:9000/ragflow-public-assets/mappings/demo_map.json) 是在脚本首次运行时通过提示框让用户输入,或者直接在脚本中配置一个默认值。正确配置此 URL 是脚本能否找到正确图片链接的关键。它是连接占位符与实际图片的桥梁。]

3.3

   

CSS 选择器

为了在网页上准确地找到需要处理的内容(即机器人回复的聊天气泡,以及包含这些气泡的总容器),需要使用 CSS 选择器帮助脚本在复杂的 HTML 结构中定位到目标元素。

究极方案:油猴脚本实现RAG问答前端图片流式体验
注:通过浏览器开发者工具(F12)仔细检查 Dify 聊天页面的 HTML 代码,找到能够稳定标识机器人消息(例如,通过特定的 class 名如 div.markdown-body 或更复杂的组合选择器)和聊天总容器的 CSS 选择器,并将其配置在脚本中。

BOT_MESSAGE_SELECTOR:用于定位每一条由机器人发出的消息的 HTML 元素。脚本将在这个元素内部查找并替换图片占位符。CHAT_MESSAGE_CONTAINER_SELECTOR:用于定位包裹所有聊天消息(用户和机器人的)的父容器。MutationObserver 会监控这个容器的内容变化。

这是脚本能否正确工作的核心中的核心。如果这些选择器不正确或不够精确,脚本可能根本找不到机器人回复,或者错误地修改了页面的其他部分。由于 Dify(或其他任何 Web 应用)的前端 HTML 结构可能更新,这些选择器是最需要根据实际情况进行细致调试和配置的部分。

3.4

   

油猴脚本的局限性

首先是用户端依赖问题,因为不向后端服务是面向所有用户修改的,油猴脚本的使用需要每个用户在自己的浏览器上安装 Tampermonkey 扩展并安装该特定脚本,这个虽然也不复杂,但确实增加了一定的用户侧操作成本。

其次是前端结构依赖问题,因为脚本强依赖于 Dify 前端页面的 HTML 结构(CSS 选择器),如果 Dify 前端更新导致结构变化,脚本可能失效,所以可能需要不定期维护。

4

   

写在最后

4.1

   

RAGFlow+Dify 组合的必要性

在当前这个方案中,RAGFlow 负责知识库处理,Dify 负责应用层和交互,油猴脚本负责前端体验增强,三者协同工作。即使图片渲染由油猴脚本在前端完成,Dify 作为应用编排和用户交互界面的价值依然存在,尤其是在面对更复杂工作流编排的需求时。

究极方案:油猴脚本实现RAG问答前端图片流式体验

究极方案:油猴脚本实现RAG问答前端图片流式体验

4.2

   

油猴脚本的一些其他玩法

对于没有接触过油猴脚本的盆友,这里再列举两个经典的用法,感兴趣的可以试试看:

API 接口调试与 Mock 数据注入

在前端开发或与后端进行接口联调时,经常会遇到后端接口尚未开发完成、接口不稳定、或者需要特定返回数据才能触发某些前端逻辑的情况。油猴脚本可以用来拦截前端发起的 API 请求(通常是 XMLHttpRequest 或 fetch),并对其进行修改或直接返回一个预设的(Mock)响应。

网页自动化测试与表单填充

对于一些需要频繁进行回归测试的 Web 应用,或者需要重复填写大量表单的场景,油猴脚本可以编写自动化脚本来模拟用户操作,如点击按钮、输入文本、选择下拉框、提交表单等。

4.3

   

“雕花”的阶段性选择

从步骤数量和涉及的组件来看,这个项目流程显得有些复杂。但这种复杂性也往往源于我们试图在现有框架(Dify/RAGFlow等)的限制下,实现一个它们并未原生完美支持的流式图文混合输出。

随着多模态 LLM 能力的增强,也许能更直接地处理和引用上下文中的图片,减少对占位符的依赖,或者输出更结构化的指令来显示图片。 引导 LLM 输出更结构化的数据(如 JSON 对象列表,明确指示文本和图片),而不是自由文本中夹杂占位符,能让后续处理更简单。

Anyway,图片回答更多是锦上添花,文档预处理和分块等基础环节的优先级依然是最高的。

RAG技术前沿技术新闻资讯

RAG 挑战赛冠军方案解析:从数据解析到多路由器检索的工程实践,推荐阅读!

2025-5-24 1:17:28

RAG技术前沿技术新闻资讯

【论文解读】Agentic-RAG:RAG发展调研

2025-5-24 3:13:41

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
购物车
优惠劵
搜索