成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

為什么你的RAGFlow需要一個Markdown預(yù)覽器(油猴腳本方案)

開發(fā)
如何通過利用油猴腳本的腳本注入和跨域請求能力,攔截用戶對 .md 鏈接的點(diǎn)擊,通過調(diào)用 RAGFlow 的內(nèi)部 API 獲取原始數(shù)據(jù),并動態(tài)生成一個預(yù)覽頁面;以及在實(shí)現(xiàn)上述過程中踩到的坑與迭代方向參考。

前幾天知識星球中有個提問,關(guān)于 RAGFlow 的聊天助手回答引用文件是 markdown 格式時,如何跳轉(zhuǎn)進(jìn)行預(yù)覽的實(shí)現(xiàn)問題。

目前在 RAGFlow 的聊天助手中,如果引用文件格式是 pdf 和 docx,是可以直接點(diǎn)擊跳轉(zhuǎn)打開新的標(biāo)簽頁進(jìn)行預(yù)覽的,不過暫時確實(shí)不支持 markdown 格式。早在今年 2 月份,在 RAGFlow 的 Github issue 上,就有一個關(guān)于“回答中引用的 MD 文檔無法原生預(yù)覽”的討論(#4979)。但是目前并沒有官方或者其他開發(fā)者給出解決方案。不過既然是前端顯示問題,那就沒什么是一個油猴腳本解決不了的。

這篇試圖說清楚:

如何通過利用油猴腳本的腳本注入和跨域請求能力,攔截用戶對 .md 鏈接的點(diǎn)擊,通過調(diào)用 RAGFlow 的內(nèi)部 API 獲取原始數(shù)據(jù),并動態(tài)生成一個預(yù)覽頁面;以及在實(shí)現(xiàn)上述過程中踩到的坑與迭代方向參考。

以下,enjoy:

1、需求場景分析

在追求高精度問答的實(shí)際落地場景中,對于企業(yè)知識庫中充斥的格式各異、布局復(fù)雜的文檔,比如跨越多欄的 PDF 報告、包含復(fù)雜嵌套表格的 Word 文檔、或是圖文混排的產(chǎn)品手冊。直接把這些原始文檔投喂給任何 RAG 系統(tǒng),都可能面臨“Garbage In, Garbage Out”的問題。

而 Ragflow 自帶的 DeepDoc 或者使用 MinerU 解析器雖然強(qiáng)大,但面對這些“臟數(shù)據(jù)”的時候,也難免會產(chǎn)生語義不連貫、上下文割裂的文本塊,這會直接影響檢索的準(zhǔn)確性和最終生成答案的質(zhì)量。

1.1預(yù)處理的主要做法

為了確保送入 RAG 管道的數(shù)據(jù)是高質(zhì)量的,文檔預(yù)處理幾乎是所有嚴(yán)肅 RAG 應(yīng)用中不可或缺的一步。這一步不只是簡單的格式轉(zhuǎn)換,更是一個結(jié)合文檔特點(diǎn)進(jìn)行結(jié)構(gòu)化重塑過程。核心目標(biāo)包括:

  • 消除噪音:剔除頁眉、頁腳、頁碼等無關(guān)信息。
  • 保留結(jié)構(gòu):正確識別標(biāo)題層級、列表、表格和代碼塊,將視覺結(jié)構(gòu)轉(zhuǎn)化為語義結(jié)構(gòu)。
  • 確保連貫性:將被物理分頁或分欄打斷的完整段落重新連接起來。

通過定制化的腳本(如 Python 腳本結(jié)合 PyMuPDF, python-docx 等庫)進(jìn)行預(yù)處理,可以把一份原始復(fù)雜的文檔,清洗并轉(zhuǎn)化為一份干凈結(jié)構(gòu)化的中間格式。

1.2為什么選擇 Markdown 作為中間格式?

為了更加直觀的對比常見的四種中間格式的適用情形,下面結(jié)合這個表格來一起看一下:

特性維度

TXT (.txt)

Markdown (.md)

HTML (.html)

JSON (.json)

語義結(jié)構(gòu)保留

極低。丟失所有標(biāo)題、列表、表格等結(jié)構(gòu),退化為純文本流。

良好。能通過簡單標(biāo)記保留標(biāo)題層級、列表、代碼塊等核心語義。

極高。可以像素級地保留所有原始結(jié)構(gòu)和樣式。

靈活可定義。可以將內(nèi)容和元數(shù)據(jù)以任意復(fù)雜的結(jié)構(gòu)進(jìn)行組織。

LLM 親和度

中等。文本干凈,但缺乏結(jié)構(gòu)會影響 LLM 對上下文層次的理解。

高。LLM 對 Markdown 有天生的理解力,結(jié)構(gòu)標(biāo)記有助于理解上下文。

中等。標(biāo)簽本身是噪音,若不經(jīng)清洗會嚴(yán)重干擾嵌入質(zhì)量。需要額外處理。

高(指 JSON 中的文本值)。需要程序解析后將干凈文本喂給模型。

人類可讀性

高。非常直觀,所見即所得。

極高。既保留了結(jié)構(gòu),又非常易于人類閱讀和調(diào)試。

低。充斥著標(biāo)簽,不便于直接閱讀原始內(nèi)容。

低。是為機(jī)器設(shè)計的格式,人類難以直接閱讀長篇內(nèi)容。

實(shí)現(xiàn)復(fù)雜度

低。幾乎所有解析庫都能輕松輸出純文本。

中等。需要編寫邏輯將文檔結(jié)構(gòu)(如 Word 的 Heading 1)映射到 MD 標(biāo)記(#)。

高。要生成干凈、有效的 HTML,需要復(fù)雜的轉(zhuǎn)換邏輯和嚴(yán)格的 XSS 過濾。

高。需要為數(shù)據(jù)定義清晰的 schema(模式),并進(jìn)行序列化。

元數(shù)據(jù)處理

無。無法內(nèi)嵌元數(shù)據(jù)(如來源頁碼)。

有限。可以通過 YAML Front Matter 注入,但不是原生標(biāo)準(zhǔn)。

優(yōu)秀。可以通過 data-* 屬性將元數(shù)據(jù)與具體元素綁定。

極佳。天生就是為組織“數(shù)據(jù)和元數(shù)據(jù)”而設(shè)計的。

一句話總結(jié)

適用簡單場景:用于處理純散文、無結(jié)構(gòu)的文章,追求極簡。

平衡之選:最適合需要保留核心結(jié)構(gòu)且兼顧人機(jī)可讀性的場景。

追求高保真:當(dāng)需要復(fù)現(xiàn)復(fù)雜表格或布局,且不惜處理成本時使用。

系統(tǒng)化管道:用于需要精細(xì)控制、攜帶大量元數(shù)據(jù)的自動化、工程化 RAG 流程。

從上表可以看出來,其實(shí)并不存在“最好”的格式,只有“最適合”的場景。而之所以在許多實(shí)踐中傾向于選擇 Markdown,是因?yàn)樗?“保留關(guān)鍵結(jié)構(gòu)”、“模型友好度”和“人類可讀性(易于調(diào)試)” 這三個對維度上取得了很好的平衡。

注:針對更為復(fù)雜的的表格或頁面布局建議還是使用 html 格式,這部分內(nèi)容預(yù)計 7 月初我會在結(jié)合歷史文章中提到的 IBM 的 RAG 冠軍賽項目復(fù)現(xiàn)文章中進(jìn)行具體介紹。

2、實(shí)現(xiàn)原理解析

這個流程圖展示了 RAGFlow 的聊天助手中,引用文件是 Markdown 時的預(yù)覽功能完整實(shí)現(xiàn)機(jī)制。通過 MutationObserver 實(shí)時監(jiān)聽聊天界面的 DOM 變化,自動為 .md 文件鏈接綁定自定義點(diǎn)擊事件,當(dāng)用戶點(diǎn)擊時攔截默認(rèn)跳轉(zhuǎn)行為并創(chuàng)建新標(biāo)簽頁,同時從鏈接中提取文檔 ID 并讀取認(rèn)證憑據(jù),通過 GM_xmlhttpRequest 向后端 /v1/chunk/list 接口請求文檔數(shù)據(jù),最后利用 marked.js 庫解析 JSON 響應(yīng)中的內(nèi)容塊,動態(tài)構(gòu)建并渲染格式化的 HTML 頁面,實(shí)現(xiàn)了無侵入式的文檔預(yù)覽功能增強(qiáng)。整個流程采用異步處理和錯誤處理機(jī)制,確保使用的流暢性和系統(tǒng)穩(wěn)定性。

3、核心模塊拆解

3.1動態(tài)事件監(jiān)聽與觸發(fā)

由于 Ragflow 是一個現(xiàn)代化的單頁應(yīng)用 (SPA),其聊天內(nèi)容是動態(tài)加載到頁面中的。傳統(tǒng)的頁面加載事件無法監(jiān)聽到這些后續(xù)出現(xiàn)的內(nèi)容。因此,腳本的核心入口采用了 MutationObserver API。

實(shí)現(xiàn)邏輯

初始化一個 MutationObserver 實(shí)例,配置它來監(jiān)視 document.body 及其所有后代節(jié)點(diǎn) (subtree: true) 的添加或刪除 (childList: true)。

當(dāng)監(jiān)聽到有新節(jié)點(diǎn)被添加到頁面時,腳本會遍歷這些節(jié)點(diǎn),并使用 querySelectorAll 高效地查找其中所有指向 .md 文件的 (這里有個東西)鏈接。

為了避免重復(fù)綁定,腳本為每個處理過的鏈接添加了一個 data-md-preview-handled 屬性作為標(biāo)記。如果鏈接未被標(biāo)記,則為其 click 事件綁定核心處理函數(shù) processMarkdownLink。

關(guān)鍵函數(shù)說明

new MutationObserver(callback): 創(chuàng)建一個觀察者對象,當(dāng)指定的 DOM 變化發(fā)生時,執(zhí)行回調(diào)函數(shù)。這是應(yīng)對動態(tài)網(wǎng)頁內(nèi)容變化的不二之選。

observer.observe(targetNode, options): 啟動觀察者。{ childList: true, subtree: true } 是性能和功能之間的完美平衡,確保了不會錯過任何動態(tài)添加的鏈接。

3.2核心處理流程

當(dāng)用戶點(diǎn)擊目標(biāo)鏈接后,這個函數(shù)會被觸發(fā),并按序執(zhí)行一系列操作。

實(shí)現(xiàn)邏輯

即時響應(yīng)與阻止默認(rèn):立刻調(diào)用 event.preventDefault() 和 event.stopPropagation(),阻止瀏覽器執(zhí)行默認(rèn)的、無法預(yù)覽的跳轉(zhuǎn)行為。同時,window.open() 打開一個新標(biāo)簽頁并顯示“加載中”,給予用戶即時反饋。

信息采集:通過正則表達(dá)式從鏈接的 href 屬性中精確提取出 doc_id。隨后,從瀏覽器的 localStorage 中獲取 Authorization Token,這是后續(xù)與后端 API 進(jìn)行認(rèn)證通信的關(guān)鍵憑證。

關(guān)鍵函數(shù)說明

event.preventDefault(): 阻止事件的默認(rèn)動作,此處即阻止鏈接跳轉(zhuǎn)。

window.open('', '_blank'): 打開一個新窗口,并保留其句柄(tempWindow),以便后續(xù)向其寫入內(nèi)容。

localStorage.getItem('Authorization'): 從瀏覽器本地存儲中獲取身份驗(yàn)證令牌。腳本的運(yùn)行環(huán)境與 Ragflow 頁面相同,因此可以直接訪問。

3.3后端數(shù)據(jù)安全交互

獲取到必要信息后,腳本需要與 Ragflow 的后端進(jìn)行通信,以拉取文檔的完整內(nèi)容。

實(shí)現(xiàn)邏輯

利用油猴提供的 GM_xmlhttpRequest 發(fā)起一個 POST 請求到 Ragflow 的 /v1/chunk/list API 端點(diǎn)。

請求頭中必須包含 Content-Type 和從 localStorage 中獲取的 Authorization Token。

請求體是一個 JSON 對象,包含了需要查詢的 doc_id 和一個較大的 limit 值,以確保能一次性獲取所有內(nèi)容塊 (chunks)。

通過設(shè)置 onload、onerror 和 ontimeout 回調(diào)函數(shù),對請求的成功、失敗和超時情況進(jìn)行全面處理。

關(guān)鍵函數(shù)說明

GM_xmlhttpRequest(details): 油猴的特權(quán) API,可以突破同源策略 (CORS) 的限制,是實(shí)現(xiàn)該功能的核心。腳本頭部的 @connect localhost 和 @connect 127.0.0.1 就是在為此 API 授權(quán)。

JSON.stringify(payload): 將 JavaScript 對象序列化為 JSON 字符串,作為請求體發(fā)送。

3.4前端動態(tài)渲染與呈現(xiàn)

這是把枯燥的數(shù)據(jù)轉(zhuǎn)化為美觀頁面的最后一步,也是用戶最終能感知到的部分。

實(shí)現(xiàn)邏輯

數(shù)據(jù)解析:在 onload 回調(diào)中,腳本首先解析 API 返回的 JSON 數(shù)據(jù),分離出文檔的元數(shù)據(jù) (doc) 和內(nèi)容塊數(shù)組 (chunks)。

HTML 結(jié)構(gòu)構(gòu)建:腳本使用模板字符串動態(tài)生成一個完整的 HTML 頁面結(jié)構(gòu)。這包括:

一個美觀的頭部,包含文檔標(biāo)題。

一個展示文檔 ID、創(chuàng)建時間等信息的“元數(shù)據(jù)卡片”。

遍歷 chunks 數(shù)組,為每個內(nèi)容塊創(chuàng)建一個獨(dú)立的“內(nèi)容卡片”。一個巧妙的設(shè)計是,每個塊的原始 Markdown 內(nèi)容被存儲在一個隱藏的 <textarea>中,這可以防止瀏覽器錯誤地解析其中的特殊字符。

集成 Markdown 解析器:在生成的 HTML 的<head>部分,通過 CDN 引入了輕量且強(qiáng)大的 marked.js 庫。

最終渲染:頁面主體包含一段內(nèi)聯(lián)的<script>。它在 DOMContentLoaded 事件觸發(fā)后執(zhí)行,遍歷所有的內(nèi)容卡片,讀取隱藏<textarea>中的 Markdown 原文,使用 marked.parse() 將其轉(zhuǎn)換為 HTML,最后注入到對應(yīng)的容器中,完成最終的渲染。

寫入頁面:調(diào)用 tempWindow.document.write(),將整個拼接好的 HTML 字符串寫入之前打開的新標(biāo)簽頁中,瀏覽器會解析并展示這個頁面。

關(guān)鍵函數(shù)說明

marked.parse(markdownString): marked.js 庫的核心函數(shù),將傳入的 Markdown 格式字符串轉(zhuǎn)換為 HTML 字符串。

tempWindow.document.write(html): 向指定窗口的文檔流中寫入數(shù)據(jù)。這是動態(tài)創(chuàng)建整個頁面的關(guān)鍵。

4、填過的這些坑

4.1鏈接與彈窗的直接對抗

我最開始的想法是攔截鏈接的點(diǎn)擊事件,直接使用 fetch 請求鏈接地址,然后將獲取到的文本內(nèi)容放入一個自己創(chuàng)建的彈窗中顯示。但是發(fā)現(xiàn)彈窗成功彈出,但里面是空的。

通過 F12 開發(fā)者工具的“控制臺”和“網(wǎng)絡(luò)”面板發(fā)現(xiàn),請求鏈接返回的是整個 Ragflow 應(yīng)用的 HTML 主頁面。這是第一個碰到的障礙:單頁應(yīng)用(SPA)的路由機(jī)制。就是服務(wù)器被配置為將所有無法直接匹配的路徑都重定向到應(yīng)用的入口 index.html,由前端 JavaScript 來接管后續(xù)的路由和數(shù)據(jù)加載。這說明最初的腳本從一開始就敲錯了門。

4.2尋找真正的 API

既然直接請求鏈接行不通,那一定有一個隱藏的、真正用來獲取文件內(nèi)容的 API 接口,這就要通過監(jiān)視 Ragflow 自身的網(wǎng)絡(luò)請求來找到它。

通過在知識庫頁面操作,成功的在“網(wǎng)絡(luò)”面板捕獲到了一個形如 /v1/chunk/list 的 API 請求。但是當(dāng)腳本去請求這個新發(fā)現(xiàn)的 API 時,服務(wù)器卻返回了“401 未授權(quán)”的錯誤。測試下來發(fā)現(xiàn)其實(shí)是兩層防御問題需要克服下:

第一層防御:HttpOnly Cookie

最初嘗試用 document.cookie 手動附加 Cookie,但失敗了。這意味著最關(guān)鍵的登錄憑證被存儲在 HttpOnly Cookie 中,JavaScript 無法讀取。

第二層防御:授權(quán)令牌 (Authorization Token)

即使讓油猴腳本自動攜帶 Cookie,請求依然失敗。最后發(fā)現(xiàn),Ragflow 使用了更安全的 Token 認(rèn)證機(jī)制。真正的“通行證”不是 Cookie,而是一個存儲在 localStorage 中的、名為 Authorization 的令牌,它必須被放在請求頭中發(fā)送。

4.3與前端框架的終極博弈

在擁有了所有正確的鑰匙(API 地址、請求方法、認(rèn)證令牌、請求參數(shù)),成功獲取到了包含 Markdown 內(nèi)容的 JSON 數(shù)據(jù)。現(xiàn)在只需要將它顯示出來即可。但是彈窗就是無法穩(wěn)定地顯示在頁面上。有時會閃現(xiàn)一下,有時干脆沒有任何反應(yīng),控制臺也沒有任何錯誤。

通過使用 debugger 凍結(jié)時間的方式發(fā)現(xiàn),彈窗確實(shí)被成功添加到了頁面上,但幾乎在同一瞬間就被 Ragflow 的前端框架(React)給“凈化”或移除了,因?yàn)樗粚儆诳蚣芄芾淼摹疤摂M DOM”結(jié)構(gòu)。這個算是碰到了現(xiàn)代前端框架最核心的壁壘——絕對的 DOM 控制權(quán)。任何試圖在框架“管轄范圍”之外直接操作 DOM 的行為,都注定會失敗。

4.4放棄對抗,另辟蹊徑

最后我放棄了在原頁面顯示彈窗的方案,選擇和 RAGFlow 的原生文件預(yù)覽方式一樣,在新標(biāo)簽頁中渲染。也就是在腳本攔截點(diǎn)擊后,在后臺完成所有正確的數(shù)據(jù)請求。然后,它將獲取到的元數(shù)據(jù)和 Markdown 內(nèi)容,動態(tài)地構(gòu)建成一個完整的、獨(dú)立的 HTML 頁面。最后,通過打開一個新標(biāo)簽頁來展示這個頁面。

最后展示的效果中,進(jìn)一步提取了文檔的元數(shù)據(jù)(如 ID、創(chuàng)建時間等),并采用 iOS 扁平化設(shè)計風(fēng)格,將所有信息以清晰的卡片式布局呈現(xiàn)出來。

5、寫在最后

5.1關(guān)于油猴腳本

單頁應(yīng)用路由、Token 身份認(rèn)證、前端框架的 DOM 控制權(quán),幾乎是每一個試圖對現(xiàn)代 Web 應(yīng)用進(jìn)行個性化增強(qiáng)的開發(fā)者都會遇到的“三座大山”。而在 RAG 流程乃至更廣泛的 Web 應(yīng)用生態(tài)中,油猴腳本所代表的客戶端注入模式,積極意義遠(yuǎn)超“小打小鬧”的范疇:

敏捷與個性化

官方產(chǎn)品迭代有其固定的節(jié)奏和優(yōu)先級。而作為一線用戶的痛點(diǎn)往往是即時且個性化的。油猴腳本可以快速實(shí)現(xiàn)特定工作流中的功能補(bǔ)完,將產(chǎn)品打磨成最適合自己或團(tuán)隊的形態(tài)。

低風(fēng)險與高兼容性

這種增強(qiáng)方式是非侵入式的,不用修改任何服務(wù)端代碼,也不觸碰核心前端應(yīng)用的文件。這意味著它幾乎不會對原系統(tǒng)的穩(wěn)定性造成任何風(fēng)險。只要 Ragflow 的核心 API 保持穩(wěn)定,即使前端界面升級,腳本大概率也能繼續(xù)工作,維護(hù)成本極低。

5.2從 UI 增強(qiáng)到工作流自動化

這個 Markdown 預(yù)覽腳本只是拋磚引玉,各位可以解決自己在使用 Ragflow 或其他工具時遇到的個性化問題。值得探索的方向還有:

知識庫管理增強(qiáng)

編寫腳本,在知識庫文檔列表頁面,為每個文檔增加“計算預(yù)估 Token 數(shù)”、“快速查看分塊摘要”等按鈕,在上傳和管理階段提供更多決策支持。

聊天交互的自動化

在聊天輸入框旁,增加一個“常用 Prompt 模板”面板,一鍵發(fā)送復(fù)雜的指令。或者增加一個“導(dǎo)出對話”按鈕,將當(dāng)前問答流程以特定格式保存到本地。

跨系統(tǒng)工作流集成

更高階的玩法,是讓腳本成為連接器。例如,在預(yù)覽頁面增加一個按鈕,可以將某個重要的 Chunk 內(nèi)容,連同其元數(shù)據(jù),一鍵發(fā)送到 Notion、Obsidian 等筆記軟件中。

責(zé)任編輯:龐桂玉 來源: 韋東東
相關(guān)推薦

2022-03-03 08:02:55

數(shù)據(jù)集成平臺

2025-05-22 06:39:08

2022-04-29 08:00:06

Linux目錄網(wǎng)絡(luò)

2017-11-14 11:12:50

Go語言編譯器

2020-04-29 15:30:22

CSP網(wǎng)頁前端

2014-02-11 10:09:37

中小企業(yè)UC

2024-05-06 09:35:05

AI網(wǎng)關(guān)開源

2020-01-18 14:59:36

手機(jī)AI手機(jī)處理器

2014-04-25 10:05:42

OpenStack私有云公共云

2024-04-15 14:25:06

2015-08-11 09:48:53

2013-11-22 13:25:57

數(shù)據(jù)中心云計算

2016-01-28 10:04:09

Jenkins運(yùn)維持續(xù)交付

2010-03-15 10:37:46

Pthon腳本

2015-05-13 13:59:02

碼農(nóng)拒絕

2015-05-15 09:56:26

程序員代碼

2024-05-15 10:07:11

Agents人工智能CSV

2016-03-21 14:33:23

JavaPython程序員

2024-09-18 00:00:03

SSGSSR服務(wù)器

2010-11-17 15:43:55

軟件測試Bug
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 欧美一二三区 | 免费一区 | 欧美亚洲国产一区二区三区 | 久久精彩视频 | 久夜精品| 国产福利一区二区 | 国产精品久久久久久久久免费桃花 | 亚洲一区中文字幕 | 夜久久 | 亚洲欧美国产一区二区三区 | 成人一区二区三区在线观看 | 偷拍自拍网 | 成人在线中文字幕 | 国产色婷婷久久99精品91 | 中文字幕在线电影观看 | 国产视频综合 | 99热这里都是精品 | 黄a在线观看 | 国产欧美日韩精品在线观看 | 日韩成人一区 | 99免费精品视频 | 日韩成人 | 日本a∨精品中文字幕在线 亚洲91视频 | caoporn国产| 九九热这里只有精品在线观看 | 91美女视频| 欧美日韩国产一区二区三区 | 亚洲一二三视频 | 亚洲精品国产一区 | 美女日皮网站 | 国产精品成人一区 | 91色网站 | 国产精品久久久久久久7电影 | 久久久精品网站 | 国产精品99久久久久久久vr | 亚洲欧美综合 | 成人久久18免费网站图片 | 天天操网| 一级毛片播放 | 91麻豆精品国产91久久久久久 | 草樱av |