這集的篇幅比較長,影片分成兩集
影片連結 1/2:https://www.youtube.com/watch?v=7RU20-m4qnM
影片連結 2/2:https://www.youtube.com/watch?v=CqkMDe4aNH4
YouTube 頻道:https://www.youtube.com/c/kaochenlong
在前兩集中,我們已經建立了一個能夠記帳的 Line 聊天機器人。這個機器人可以透過 AI Agent 解析使用者的訊息,將記帳資訊寫入 Google Sheet。但這個設計有個限制:當使用者只是想跟機器人打個招呼或閒聊時,系統就會出現錯誤。
這個問題的根源在於我們的 AI Agent 使用了結構化輸出(Structured Output)。結構化輸出的設計很適合處理固定格式的資料,但對於一般對話來說反而變得綁手綁腳。當使用者輸入「你好」這類閒聊訊息時,AI Agent 無法將其轉換成我們預期的記帳格式,因此產生錯誤。
要怎麼解決呢?也許可以透過多個 AI Agent 一起協作...
我們需要在訊息進入主要處理流程之前,先放一隻 AI Agent 負責判斷使用者的意圖:
每個 Agent 都專注於處理特定類型的任務,這樣就不會互相干擾。
因為流程有點長,建議大家直接跟著 YouTube 影片操作一次,會比文字版本更容易理解。
整個工作流程的設計如下:
Webhook(Line 訊息進入點)
↓
資料整理(提取 userId、訊息內容以及 Reply Token)
↓
意圖判斷 Agent(判斷使用者想做什麼)
↓
Switch 節點(根據意圖分流)
├─→ 記帳 Agent + 日期工具 → 產生結構資料 → 寫入 Google Sheet → 回覆訊息
├─→ 查帳 Agent + 日期工具 → 使用 Google Sheet 工具 → 回覆訊息
└─→ 聊天 Agent → 回覆訊息

工作流下載:https://gist.github.com/kaochenlong/f0b04732488441c7b99106f6dda23cf3
龍哥您好~
我目前遇到一個問題
原先switch輸出上方是記帳功能,中間是查帳功能
當所有都設定好後,測了記帳功能與聊天功能是好的,但查帳功能始終有問題
當把switch輸出的順序調整後 (上方查帳、中間記帳 與影片相同)
此時查帳功能就正常了,但記帳功能卻有問題
想確認一下是我的問題,還是switch節點的順序真的有影響?
光是這樣沒辦法猜出問題出來,可能得實際上看看工作流的 JSON 才會知道 :)
這樣可以嗎?
{
"nodes": [
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"typeVersion": 1,
"position": [
-320,
560
],
"id": "f3ec700c-0cb6-46b9-ac74-87cca5fc3fbb",
"name": "Google Gemini Chat Model",
"credentials": {
"googlePalmApi": {
"id": "vscKx1s1VvQlcHeD",
"name": "Google Gemini(PaLM) Api account"
}
}
},
{
"parameters": {
"options": {}
},
"type": "n8n-nodes-base.dateTimeTool",
"typeVersion": 2,
"position": [
-192,
560
],
"id": "64af5907-5070-476c-b3be-9ac71c7f8113",
"name": "Date & Time"
},
{
"parameters": {
"jsonSchemaExample": "{\n \"ID\":\"user id\",\n \"item\":\"排骨飯\",\n \"category\":\"飲食\",\n \"amount\":-200,\n \"date\":\"yyyy/mm/dd\"\n}\n"
},
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"typeVersion": 1.3,
"position": [
-64,
560
],
"id": "2a51bd74-8d64-4da1-a0fb-551e164221a6",
"name": "Structured Output Parser"
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "1svbR7QaxfaGcZPCY_fOmO3V9K9cuuRYQb4IlEPXwso0",
"mode": "list",
"cachedResultName": "帳本",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1svbR7QaxfaGcZPCY_fOmO3V9K9cuuRYQb4IlEPXwso0/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "記帳",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1svbR7QaxfaGcZPCY_fOmO3V9K9cuuRYQb4IlEPXwso0/edit#gid=0"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"品項": "={{ $json.output.item }}",
"分類": "={{ $json.output.category }}",
"金額": "={{ $json.output.amount }}",
"日期": "={{ $json.output.date }}",
"ID": "={{ $json.output.ID }}"
},
"matchingColumns": [],
"schema": [
{
"id": "ID",
"displayName": "ID",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "品項",
"displayName": "品項",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "分類",
"displayName": "分類",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "金額",
"displayName": "金額",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "日期",
"displayName": "日期",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
144,
448
],
"id": "b01771ca-61dc-4a4e-928c-5205fbce6094",
"name": "Append row in sheet",
"credentials": {
"googleSheetsOAuth2Api": {
"id": "Y2upz5FhEqMfIL4O",
"name": "Google Sheets account"
}
}
},
{
"parameters": {
"httpMethod": "POST",
"path": "YsuperLineBot",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-1344,
448
],
"id": "9044b1cb-a64c-4cc7-ae6d-f8192a2abc2b",
"name": "Webhook",
"webhookId": "c9caccb9-47f1-4460-a681-5d9ce3b55526"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "e14d5b40-36d1-48ec-bb42-7b156a36f3df",
"name": "userId",
"value": "={{ $json.body.events[0].source.userId }}",
"type": "string"
},
{
"id": "a551901e-c78c-409f-a7b1-62cacad568b2",
"name": "replyToken",
"value": "={{ $json.body.events[0].replyToken }}",
"type": "string"
},
{
"id": "e19b446d-a395-4e58-8ad3-7a5a6cdcad09",
"name": "message",
"value": "={{ $json.body.events[0].message.text }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-1120,
448
],
"id": "c95d4c1f-303c-4251-a46b-44a098997f44",
"name": "Edit Fields"
},
{
"parameters": {
"method": "POST",
"url": "https://api.line.me/v2/bot/message/reply",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"replyToken\":\"{{ $('Edit Fields').item.json.replyToken }}\",\n \"messages\":[\n {\n \"type\":\"text\",\n \"text\":\"{{ $('記帳 Agent').item.json.output.item }} 已記帳\"\n }\n ]\n} \n",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
368,
448
],
"id": "63e3947a-81ff-4bd2-858b-eeec91ec4d21",
"name": "HTTP Request",
"credentials": {
"httpBearerAuth": {
"id": "1NsAyyzIVWm3DNyY",
"name": "Bearer Auth account (YsuperLineBot)"
}
}
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"typeVersion": 1,
"position": [
-896,
672
],
"id": "30c7d678-3377-44b4-b08d-8e426e53233f",
"name": "Google Gemini Chat Model1",
"credentials": {
"googlePalmApi": {
"id": "vscKx1s1VvQlcHeD",
"name": "Google Gemini(PaLM) Api account"
}
}
},
{
"parameters": {
"jsonSchemaExample": "{\n\t\"action\": \"使用者的意圖\"\n}"
},
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"typeVersion": 1.3,
"position": [
-768,
672
],
"id": "b209aae6-2741-4e64-a4c2-93bfe81bc0fe",
"name": "Structured Output Parser1"
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"leftValue": "={{ $json.output.action }}",
"rightValue": "查帳",
"operator": {
"type": "string",
"operation": "equals"
},
"id": "e4805799-d822-4bcb-a205-af319fb813ae"
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "查帳"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "908e54c8-6928-45f9-9ee1-131419116ddd",
"leftValue": "={{ $json.output.action }}",
"rightValue": "記帳",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "記帳"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "d7aa2c74-502c-4a7c-a219-0dd7a3af8011",
"leftValue": "={{ $json.output.action }}",
"rightValue": "一般對話",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "一般對話"
}
]
},
"options": {
"fallbackOutput": 2
}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.3,
"position": [
-544,
432
],
"id": "43672e85-742c-451b-8316-a270dcaaa762",
"name": "Switch"
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"typeVersion": 1,
"position": [
-192,
1072
],
"id": "3407fecd-2a80-4b75-b707-6180fe4076c4",
"name": "Google Gemini Chat Model2",
"credentials": {
"googlePalmApi": {
"id": "vscKx1s1VvQlcHeD",
"name": "Google Gemini(PaLM) Api account"
}
}
},
{
"parameters": {
"promptType": "define",
"text": "={{ $('Edit Fields').item.json.message }}",
"options": {
"systemMessage": "你是一個聊天機器人\n- 回答問題一律用台灣繁體中文"
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3,
"position": [
-272,
848
],
"id": "1047318e-10de-41ca-8bca-4b4fc28e3b3e",
"name": "聊天機器人"
},
{
"parameters": {
"method": "POST",
"url": "https://api.line.me/v2/bot/message/reply",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"replyToken\":\"{{ $('Edit Fields').item.json.replyToken }}\",\n \"messages\":[\n {\n \"type\":\"text\",\n \"text\":\"{{ $json.output.replace(/\\n/g, \"\") }}\"\n }\n ]\n} \n",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
144,
848
],
"id": "4bc38b80-8b7f-4230-af43-2f4392c18f92",
"name": "HTTP Request1",
"credentials": {
"httpBearerAuth": {
"id": "1NsAyyzIVWm3DNyY",
"name": "Bearer Auth account (YsuperLineBot)"
}
}
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"typeVersion": 1,
"position": [
-320,
160
],
"id": "c96deec1-847f-4c89-8c0d-b7992ed5ef9c",
"name": "Google Gemini Chat Model3",
"credentials": {
"googlePalmApi": {
"id": "vscKx1s1VvQlcHeD",
"name": "Google Gemini(PaLM) Api account"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "日期工具",
"options": {}
},
"type": "n8n-nodes-base.dateTimeTool",
"typeVersion": 2,
"position": [
-192,
160
],
"id": "d4ef8958-10de-458a-9a9a-17fc0f02c457",
"name": "Date & Time1"
},
{
"parameters": {
"method": "POST",
"url": "https://api.line.me/v2/bot/message/reply",
"authentication": "genericCredentialType",
"genericAuthType": "httpBearerAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"replyToken\":\"{{ $('Edit Fields').item.json.replyToken }}\",\n \"messages\":[\n {\n \"type\":\"text\",\n \"text\":\"{{ $json.output.replace(/\\n/g, \"\") }}\"\n }\n ]\n}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
144,
48
],
"id": "a906178a-47d2-42f5-aa9d-ac3bffbff2a1",
"name": "HTTP Request2",
"credentials": {
"httpBearerAuth": {
"id": "1NsAyyzIVWm3DNyY",
"name": "Bearer Auth account (YsuperLineBot)"
}
}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "記帳工具",
"documentId": {
"__rl": true,
"value": "1svbR7QaxfaGcZPCY_fOmO3V9K9cuuRYQb4IlEPXwso0",
"mode": "list",
"cachedResultName": "帳本",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1svbR7QaxfaGcZPCY_fOmO3V9K9cuuRYQb4IlEPXwso0/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "記帳",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1svbR7QaxfaGcZPCY_fOmO3V9K9cuuRYQb4IlEPXwso0/edit#gid=0"
},
"filtersUI": {
"values": [
{
"lookupColumn": "ID",
"lookupValue": "={{ $('Edit Fields').item.json.userId }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.googleSheetsTool",
"typeVersion": 4.7,
"position": [
-64,
160
],
"id": "dde7f96e-1e06-4f4d-882e-159a3ec11c8a",
"name": "bookkeeping",
"credentials": {
"googleSheetsOAuth2Api": {
"id": "Y2upz5FhEqMfIL4O",
"name": "Google Sheets account"
}
}
},
{
"parameters": {
"promptType": "define",
"text": "=userId:{{ $('Edit Fields').item.json.userId }}\nmessage:{{ $('Edit Fields').item.json.message }}",
"options": {
"systemMessage": "你是個聰明的助理\n- 一律使用 `bookkeeping` 這個 `記帳工具` 查詢資料\n- 一律使用 `Date & Time1` 這個 `日期工具`\n- 如果沒有特別指定日期,一律指的是今天\n- 如果是支出,使用負數,收出為正數\n- 依使用者的資訊整理記帳資訊供查詢\n- 把結果整理成簡單的描述即可\n"
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3,
"position": [
-272,
-64
],
"id": "4ef77f21-eac1-44b8-9141-2add9635e573",
"name": "查帳 Agent"
},
{
"parameters": {
"promptType": "define",
"text": "=userId:{{ $('Edit Fields').item.json.userId }}\nmessage:{{ $('Edit Fields').item.json.message }}",
"hasOutputParser": true,
"options": {
"systemMessage": "你是個精明的記帳小助理,可以把輸入的訊息變成記帳資料\n- 回答問題一律用台灣繁體中文\n- 如果是要記帳,依照以下規則\n- 如果沒有特別指定日期,一律指的是今天\n- 任何跟日期有關的計算,一律使用日期工具Date & Time\n- 從消費分析出是什麼樣的支出或收入分類:\n- 飲食、交通、娛樂、其它,共4種分類\n- 如果是支出,使用負數,收出為正數\n- 輸出結果為結構化資料:\n```\n{\n \"ID\":\"user id\",\n \"item\":\"排骨飯\",\n \"category\":\"飲食\",\n \"amount\":-200,\n \"date\":\"yyyy/mm/dd\"\n}\n```"
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3,
"position": [
-272,
336
],
"id": "d06c8393-e135-426d-ad16-77a62bfc95f3",
"name": "記帳 Agent"
},
{
"parameters": {
"promptType": "define",
"text": "={{ $json.message }}",
"hasOutputParser": true,
"options": {
"systemMessage": "你是一個聰明的助理,從使用者的訊息判斷使用者想要做什麼操作,可能的選項有:\n- 查帳\n- 記帳\n- 一般對話\n如果無法判斷意圖,使用一般對話"
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3,
"position": [
-896,
448
],
"id": "9a0f3054-5009-4b09-bbcc-d351f30e9bca",
"name": "判斷意圖 Agent"
}
],
"connections": {
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "記帳 Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Date & Time": {
"ai_tool": [
[
{
"node": "記帳 Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Structured Output Parser": {
"ai_outputParser": [
[
{
"node": "記帳 Agent",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Append row in sheet": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"Webhook": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "判斷意圖 Agent",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model1": {
"ai_languageModel": [
[
{
"node": "判斷意圖 Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Structured Output Parser1": {
"ai_outputParser": [
[
{
"node": "判斷意圖 Agent",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Switch": {
"main": [
[
{
"node": "查帳 Agent",
"type": "main",
"index": 0
}
],
[
{
"node": "記帳 Agent",
"type": "main",
"index": 0
}
],
[
{
"node": "聊天機器人",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model2": {
"ai_languageModel": [
[
{
"node": "聊天機器人",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"聊天機器人": {
"main": [
[
{
"node": "HTTP Request1",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model3": {
"ai_languageModel": [
[
{
"node": "查帳 Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Date & Time1": {
"ai_tool": [
[
{
"node": "查帳 Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"bookkeeping": {
"ai_tool": [
[
{
"node": "查帳 Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"查帳 Agent": {
"main": [
[
{
"node": "HTTP Request2",
"type": "main",
"index": 0
}
]
]
},
"記帳 Agent": {
"main": [
[
{
"node": "Append row in sheet",
"type": "main",
"index": 0
}
]
]
},
"判斷意圖 Agent": {
"main": [
[
{
"node": "Switch",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "e99bf6be83e7f8d7fb460dfa72e404ad36503d212e662c237013c8bcb116a596"
}
}