如何在 RAG 工作流程中最好地處理來自 ScholarAPI.net 的長時間執行 HTTP 請求和大型 JSON 負載?

Hi n8n 社群,

我目前正在使用 n8n 建立一個自動化研究資料匯入管道,用來驅動學術 RAG(檢索增強生成)系統。

工作流程目標:

當新的研究主題被記錄時,自動化工作流程會觸發。它會呼叫一個稱為 ScholarAPI.net 的學術資料基礎設施引擎,以取得完整文本的學術資料和高度詳細的引用元數據。一旦檢索到 JSON 負載,n8n 就會將資料傳遞給嵌入節點,並將其推送到向量存儲中。

挑戰:

在對 ScholarAPI.net 進行 HTTP 請求節點呼叫時,包含多篇科學論文完整文本的 JSON 回應可能會非常龐大(有時每個批次可能有數百萬字節的乾淨結構化文本)。

我遇到了兩個具體的架構問題,以保持工作流程的最佳化:

  1. 處理逾時/執行限制:對於大型批次的完整文本資料,上游 API 處理可能需要一些時間。在 n8n 中設定彈性重試設定檔或延長 HTTP 請求節點逾時的最佳做法是什麼,以便工作流程不會過早失敗?

  2. 記憶體/資料分割:在單個執行緒中直接處理龐大的巢狀 JSON 負載會導致記憶體使用量很大。我應該在從 ScholarAPI.net 接收資料時立即使用「項目清單」節點來分割傳入的文本陣列,還是自訂代碼節點(JavaScript/Python)在傳送到向量嵌入之前進行文本分塊會更有效率?

非常感謝任何人能提供建議或工作流程模板,特別是那些在 n8n 內建立過大規模文本匯入/資料抓取管道的人!

提前感謝。

@Asgef_Sha 關於逾時問題,HTTP Request 節點在 Options 底下有 Timeout 欄位,針對緩慢的呼叫提高它的值,並在 Settings 中開啟 Retry On Fail,設定 Max Tries 和 Wait Between Tries。注意,重試功能只有在 On Error 設為 Stop Workflow 時才會啟動;如果設為 Continue 選項,n8n 會忽略重試計數。

關於酬載問題,不要用 Item Lists 或 Code 節點手動分塊,n8n 有專門的 Recursive Character Text Splitter 節點用於此目的(Chunk Size + Chunk Overlap),將其輸送到 Default Data Loader 再進入你的向量存儲。先將 papers 陣列 Split Out,這樣每一份都會獨立流動處理,而不是在單次執行中保持整個數 MB 的資料塊。

歡迎 @Asgef_Sha!achamm 關於文本分割器的建議完全正確。針對多篇論文批次處理,還有一個額外的模式:在獲得 HTTP 響應後,使用 SplitInBatches 節點以 5-10 篇論文為一組進行批量處理,然後將每個批次通過文本分割器和向量存儲插入。這可以防止單個大型有效負載在嵌入過程中佔用記憶體,從而避免執行停滯的情況,即使延長了逾時設定也是如此。另外,建議將 HTTP Request 逾時設置為至少 120 秒以用於 ScholarAPI 的全文調用,因為他們的伺服器端聚合在大型查詢時可能會很慢。

對於這種 RAG 擷取,我不會把整個流程保持在一個長的同步執行中。先將原始的 ScholarAPI 回應儲存起來,將承載資料分割成可管理的區塊,然後用重試和檢查點分步驟進行處理/嵌入。大型 JSON 加上冗長的 HTTP 呼叫是一個地方,平凡的可靠性勝過聰明的一體化工作流程。你能在嵌入步驟開始之前將原始回應儲存在某處嗎?

上面已經有不少好答案。文字分割器加上 Split Out 加上 SplitInBatches 可以處理分塊,OMGItsDerek 說得對,你想在嵌入前保留原始回應。我會建立在最後一點,因為對於這種類型的攝入,架構比任何單一節點設定更重要。

在大型文本攝入管道上救過我的幾件事:

  1. 把它分成兩個工作流,不是一個。工作流 A 只是呼叫 ScholarAPI,將每篇論文寫入表格或物件存儲,狀態為「已抓取」。工作流 B 拾取狀態為「已抓取」的列,進行分塊、嵌入,然後將其翻轉為「已嵌入」。昂貴的 API 拉取和緩慢的嵌入然後獨立失敗。如果嵌入在 50 篇論文中的第 40 篇失敗,你永遠不會重新拉取批次,B 只是繼續未完成的列。那個狀態列是你的檢查點。

  2. 讓它具有冪等性。用穩定的 id(DOI 或 ScholarAPI id)為每篇論文設定鍵,並在嵌入前檢查存儲。在長時間的工作中,重試和重新執行是不可避免的,沒有去重鍵,你會花錢嵌入同一篇論文兩次,最終會得到污染檢索的重複向量。那是最便宜的可靠性勝利。

  3. 如果可以的話,在源頭修復大小。如果 ScholarAPI 支援分頁或每篇論文抓取,拉取較小的頁面而不是一個多 mb 的批次。不在大型 JSON 上耗盡記憶體的最可靠方法,是一開始就永遠不要將其作為單一項目保留。提早進行 Split Out 會有幫助,較小的上游呼叫幫助更大。

  4. 對上面重試點的一個小澄清:「失敗時重試」將重試該節點,不管怎樣。「錯誤時」設定決定重試耗盡後會發生什麼,「停止工作流」會中止,「繼續」會將失敗的項目傳遞到下游。對於攝入工作,我保持打開重試,「錯誤時」設定為繼續(使用錯誤輸出),並將失敗路由到死信表,這樣一篇不好的論文就不會沉沒整個執行。

  5. 如果你是自託管的,且有效載荷確實很大,兩個環境槓桿會有幫助:用 NODE_OPTIONS=–max-old-space-size 提高 Node 的堆,並關閉執行資料保留,這樣多 mb 的執行就不會使你的資料庫膨脹。

總的來說,無聊的生產者-消費者分割加上狀態列和去重鍵,會讓你走得比在一次大執行中調整逾時更遠。樂意詳細說明其中任何一個。

:waving_hand:
我認為我理解你的意思——這裡的問題不只是超時或批量處理,而是 n8n 在單一執行環境中處理非常大的 JSON 載體。

對於像 ScholarAPI 返回巨大結構化文本這樣的情況,真正的瓶頸通常是執行記憶體 + 節點鏈接,而不僅僅是 HTTP 限制。

我見過類似的情況,只是分割或「非同步化」流程並不能完全解決問題,因為資料在執行生命週期期間仍然被保存在記憶體中。

好奇你是否試過把擷取步驟完全與轉換步驟隔離開來(不只是在同一工作流程內進行分割)?

兩個具體的 n8n 設定來解決這個問題:首先,在 HTTP Request 節點 > Options > Timeout 中,將其設為 300000(5 分鐘),這樣大型負載的回應就不會被中斷。其次,如果 ScholarAPI 回傳一個論文陣列,在嵌入步驟前,將輸出通過設定為批次大小 1 的 Split In Batches 節點——這樣一次只會在執行環境中保留一篇論文,而不是全部。若要像 @Bella123 建議的那樣完全隔離記憶體作用域,可以透過 Execute Workflow 呼叫一個獨立的子工作流程來進行嵌入 + 儲存步驟。這樣每篇論文的文本在子工作流程返回後就會被垃圾回收,而不會在父執行中累積。

我會把這分成攝取、正規化和嵌入三個部分,而不是試圖用一個大型工作流同時處理所有內容。

對於這種 ScholarAPI → RAG 路徑,風險部分通常不只是長時間的 HTTP 請求。問題在於巨大的payload可以在多個不同的邊界點失敗:

  1. 取得邊界
    HTTP 呼叫可能會逾時,或者返回太多資料,導致單次執行無法舒適地保存。

  2. 正規化邊界
    在分塊之前,你需要決定什麼是一個「文件」:論文、摘要、章節、引用區塊或作者元資料。

  3. 分塊邊界
    嵌入步驟應該接收可預測的單位,而不是原始 API payload。

  4. 重試邊界
    如果一筆記錄失敗,你不會想重新取得和重新嵌入整個結果集。

我會使用的模式:

  • 請求一頁/批記錄;
  • 立即將原始回應元資料寫入某個持久位置;
  • 將每篇論文分割成一個正規化項目;
  • 儲存項目 ID 和處理狀態;
  • 以第二個工作流對待處理項目運行分塊/嵌入;
  • 將每個項目標記為已取得、已正規化、已分塊、已嵌入、失敗或已略過。

這樣可以讓你實現可重新啟動性。它也使 RAG 品質更容易除錯,因為你可以檢查哪個階段產生了不良的塊。

一個非敏感的問題:ScholarAPI 是否讓你按日期/查詢分頁或過濾結果,還是你接收一個必須在請求後分割的大型回應?