微信扫码
添加专属顾问
我要投稿
探索油猴脚本在RAG图片问答中的创新应用,实现前端图片流式体验。 核心内容: 1. RAG图片问答的三种方案迭代过程 2. 油猴脚本(Tampermonkey)的具体实现方式 3. 项目架构梳理及用户体验优化
一个月前发了篇文章介绍了基于 MiniO 存储的 RAGFlow+Dfiy 图片处理方案,之后有几个知识星球内的星友提问,如何优化上一版方案中不能流式输出的问题。
这篇试图说清楚,三种 RAG 图片问答的方案迭代过程,油猴脚本 (Tampermonkey)的具体实现方式,以及项目架构梳理。
以下,enjoy:
1
多阶段方案演进
在现有开源框架基础上,不改动核心代码,又要达到较好的流式图文体验,这自然会引入一些“变通方案”。本着“规避 LLM 弱点”、“模块化处理”、“逐步优化体验”的工程实践思路,我前后进行了以下三种方案的探索,其中前两个历史文章已经详细介绍过,这篇先大致回顾下前两种的核心理念,后续主要就第三种方案展开。
1.1
直接嵌入 HTML 的局限性
RAGFlow如何实现图片问答:原理分析+详细步骤(附源码)
为了能在 RAGFlow、dify 这类封装度较高的框架中,回答显示原文相关图片,富文本处理 (图片转 URL)是基础,也是让图片有被浏览器渲染的可能。我在最开始的工程化尝试里,采用了最简单直接的方式,就是通过图片服务器容器化方案,把图片直接使用 <img> 标签和其公开的 HTTP URL 放处理后的富文本中,这样只要在回答中输出图片 URL,浏览器就可以直接渲染。
但 LLM 在处理包含复杂 HTML 结构或 URL 的文本时,可能会“创造性地”修改它们。尤其明显的就是 LLM 会想当然的按照 Next token prediction 的套路修改 URL 中的图片名称,从而导致加载失败。
1.2
后端占位符替换
Dify+RAGFLow:基于占位符的图片问答升级方案(最佳实践)
为了解决“LLM 可能会改动富文本中的 URL”这个问题,把不稳定的 URL 替换为 LLM 更难篡改的、有特定格式的占位符,并把替换逻辑后置,这样可以尽可能的确保数据在经过 LLM 后的完整性和可解析性。
具体来说,本地 python 脚本针对原文档进行预处理后,提取图片和 map.json 存入 MinIO,文本中插入占位符上传至 RAGFlow 的知识库。紧接着 Dify 工作流中,通过 HTTP 节点获取 map.json,Code 节点进行后端替换。
这个迭代方案的优点是最大限度的解决了 LLM 修改图片链接的问题,保证了图片引用的准确性。但也带来了新的痛点,就是 Dify Code 节点的批处理特性,导致图片无法随 LLM 的流式回答实时显示,用户体验有明显的延迟,尤其是针对选择了带显示思维链的 LLM 而言,提问后的等待就变得更加反人性。
1.3
前端实时渲染
先做下定义扫盲,简单来说油猴脚本就是个浏览器扩展插件,在授权前提下允许用户运行自定义 JavaScript 脚本来修改网页。通俗解释就是给网页打“补丁”,增强或改变其功能。选择这种做法的核心原因是可以非侵入式的修改 Dify 前端行为,免去了改动 Dify/RAGFlow 源码的风险。(直接增强后端流式处理能力或前端渲染逻辑。成本高,且影响框架升级。)
经过了前两个阶段的试错之后,就很自然的过渡到这个前端实时渲染的方案。引入油猴脚本后,就是把图片占位符的替换工作从 Dify 后端转移到用户浏览器前端,从而实现图片随 LLM 文本流式输出同步显示,优化用户的使用体验。
总结来说,目前主流的、主要基于文本处理的 LLM(即使是某些声称支持多模态的,在处理嵌入式 HTML 时也不完美),都不擅长精确保持和输出复杂的结构化数据如 HTML。上述后两种方案讨论的“占位符+映射替换”策略,本质上是将“结构化内容渲染”这个任务从 LLM 的职责中剥离出来,交给了后续更可控的程序(Code 节点或前端脚本),这是一种有效的规避 LLM 短板的思路。
如果使用 Langchain、LlamaIndex 这些自主开发 RAG 系统,在回答中显示图片的效果会简化很多,但图片提取、存储和 URL 管理的复杂性依然存在,而且需要编写更多的代码来搭建整个应用。
2
项目架构图
为了让大家看的清楚些,整了张流程图再还原下整个数据流和处理环节:从 PDF 到 MinIO,再到 RAGFlow 知识库,Dify 调用,LLM 处理,最终油猴脚本渲染。
3
油猴脚本的具体实现
3.1
@match 指令
这个指令位于油猴脚本的元数据头部(// ==UserScript== 块内),它告诉 Tampermonkey 扩展只有当用户访问的网址符合我列出的这些模式时,才需要激活并运行我这个脚本。
在这个本项目中, 需要将 @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 的内容。
在这个本项目中: 这个 URL(例如 http://localhost:9000/ragflow-public-assets/mappings/demo_map.json) 是在脚本首次运行时通过提示框让用户输入,或者直接在脚本中配置一个默认值。正确配置此 URL 是脚本能否找到正确图片链接的关键。它是连接占位符与实际图片的桥梁。]
3.3
CSS 选择器
为了在网页上准确地找到需要处理的内容(即机器人回复的聊天气泡,以及包含这些气泡的总容器),需要使用 CSS 选择器帮助脚本在复杂的 HTML 结构中定位到目标元素。
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 作为应用编排和用户交互界面的价值依然存在,尤其是在面对更复杂工作流编排的需求时。
4.2
油猴脚本的一些其他玩法
对于没有接触过油猴脚本的盆友,这里再列举两个经典的用法,感兴趣的可以试试看:
API 接口调试与 Mock 数据注入
在前端开发或与后端进行接口联调时,经常会遇到后端接口尚未开发完成、接口不稳定、或者需要特定返回数据才能触发某些前端逻辑的情况。油猴脚本可以用来拦截前端发起的 API 请求(通常是 XMLHttpRequest 或 fetch),并对其进行修改或直接返回一个预设的(Mock)响应。
网页自动化测试与表单填充
对于一些需要频繁进行回归测试的 Web 应用,或者需要重复填写大量表单的场景,油猴脚本可以编写自动化脚本来模拟用户操作,如点击按钮、输入文本、选择下拉框、提交表单等。
4.3
“雕花”的阶段性选择
从步骤数量和涉及的组件来看,这个项目流程显得有些复杂。但这种复杂性也往往源于我们试图在现有框架(Dify/RAGFlow等)的限制下,实现一个它们并未原生完美支持的流式图文混合输出。
随着多模态 LLM 能力的增强,也许能更直接地处理和引用上下文中的图片,减少对占位符的依赖,或者输出更结构化的指令来显示图片。 引导 LLM 输出更结构化的数据(如 JSON 对象列表,明确指示文本和图片),而不是自由文本中夹杂占位符,能让后续处理更简单。
Anyway,图片回答更多是锦上添花,文档预处理和分块等基础环节的优先级依然是最高的。
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2025-05-22
RAG技术的三大范式和技术演进
2025-05-22
漫画:“向量数据库 + RAG”到底是什么?看完秒懂!
2025-05-22
讨厌RAG生成幻觉?试一下SAT重构文本分块,按语义而不是Token
2025-05-22
拆解智能体系统的能力和构成,我们需要的是可靠的AI系统,而不是Agents。
2025-05-22
【论文解读】Agentic-RAG:RAG发展调研
2025-05-21
RAG 挑战赛冠军方案解析:从数据解析到多路由器检索的工程实践,推荐阅读!
2025-05-21
一文搞懂基于大模型快速搭建本地RAG知识库应用实践
2025-05-21
【LLM应用框架】DSPy构建RAG
2024-10-27
2024-09-04
2024-05-05
2024-07-18
2024-06-20
2024-06-13
2024-07-09
2024-07-09
2024-05-19
2024-07-07
2025-05-16
2025-05-15
2025-05-14
2025-05-14
2025-05-13
2025-05-11
2025-05-08
2025-05-05