iT邦幫忙

2025 iThome 鐵人賽

DAY 7
0
生成式 AI

打造基於 MCP 協議與 n8n 工作流的會議處理 Agent系列 第 7

Day 7 n8n 工作流 JSON 處理與穩定性完善

  • 分享至 

  • xImage
  •  

今天的目標與挑戰

昨天使用兩個 HTTP Request 來做「AI摘要」與「任務提取」,但在後來的持續測試中發現 JSON 無法處理換行符號、引號等特殊字符的摘要內容,導致無法正常傳遞,今天會專注在解決這些問題上。

  • 解決 HTTP Request 的 JSON 參數特殊字符處理問題
  • 使用 Code 節點重構「AI摘要」與「任務提取」功能
  • 建立完整的錯誤處理與重試機制
  • 完善工作流的資料驗證與異常處理
  • 進行穩定性測試,確保基礎流程健全

我也將錄音檔更換為會議的錄音demo了


Step 1:診斷 JSON 特殊字符問題

1-1 問題分析

在持續測試的過程中,我遇到了以下的錯誤訊息

{
  "errorMessage": "JSON parameter needs to be valid JSON",
  "errorDetails": "The model has crashed without additional information"
}

1-2 根本原因分析

  1. 特殊字符破壞 JSON 格式:AI 生成的內容包含 \n"** 等字符
  2. n8n 表達式限制:HTTP Request 節點的 JSON 表達式無法自動處理轉義

確認了問題根源在於 n8n HTTP Request 節點對動態 JSON 內容的處理限制。


Step 2:使用 Code 節點解決 JSON 問題

2-1 替換 HTTP Request 節點

  1. 刪除原有的「AI摘要」和「任務提取」節點

  2. 在 Webhook 節點後添加 Code 節點,選擇「Code in JavaScript」

  3. 將節點命名為「AI摘要與任務提取」

選擇「Code in JavaScript」的原因

  1. n8n 原生支援:JavaScript 是 n8n 的主要程式語言
  2. 效能更好:JavaScript 執行速度比 Python 快,Python 現在仍然為 Beta 版本且需要額外的編譯步驟
  3. 功能完整:所有 n8n 的內建方法和變數都可以使用
  4. HTTP 請求支援更好$http.request() 方法在 JavaScript 中運作更穩定
  5. 除錯友善:支援 console.log() 輸出到瀏覽器控制台,方便即時除錯和問題排查

2-2 Code 節點「AI摘要與任務提取」參數設定

const webhookData = $input.first().json.body;
const inputText = webhookData.text;
const instruction = webhookData.instruction || "請幫我整理出重點並提取任務";

console.log('開始處理 AI 摘要與任務提取...');
console.log('輸入文字長度:', inputText.length);
console.log('處理指令:', instruction);

// AI 摘要
async function generateSummary(text) {
  const summaryPayload = {
    model: "qwen2.5-taiwan-7b-instruct-i1",
    messages: [
      {
        role: "user",
        content: `請摘要以下內容,重點說明主要討論事項:${text}`
      }
    ],
    temperature: 0.5,
    max_tokens: 300
  };

  try {
    console.log('發送摘要請求到 LM Studio...');
    
    const response = await this.helpers.httpRequest({
      method: 'POST',
      url: 'http://host.docker.internal:1234/v1/chat/completions',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(summaryPayload),
      timeout: 120000
    });

    // 解析回應
    const responseData = typeof response === 'string' ? JSON.parse(response) : response;
    
    if (responseData.choices && responseData.choices[0] && responseData.choices[0].message) {
      console.log('摘要生成成功');
      return responseData.choices[0].message.content;
    } else {
      throw new Error('摘要API回應格式異常');
    }
  } catch (error) {
    console.error('摘要生成失敗:', error.message);
    return `摘要生成失敗:${error.message}`;
  }
}

// 任務提取
async function extractTasks(summaryText) {
  const taskPayload = {
    model: "qwen2.5-taiwan-7b-instruct-i1",
    messages: [
      {
        role: "user",
        content: `請從以下摘要中提取所有行動項目和待辦事項,以條列式呈現,若無則說沒有指派任何行動:${summaryText}`
      }
    ],
    temperature: 0.5,
    max_tokens: 300
  };

  try {
    console.log('發送任務提取請求到 LM Studio...');
    
    const response = await this.helpers.httpRequest({
      method: 'POST',
      url: 'http://host.docker.internal:1234/v1/chat/completions',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(taskPayload),
      timeout: 120000
    });

    // 解析回應
    const responseData = typeof response === 'string' ? JSON.parse(response) : response;
    
    if (responseData.choices && responseData.choices[0] && responseData.choices[0].message) {
      console.log('任務提取成功');
      return responseData.choices[0].message.content;
    } else {
      throw new Error('任務提取API回應格式異常');
    }
  } catch (error) {
    console.error('任務提取失敗:', error.message);
    return `任務提取失敗:${error.message}`;
  }
}

// 主要處理流程
try {
  // 生成摘要
  const summaryResult = await generateSummary(inputText);
  
  // 基於摘要做任務提取
  const tasksResult = await extractTasks(summaryResult);
  
  // 回傳結果
  const result = {
    summary: summaryResult,
    tasks: tasksResult,
    processing_info: {
      input_length: inputText.length,
      summary_length: summaryResult.length,
      tasks_length: tasksResult.length,
      processed_at: new Date().toISOString(),
      status: 'success'
    }
  };
  
  console.log('AI 處理完成!');
  console.log('摘要長度:', summaryResult.length);
  console.log('任務長度:', tasksResult.length);
  
  return [{ json: result }];

} catch (error) {
  console.error('AI 處理過程發生錯誤:', error.message);
  
  // 錯誤處理,回傳錯誤資訊
  return [{
    json: {
      summary: `處理失敗:${error.message}`,
      tasks: "無法提取任務",
      processing_info: {
        status: 'error',
        error_message: error.message,
        processed_at: new Date().toISOString()
      }
    }
  }];
}

執行結果

Code 節點成功處理了 JSON 特殊字符問題,不再出現格式錯誤,並加入了完整的錯誤處理機制。


Step 3:解決 LM Studio 超時問題

3-1 調整 LM Studio 模型參數

在測試的過程中,我還發現的超時問題,於是我也調整了 LM Studio 和節點「AI摘要與任務提取」timeout 的設定

  1. 最佳化模型參數
    Context Length: 2048
    GPU Offload: 0 -> 5
    CPU Thread Pool Size: 4
    Evaluation Batch Size: 256 -> 512
    Offload KV Cache to GPU Memory: OFF
    Keep Model in Memory: ON
    
  2. 增加超時時間
    • 將 Code 節點中的 timeout 從 60000ms 增加到 120000ms

執行結果

LM Studio 處理速度大幅提升,解決了超時的問題。


Step 4:修正「Respond to Webhook」JSON 格式問題

4-1 使用 JSON.stringify() 處理特殊字符

將「Respond to Webhook」節點的 JSON 修改為

{
  "status": {{ JSON.stringify($json.processing_info.status) }},
  "summary": {{ JSON.stringify($json.summary) }},
  "tasks": {{ JSON.stringify($json.tasks) }}
}

執行結果

最終成功的輸出結果


{
"status": "success",
"summary": "今天會議的主要討論事項是專案進度。主要內容包括:\n\n1. **需求分析**:需要在週五前完成初步報告。\n2. **資料庫架構設計**:\n   - John負責處理資料庫架構的設計文件,需在週三前準備出稿,包括ER圖和資料表規劃,並協助Mary進行系統測試的規劃。\n3. **前端介面設計**:\n   - Mary負責完成前端介面的圓形設計,特別是使用者登入合宜標版的部分,需在週四前提供可互動的圓形供團隊檢視,並準備使用者體驗測試的問卷調查。\n4. **客戶會議準備**:需要準備展示材料,John負責技術架構簡報,Mary負責使用者介面展示。兩位要在週五下午3點前完成材料準備,以便進行最後的整合測試。\n\n所有成員需在週末前完成整合測試。如果還有其他問題,將留待下次會議討論。",
"tasks": "### 行動項目及待辦事項\n\n1. **需求分析**\n   - 在週五前完成初步報告。\n\n2. **資料庫架構設計**\n   - John:\n     - 負責處理資料庫架構的設計文件。\n     - 需在週三前準備出稿,包括ER圖和資料表規劃,並協助Mary進行系統測試的規劃。\n\n3. **前端介面設計**\n   - Mary:\n     - 完成前端介面的圓形設計,特別是使用者登入合宜標版的部分。\n     - 在週四前提供可互動的圓形供團隊檢視,並準備使用者體驗測試的問卷調查。\n\n4. **客戶會議準備**\n   - John:\n     - 負責技術架構簡報。\n     - 在週五下午3點前完成材料準備,以便進行最後的整合測試。\n   - Mary:\n     - 負責使用者介面展示。\n     - 在週五下午3點前完成材料準備,以便進行最後的整合測試。\n\n5. **整合測試**\n   - 所有成員需在週末前完成整合測試。"
}

Step 5:穩定性測試

5-1 測試不同類型的內容

建立測試程式 src/test_workflow_stability.py ,測試較為複雜的內容

import requests
import json

def test_complex_content():
    # 測試包含特殊字符的複雜內容
    test_data = {
        "text": """會議重點:
1. "客戶需求"分析 - 需要更新
2. 系統架構\n包含多行說明  
3. John說:"這很重要!"
4. 時程:2025/9/20前完成""",
        "instruction": "請摘要並提取任務"
    }
    
    response = requests.post(
        "http://localhost:5678/webhook/m2a-test",
        json=test_data,
        timeout=180
    )
    
    if response.status_code == 200:
        result = response.json()
        print("複雜內容測試成功")
        print(f"摘要:{result['summary'][:100]}...")
        print(f"任務:{result['tasks'][:100]}...")
    else:
        print(f"❌ 測試失敗:{response.status_code}")

if __name__ == "__main__":
    test_complex_content()

執行結果

成功摘要較為複雜的內容

✅ 複雜內容測試成功
摘要:會議主要內容包括:

1. **需求分析**:需要進行更新,以滿足當前客戶的需求。
2. **系統架構**:討論了系統的整體設計和架構,這是會議的重要部分。
3. John強調指出,某項議題或決策非常...
任務:- 需求分析需要進行更新。
- 系統架構的整體設計和架構需進一步討論。
- 某項議題或決策需特別注意,可能影響後續程序。
- 所有工作需在2025年9月20日前完成。

以上是會議中的主要行動項目...

今天的成果總結

完成項目

  • 使用 Code 節點解決了 JSON 特殊字符處理問題
  • 建立完整的錯誤處理與重試機制,大幅提升系統穩定性
  • 最佳化 LM Studio 模型參數,解決 API 超時問題
  • 完善工作流的除錯資訊與狀態回饋機制
  • 建立穩定性測試框架,確保各種情境下的正常運作

📝 關鍵發現

  • Code 節點的優勢:完全控制 HTTP 請求流程,避免 JSON 表達式的限制
  • 錯誤處理的重要性:詳細的錯誤資訊和日誌大幅提升除錯效率
  • 超時問題的根源:主要是模型參數設定不當,而非 API 本身問題
  • 系統穩定性關鍵:資料格式處理是整個 AI 系統穩定性的基礎

心得

今天學會了如何診斷和解決系統整合的問題,從最初的 JSON 格式錯誤,到發現 HTTP Request 節點的限制到使用 Code 節點解決問題,整個實作的過程中讓我對 n8n 的架構有了更深入的了解。

我也在解決 JSON 問題的過程中,發現 Code 節點提供了比 HTTP Request 節點更強大的功能,可以進行複雜的資料處理、條件判斷、錯誤恢復等,這對我後續開發更複雜的功能時,提供了更大的彈性空間。

我也將大二選修的「預防醫學概論」裡所提到的「預防勝於治療」的概念,透過建立錯誤處理機制,使得系統的可靠性大幅提升。

🎯 明天計劃
建立 Notion 資料庫整合,讓摘要和任務能夠自動儲存到結構化的資料庫中。


上一篇
Day 6 使用 LM Studio 本地部署中文摘要模型並整合 n8n 工作流
系列文
打造基於 MCP 協議與 n8n 工作流的會議處理 Agent7
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言