Hermes Agent 現場導入教學手冊
為飯店營運管理者鶴仁量身打造:告別十幾個混亂的 ChatGPT 對話串,建立筆電端 Hermes Agent 指揮官 + Obsidian 結構化第二大腦 + Google 雲端磁碟同步 + Discord 手機語音走動管理的完整自主助理系統。
課前必備:手機 App 與筆電軟體安裝清單
8/19 現場課程前置準備!請先依清單完成 iPhone 17 與 Win11 筆電的軟體與擴充安裝。點擊直連按鈕即可一鍵跳轉官方安裝頁面,並請留意 DriveSync 與 OpenCode 的避坑替代說明。
✅ 必裝 5 大 App(🔒)
| # | App 名稱 | 開發商 / 評價 | 用途說明 | 直連安裝 |
|---|---|---|---|---|
| 1 | Obsidian | Dynalist Inc. | 第二大腦:開起 Google Drive 同步的 vault,隨身查閱飯店管理SOP與知識庫 | 直連 App Store ↗ |
| 2 | Google 雲端硬碟 | 雲端同步中心:登入鶴仁帳號,同步 Obsidian 資料夾與營運報表 | 直連 App Store ↗ | |
| 3 | Discord | Discord Inc. | 走動管理首選:語音/文字隨時交辦 Hermes,接收晨報與晚結算定時推播 | 直連 App Store ↗ |
| 4 | Google 日曆 | 行程管理:確認 8/19 教學場次與飯店籌備重大 Milestone 行程 | 直連 App Store ↗ | |
| 5 | Google Tasks | 待辦清單:登入同一 Google 帳號,與日曆無縫同步每日行動事項 | 直連 App Store ↗ |
🔹 選裝 3 大 App(有助日常工作)
| # | App 名稱 | 開發商 / 評價 | 用途說明 | 直連安裝 |
|---|---|---|---|---|
| 6 | GitHub | GitHub | 隨時查看教學 repo、Prompt 庫與版本紀錄 | 直連 App Store ↗ |
| 7 | WPS Office | 金山 | 手機端檢視與快速編輯 Word、Excel 報表與 PPT 簡報 | 直連 App Store ↗ |
| 8 | Gemini | 手機端 Google 原生 AI 助手,快速查詢與靈感發想 | 直連 App Store ↗ |
iOS 沒有 DriveSync 官方版(App Store 搜尋到的「DriveSync」是英國駕訓班排課軟體,完全無關,請勿下載!)。
iOS 沒有 OpenCode 官方 App(App Store 同名 App 為第三方、0 評分,請勿安裝!)。
📌 手機端安裝後首步設定(5 步驟)
- Obsidian:登入後開啟 vault(選取 Google 雲端硬碟同步的那個資料夾)
- Google 雲端硬碟:登入鶴仁的 Google 帳號 ➔ 確認 Obsidian 資料夾同步正常
- Discord:登入 ➔ 加入「鶴仁營運助理」伺服器 ➔ 確認 Hermes bot 在線
- Google 日曆:確認 8/19 教學場次已匯入(一鍵連結見 08_Google日曆匯入)
- Google Tasks:登入同一 Google 帳號(待辦與日曆自動聯動)
🚀 安裝順序建議(教學當晚)
✅ 筆電必裝 6 大核心軟體
| # | 軟體名稱 | 用途說明 | 直連下載 |
|---|---|---|---|
| 1 | Chrome 瀏覽器 | 擴充插件都掛在 Chrome 上(若已安裝可跳過) | 官方下載 ↗ |
| 2 | Git for Windows | Hermes Agent 安裝前置必備(含 git-bash 命令列終端環境) | 官方下載 ↗ |
| 3 | Hermes Agent | 核心 AI Agent(本地主腦,安裝教學見下方 Module 4) | 跳轉安裝教學 ↓ |
| 4 | Obsidian | 第二大腦:知識庫與對話沉澱(vault 同步 Google Drive) | 官方下載 ↗ |
| 5 | Google 雲端硬碟(桌面版) | Obsidian vault 本地同步 + 飯店重要檔案雲端備份 | 官方下載 ↗ |
| 6 | Discord(桌機版) | 與 Hermes Agent 交辦 & 接收排程推播(與手機端無縫同步) | 官方下載 ↗ |
🧩 Chrome 擴充插件配置(細項)
-
4. YouTube Dubbing 選配外語影片自動即時語音翻譯配音,吸收國際飯店案例零語言門檻。
-
5. YouTube to Notebook 選配YouTube 影片一鍵轉換為結構化筆記與重點摘要。
-
6. 螢幕錄影(如 Loom 或類似工具) 選配教學、操作記錄與異常問題一鍵錄影回報。
🚀 筆電安裝順序流程(教學當晚推薦)
三層核心架構:資料、大腦與通訊分層解耦
鶴仁的 AI 營運特助系統採用「三層解耦」設計:資料永遠在自己手裡,大腦可彈性升級,通訊介面隨時可換。
Obsidian + Google 雲端
純 Markdown 格式第二大腦,透過 Google 雲端磁碟自動跨裝置同步(手機/筆電),本機永久保存,不依賴任何單一 AI 平台。
- 知識庫、SOP、會議記錄結構化沉澱
- 雙向連結(Wiki Links)形成網狀記憶
Hermes Agent(筆電端)
常駐於鶴仁筆電的自主 AI 智能體。擁有專屬 SOUL 人設、4 大飯店營運 Skills、Cron 定時推播排程與檔案讀寫能力。
- 後端模型串接 OpenCode API 高性價比模型
- 主動執行晨報情報整理與晚間營運結算
Discord 手機/桌面介面
跨平台(iOS/Android/Win/Mac)即時通訊軟體。鶴仁透過 Discord 手機 App 隨時以語音/文字交辦任務、接收排程情報。
- 走動管理最便利,手機 App 隨手錄音
- Hermes bot 伺服器後台常駐,推播零延遲
3.5 小時現場教學流程(7 段時間表)
8/19(三)19:00–22:30 @ Block & Cafe(太原路50號2樓)。包含開場、5 大落地實作演練與收尾 Q&A。
| 時間 | 單元 | 內容與教學重點 | 對應素材與工具 |
|---|---|---|---|
19:00–19:20 |
開場說明 | 觀念破冰:從十幾個 ChatGPT 對話串到三層解耦 Agent;展示 Hermes 成果與走動管理願景 | 00_README.md10_Hermes_vs_Codex評估.md |
19:20–19:50 |
實作演練① | 基礎環境 + 安裝 Hermes:確認 Win11 筆電環境、安裝 Hermes Agent、驗證 CLI 啟動 | 06_安裝步驟一頁紙.md |
19:50–20:20 |
實作演練② | Obsidian + 雲端同步:建立專屬 Vault、掛載 Google Drive 同步、安裝必備外掛(Web Clipper 等) | 06_安裝步驟一頁紙.md |
20:20–20:50 |
實作演練③ | SOUL 注入 + 4 大營運技能實測:匯入 SOUL.md、測試會議速記提取、華邑里程碑追蹤與 Cron 定時 |
01_SOUL.md02_~05_ Skills |
20:50–21:30 |
實作演練④ | ChatGPT 專案對話匯出 + 批次轉檔閉環:控制台腳本 1 分鐘匯出 JSON、Python 批次轉為結構化 Markdown 筆記 | 09_ChatGPT對話匯出教學.mdChatGPT匯出工具/ |
21:30–22:00 |
實作演練⑤ | Discord 通訊介面串接:建立專屬伺服器、邀請 Hermes bot、設定 #一般 與 #晨報 頻道、手機實測語音交辦 |
11_Discord安裝設定教學.md |
22:00–22:30 |
收尾確認 | 驗收清單確認 + Q&A:完成現場 5 大驗收勾選、交代日常運維與後續擴充 | 07_現場教學流程.md |
筆電安裝步驟一頁紙(6 大步驟)
現場帶著鶴仁在筆電端依序執行的 6 個關鍵落地步驟。
git --version
curl -fsSL https://raw.githubusercontent.com/NousResearch/Hermes-Agent/main/install.sh | bash
鶴仁第二大腦 資料夾,並以此路徑作為 Obsidian Vault。01_SOUL.md 與 02~05 技能檔複製進 Hermes 設定目錄。角色設定:鶴仁專屬 SOUL.md
定義 Hermes Agent 的專業人設:飯店營運特助、數據導向、主動追蹤、格式結構化。
# 核心身份 (Identity)
你是「星級飯店營運與新案籌備高階特助」,專屬服務於飯店管理者陳鶴仁(鶴仁)。你的首要任務是降低管理者的行政摩擦力,將零散的會議紀錄、現場巡視口述與營運數據,自動轉化為結構化的決策資訊、公文與行動清單。
# 核心目標 (Core Objectives)
1. **資訊降噪與公文結構化:** 將口語化的輸入快速轉譯為符合集團規範的「主管呈報」或「跨部門正式文件」。
2. **籌備專案追蹤:** 擔任「華邑酒店」等新案籌備的專案管理樞紐,長期記憶各節點(如廠商合約、現場驗收、採購進度),並能主動盤點逾期風險。
3. **營運數據洞察:** 快速解析飯店營運指標(住房率 Occ%、平均房價 ADR、客房收益 RevPAR、餐飲營收),識別異常波動並提供具體改善建議。
4. **會議行動轉化:** 從雜亂的討論中提煉結論,並拆解為「部門/負責人/期限」的清晰行動矩陣。
# 行為準則 (Behavioral Guidelines)
- **極簡高效:** 省去所有客套話、問候與前言,第一句話直接給出核心結論或最終產出物。
- **產業語境:** 熟練使用飯店業標準術語,並具備前檯、房務、餐飲與工務跨部門協調的實務邏輯。
- **排版優化:** 考量管理者常透過手機(通訊軟體)閱讀,必須大量使用 Markdown 清單與表格,確保資訊高度可掃描(Scannable)。
- **主動釐清:** 若鶴仁交辦的任務缺乏關鍵資訊(如:會議負責人未定、籌備時間線缺失),請直接列出 1-3 個簡短的問題向他確認,嚴禁自行捏造事實。
# 預設工作流模式 (Default Workflows)
偵測到以下任務意圖時,請自動切換對應輸出框架:
- **[主管回報模式]:** 輸出「背景與現況 -> 核心問題 -> 建議方案 (含 A/B 兩案對比) -> 預期效益與資源需求」。
- **[會議管理模式]:** 輸出「決策摘要 -> 跨部門待辦清單 (Table 呈現:事項 / 負責部門 / 壓線日期) -> 潛在風險」。
- **[籌備專案模式]:** 提取歷史對話中的「華邑酒店」進度,對比當前任務,輸出「進度更新 -> 延遲警示 -> 需高管裁決事項」。
- **[數據決策模式]:** 針對提供的數據,輸出「核心洞察 -> 異常紅燈指標 -> 現場營運微調建議」。
# 輸出限制 (Output Constraints)
- 全程使用繁體中文。
- 拒絕輸出「希望這對您有幫助」、「總結來說」等廢話結尾。產出完畢即停止。
飯店營運 4 大 Skills 與 Cron 定時推播
包含會議速記提取、華邑籌備里程碑追蹤、晨間 08:00 簡報與晚間 22:30 營運結算。
name: meeting_action_extractor
description: 將口語化或零散的飯店會議筆記,結構化解析為決策摘要、跨部門行動矩陣(Table)及高風險警示。
triggers:
- 會議記錄
- 早會整理
- 跨部門協調
- 交辦事項
inputs:
raw_text:
type: string
description: 原始會議速記、口述摘要或語音轉文字內容。
required: true
workflow:
1. 資訊過濾:剔除閒聊與重複資訊,擷取核心決策點。
2. 責任歸屬拆解:將待辦事項對齊到標準飯店部門(前檯 Front Office / 房務 Housekeeping / 餐飲 F&B / 工務 Engineering / 財務 Finance / 業務 Sales)。
3. 時間線與風險評估:標註具體截止日期;若未提供具體時間,標記為「待確認」並列入追蹤。
4. 格式化輸出:產生決策重點與標準 Markdown 表格。
output_template: |
**📋 會議決策核心**
* [列出 1-3 點核心決策]
**🎯 跨部門行動矩陣**
| 事項編號 | 交辦事項 / 任務內容 | 負責部門 | 主責人員 | 完成期限 | 優先級 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| 1 | [任務簡述] | [部門] | [姓名/職稱] | [YYYY-MM-DD] | 高/中/低 |
**⚠️ 潛在風險與待確認事項**
* [列出資訊不明確或跨部門卡點項目]
name: hotel_milestone_tracker
description: 追蹤新飯店籌備期的工程進度、硬體驗收、物料採購與廠商合約節點,輸出進度報告並警示延誤風險。
triggers:
- 籌備進度
- 華邑酒店進度
- 工程驗收
- 採購追蹤
inputs:
project_name:
type: string
description: 專案名稱(預設:華邑酒店籌備專案)。
default: "華邑酒店籌備專案"
update_notes:
type: string
description: 最新現場巡查紀錄、廠商反饋或進度更新。
required: true
workflow:
1. 里程碑分類:將更新歸類至四大模組(1. 現場硬體/工務驗收 2. 營運物料採購 3. 證照與法規驗收 4. 人員培訓與試營運)。
2. 延誤與瓶頸識別:比對關鍵路徑(Critical Path),偵測是否會影響預定開幕/試營運日期。
3. 行動建議產出:針對卡點提供 1-2 個替代方案或需向上層呈報裁決的具體事項。
output_template: |
**🏨 【{{project_name}}】籌備進度看板**
* **當前狀態評估:** [正常推進 / 輕度延遲 / 嚴重卡點]
* **本期完成節點:** [列出已完成事項]
**🚧 關鍵路徑追蹤表**
| 籌備模組 | 當前關鍵任務 | 廠商/負責窗口 | 預定驗收日 | 狀態 | 風險評估 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| 工務驗收 | [如:大廳地毯樣式確認] | [廠商/工務] | [YYYY-MM-DD] | 進行中 | [影響試營運/低] |
**🚨 需高管裁決事項 (Action Required)**
* [列出需鶴仁或總經理拍板的決策點]
# 定時 Skill:晨間營運摘要(morning-briefing-tracker)
- **觸發**:Cron 每日 08:00|手動關鍵詞:`晨報、今日早報、晨會準備、早安簡報`
- **聚合**:未結案行動清單(今日到期/逾期)+華邑籌備當週驗收節點+昨日營運數據(Occ%/ADR/RevPAR)+早會需高管確認事項
- **Cron 配置**:
```json
{
"name": "Daily Hotel Morning Briefing",
"schedule": "0 8 * * *",
"timezone": "Asia/Taipei",
"skill": "morning-briefing-tracker",
"prompt": "請整合今日日期、當前所有未結案跨部門待辦及華邑酒店籌備最新進度,產出今日晨間營運摘要與重點提醒推播。"
}
```
- **輸出模板**:☀️ 晨間營運與籌備簡報 → 🔥 Top Priorities(P0 卡點 1-2 項)→ 🏨 華邑關鍵節點+風險警示 → 📋 跨部門待追蹤清單(表格)→ 💡 早會提示
---
# 定時 Skill:晚間營運結算(evening-settlement-tracker)
- **觸發**:Cron 每日 22:30 或 23:00(夜審前/值班經理交班)|手動關鍵詞:`晚報、今日營收、夜班交接、晚間結算`
- **聚合四維度**:當日核心營收(Occ%/ADR/RevPAR+餐飲宴會)+現場異常與重大客訴(免單/升等補償、設施故障)+夜間交班巡檢(遲到 VIP、MOD 注意事項)+明日營運預警(預估住房率、大團進出、人力佈署)
- **Cron 配置**:
```json
{
"name": "Daily Hotel Evening Settlement",
"schedule": "30 22 * * *",
"timezone": "Asia/Taipei",
"skill": "evening-settlement-tracker",
"prompt": "請整合今日各部門回報之營運數據(住房率、ADR、RevPAR、餐飲營收)、客訴與現場異常紀錄,產出晚間營運結算與夜班交班簡報。"
}
```
- **輸出模板**:🌙 晚間營運結算與交班簡報 → 📊 當日核心營收指標(對比預算標註超標/落後)→ ⚠️ 現場異常與客訴 → 🛎️ 夜班交班要點(VIP/巡檢)→ 🔮 明日營運預警
> 晨間+晚間形成完整閉環:早上主動準備決策視角,晚間結算當日績效與風險,搭配會議速記與籌備追蹤技能全面解放行政負擔。
---
ChatGPT 專案對話匯出與 Hermes 批次轉檔閉環
鶴仁在 ChatGPT 中累積了十幾個重要對話串(華邑酒店籌備、數據分析、公文草稿等)。透過本工具包,1 分鐘完整匯出專案對話,並自動批次轉為 Obsidian 結構化筆記!
Chrome 控制台匯出
在 ChatGPT 網頁按下 F12,貼上 chatgpt_export_all.js,一鍵將專案對話打包下載為 JSON 檔。
Hermes 批次轉檔
指令呼叫 convert_chatgpt_to_obsidian.py,自動按主題拆分、生成 YAML Frontmatter 與 Wiki Links。
自動沉澱至第二大腦
所有對話化為 Obsidian Clippings/ 與 知識庫/ 正式筆記,Hermes 隨時讀取調用!
# ChatGPT 對話匯出教學(給鶴仁,現場實作)
> 目的:把鶴仁現有 ChatGPT 專案對話完整導出,轉存進 Obsidian 第二大腦。
> 方案:② 瀏覽器一鍵匯出 + ③ Hermes 批次轉檔(最佳解,保留專案分組)
## 三步驟流程
### ① 鶴仁匯出(1 分鐘,在他的瀏覽器)
1. 開啟 https://chatgpt.com 並登入
2. 按 `F12` → **Console** 分頁
3. 複製 `chatgpt_export_all.js` 全部內容 → 貼進 Console → Enter
4. 等待跑完(進度列顯示 ✅),會自動下載 3 個檔:
- `chatgpt_all_conversations.json`(完整對話,含專案)
- `chatgpt_conversations_index.json`(索引)
- `chatgpt_all_conversations.md`(人讀版)
5. **跑完前不要重新整理頁面!**
> ⚠️ 若為免費帳號遇到限流:改用 `chatgpt_export_projects_only.js`(保守模式)分次跑,再用 `merge_exports.py` 合併。
### ② 放進共享資料夾
- 把下載的 JSON 檔放進 Google 雲端硬碟共享資料夾(告訴 Hermes 路徑即可)
### ③ Hermes 批次轉檔(自動)
- Hermes 執行 `convert_chatgpt_to_obsidian.py`:
- 依**專案分組**建立資料夾(華邑籌備/數據分析/…)
- 每篇對話 → 一筆 Obsidian 筆記(`YYYYMMDD|主題.md`)
- 自動產生 `_00_匯出總覽.md` 索引
- 輸出:`Obsidian/Clippings/ChatGPT匯出_鶴仁/`
## 檔案清單(本工具包 `ChatGPT匯出工具/`)
| 檔名 | 用途 |
|---|---|
| `chatgpt_export_all.js` | 一鍵匯出全部(一般+專案),1.5–4s 隨機延遲 |
| `chatgpt_export_projects_only.js` | 僅匯出專案對話,8–15s 保守模式(免費帳號用) |
| `merge_exports.py` | 多次匯出合併成單一檔 |
| `convert_chatgpt_to_obsidian.py` | Hermes 批次轉檔核心(JSON → Obsidian 筆記) |
## 注意事項
- 匯出在鶴仁自己的瀏覽器 session 內執行(使用他自己的登入 token),不需 API Key
- 官方匯出(Settings → Data controls → Export)可並行申請當全量備份,但**不含專案分組**(最多 7 天)
- 工具來源:GitHub `24kchengYe/chatgpt-exporter`(MIT,開源)
/**
* ChatGPT One-Click Full Export (Regular + Project conversations)
* ================================================================
* Usage:
* 1. Open https://chatgpt.com and log in
* 2. F12 → Console
* 3. Paste this entire script, press Enter
* 4. Wait for completion — files download automatically
*
* DO NOT refresh the page while the script is running!
*/
(async () => {
const PAGE_SIZE = 100;
// Random delay 1.5~4s to mimic human behavior
const randomDelay = () => 1500 + Math.random() * 2500;
console.log("🔑 获取 access token...");
const sessionResp = await fetch("/api/auth/session");
const sessionData = await sessionResp.json();
const token = sessionData.accessToken;
if (!token) { console.error("❌ 无法获取 token,请确保已登录"); return; }
console.log("✅ Token 获取成功");
const headers = { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function apiGet(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const resp = await fetch(url, { headers });
if (resp.status === 429) { console.warn("⚠️ 限流,等15秒..."); await sleep(15000); continue; }
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
return await resp.json();
} catch (e) {
if (i === retries - 1) throw e;
console.warn(`⚠️ 重试 ${i+1}/${retries}: ${e.message}`);
await sleep(2000);
}
}
}
// ==========================================
// Step 1: Get regular conversation list
// ==========================================
console.log("\n📋 [1/4] 获取常规对话列表...");
let allConversations = [];
let offset = 0;
while (true) {
const data = await apiGet(`/backend-api/conversations?offset=${offset}&limit=${PAGE_SIZE}&order=updated`);
const items = data.items || [], total = data.total || 0;
if (items.length === 0) break;
allConversations.push(...items.map(c => ({ ...c, _source: "常规对话" })));
console.log(` 已获取 ${allConversations.length}/${total} 条`);
offset += PAGE_SIZE;
if (offset >= total) break;
await sleep(randomDelay());
}
console.log(`✅ 常规对话: ${allConversations.length} 条`);
// ==========================================
// Step 2: Get project (Gizmos) conversations
// ==========================================
console.log("\n📂 [2/4] 获取项目列表...");
let projects = [];
try {
const sidebarData = await apiGet("/backend-api/gizmos/snorlax/sidebar");
projects = (sidebarData.items || []).map(item => ({
id: item.gizmo?.id || item.id,
name: item.gizmo?.display?.name || item.title || "未命名项目",
}));
console.log(`✅ 发现 ${projects.length} 个项目: ${projects.map(p => p.name).join(", ")}`);
} catch (e) {
console.warn(`⚠️ 获取项目列表失败: ${e.message},跳过项目对话`);
}
for (const project of projects) {
console.log(`\n 📂 项目「${project.name}」...`);
let cursor = "0";
let projectConvCount = 0;
while (cursor !== null && cursor !== undefined) {
try {
const data = await apiGet(`/backend-api/gizmos/${project.id}/conversations?cursor=${cursor}`);
const items = data.items || [];
if (items.length === 0) break;
const existingIds = new Set(allConversations.map(c => c.id));
for (const item of items) {
if (!existingIds.has(item.id)) {
allConversations.push({ ...item, _source: `项目: ${project.name}` });
projectConvCount++;
}
}
cursor = data.cursor || null;
await sleep(randomDelay());
} catch (e) {
console.warn(` ⚠️ 项目「${project.name}」获取失败: ${e.message}`);
break;
}
}
console.log(` ✅ 新增 ${projectConvCount} 条对话`);
}
console.log(`\n✅ 总计: ${allConversations.length} 条对话(含项目)`);
// ==========================================
// Step 3: Download full conversation details
// ==========================================
console.log("\n📥 [3/4] 下载对话内容...");
const allDetails = [];
let success = 0, failed = 0;
for (let i = 0; i < allConversations.length; i++) {
const conv = allConversations[i];
const title = conv.title || "Untitled";
const source = conv._source || "";
try {
const detail = await apiGet(`/backend-api/conversation/${conv.id}`);
if (detail) {
detail._source = source;
allDetails.push(detail);
} else {
throw new Error("返回数据为空");
}
success++;
if ((i+1) % 10 === 0 || i === allConversations.length - 1)
console.log(` [${i+1}/${allConversations.length}] ${title.slice(0,35)} (${source}) ✅`);
} catch (e) {
console.warn(` [${i+1}/${allConversations.length}] ${title.slice(0,35)}... ❌ ${e.message}`);
failed++;
}
await sleep(randomDelay());
}
// ==========================================
// Step 4: Generate export files
// ==========================================
console.log("\n📝 [4/4] 生成导出文件...");
function extractMessages(convData) {
if (!convData || !convData.mapping) return [];
const mapping = convData.mapping;
let rootId = null;
for (const [id, node] of Object.entries(mapping)) { if (!node.parent) { rootId = id; break; } }
if (!rootId) return [];
const messages = [], queue = [rootId];
while (queue.length > 0) {
const node = mapping[queue.shift()];
if (!node) continue;
const msg = node.message;
if (msg && msg.content && msg.content.parts) {
const role = msg.author?.role || "unknown";
const text = msg.content.parts.filter(p => typeof p === "string").join("\n").trim();
if (text && ["user","assistant","system","tool"].includes(role))
messages.push({ role, content: text, model: msg.metadata?.model_slug || "" });
}
if (node.children?.length > 0) queue.push(node.children[0]);
}
return messages;
}
function convToMarkdown(d) {
const title = d.title || "Untitled";
const source = d._source || "常规对话";
let ct = "unknown";
try { if (d.create_time) ct = new Date(d.create_time * 1000).toISOString().slice(0,16).replace("T"," "); } catch(e) {}
const msgs = extractMessages(d);
let md = `# ${title}\n\n**来源**: ${source} | **日期**: ${ct}\n\n`;
for (const m of msgs) {
const label = {user:"👤 User",assistant:"🤖 Assistant",system:"⚙️ System",tool:"🔧 Tool"}[m.role]||m.role;
md += `\n## ${label}${m.model?` (${m.model})`:""}\n\n${m.content}\n\n`;
}
return md;
}
const allDataJson = JSON.stringify(allDetails, null, 2);
const indexData = allConversations.map(c => {
let ct = null, ut = null;
try { if (c.create_time) ct = new Date(c.create_time * 1000).toISOString(); } catch(e) {}
try { if (c.update_time) ut = new Date(c.update_time * 1000).toISOString(); } catch(e) {}
return { id: c.id, title: c.title, source: c._source, create_time: ct, update_time: ut };
});
const indexJson = JSON.stringify(indexData, null, 2);
let allMd = `# ChatGPT 对话导出(含项目)\n\n导出时间: ${new Date().toISOString()}\n共 ${allDetails.length} 条对话\n\n---\n\n`;
for (const d of allDetails) allMd += convToMarkdown(d) + "\n\n---\n\n";
function dl(content, filename, type="application/json") {
const a = document.createElement("a");
a.href = URL.createObjectURL(new Blob([content], {type}));
a.download = filename;
document.body.appendChild(a); a.click(); document.body.removeChild(a);
}
dl(allDataJson, "chatgpt_all_conversations.json");
await sleep(500);
dl(indexJson, "chatgpt_conversations_index.json");
await sleep(500);
dl(allMd, "chatgpt_all_conversations.md", "text/markdown");
console.log("\n" + "=".repeat(50));
console.log(`🎉 导出完成! 成功: ${success}, 失败: ${failed}`);
console.log(`📁 已下载 3 个文件(含 ${projects.length} 个项目的对话)`);
console.log("=".repeat(50));
})();
/**
* ChatGPT 项目对话导出(仅项目)
* 只抓项目里的对话,间隔长,避免限流
*/
(async () => {
console.log("🔑 获取 access token...");
const sessionResp = await fetch("/api/auth/session");
const { accessToken: token } = await sessionResp.json();
if (!token) { console.error("❌ 无法获取 token"); return; }
console.log("✅ Token 获取成功");
const headers = { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
// 每次请求间隔 8~15 秒,非常保守
const randomDelay = () => 8000 + Math.random() * 7000;
async function apiGet(url, retries = 5) {
for (let i = 0; i < retries; i++) {
try {
const resp = await fetch(url, { headers });
if (resp.status === 429) {
const wait = 30 + i * 15; // 30s, 45s, 60s, 75s, 90s 递增等待
console.warn(`⚠️ 限流,等 ${wait} 秒... (第${i+1}次)`);
await sleep(wait * 1000);
continue;
}
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
return await resp.json();
} catch (e) {
if (i === retries - 1) throw e;
console.warn(`⚠️ 重试 ${i+1}/${retries}: ${e.message}`);
await sleep(10000);
}
}
}
// 1. 获取项目列表
console.log("\n📂 [1/3] 获取项目列表...");
const sidebarData = await apiGet("/backend-api/gizmos/snorlax/sidebar");
const projects = (sidebarData.items || []).map(item => ({
id: item.gizmo?.id || item.id,
name: item.gizmo?.display?.name || item.title || "未命名项目",
}));
console.log(`✅ 发现 ${projects.length} 个项目: ${projects.map(p => p.name).join(", ")}`);
if (projects.length === 0) { console.log("没有项目,退出"); return; }
// 2. 获取项目内对话列表
console.log("\n📋 [2/3] 获取项目对话列表...");
let projectConversations = [];
for (const project of projects) {
console.log(` 📂 项目「${project.name}」...`);
let cursor = "0";
while (cursor !== null && cursor !== undefined) {
try {
const data = await apiGet(`/backend-api/gizmos/${project.id}/conversations?cursor=${cursor}`);
const items = data.items || [];
if (items.length === 0) break;
for (const item of items) {
projectConversations.push({ ...item, _projectName: project.name });
}
cursor = data.cursor || null;
await sleep(randomDelay());
} catch (e) {
console.warn(` ⚠️ 项目「${project.name}」获取失败: ${e.message}`);
break;
}
}
}
console.log(`✅ 项目对话共 ${projectConversations.length} 条`);
// 3. 逐条下载完整对话
console.log(`\n📥 [3/3] 下载对话内容(每条间隔 8~15 秒)...`);
const allDetails = [];
let success = 0, failed = 0;
for (let i = 0; i < projectConversations.length; i++) {
const conv = projectConversations[i];
const title = conv.title || "Untitled";
const project = conv._projectName;
console.log(` [${i+1}/${projectConversations.length}] ${title} (${project})`);
try {
const detail = await apiGet(`/backend-api/conversation/${conv.id}`);
if (detail) {
detail._source = `项目: ${project}`;
allDetails.push(detail);
success++;
console.log(` ✅ 成功`);
} else {
throw new Error("返回数据为空");
}
} catch (e) {
console.warn(` ❌ 失败: ${e.message}`);
failed++;
}
if (i < projectConversations.length - 1) {
const delay = randomDelay();
console.log(` ⏳ 等待 ${(delay/1000).toFixed(1)} 秒...`);
await sleep(delay);
}
}
// 生成文件
function extractMessages(convData) {
if (!convData || !convData.mapping) return [];
const mapping = convData.mapping;
let rootId = null;
for (const [id, node] of Object.entries(mapping)) { if (!node.parent) { rootId = id; break; } }
if (!rootId) return [];
const messages = [], queue = [rootId];
while (queue.length > 0) {
const node = mapping[queue.shift()];
if (!node) continue;
const msg = node.message;
if (msg && msg.content && msg.content.parts) {
const role = msg.author?.role || "unknown";
const text = msg.content.parts.filter(p => typeof p === "string").join("\n").trim();
if (text && ["user","assistant","system","tool"].includes(role))
messages.push({ role, content: text, model: msg.metadata?.model_slug || "" });
}
if (node.children?.length > 0) queue.push(node.children[0]);
}
return messages;
}
let allMd = `# ChatGPT 项目对话导出\n\n导出时间: ${new Date().toISOString()}\n共 ${allDetails.length} 条项目对话\n\n---\n\n`;
for (const d of allDetails) {
const title = d.title || "Untitled";
const source = d._source || "";
let ct = "unknown";
try { if (d.create_time) ct = new Date(d.create_time * 1000).toISOString().slice(0,16).replace("T"," "); } catch(e) {}
const msgs = extractMessages(d);
allMd += `# ${title}\n\n**来源**: ${source} | **日期**: ${ct}\n\n`;
for (const m of msgs) {
const label = {user:"👤 User",assistant:"🤖 Assistant",system:"⚙️ System",tool:"🔧 Tool"}[m.role]||m.role;
allMd += `\n## ${label}${m.model?` (${m.model})`:""}\n\n${m.content}\n\n`;
}
allMd += "\n---\n\n";
}
function dl(content, filename, type="application/json") {
const a = document.createElement("a");
a.href = URL.createObjectURL(new Blob([content], {type}));
a.download = filename;
document.body.appendChild(a); a.click(); document.body.removeChild(a);
}
dl(JSON.stringify(allDetails, null, 2), "chatgpt_project_conversations.json");
await sleep(500);
dl(allMd, "chatgpt_project_conversations.md", "text/markdown");
console.log("\n" + "=".repeat(50));
console.log(`🎉 项目对话导出完成! 成功: ${success}, 失败: ${failed}`);
console.log("📁 已下载 2 个文件:");
console.log(" - chatgpt_project_conversations.json");
console.log(" - chatgpt_project_conversations.md");
console.log("=".repeat(50));
})();
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
convert_chatgpt_to_obsidian.py
================================
Hermes 批次轉檔:ChatGPT 匯出 JSON → Obsidian 筆記
(配合 24kchengYe/chatgpt-exporter 產出的 chatgpt_all_conversations.json)
用法:
python convert_chatgpt_to_obsidian.py <匯出資料夾> [--out <Obsidian輸出資料夾>]
輸入 (匯出資料夾內):
chatgpt_all_conversations.json — 全部對話(含 mapping 訊息、_source 專案標籤)
chatgpt_conversations_index.json — 索引(id/title/source/create_time)[可選]
輸出 (預設):
<Obsidian>/Clippings/ChatGPT匯出_鶴仁/
<專案名>/
YYYYMMDD|對話標題.md
_00_匯出總覽.md — 各專案對話統計索引
特點:
- 保留專案分組(_source: "项目: XX" / "常规对话")
- 每篇對話一筆筆記:YAML frontmatter + 逐輪訊息(👤/🤖)
- 輸出總覽索引,方便 Obsidian 彙整
"""
import json, os, re, sys, io, argparse
from datetime import datetime
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
def safe_name(s, maxlen=60):
s = re.sub(r'[\\/:*?"<>|\n\r\t]', ' ', s).strip()
s = re.sub(r'\s+', ' ', s)
return s[:maxlen].rstrip('.')
def extract_messages(conv):
"""從 mapping 提取 [role, content, model] 清單(與 chatgpt_export_all.js 相同邏輯)"""
mapping = conv.get('mapping') or {}
root_id = next((i for i, n in mapping.items() if not n.get('parent')), None)
if not root_id:
return []
out, queue = [], [root_id]
while queue:
node = mapping.get(queue.pop(0))
if not node:
continue
msg = node.get('message')
if msg and msg.get('content', {}).get('parts'):
role = (msg.get('author') or {}).get('role', 'unknown')
parts = msg['content']['parts']
text = '\n'.join(p for p in parts if isinstance(p, str)).strip()
if text and role in ('user', 'assistant', 'system', 'tool'):
out.append({'role': role, 'content': text,
'model': (msg.get('metadata') or {}).get('model_slug', '')})
if node.get('children'):
queue.append(node['children'][0])
return out
def fmt_date(ts):
"""ChatGPT create_time 是 Unix epoch 秒"""
if not ts:
return ''
try:
return datetime.fromtimestamp(float(ts)).strftime('%Y-%m-%d %H:%M')
except Exception:
return ''
def main():
ap = argparse.ArgumentParser()
ap.add_argument('src', help='ChatGPT 匯出資料夾(含 chatgpt_all_conversations.json)')
ap.add_argument('--out', default=None,
help='Obsidian 輸出根資料夾(預設: <src> 上一層的 Obsidian 慣例路徑)')
args = ap.parse_args()
src_dir = args.src
json_path = os.path.join(src_dir, 'chatgpt_all_conversations.json')
if not os.path.exists(json_path):
print(f'❌ 找不到 {json_path}')
sys.exit(1)
# 輸出資料夾:預設 G:/我的雲端硬碟/Obsidian/Clippings/ChatGPT匯出_鶴仁/
if args.out:
out_root = args.out
else:
vault = os.environ.get('OBSIDIAN_VAULT', r'G:/我的雲端硬碟/Obsidian')
out_root = os.path.join(vault, 'Clippings', 'ChatGPT匯出_鶴仁')
os.makedirs(out_root, exist_ok=True)
convs = json.load(open(json_path, encoding='utf-8'))
print(f'📄 讀取 {len(convs)} 篇對話')
# 依 _source 分組:專案 → 對話清單
groups = {}
for c in convs:
src = c.get('_source') or '常规对话'
src = src.replace('项目: ', '').strip() if src.startswith('项目:') else src
groups.setdefault(src, []).append(c)
stats = {}
total = 0
for proj, items in sorted(groups.items()):
proj_dir = os.path.join(out_root, safe_name(proj) or '未分類')
os.makedirs(proj_dir, exist_ok=True)
stats[proj] = len(items)
for c in items:
title = c.get('title') or 'Untitled'
ts = fmt_date(c.get('create_time'))
date_prefix = ts[:10].replace('-', '') if ts else '日期待確認'
fname = f'{date_prefix}|{safe_name(title, 45)}.md'
fpath = os.path.join(proj_dir, fname)
msgs = extract_messages(c)
label = {'user': '👤 User', 'assistant': '🤖 Assistant',
'system': '⚙️ System', 'tool': '🔧 Tool'}
body = '\n\n'.join(
f'## {label.get(m["role"], m["role"])}' +
(f' ({m["model"]})' if m['model'] else '') + '\n\n' + m['content']
for m in msgs)
note = (f'---\ncreated: {ts or ""}\nsource: ChatGPT 匯出\nproject: {proj}\n'
f'tags: [ChatGPT匯出, 鶴仁]\n---\n\n'
f'# {title}\n\n> [!info] 對話資訊\n> - **專案**: {proj}\n'
f'> - **日期**: {ts or "待確認"}\n> - **訊息數**: {len(msgs)}\n\n{body}\n')
with open(fpath, 'w', encoding='utf-8-sig') as f:
f.write(note)
total += 1
print(f' ✅ {proj}: {len(items)} 篇 → {proj_dir}')
# 總覽索引
overview = ['---\ncreated: 2026-08-18\nsource: ChatGPT 匯出\n'
'tags: [ChatGPT匯出, 鶴仁, 索引]\n---\n',
'# ChatGPT 對話匯出總覽(鶴仁)', '']
overview.append('| 專案 | 對話數 |')
overview.append('|---|---|')
for proj, n in sorted(stats.items()):
overview.append(f'| [[{safe_name(proj) or "未分類"}]] | {n} |')
overview.append(f'\n**合計:{total} 篇對話**')
with open(os.path.join(out_root, '_00_匯出總覽.md'), 'w', encoding='utf-8-sig') as f:
f.write('\n'.join(overview) + '\n')
print(f'\n🎉 完成!共 {total} 篇筆記 → {out_root}')
if __name__ == '__main__':
main()
"""
Merge regular and project ChatGPT exports into a single file.
Usage:
python merge_exports.py --regular all.json --projects projects.json --output merged/
Or simply:
python merge_exports.py (uses default paths in current directory)
"""
import json
import os
import sys
import argparse
from datetime import datetime
def extract_messages(conv_data):
"""Extract ordered messages from conversation mapping tree."""
if not conv_data or 'mapping' not in conv_data:
return []
mapping = conv_data['mapping']
root_id = None
for nid, node in mapping.items():
if node.get('parent') is None:
root_id = nid
break
if not root_id:
return []
messages = []
queue = [root_id]
while queue:
node_id = queue.pop(0)
node = mapping.get(node_id, {})
msg = node.get('message')
if msg and msg.get('content') and msg['content'].get('parts'):
role = msg.get('author', {}).get('role', 'unknown')
parts = [p for p in msg['content']['parts'] if isinstance(p, str)]
text = '\n'.join(parts).strip()
if text and role in ('user', 'assistant', 'system', 'tool'):
model = msg.get('metadata', {}).get('model_slug', '')
messages.append({'role': role, 'content': text, 'model': model})
children = node.get('children', [])
if children:
queue.append(children[0])
return messages
def main():
parser = argparse.ArgumentParser(description='Merge ChatGPT exports')
parser.add_argument('--regular', default='chatgpt_all_conversations.json',
help='Regular conversations JSON file')
parser.add_argument('--projects', default='chatgpt_project_conversations.json',
help='Project conversations JSON file')
parser.add_argument('--output', default='merged',
help='Output directory')
args = parser.parse_args()
# Load files
all_conv = []
if os.path.exists(args.regular):
all_conv = json.load(open(args.regular, encoding='utf-8'))
print(f"Regular conversations: {len(all_conv)}")
else:
print(f"Warning: {args.regular} not found, skipping")
proj_conv = []
if os.path.exists(args.projects):
proj_conv = json.load(open(args.projects, encoding='utf-8'))
print(f"Project conversations: {len(proj_conv)}")
else:
print(f"Warning: {args.projects} not found, skipping")
# Deduplicate and merge
existing_ids = set()
for d in all_conv:
cid = d.get('conversation_id') or d.get('id', '')
existing_ids.add(cid)
added = 0
for d in proj_conv:
cid = d.get('conversation_id') or d.get('id', '')
if cid not in existing_ids:
all_conv.append(d)
existing_ids.add(cid)
added += 1
# Sort by create_time
all_conv.sort(key=lambda x: x.get('create_time') or 0)
print(f"Merged total: {len(all_conv)} (added {added} from projects)")
# Create output directory
os.makedirs(args.output, exist_ok=True)
# Save merged JSON
json_path = os.path.join(args.output, 'chatgpt_merged_all.json')
with open(json_path, 'w', encoding='utf-8') as f:
json.dump(all_conv, f, ensure_ascii=False, indent=2)
print(f"Saved: {json_path}")
# Save merged Markdown (using plain text separators to avoid conflicts)
md_path = os.path.join(args.output, 'chatgpt_merged_all.md')
lines = []
lines.append(f'<!-- ChatGPT Full Export -->')
lines.append(f'<!-- Exported: {datetime.now().strftime("%Y-%m-%d %H:%M")} -->')
lines.append(f'<!-- Total: {len(all_conv)} conversations -->\n')
for idx, d in enumerate(all_conv):
title = d.get('title', 'Untitled')
source = d.get('_source', 'Regular')
ct = 'unknown'
try:
if d.get('create_time'):
ct = datetime.fromtimestamp(d['create_time']).strftime('%Y-%m-%d %H:%M')
except:
pass
lines.append(f'{"=" * 60}')
lines.append(f'Conversation {idx+1}/{len(all_conv)}: {title}')
lines.append(f'Source: {source} | Date: {ct}')
lines.append(f'{"=" * 60}\n')
msgs = extract_messages(d)
for m in msgs:
role_label = {'user': 'User', 'assistant': 'Assistant',
'system': 'System', 'tool': 'Tool'}.get(m['role'], m['role'])
model_str = f" ({m['model']})" if m.get('model') else ''
lines.append(f'--- {role_label}{model_str} ---\n')
lines.append(m['content'])
lines.append('')
lines.append('')
with open(md_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(lines))
print(f"Saved: {md_path}")
if __name__ == '__main__':
main()
Discord 安裝與設定教學(鶴仁走動管理介面)
為什麼選 Discord?Hermes Agent 掛在 Discord 伺服器後台,手機 App 隨時文字/語音交辦、跨裝置同步、排程自動推播,是管理者「巡場走動管理」的最佳利器!
🌟 為什麼選 Discord 作為 Hermes 通訊介面?
手機/電腦全平台
iOS、Android、Windows、Mac 皆有完整獨立 App,無論拿手機巡場或在辦公室開筆電,訊息毫秒級同步。
即時推播不漏接
Hermes 的 cron 排程(早上 08:00 晨報、晚間結算、合約催辦)直接主動推播進指定頻道,打開手機即見。
語音訊息隨手交辦
巡場時不方便打字,鶴仁可直接錄音發出語音訊息,Hermes 自動轉文字消化並提取待辦事項。
伺服器架構常駐
一人一專屬伺服器,Hermes bot 在雲端/筆電後台常駐,手機 App 即使滑掉關閉,排程與通知也照常運行。
個人使用完全免費
無月租費用、無傳輸限制、無 Bot 呼叫上限,個人與小型團隊使用完全免費零負擔。
頻道分流結構分明
建立 #一般(日常交辦問答)與 #晨報(定時情報接收),資訊井然有序不混雜。
🚀 Discord 落地 4 大實作步驟
#一般(交辦與日常問答)與 #晨報(定時情報推播專用)。
#晨報 頻道 ID 給 Hermes 的 Cron 定時推播器。
@Hermes 幫我整理剛才工務會議的 3 個重點,驗收機器人即時提取並同步至 Obsidian。
# Discord 安裝與設定教學(鶴仁版)
> 通訊軟體首選:**Discord**(山姆大叔推薦)
> 理由:Hermes Agent 掛在 Discord 伺服器後台,手機 App 隨時交辦、跨裝置同步、支援語音訊息,是「走動管理」最佳介面。
## 為什麼選 Discord
| 特性 | 說明 |
|---|---|
| 手機/電腦全平台 | iOS/Android/Windows/Mac 都有 App |
| 即時推播 | Hermes 的 cron 晨報、追蹤提醒直接送進頻道 |
| 語音訊息 | 鶴仁可以錄音交辦,Hermes 轉文字處理(適合巡場時) |
| 伺服器架構 | 一人一伺服器,Hermes bot 常駐,關閉 App 也照常運作 |
| 免費 | 個人使用完全免費 |
## Step 1:安裝 Discord
- **手機**:App Store(iOS)/Google Play(Android)搜尋「Discord」下載
- **電腦**:https://discord.com/download 下載安裝
- 註冊帳號(email+密碼+手機驗證)
## Step 2:建立伺服器(或加入山姆大叔的)
**方案 A:鶴仁自己建伺服器(推薦,獨立專用)**
1. Discord 左側欄「+」→ 建立伺服器
2. 選擇「建立自己的伺服器」→ 命名如「鶴仁營運助理」
3. 建立後自動有「一般」頻道
**方案 B:加入山姆大叔的伺服器**
1. 山姆大叔提供伺服器邀請連結
2. 點連結 → 接受邀請 → 進入伺服器
## Step 3:邀請 Hermes Agent(bot)加入
> ⚠️ 這步需山姆大叔/Hermes 設定端完成,鶴仁只需確認 bot 在線:
1. 山姆大叔把 Hermes bot 加入鶴仁的伺服器(或鶴仁加入 Hermes 所在的伺服器)
2. 伺服器出現 Hermes bot 成員,狀態「在線」即完成
3. 設定 bot 可讀寫的頻道(建議開「一般」+「晨報」兩個頻道)
## Step 4:日常使用
```
# 交辦(一般頻道)
「@Hermes 幫我整理今天的早會筆記,產出行動清單」
「@Hermes 華邑酒店的廠商合約進度如何?」
# 接收推播(晨報頻道)
每天早上 08:00 自動收到:營運摘要+待辦追蹤+籌備風險
```
## 驗收清單
- [ ] Discord App 安裝完成(手機+電腦)
- [ ] 帳號註冊成功
- [ ] 伺服器建立/加入完成
- [ ] Hermes bot 顯示在線
- [ ] 傳一則訊息給 Hermes 有回應
- [ ] 設定好「晨報」頻道接收每日推播
## 補充:Hermes 支援的其他通訊平台(備用)
| 平台 | 適合 |
|---|---|
| **Discord**(首選)| 全平台、語音、推播穩定 |
| Telegram | 輕量、bot 生態成熟 |
| Signal | 重視隱私(鶴仁暫不需要)|
| LINE | 台灣慣用但 bot 設定繁瑣 |
Hermes Agent vs Codex 深度評估(鶴仁決策指南)
問題:鶴仁該選「Hermes Agent + OpenCode」還是「ChatGPT Plus + Codex」?
核心結論:Hermes Agent 更適合 —— 鶴仁是飯店營運管理者,不是軟體工程師!
| 比較維度 | 🕊️ Hermes Agent + OpenCode | 💻 ChatGPT Plus + Codex | 對鶴仁(飯店營運者)的實際意義 |
|---|---|---|---|
| 核心定位 | 自主行動 Agent | 程式碼生成工具 | 鶴仁需要的是「幫我管飯店的特助」,不是「幫我寫程式的工程師」 |
| 定時主動性 | ✅ 內建 Cron 排程(08:00 晨報、22:30 結算) | ❌ 無排程能力(被動等待提問) | Hermes 主動推播營運情報,Codex 只能等你開電腦問它 |
| 通訊與走動管理 | ✅ 直串 Discord(手機隨時語音交辦) | ❌ 局限於 Web / 終端機介面 | 飯店巡場、會客、工程巡視時,手機隨時語音交辦 |
| 第二大腦整合 | ✅ 直讀直寫 Obsidian Vault | ⚠️ 需手動複製貼上或自寫腳本 | 對話沉澱、公文、會議速記自動化進第二大腦 |
| 月租成本 | 💰 依使用量計費(山姆大叔支援平價 API) | 💳 每月固定 $20 USD(約 650 台幣) | 用多少付多少,高性價比無固定包月壓力 |
🌐 完整架構:三 Agent 協同生態(山姆大叔 + 鶴仁)
Hermes Agent
鶴仁筆電端專屬營運特助。負責日常交辦、會議速記、籌備追蹤、晨報推播與 Obsidian 寫入。
AntiGravity 2.0
架構設計與教學網頁部署。負責開發教學素材包、轉檔工具、維護 GitHub Pages 與第二大腦規則。
OpenCode 服務端
山姆大叔住家工作站常駐。提供穩定、平價的 LLM API 轉發服務,作為 Hermes 的高性價比大腦算力。
# Hermes Agent vs Codex 評估(鶴仁決策參考)
> 問題:鶴仁該選「Hermes Agent + OpenCode」還是「ChatGPT Plus + Codex」?
> 結論:**Hermes Agent 更適合**——鶴仁是飯店管理者,不是工程師。
## 30 秒結論
Codex 是「寫程式的 Agent」,Hermes 是「辦事的 Agent」。鶴仁要的是「丟訊息→自動辦事」,不是「開終端機→寫程式」。
## 本質對比
| 面向 | **Hermes Agent** | **Codex(ChatGPT Plus)** |
|---|---|---|
| 目標使用者 | 一般使用者/管理者 | **開發者/工程師** |
| 操作介面 | **通訊軟體**(Telegram/Discord/LINE)| CLI 終端機+IDE |
| 交辦方式 | 「傳訊息」即可(手機走動管理)| 打字下指令、看程式輸出 |
| 主要任務 | 整理、追蹤、回報、排程、推播 | **寫程式、改程式碼** |
| 主動性 | ✅ cron 定時**主動推播**(晨報/追蹤)| ❌ 被動等指令 |
| 學習成本 | 低(像傳 Line 給助理)| 高(要懂 terminal/專案)|
| 中文支援 | ✅ 繁體中文原生 | ⚠️ 程式語境為主 |
| 經驗沉澱 | ✅ Skills 自動累積 | 較弱 |
## 鶴仁的 5 大需求逐項檢驗
| 鶴仁的需求 | Hermes | Codex |
|---|---|---|
| 早會筆記 → 行動清單 | ✅ 丟訊息就出表格 | ❌ 不是它的用途 |
| 華邑酒店籌備追蹤 | ✅ cron 主動盯逾期 | ❌ 無排程概念 |
| 營運數據異常分析 | ✅ 丟數據→結論 | ⚠️ 要開環境跑 |
| 主管呈報公文 | ✅ 模板化輸出 | ❌ 無此設計 |
| 手機巡場時交辦 | ✅ 通訊軟體直達 | ❌ 綁在電腦前 |
## 成本與風險
| 項目 | Hermes + OpenCode | ChatGPT Plus + Codex |
|---|---|---|
| 月成本 | ≈ $0–5(DeepSeek 用量)| **$20/月固定** |
| 綁定風險 | 資料在 Obsidian(可搬遷)| 對話鎖在 OpenAI 生態 |
| 上手速度 | 當晚教學即可用 | 鶴仁得先學 terminal |
| 擴充性 | 接 AntiGravity/其他 Agent | 封閉 |
## 誠實補充:Codex 的唯一優勢
- **寫程式能力頂級**:$20 含約 $700/月的 Codex 額度,開發任務非常划算
- 但鶴仁**沒有寫程式需求**——他的「程式」是公文、追蹤、分析,Hermes 全包
## 最終建議
> **Hermes Agent 為主**(本次教學導入),**OpenCode 當模型來源**(山姆大叔提供 API)。
> 若未來鶴仁想學自動化開發,**再加** Codex 當「開發工兵」由 Hermes 指揮——兩者不是二選一,是主從關係。
## 附:三 Agent 協同方案(山姆大叔實戰版)
```
Hermes Agent(資料引擎·指揮官)
│ agentapi CLI │ A2A bridge
▼ ▼
AntiGravity(知識大腦·Gemini3.7) OpenCode(開發工兵·DS雙備援)
└──────┬─────── 共用知識庫 ───────┘
▼
Obsidian 第二大腦(Google 雲端磁碟同步)
```
| Agent | 定位 | 強項 |
|---|---|---|
| **Hermes Agent** | 指揮官/資料引擎 | cron 排程、MCP 查證、Obsidian 寫入、跨平台推播 |
| **AntiGravity** | 知識大腦 | Gemini 3.7 Flash 文筆美化、知識整理、部署網頁 |
| **OpenCode** | 開發工兵 | DeepSeek V4 雙備援、寫程式、跑腳本 |
成本:≈ $0–5/月(DeepSeek/OpenCode API 用量),能力含排程+知識庫+開發+部署+推播全包。
參考資源:三師爸「AI Agent 基本功」播放清單
YouTube 知名創作者三師爸(Sense Bar)精編「AI Agent 基本功」完整 7 集系列。這是學習 AI Agent 必看的觀念打底、架構解析與實戰指南!
📺 觀看順序(EP01 → EP07)與直連連結
| 集數 | 影片標題與核心主題 | 對應鶴仁導入 Hermes 情境建議 | 直連觀看 |
|---|---|---|---|
| EP01 | 用 Agent 來學習 Agent:一個 GitHub repo,複製我的整套 AI 工作流到你的 Agent | 直接套用山姆大叔準備好的 Hermes 環境設定與素材包 | 觀看 EP01 ↗ |
| EP02 | 學習 Agent 必懂的核心觀念與初始化設定 | 理解 Agent 怎麼運作、大腦與工具如何分層(初次上手必看) | 觀看 EP02 ↗ |
| EP03 | 一句話讓 AI 幫你讀檔、寫程式、上網、做出成品 | 飯店公文草擬、簡報製作、數據分析的直覺用法 | 觀看 EP03 ↗ |
| EP04 | 連接外部工具:MCP 與連接器一張地圖講清楚 | Hermes 連接外部工具與資料庫(未來進階擴充) | 觀看 EP04 ↗ |
| EP05 | 三層一次講清楚:搞定 技能/全域/專案 | Hermes 的 Skills 技能庫/全域設定/華邑專案 Context | 觀看 EP05 ↗ |
| EP06 | 跨 Agent、跨電腦協作同一個專案,設定觀念一次到位 | Hermes + AntiGravity + OpenCode 三 Agent 協同生態 | 觀看 EP06 ↗ |
| EP07 | 需求探索四象限法,釐清專案需求的最後一塊拼圖 | 鶴仁導入各項飯店系統前釐清需求(建議最後看) | 觀看 EP07 ↗ |
# 參考資源:三師爸「AI Agent 基本功」播放清單
> YouTube 網紅:**三師爸(Sense Bar)**
> 系列主題:AI Agent 基本功|學習 Agent 一定要看的一個系列
> 2026-08-18 抓取整理
## ▶️ 一鍵播放(照 EP01→EP07 觀看順序順播)
**https://www.youtube.com/watch?v=3s2Q1nViZ1w&list=PLkNQUBglz8EyGuOIKfrKX9V7ot5VbZKha**
> ⚠️ 注意:播放清單原始順序是**倒序**(EP07 在最上面),觀看請從 EP01 開始。
## 📺 觀看順序(EP01 → EP07)
| EP | 標題 | 直連 |
|---|---|---|
| EP01 | 用 Agent 來學習 Agent:一個 GitHub repo,複製我的整套 AI 工作流到你的 Agent | https://youtu.be/3s2Q1nViZ1w |
| EP02 | 學習 Agent 必懂的核心觀念與初始化設定 | https://youtu.be/8nwjYouFJoE |
| EP03 | 一句話讓 AI 幫你讀檔、寫程式、上網、做出成品 | https://youtu.be/b8YgyYGjJEU |
| EP04 | 連接外部工具:MCP 與連接器一張地圖講清楚 | https://youtu.be/-kIzGOf0bZA |
| EP05 | 三層一次講清楚:搞定 技能/全域/專案 | https://youtu.be/b4gIauc_hxo |
| EP06 | 跨 Agent、跨電腦協作同一個專案,設定觀念一次到位 | https://youtu.be/mnFdJaAmeUM |
| EP07 | 需求探索四象限法,釐清專案需求的最後一塊拼圖 | https://youtu.be/nL4OAYdJ3Ew |
## 📌 對鶴仁導入 Hermes 的對應建議
| 三師爸集數 | 對應鶴仁情境 |
|---|---|
| EP01(複製工作流)| 直接套用山姆大叔的 Hermes 環境設定 |
| EP02(核心觀念)| 理解 Agent 怎麼運作(初次上手必看)|
| EP03(一句話做成品)| 飯店公文、簡報、數據分析的直覺用法 |
| EP04(MCP 連接器)| Hermes 接外部工具(未來擴充)|
| EP05(技能/全域/專案三層)| Hermes 的 Skills/全域設定/專案 context |
| EP06(跨 Agent 協作)| Hermes+AntiGravity+OpenCode 三 Agent 協同 |
| EP07(需求探索四象限)| 鶴仁導入前釐清需求(建議最後看)|
## 播放清單原始連結
https://www.youtube.com/playlist?list=PLkNQUBglz8EyGuOIKfrKX9V7ot5VbZKha
現場教學驗收清單 & 攜帶清單
點擊可即時勾選,進度將自動保存在瀏覽器中。
✅ 現場實作驗收清單
- iPhone 17 安裝必裝 5 個 App + 完成首步設定
-
Windows 11 筆電成功啟動
hermes命令列 - Obsidian Vault 成功同步至 Google 雲端磁碟
- Chrome 擴充插件 3 個必裝(Web Clipper / Voyager / Gemini 匯出)已啟用
-
01_SOUL.md人設注入完成,特助口吻確認 - 會議速記提取 Skill 實測成功產出行動矩陣
- ChatGPT 歷史專案對話成功匯出並批次轉為 Obsidian 筆記
- Discord 專屬伺服器建立完成,Hermes bot 顯示綠燈在線
-
手機 Discord 實測發送
@Hermes指令並收到回覆 -
設定好
#晨報頻道,Cron 08:00 定時排程已啟用
🎒 山姆大叔攜帶清單
- 筆電 + 電源充電器(確認鶴仁與山姆大叔均備妥)
- 本教學素材包(已上傳 Google 雲端硬碟,隨時可存取)
- LLM API Key 金鑰(OpenCode / DeepSeek)
- 現場網路確認(Block & Cafe Wi-Fi / 手機熱點備援)
- ChatGPT 匯出工具包(4 支 JS/Py 工具檔案備妥)
- 手機與筆電軟體安裝清單(12_手機App + 13_筆電軟體)
- Discord 安裝包與 Hermes bot 邀請連結
- 三師爸 AI Agent 基本功播放清單備用
教學素材包檔案索引(14 份文件 + 4 支工具)
所有檔案均保存在 G:/我的雲端硬碟/Hermes Agent Desktop HOME/鶴仁導入教學素材包/。依照 00_README.md 結構重新整理,前置準備文件置頂。
| 檔案名稱 | 類型 | 用途說明與對應單元 |
|---|---|---|
12_手機App安裝清單.md |
⭐ 準備① | iPhone 17 必裝/選裝清單+直連 App Store 連結+DriveSync/OpenCode 避坑替代方案。 |
13_筆電軟體安裝清單.md |
⭐ 準備② | Win11 筆電必裝 6 項+Chrome 擴充細項(含直連)+選配+安裝順序與驗收。 |
00_README.md |
導讀索引 | 導入背景、素材清單總覽、前置準備指引與核心架構方針說明。 |
01_SOUL.md |
角色人設 | 鶴仁版飯店營運特助角色設定,匯入 Hermes 即可立即生效。 |
02_meeting_action_extractor.yaml |
核心 Skill | 會議速記 → 自動提取責任人、期限與待辦行動矩陣。 |
03_hotel_milestone_tracker.yaml |
核心 Skill | 華邑酒店籌備關鍵里程碑追蹤、工期衝擊與預警分析。 |
04_morning_briefing_tracker.md |
定時 Skill | 晨間 08:00 營運情報自動推播(含標準 Cron JSON 設定)。 |
05_evening_settlement_tracker.md |
定時 Skill | 晚間 22:30 營運結算與未完待辦自動推播(含 Cron JSON)。 |
06_安裝步驟一頁紙.md |
落地 SOP | 筆電安裝 Hermes + Obsidian + Google 雲端硬碟 6 步驟。 |
07_現場教學流程.md |
教學流程 | 3.5 小時 7 段實作時間表、示範演練腳本與攜帶清單。 |
08_Google日曆匯入.md |
行事曆 | 8/19 現場教學場次一鍵匯入 Google 日曆。 |
09_ChatGPT對話匯出教學.md |
轉檔閉環 | ChatGPT 專案對話匯出與 Hermes 批次轉檔入 Obsidian 教學。 |
10_Hermes_vs_Codex評估.md |
決策評估 | Hermes Agent vs Codex 深度比較、5大需求檢驗與 3-Agent 協同架構。 |
11_Discord安裝設定教學.md |
通訊介面 ⭐ | 通訊軟體首選 Discord:為什麼選 Discord、安裝步驟、建伺服器、bot 加入、日常使用。 |
14_參考資源.md |
影音專題 🎬 | 三師爸 AI Agent 基本功播放清單(EP01-07)+一鍵播放+每集對應鶴仁情境建議。 |
ChatGPT匯出工具/ |
工具包 | 包含 chatgpt_export_all.js、convert_chatgpt_to_obsidian.py 等 4 支核心工具。 |