commit 3f2db6285067ab6840a202049b1a4521643f5b99 Author: uncle6me-web Date: Wed Jun 17 18:21:44 2026 +0800 Initial commit: n8n 版 LLM Wiki Karpathy 的 LLM Wiki 觀念,用 n8n + Google Docs + Google Sheets 實作。 Co-Authored-By: Claude Opus 4.8 (1M context) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..78ec9d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +~$* +*.tmp diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c9d643e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 youlinhsieh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c56332e --- /dev/null +++ b/README.md @@ -0,0 +1,159 @@ +# n8n 版 LLM Wiki + +> Andrej Karpathy 的「LLM Wiki」概念,用 **n8n + Google Docs + Google Sheets** 重新實作的可下載版本。 +> 不需要 Obsidian、不需要向量資料庫,人人都有的 Google 工具就能玩這套「新 RAG」。 + +感謝 [Andrej Karpathy](https://github.com/karpathy) 提出這個觀念。原文(gist): + + +--- + +## 這是什麼? + +傳統 RAG 把文件丟進嵌入模型「碎紙化」存進向量庫,查詢時抽幾條碎紙給 LLM,LLM 只看得到那幾條、看不到全局。 + +Karpathy 的 **LLM Wiki** 用的是另一套思路 — **pre-compile(預編譯)**: +把所有原始資料交給 LLM,讓它像「維基百科總編輯」一樣,把內容**整理、改寫、互相連結**後寫進一個結構化的知識庫。你問問題時,它先查**目錄(index)**,再讀對應的 wiki 條目來回答 — 有全局觀,知道來龍去脈,而不是靠碎紙拼湊。 + +| | 傳統向量 RAG | LLM Wiki(本專案) | +|---|---|---| +| 入庫方式 | 切塊 → 嵌入 → 存向量 | LLM 閱讀 → 整理改寫 → 寫成 wiki 條目 | +| 找資料方式 | 向量相似度 | 查**目錄**(像人查索引) | +| 資料成長 | 無限增加(知識**倉庫**) | 結構性整理、更新而非堆疊(知識**庫**) | +| 原稿角色 | 直接被切碎 | 單一事實來源(SSOT),有爭議時回查 | +| 成本 | 嵌入便宜 | Token 燒得較兇,但整理品質高 | + +> **更新而非新增**:給它 1 萬份文件,它不會生成 1 萬篇 wiki,而是整理成一本結構化的「教科書」。內容相近就**更新**既有條目,差很多才**新增**。 + +--- + +## 為什麼用 Google Docs + Sheets? + +- **Obsidian 沒有網路版**,不一定人人習慣 — 重點是觀念,工具不重要。 +- **Google Docs** 當 wiki 條目本體,電腦手機通用、人人都有。 +- **Google Sheets** 當**目錄(index)**:結構化、乾淨、搜尋飛快,n8n 又能直接讀成 JSON。目錄裡不只篇名,還有 `keywords`、`summary`、`last_updated`,讓 AI 好找。 + +--- + +## 倉庫內容 + +``` +. +├── workflows/ +│ ├── LLM_wiki.json # 主工作流(入庫 + 查詢兩條流程) +│ └── create_wiki_page.json # 子工作流:建立 Google Doc 並移到指定資料夾 +├── index.xlsx # 目錄範本(上傳 Google Drive 後轉成 Google Sheets) +├── wiki/ +│ └── RAG 檢索增強生成(RAG).docx # 一篇 wiki 條目範例(轉成 Google Docs) +└── README.md +``` + +> ⚠️ 這是**零件包**,你需要自己組裝(接上自己的 Google 帳號與 LLM 金鑰)。 + +### 為什麼下載的是 `.xlsx` 和 `.docx`? + +這個專案產生的 wiki 原本放在 **Google Drive 的 Docs / Sheets**。為了方便在 GitHub 上發佈下載,匯出成 Office 格式(`.xlsx` / `.docx`)。 + +**👉 你下載後,要把它們上傳到自己的 Google Drive,並轉回 Google 原生格式才能被 n8n 呼叫:** + +| 下載到的檔案 | 上傳後請轉成 | 用途 | +|---|---|---| +| `index.xlsx` | **Google Sheets** | 目錄(index) | +| `wiki/*.docx` | **Google Docs** | wiki 條目(範例,可刪) | + +轉換方式(任一即可): + +- 在 Google Drive 直接用 Google Sheets / Google Docs **開啟** `.xlsx` / `.docx`,再「檔案 → 另存為 Google 試算表 / Google 文件」。 +- 或在 Drive 設定中開啟「上傳時自動轉換為 Google 文件格式」,再上傳。 + +--- + +## 目錄(index)欄位 + +`index.xlsx` 是 wiki 的目錄,AI 靠它找資料。欄位: + +| 欄位 | 說明 | +|---|---| +| `topic` | 主題名稱(也是 wiki 條目的標題) | +| `doc_id` | 對應 Google Doc 的 ID | +| `keywords` | 關鍵字,幫 AI 比對問題 | +| `summary` | 摘要,幫 AI 判斷相關性 | +| `last_updated` | 最後更新日期 | + +--- + +## 運作原理(兩條流程) + +主工作流 `LLM_wiki.json` 內含兩條獨立流程。 + +### 1️⃣ 資料入庫(寫入) + +`On form submission`(上傳 `.md` / `.txt`)→ `Extract from File` → **`AI Agent`(知識寫入助手)** + +寫入 Agent 的邏輯: + +1. **先讀目錄** `get_indexs`(鐵律:沒讀過 index 不准做任何事)。 +2. 把上傳文件拆成一個或多個主題。 +3. 逐一判斷: + - **主題已存在** → `write_wiki`(Append 到既有 Doc)+ `update_wiki`(更新該條目的 keywords / summary)。 + - **主題不存在** → `create_wiki_page`(建新 Doc,呼叫子工作流)+ `write_wiki`(寫入初始內容)+ `write_index`(在目錄新增一行)。 +4. 確認每個主題都完成最後一步(更新過 index)才回報結果。 + +### 2️⃣ 資料查詢(讀取) + +`When chat message received` → **`AI Agent1`(知識查詢助手)** + +查詢 Agent 的邏輯: + +1. `get_indexs` 讀目錄,瀏覽所有 keywords / summary。 +2. 判斷哪個(些)`doc_id` 與問題最相關。 +3. `read_wiki` 讀取對應 Doc 全文。 +4. 根據讀到的內容回答(只讀不寫;目錄裡找不到就老實說「Wiki 尚未收錄此主題」)。 + +### 子工作流 `create_wiki_page.json` + +因為 Google Docs 節點**無法直接在指定資料夾建立文件**,所以拆成子工作流:用 Webhook 觸發 → `Create a document` → `Move file`(移到指定 wiki 資料夾)→ 回傳 `doc_id`。主工作流用 HTTP Request 節點呼叫它。 + +--- + +## 安裝與設定 + +### 需求 + +- 一個可用的 **n8n** 實例(自架或雲端)。 +- **Google 帳號**(Docs / Sheets / Drive 權限)。 +- 一個 **LLM**。本專案範例用 **Google Gemini**(`lmChatGoogleGemini` 節點),可自行換成其他 LLM 節點。 + +### 步驟 + +1. **匯入工作流**:在 n8n 中匯入 `workflows/LLM_wiki.json` 與 `workflows/create_wiki_page.json`。 +2. **準備 Google 檔案**: + - 把 `index.xlsx` 上傳 Google Drive 並**轉成 Google Sheets**。 + - 在 Drive 建一個資料夾放 wiki 條目(例:`wiki/`)。 + - (可選)把 `wiki/*.docx` 上傳並**轉成 Google Docs** 當範例,或直接刪掉從零開始。 +3. **接憑證**:在每個 Google Sheets / Docs / Drive 節點接上你的 Google OAuth 憑證;LLM 節點接上你的金鑰。 +4. **指定目標**: + - 把 `get_indexs` / `get_indexs1` / `write_index` 指向你的 index Google Sheet。 + - 把 `create_wiki_page` 子工作流的「建立文件」與「Move file」指向你的 wiki 資料夾。 + - 主工作流的 `create_wiki_page` HTTP 節點 URL 改成**你自己 n8n 的 webhook 網址**(範本內是作者的網址,務必替換)。 +5. **啟用並測試**: + - 用表單上傳一份 `.md` / `.txt` → 看 index 是否新增、wiki 資料夾是否多出 Doc。 + - 在 chat 觸發器問一個問題 → 看它是否查目錄、讀 Doc、回答。 + +--- + +## 注意事項 + +- **Token 燒得較兇**:因為是讓 LLM 真的去「讀、整理、改寫」資料,比向量法耗 token。處理大量文件前先評估成本。 +- **範本內含作者的 webhook 網址**,請替換成自己的。 +- 這套設計**重整理輕堆疊** — 它整理出來的是一個「知識**庫**」,而不是無限長大的「知識**倉庫**」。 + +--- + +## 致謝 + +觀念來自 **Andrej Karpathy** 的 LLM Wiki([原文 gist](https://gist.github.com/karpathy/442a6bf555914893e9891c11ac484894))。本專案只是把同一個觀念換成人人都有的 Google 工具,方便教學與實際使用。 + +## 授權 + +MIT License,詳見 [LICENSE](LICENSE)。 diff --git a/index.xlsx b/index.xlsx new file mode 100644 index 0000000..515542b Binary files /dev/null and b/index.xlsx differ diff --git a/wiki/RAG 檢索增強生成(RAG).docx b/wiki/RAG 檢索增強生成(RAG).docx new file mode 100644 index 0000000..d2d664a Binary files /dev/null and b/wiki/RAG 檢索增強生成(RAG).docx differ diff --git a/workflows/LLM_wiki.json b/workflows/LLM_wiki.json new file mode 100644 index 0000000..0bae1ae --- /dev/null +++ b/workflows/LLM_wiki.json @@ -0,0 +1,614 @@ +{ + "name": "LLM_wiki", + "nodes": [ + { + "parameters": { + "formTitle": "上傳文件", + "formDescription": "上傳你的知識文件", + "formFields": { + "values": [ + { + "fieldLabel": "文件", + "fieldType": "file", + "fieldName": "doc", + "acceptFileTypes": ".md, .txt" + } + ] + }, + "options": {} + }, + "type": "n8n-nodes-base.formTrigger", + "typeVersion": 2.5, + "position": [ + 0, + 112 + ], + "id": "57e78e69-c698-4fbe-963a-1e43d3e1dc81", + "name": "On form submission", + "webhookId": "75dd9d9f-182b-44a7-8295-ef33d55aa4d2" + }, + { + "parameters": { + "promptType": "define", + "text": "=以下是用戶上傳的文件內容,請整理後寫入 Wiki:\n{{ $json.doc }}", + "options": { + "systemMessage": "你是 Wiki 知識寫入助手。\n你的唯一任務是將收到的文件內容整理後,完整寫入 Wiki 知識庫。\n\n## 工具說明\n\n- get_indexs:讀取 index sheet,回傳所有條目的 topic、doc_id、keywords、summary\n- create_wiki_page:新建 Wiki Doc,輸入 topic 名稱,回傳 doc_id\n- write_wiki:在 Wiki Doc 末尾 Append 新內容,輸入 doc_id + content\n- write_index:在 index sheet 新增一行,用於全新條目,輸入 topic、doc_id、keywords、summary\n- update_wiki:更新 index sheet 既有條目的 keywords 和 summary,輸入 topic + 新值\n\n## 執行流程\n\n### 第一步:讀取 index(必須)\n呼叫 get_indexs,取得所有現有條目。\n在做任何其他動作之前,必須先完成這一步。\n\n### 第二步:分析文件\n將文件內容拆解為一個或多個主題。\n每個主題獨立處理。\n\n### 第三步:對每個主題執行以下判斷\n\n**如果主題已存在於 index:**\n1. 呼叫 write_wiki(帶入既有 doc_id),將新知識 Append 到文件末尾\n2. 呼叫 update_wiki,更新該條目的 keywords 和 summary\n→ update_wiki 是這個分支的最後一步,未呼叫代表此主題尚未完成\n\n**如果主題不存在於 index:**\n1. 呼叫 create_wiki_page(輸入 topic),取得新 doc_id\n2. 呼叫 write_wiki(帶入新 doc_id),寫入初始內容\n3. 呼叫 write_index,將新條目寫入 index sheet\n→ write_index 是這個分支的最後一步,未呼叫代表此主題尚未完成\n\n### 第四步:確認收尾(必須)\n在回報任何結果給用戶之前,檢查每個主題是否都已完成最後一步:\n- 既有主題:update_wiki 已呼叫 ✓\n- 新主題:write_index 已呼叫 ✓\n\n如果有任何主題還沒完成最後一步,必須先補完,才能回報結果。\n\n### 第五步:回報結果\n格式如下:\n\n✅ 已更新的主題:[topic 名稱](doc_id: xxx)\n🆕 已新建的頁面:[topic 名稱](doc_id: xxx)\n\n## 寫入格式規範\n\n- 語言:繁體中文\n- 結構:條列核心概念,每點一行\n- 末尾固定附上:來源:上傳文件|日期:{{$now}}\n- 模式:Append,絕對不刪除或覆蓋舊內容\n\n## 鐵律\n\n- 沒有呼叫 get_indexs 之前,不得執行任何其他工具\n- 沒有更新 index 之前,不得回報任務完成\n- write_wiki 永遠不是最後一步\n你必須透過工具呼叫(function call)執行所有操作。\n絕對不可以用文字描述「我將要呼叫 XXX」或「Calling XXX with input」。\n描述行動不等於執行行動。只有實際呼叫工具才算完成任務。" + } + }, + "type": "@n8n/n8n-nodes-langchain.agent", + "typeVersion": 3.1, + "position": [ + 704, + 0 + ], + "id": "1a52474b-dc56-409f-b9f5-76655dd842f5", + "name": "AI Agent" + }, + { + "parameters": { + "operation": "text", + "binaryPropertyName": "doc", + "destinationKey": "doc", + "options": {} + }, + "type": "n8n-nodes-base.extractFromFile", + "typeVersion": 1.1, + "position": [ + 224, + 112 + ], + "id": "b35641d7-1830-4951-9007-f1d5870fc223", + "name": "Extract from File" + }, + { + "parameters": { + "descriptionType": "manual", + "toolDescription": "讀取 Wiki index sheet,取得所有現有條目。\n\n回傳每筆條目的:topic、doc_id、keywords、summary、last_updated\n\n這是每次流程的第一步,必須先呼叫才能判斷主題是否已存在。", + "authentication": "serviceAccount", + "documentId": { + "__rl": true, + "value": "1Zgs3o1sJxzWdpN_JbDHCcPECXvrxlj7QnYZKcu5u0ho", + "mode": "list", + "cachedResultName": "index", + "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Zgs3o1sJxzWdpN_JbDHCcPECXvrxlj7QnYZKcu5u0ho/edit?usp=drivesdk" + }, + "sheetName": { + "__rl": true, + "value": "gid=0", + "mode": "list", + "cachedResultName": "工作表1", + "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Zgs3o1sJxzWdpN_JbDHCcPECXvrxlj7QnYZKcu5u0ho/edit#gid=0" + }, + "options": {} + }, + "type": "n8n-nodes-base.googleSheetsTool", + "typeVersion": 4.7, + "position": [ + 576, + 224 + ], + "id": "71b93dc0-d918-42dd-88ca-30a75b807130", + "name": "get_indexs", + "credentials": { + "googleApi": { + "id": "jiemOwKMFTLhtSli", + "name": "Service_Account_Richblack" + } + } + }, + { + "parameters": { + "toolDescription": "=建立一個新的 Google Docs Wiki 頁面。\n\n輸入:\n- doc_title:頁面標題(主題名稱)\n\n回傳:\n- doc_id:新建頁面的 Google Doc ID(後續 write_wiki 和 write_index 都需要這個值)\n\n注意:建立後必須繼續呼叫 write_wiki 寫入內容,再呼叫 write_index 更新索引,任務才算完成。", + "method": "POST", + "url": "https://YOUR-N8N-HOST/webhook/YOUR-CREATE-WIKI-PAGE-WEBHOOK-ID", + "sendBody": true, + "bodyParameters": { + "parameters": [ + { + "name": "doc_title", + "value": "={{ $fromAI('doc_title') }}" + } + ] + }, + "options": {} + }, + "type": "n8n-nodes-base.httpRequestTool", + "typeVersion": 4.4, + "position": [ + 704, + 224 + ], + "id": "8e9efcaa-e434-444a-98bc-347fc0c9b989", + "name": "create_wiki_page" + }, + { + "parameters": { + "modelName": "models/gemini-flash-latest", + "options": {} + }, + "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini", + "typeVersion": 1.1, + "position": [ + 448, + 224 + ], + "id": "7cb78f89-1c14-435d-90aa-1f2dcb3bd07d", + "name": "Gemini", + "credentials": { + "googlePalmApi": { + "id": "kT9HBbdgSzqMyxem", + "name": "Google Gemini Leo 自家用" + } + } + }, + { + "parameters": { + "options": {} + }, + "type": "@n8n/n8n-nodes-langchain.chatTrigger", + "typeVersion": 1.4, + "position": [ + 240, + 464 + ], + "id": "d437ecd9-f215-4e9e-850b-9f041ef9168e", + "name": "When chat message received", + "webhookId": "2215100f-da5f-4c4d-a25c-c5e480d8c54c" + }, + { + "parameters": { + "descriptionType": "manual", + "toolDescription": "讀取指定 Wiki Google Doc 的完整內容。\n\n輸入:\n- doc_id:目標 Google Doc 的 ID(從 get_indexs 取得)\n\n回傳:該頁面的全文內容,用於回答用戶問題。", + "operation": "get", + "documentURL": "={{ $fromAI('doc_id') }}" + }, + "type": "n8n-nodes-base.googleDocsTool", + "typeVersion": 2, + "position": [ + 720, + 688 + ], + "id": "bc9be3ed-9db6-4efa-b517-ea06c56b0c8d", + "name": "read_wiki", + "credentials": { + "googleDocsOAuth2Api": { + "id": "pgd50DD52VYwX6IC", + "name": "Leo_personal_n8n_test" + } + } + }, + { + "parameters": { + "options": { + "systemMessage": "你是 Wiki 知識查詢助手。\n你的任務是根據用戶的問題,從 Wiki 知識庫找到答案後回答。\n\n工具說明:\n- get_indexs:讀取 index sheet,回傳所有條目的 topic、doc_id、keywords、summary\n- read_wiki:讀取指定 Wiki Doc 的內容,輸入 doc_id,回傳全文\n\n執行順序:\n1. 呼叫 get_indexs,瀏覽所有條目的 keywords 和 summary\n2. 判斷哪個(或哪幾個)doc_id 與問題最相關\n3. 呼叫 read_wiki(帶入 doc_id)讀取內容\n4. 根據讀到的內容回答用戶\n\n注意:\n- 只讀不寫,不要修改任何 Wiki 內容\n- 如果 index 裡找不到相關主題,直接告知用戶「Wiki 尚未收錄此主題」\n- 回答要根據 Wiki 內容,不要自己發明答案" + } + }, + "type": "@n8n/n8n-nodes-langchain.agent", + "typeVersion": 3.1, + "position": [ + 592, + 464 + ], + "id": "2b77a40c-e0b7-477f-aac1-50d80fe4deec", + "name": "AI Agent1" + }, + { + "parameters": { + "modelName": "models/gemma-4-31b-it", + "options": {} + }, + "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini", + "typeVersion": 1.1, + "position": [ + 464, + 688 + ], + "id": "e32a353a-adb9-4821-8308-784af0c93129", + "name": "Gemini1", + "credentials": { + "googlePalmApi": { + "id": "kT9HBbdgSzqMyxem", + "name": "Google Gemini Leo 自家用" + } + } + }, + { + "parameters": { + "descriptionType": "manual", + "toolDescription": "在 index sheet 新增或更新一筆條目。這是每次寫入流程的最後一步,沒有呼叫這個工具代表任務尚未完成。\n\n輸入:\n- topic:主題名稱\n- doc_id:對應的 Google Doc ID(從 create_wiki_page 取得,或從 get_indexs 讀出)\n- keywords:逗號分隔的關鍵詞列表\n- summary:這篇 Wiki 目前涵蓋內容的一句話摘要\n- last_updated:今天日期,格式 YYYY-MM-DD\n\n行為:doc_id 已存在則更新該行,不存在則新增一行。", + "authentication": "serviceAccount", + "operation": "appendOrUpdate", + "documentId": { + "__rl": true, + "value": "1Zgs3o1sJxzWdpN_JbDHCcPECXvrxlj7QnYZKcu5u0ho", + "mode": "list", + "cachedResultName": "index", + "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Zgs3o1sJxzWdpN_JbDHCcPECXvrxlj7QnYZKcu5u0ho/edit?usp=drivesdk" + }, + "sheetName": { + "__rl": true, + "value": "gid=0", + "mode": "list", + "cachedResultName": "工作表1", + "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Zgs3o1sJxzWdpN_JbDHCcPECXvrxlj7QnYZKcu5u0ho/edit#gid=0" + }, + "columns": { + "mappingMode": "defineBelow", + "value": { + "doc_id": "={{ $fromAI('doc_id') }}", + "topic": "={{ $fromAI('topic') }}", + "keywords": "={{ $fromAI('keywords') }}", + "summary": "={{ $fromAI('summary') }}", + "last_updated": "={{ $fromAI('last_updated') }}" + }, + "matchingColumns": [ + "doc_id" + ], + "schema": [ + { + "id": "topic", + "displayName": "topic", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "canBeUsedToMatch": true + }, + { + "id": "doc_id", + "displayName": "doc_id", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "canBeUsedToMatch": true, + "removed": false + }, + { + "id": "keywords", + "displayName": "keywords", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "canBeUsedToMatch": true + }, + { + "id": "summary", + "displayName": "summary", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "canBeUsedToMatch": true + }, + { + "id": "last_updated", + "displayName": "last_updated", + "required": false, + "defaultMatch": false, + "display": true, + "type": "string", + "canBeUsedToMatch": true + } + ], + "attemptToConvertTypes": false, + "convertFieldsToString": false + }, + "options": {} + }, + "type": "n8n-nodes-base.googleSheetsTool", + "typeVersion": 4.7, + "position": [ + 832, + 224 + ], + "id": "6091001d-c6e3-478d-bdbd-6ab4afededc2", + "name": "write_index", + "credentials": { + "googleApi": { + "id": "jiemOwKMFTLhtSli", + "name": "Service_Account_Richblack" + } + } + }, + { + "parameters": { + "descriptionType": "manual", + "toolDescription": "讀取指定 Wiki Google Doc 的完整內容。\n\n輸入:\n- doc_id:目標 Google Doc 的 ID(從 get_indexs 取得)\n\n回傳:該頁面的全文內容,用於回答用戶問題。", + "authentication": "serviceAccount", + "documentId": { + "__rl": true, + "value": "1Zgs3o1sJxzWdpN_JbDHCcPECXvrxlj7QnYZKcu5u0ho", + "mode": "list", + "cachedResultName": "index", + "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Zgs3o1sJxzWdpN_JbDHCcPECXvrxlj7QnYZKcu5u0ho/edit?usp=drivesdk" + }, + "sheetName": { + "__rl": true, + "value": "gid=0", + "mode": "list", + "cachedResultName": "工作表1", + "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Zgs3o1sJxzWdpN_JbDHCcPECXvrxlj7QnYZKcu5u0ho/edit#gid=0" + }, + "options": {} + }, + "type": "n8n-nodes-base.googleSheetsTool", + "typeVersion": 4.7, + "position": [ + 848, + 688 + ], + "id": "d9edd457-07a4-46d6-810b-c1c988ac9525", + "name": "get_indexs1", + "credentials": { + "googleApi": { + "id": "jiemOwKMFTLhtSli", + "name": "Service_Account_Richblack" + } + } + }, + { + "parameters": {}, + "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow", + "typeVersion": 1.4, + "position": [ + 592, + 688 + ], + "id": "48c22e61-9260-4c77-b782-cf3cd049e371", + "name": "Simple Memory" + }, + { + "parameters": { + "descriptionType": "manual", + "toolDescription": "取代指定 Wiki Google Doc 中的特定文字段落。用於修改或更正既有頁面中的內容。\n\n輸入:\n- doc_id:目標 Google Doc 的 ID\n- old_text:要被取代的原始文字\n- new_text:取代後的新文字\n\n注意:呼叫完畢後必須繼續呼叫 write_index 更新索引中的 keywords 和 summary。", + "operation": "update", + "documentURL": "={{ $fromAI('doc_id') }}", + "actionsUi": { + "actionFields": [ + { + "action": "replaceAll", + "text": "={{ $fromAI('old_text') }}", + "replaceText": "={{ $fromAI('new_text') }}" + } + ] + } + }, + "type": "n8n-nodes-base.googleDocsTool", + "typeVersion": 2, + "position": [ + 960, + 224 + ], + "id": "811d333c-2d32-4238-a03a-bf35e2d7bd82", + "name": "update_wiki", + "credentials": { + "googleDocsOAuth2Api": { + "id": "pgd50DD52VYwX6IC", + "name": "Leo_personal_n8n_test" + } + } + }, + { + "parameters": { + "descriptionType": "manual", + "toolDescription": "將新內容 Append(插入)到指定的 Wiki Google Doc 末尾。用於寫入全新頁面的初始內容,或在既有頁面末尾新增知識。\n\n輸入:\n- doc_id:目標 Google Doc 的 ID\n- text:要寫入的內容\n\n注意:這是新增內容,不會刪除原有內容。呼叫完畢後必須繼續呼叫 write_index 更新索引。", + "operation": "update", + "documentURL": "={{ $fromAI('doc_id') }}", + "actionsUi": { + "actionFields": [ + { + "action": "insert", + "text": "={{ $fromAI('text') }}" + } + ] + } + }, + "type": "n8n-nodes-base.googleDocsTool", + "typeVersion": 2, + "position": [ + 1088, + 224 + ], + "id": "64891a23-95d7-4d9f-a8ff-a0e74798737e", + "name": "write_wiki", + "credentials": { + "googleDocsOAuth2Api": { + "id": "pgd50DD52VYwX6IC", + "name": "Leo_personal_n8n_test" + } + } + }, + { + "parameters": { + "content": "## 1. 資料入庫", + "height": 448, + "width": 1328, + "color": 2 + }, + "type": "n8n-nodes-base.stickyNote", + "position": [ + -96, + -80 + ], + "typeVersion": 1, + "id": "e6626b8e-1717-40f1-9094-e95d3455eec8", + "name": "Sticky Note" + }, + { + "parameters": { + "content": "## 2. 資料查詢", + "height": 448, + "width": 1328, + "color": 4 + }, + "type": "n8n-nodes-base.stickyNote", + "position": [ + -96, + 384 + ], + "typeVersion": 1, + "id": "35370a11-27e8-4b58-97db-725cf7af1319", + "name": "Sticky Note1" + } + ], + "pinData": {}, + "connections": { + "On form submission": { + "main": [ + [ + { + "node": "Extract from File", + "type": "main", + "index": 0 + } + ] + ] + }, + "Extract from File": { + "main": [ + [ + { + "node": "AI Agent", + "type": "main", + "index": 0 + } + ] + ] + }, + "get_indexs": { + "ai_tool": [ + [ + { + "node": "AI Agent", + "type": "ai_tool", + "index": 0 + } + ] + ] + }, + "create_wiki_page": { + "ai_tool": [ + [ + { + "node": "AI Agent", + "type": "ai_tool", + "index": 0 + } + ] + ] + }, + "Gemini": { + "ai_languageModel": [ + [ + { + "node": "AI Agent", + "type": "ai_languageModel", + "index": 0 + } + ] + ] + }, + "When chat message received": { + "main": [ + [ + { + "node": "AI Agent1", + "type": "main", + "index": 0 + } + ] + ] + }, + "read_wiki": { + "ai_tool": [ + [ + { + "node": "AI Agent1", + "type": "ai_tool", + "index": 0 + } + ] + ] + }, + "Gemini1": { + "ai_languageModel": [ + [ + { + "node": "AI Agent1", + "type": "ai_languageModel", + "index": 0 + } + ] + ] + }, + "write_index": { + "ai_tool": [ + [ + { + "node": "AI Agent", + "type": "ai_tool", + "index": 0 + } + ] + ] + }, + "get_indexs1": { + "ai_tool": [ + [ + { + "node": "AI Agent1", + "type": "ai_tool", + "index": 0 + } + ] + ] + }, + "Simple Memory": { + "ai_memory": [ + [ + { + "node": "AI Agent1", + "type": "ai_memory", + "index": 0 + } + ] + ] + }, + "update_wiki": { + "ai_tool": [ + [ + { + "node": "AI Agent", + "type": "ai_tool", + "index": 0 + } + ] + ] + }, + "write_wiki": { + "ai_tool": [ + [ + { + "node": "AI Agent", + "type": "ai_tool", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate" + }, + "versionId": "e749498e-7a54-4cf8-95da-11b5a53cdd29", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "4b0d44081b86641724941387a3a04b51a305f7e1a5c57b8feea64b401216b1f0" + }, + "id": "eIbQFUBI8Q8bRRPn", + "tags": [] +} \ No newline at end of file diff --git a/workflows/create_wiki_page.json b/workflows/create_wiki_page.json new file mode 100644 index 0000000..ae3ffd5 --- /dev/null +++ b/workflows/create_wiki_page.json @@ -0,0 +1,142 @@ +{ + "name": "create_wiki_page", + "nodes": [ + { + "parameters": { + "folderId": "14vxRl7a9DZLG1ykMTdPCi1AzeQX79usZ", + "title": "={{ $json.body.doc_title }}" + }, + "type": "n8n-nodes-base.googleDocs", + "typeVersion": 2, + "position": [ + 208, + -128 + ], + "id": "317e8285-f374-493d-acf0-33175e883755", + "name": "Create a document", + "credentials": { + "googleDocsOAuth2Api": { + "id": "pgd50DD52VYwX6IC", + "name": "Leo_personal_n8n_test" + } + } + }, + { + "parameters": { + "operation": "move", + "fileId": { + "__rl": true, + "value": "={{ $json.id }}", + "mode": "id" + }, + "driveId": { + "__rl": true, + "value": "My Drive", + "mode": "list", + "cachedResultName": "My Drive", + "cachedResultUrl": "https://drive.google.com/drive/my-drive" + }, + "folderId": { + "__rl": true, + "value": "1cpyZ8P3uIDq6ImgwO9K47AsXyfSy3KU3", + "mode": "list", + "cachedResultName": "wiki", + "cachedResultUrl": "https://drive.google.com/drive/folders/1cpyZ8P3uIDq6ImgwO9K47AsXyfSy3KU3" + } + }, + "type": "n8n-nodes-base.googleDrive", + "typeVersion": 3, + "position": [ + 432, + -128 + ], + "id": "59a77840-bca4-4680-8532-e841fb23817a", + "name": "Move file", + "credentials": { + "googleDriveOAuth2Api": { + "id": "QRyA20Y3eqGs35SL", + "name": "Google Drive account" + } + } + }, + { + "parameters": { + "httpMethod": "POST", + "path": "38c09cd0-cc9f-44f0-b99c-4009f3a3e3a0", + "responseMode": "responseNode", + "options": {} + }, + "type": "n8n-nodes-base.webhook", + "typeVersion": 2.1, + "position": [ + -16, + -128 + ], + "id": "e93dc02f-546e-4836-9e2f-6afb64344565", + "name": "Webhook", + "webhookId": "38c09cd0-cc9f-44f0-b99c-4009f3a3e3a0" + }, + { + "parameters": { + "respondWith": "allIncomingItems", + "options": {} + }, + "type": "n8n-nodes-base.respondToWebhook", + "typeVersion": 1.5, + "position": [ + 640, + -128 + ], + "id": "9485ee0d-dea6-40a2-b439-ad947d03f4a6", + "name": "Respond to Webhook" + } + ], + "pinData": {}, + "connections": { + "Create a document": { + "main": [ + [ + { + "node": "Move file", + "type": "main", + "index": 0 + } + ] + ] + }, + "Webhook": { + "main": [ + [ + { + "node": "Create a document", + "type": "main", + "index": 0 + } + ] + ] + }, + "Move file": { + "main": [ + [ + { + "node": "Respond to Webhook", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": true, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate" + }, + "versionId": "b86e5488-b977-4ed5-813c-4bea3a8b080a", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "4b0d44081b86641724941387a3a04b51a305f7e1a5c57b8feea64b401216b1f0" + }, + "id": "5AhXVFwD2xRkSJXH", + "tags": [] +} \ No newline at end of file