顯示具有 telegram 標籤的文章。 顯示所有文章
顯示具有 telegram 標籤的文章。 顯示所有文章

2026年10月21日 星期三

[OpenAI API] 用免費額度:從 Tier 確認到 Hermes 接入的完整流程 + 踩坑記錄

📺 影片版(約 6 分鐘):想先看實際操作的話可以直接看這支影片,完整步驟、設定與程式碼在下方文字版。
YouTube:https://youtu.be/5kGw4HqSjbE(winson-DIY 頻道)

前言:為什麼要把 Hermes 接上 OpenAI

Hermes 預設跑本機 Ollama 模型(qwen3.8:27b、gpt-oss:20b),優點是免費、不花錢;缺點是本機 ARM 機上 tok/s 不高、長 context 慢。想讓某一條 profile(例如 designer)用 OpenAI 的快模型,只要把 API key 接上、把模型指過去——這篇文章整理完整流程、實際踩到的坑,以及「免費額度」到底怎麼算。

一、先搞清楚:OpenAI 的「免費額度」

OpenAI API 平台有一個「資料共享換每日免費 token」的機制:同意把 API 的輸入與輸出分享給 OpenAI 用來改進模型,系統就每天給一份免費的 token 額度,用完才開始扣儲值餘額。

開啟步驟

  1. 確認帳戶已儲值:帳戶要是 Pay-as-you-go(按量計費),而且有過付費儲值紀錄(至少 $5),才有資格開啟。
  2. 登入 https://platform.openai.com → 左側選單 Settings(齒輪圖示)→ Organization → Data controls。
  3. 找到「Share inputs and outputs with OpenAI」(與 OpenAI 共享輸入和輸出),切到 ON。

注意事項

  • 額度怎麼算:開啟後每天自動重置一份免費 token,只適用於特定模型;各模型的額度與適用範圍以 Data controls 頁面上的說明為準。
  • 代價是資料共享:經過這個組織送出的內容會被拿去改進模型——含個資、公司或客戶資料的請求,不要走這個組織的 key。
  • 用完照常計費:免費額度用完就照一般價格從儲值餘額扣款,所以後面的「預算警示」一樣要設。

確認目前帳號層級(實測步驟)

  1. 開 https://platform.openai.com(要登進 API 平台,不是 ChatGPT)
  2. 左側選單 Settings → Limits
  3. Usage Tiers 區塊顯示目前等級(如上圖):Free tier → Build($5)→ Launch($100)→ Grow($500),累積儲值達門檻會自動升級
  4. 同一頁上方可以設 預算警示(例如花到 80%、100% 時寄信)
  5. 往下的 Rate limits 是各模型的 RPM / TPM — 以該頁表格為準,各模型、各等級數字不同

小提示:同一個 API key 對應的 org 才有 tier;如果你有多個帳號,先確認你要用哪個,key 也是那個帳號發的。

二、建立 API Key

  1. platform.openai.com → API keys → Create new secret key
  2. 記下 key(以 sk- 開頭)。離開頁面後就再也看不到完整 key——存進檔案或密碼管理器
  3. 權限建議依需求最小化;別用超級管理員 key
  4. 這個 key 就是帳單歸屬:之後 Hermes 每打一個 token 都算在你這把 key 上

三、把 OpenAI 接到 Hermes(實測流程)

以下每個指令都在 designer profile 實測過(2026-10-21):

1. 把 key 放進 .env(金鑰絕不過 chat)

# 用你的 profile 的 .env(預設在 ~/.hermes/.env,profile 各有獨立一份)
echo 'OPENAI_API_KEY=***' >> ~/.hermes/profiles/designer/.env
chmod 600 ~/.hermes/profiles/designer/.env

Hermes 的約定:secret 放 .env,一般設定放 config.yaml。放對了,log / 輸出 / 回應都會自動把 key 遮蔽成 ***。

2. 切換 provider 和模型

hermes --profile designer config set model.provider openai
hermes --profile designer config set model.default gpt-4o-mini

gpt-4o-mini 是「聰明 / 便宜 / 快」三者平衡的常見選擇;要更強可以換更高階模型,但單次成本會差幾十倍,先跑 mini 驗證通話再升。

3. 清掉 Ollama 殘留(最容易漏)

如果這個 profile 之前指向本機 Ollama,config 裡會有兩個欄位蓋過你的 OpenAI 設定:

model:
  base_url: http://127.0.0.1:11434/v1/    # 這行會讓所有請求打到本機
  api_key: ollama                          # 這行會讓 OpenAI 401

必須清:

hermes --profile designer config unset model.base_url
hermes --profile designer config unset model.api_key

沒清的話,你會看到「OpenAI API 401 / 找不到模型」的假象,其實是請求被打回 127.0.0.1:11434。

4. 重啟 gateway

每支 profile 有獨立的 systemd user service,例如 designer 是 hermes-gateway-designer。重啟指令要在 gateway 外面的 terminal 跑(gateway 內部會擋住自我重啟):

systemctl --user restart hermes-gateway-designer

5. 驗證(兩層)

# 層 1:CLI 直跑,確認 OpenAI 真的回
hermes --profile designer chat -q 'Reply with exactly: OK'

層 2:在 designer 的 Telegram bot 發 /status,應看到:

Model: gpt-4o-mini

四、真正的坑:舊 session 還鎖著舊模型

改完 config、重啟完 gateway,發 /status 卻看到 gpt-oss:20b(本機模型)——那是這個 session 之前用 /model 切過的舊值。Hermes 允許「session-level model override」,會蓋過 config 預設。

解法(二擇一):

  1. 在這個 bot 對話發 /new — 開新 session,吃 config 預設 = gpt-4o-mini
  2. 或在現 session 發 /model gpt-4o-mini — 本 session 直接切

/new 是乾淨做法;/model 是「保留這個 session 的記憶 / context」的做法。

五、注意事項(成本、Tier、安全)

1. 成本:context 比你以為的大

Hermes 每一回合都帶 system prompt + tools + 歷史 context,一次 API call 進到 10 萬 token 很平常。gpt-4o-mini 單 turn 大概 $0.01~0.03;更高階模型可能一個 turn $3~15。建議:

  • 開 Billing → Set budget alert,把月預算鎖在 $5~$20,超了會收到 mail
  • 跑長任務前先估:每回合平均 input token × 單價 × 預估回合數

2. Tier 卡住(429 Too Many Requests)

Tier 1 的 RPM 很緊,連發幾條就 429。解法:降頻、換更便宜的模型、累積消費升 Tier(門檻以 Billing 頁顯示為準)、或用 hermes fallback 接備援 provider。

3. API key 安全

  • 永遠不要把 key 貼在 chat、Telegram、GitHub 或任何公開處
  • Hermes 的 secret-redaction 會遮蔽輸出,不代表你可以亂放
  • 如果 key 洩漏:立刻回平台 Revoke,重發一把、改 .env、重啟 gateway

4. .env 權限

chmod 600 ~/.hermes/profiles/designer/.env

只有 owner 可讀;多 user 主機上尤其重要。

5. 別混 OpenAI 和 Ollama

同一台機器上,OpenAI 打 api.openai.com,Ollama 打 127.0.0.1:11434。不要指望 .env 同時開兩個 provider 然後自動切——那要用 model_aliases + key_env 明確指定。

6. 本地模型仍是備援

OpenAI 掛、429、斷網時,fallback 回本機 gpt-oss:20b 或 qwen3.8:27b 能繼續跑(慢但可用)。用 hermes fallback add 設好。

小結

步驟 一句話
看 Tier platform.openai.com → Billing / Limits
建 key API keys → Create,離開頁面就看不到了
.env OPENAI_API_KEY=sk-…(600 權限、不入 chat)
config set model.provider openai + set model.default gpt-4o-mini
清 Ollama 殘留 unset model.base_url + unset model.api_key(漏掉最悲)
重啟 gateway 外面的 terminal 跑重啟指令
驗證 CLI chat -q + bot /status
舊 session /status 還舊模型 → 發 /new
控成本 Billing 設 budget alert,鎖 $5~$20/月

接上 OpenAI 之後,Hermes 的 designer profile 就能跑雲端模型,成本可預期、Tier 可觀察、本地模型留當備援。這套流程在 coder、tester 或任何 profile 都可以重用。

更新紀錄

2026-10-28:改寫「免費額度」一節(原本是貼上的 AI 回答,夾帶多餘格式),補上資料共享的隱私提醒;「確認帳號層級」改成與截圖一致的 Free / Build / Launch / Grow 等級。



延伸閱讀

2026年10月17日 星期六

CNC 錄片影像與狀態文字融合:方式、步驟與注意事項

📺 影片版(約 8 分鐘):想先看實際操作的話可以直接看這支影片,完整步驟、設定與程式碼在下方文字版。
YouTube:https://youtu.be/sWH-90AiBRA(winson-DIY 頻道)

CNC 錄片「影像 + 狀態文字」融合:方式、步驟與注意事項

一、要做的事

把 CNC 執行時的控制面板狀態(log)疊在 webcam 錄片上,變成單一支「畫面 + 即時狀態字幕」的影片。
狀態文字包含:Run / Idle / Alarm、MPos 座標、FS 進給速度、SD 目前指令、Ov 倍率等,每 0.4 秒更新一次。

二、融合方式總覽

                 ┌──────────────────────────────────────────┐
  webcam ──RTSP──►  Pi4 (mediamtx) ──RTSP──► Pi4 (monitor)
                 │                                        │
  ESP32 FluidNC ──WebSocket(即時狀態)─► monitor 同步記 log
                 │                                        │
                 └────────► 收檔 run_YYYYMMDD_HHMMSS.mp4
                                 + .mp4.log (每 0.4s 一行的狀態)
  • 錄製端(Pi4):monitor.py 同時抓兩路——RTSP 影像串流寫 MP4,WebSocket 狀態寫成時間軸 log。
  • 收檔觸發:偵測到 Run/Hold/Alarm 開始錄,回 Idle 8 秒確認後收檔。
  • 回傳/疊字端(GX10):Pi4 的 sync timer 每 2 分鐘掃一次,把新錄片 + log scp 到 GX10 /home/gx/cnc_records/,校驗大小一致後 Pi4 端直接刪除(不留檔,檔案只存在 GX10),並自動跑 stitch.py 疊字。


三、stitch.py 如何把 log 融進影片(技術)

  1. 不用「切段再拼」:ffmpeg -ss 切段 drawtext → concat 的方式,seek 是跳到關鍵幀,每段最多漂 1~2 秒,字幕會「跟不上畫面」。
  2. 改用 ASS 字幕單次 pass:每行 log → 一條 Dialogue(起始/结束時間戳),ffmpeg -i in.mp4 -vf ass=subs.ass 一次解碼一次編碼就疊完,逐幀精確。
  3. 每行 log 都是字幕:161 行 log = 161 條字幕,MPos 數字隨機器逐行跳動,不會被「同狀態 5 秒合併」規則吃掉的訊息。
  4. 時基校正 (L2V_OFF = 2.7s):影片走 webcam → mediamtx → RTSP → ffmpeg,有固定的串流累積延遲(本環境實測 2.7 秒,用圓弧起點兩點對齊 + 像素運動法交叉驗證)。所有字幕時間戳統一 +2.7s。
  5. 結尾裁定 (TAIL = 0.5s):錄片尾段有 mediamtx 的凍住緩衝幀(收檔瞬間 ffmpeg 還在吐尾),cut = min(log_tstop + L2V_OFF + TAIL, 影片長度),ffmpeg -t cut 直接裁掉凍尾。
  6. ASS 時間戳格式:HH:MM:SS.cc(ASS 用 1/100 秒的兩位小數)。65 秒 = 00:01:05.00,不是 00:65.00——寫錯的話 ASS 會把「分」和「秒」搞亂,整支片時間軸錯位。

四、一次完整流程

[1] 執行工: ssh Pi4 → 透過 curl 對 FluidNC 下 $LocalFS/Run=<檔>.gcode 指令要包含$跟gcode要對
[2] Pi4 monitor 偵測到 Run,自動起錄 webcam + WebSocket 狀態 log
[3] 機器回 Idle → 8s 後收檔 recordings/run_*.mp4 + .mp4.log
[4] cnc-sync.timer (每 2 min) 掃 recordings/ → 校驗+回傳 GX10
[5] 回傳成功 → Pi4 端刪除; 失敗 → 保留等待下輪
[6] GX10 自動跑 stitch.py,產出 run_*_annotated.mp4 (同目錄)
[7] 收片於 GX10,需要就自取,不要就手動刪

五、注意事項 (踩過的坑)

1. 時基 (最重要)

  • 串流是慢的,log 是即時的——兩個時基不對齊,字幕就會「跟不上」或「超前」。
  • 換硬體/換路由器/改 mediamtx 設定後,L2V_OFF 要重新實測。
  • 實測方法:在 log 挑一個 MPos 已知的點(例如圓弧起點),用 ffmpeg -filter:v select='gt(scene,0.03)' 或逐 1 秒抽幀比像素差,找到畫面第一次出現該位置對應的時間,差值就是偏移。

2. ASS 字幕格式

  • 每一行 log = 一條 Dialogue,[start, end) 半開區間,相切不重疊。
  • 文字中的 : 是 ASS 分隔符,若文字本身要出現冒號要轉義;我們直接用半形冒號無事,遇到 : 分隔符要留意。
  • 過長行 (>64 char) 會被截斷加 …,避免單行跨兩行顯示。

3. 不要切段拼

  • drawtext -ss 切段 → concat demuxer → -c copy 看似省事,seek 會跳到關鍵幀,每段都會飄。
  • 用 ass 單次 pass,一次解碼一次編碼,零飄移,還比較快。

4. Pi4 端不要留錄片

  • 回傳校驗 size 一致 + 已落 GX10 → Pi4 端直接 rm。
  • 校驗不符 / scp 失敗 → 保留 Pi4 原檔,標記下輪重傳,絕不能雙邊都刪。
  • 收檔剛完成的檔案 (<30s) timer 會跳過(避免抓到寫入中)——這是 monitor 端 recorder_active flag 與 sync 端的約定。

5. 結尾裁定

  • mediamtx 在收檔瞬間還會吐 3~6 秒緩衝凍幀,不做 ffmpeg -t cut 會看到「畫面停了、文字還在跳」的殘影。
  • cut 用 min(log_tstop + L2V_OFF + 0.5s, 影片長度),不要超過影片實際長度(ffmpeg 會報錯)。

6. log 格式

  • START epoch=<t> / <offset> <state> <detail> / END 三行結構。
  • 解析 regex ^([\d.]+)\s+(\S+)\s*(.*)$,第一欄是「距 START 的偏移秒」不是絕對時間。
  • 若改 log 寫入格式,stitch.py 的 parse_log 要同步改。

7. 不要雙邊都跑 stitch

  • stitch 只在 GX10 跑一次,Pi4 只負責同步回傳。
  • 若 Pi4 也跑了 stitch,雙邊會產生同一個 _annotated.mp4,回傳時以 GX10 版本為準。

8. 時間戳顯示

  • 65 秒是 00:01:05,不是 00:65——ASS 的 HH:MM:SS.cc 是「小時:分鐘:秒.百分秒」,把 65 秒當秒寫進去會被解讀成 64 分鐘 + 5 秒,整支片時間軸錯位 63 分鐘以上。
  • 寫 ts(sec) 工具時:h=sec//3600, m=(sec%3600)//60, s=sec%60,不要用 %60 套在 m 上(會把 65 秒寫成 00:65.00,ASS 解讀成 0 分 65 秒,等於 1 分 5 秒,跟預期差 53 秒)。

9. 收片位置

  • 原版 run_*.mp4、log run_*.mp4.log、疊字版 run_*_annotated.mp4 三者都在 /home/gx/cnc_records/,自取即可。
  • Pi4 端 recordings/ 不應留任何 mp4(只有 trace.log 是正常)。

六、驗證清單 (每支片收工前)

項 驗法
機器跑完整支 gcode log 最後有 Idle MPos:0.000,0.000,0.000
字幕不重疊 awk 掃 ass 檔,end_i < start_{i+1}
結尾 = Idle MPos:0,0,0 抽最後 3 幀看 MPos
結尾 = 圓完整 抽最後 1 幀看圓弧
中途不跳 抽 30% / 60% / 90% 三幀,每幀 MPos 跟 log 對應行一致
Pi4 端乾淨 ls ~/cnc-monitor/recordings/*.mp4 無結果
GX10 三件齊 run_*.mp4 + run_*.mp4.log + run_*_annotated.mp4 都在

七、文件位置

檔 位置
monitor.py (錄製 + log) Pi4 ~/cnc-monitor/monitor.py
stitch.py (疊字) GX10 /home/gx/cnc_records/stitch.py
sync_rec.sh (回傳 + 清理) Pi4 ~/cnc-monitor/sync_rec.sh
錄片 + log + annotated GX10 /home/gx/cnc_records/
本文 WP http://localhost:8082/ (區網)


延伸閱讀

用 Webcam 監看 FluidNC 運轉:從串流到自動錄影的完整配置

📺 影片版(約 8 分鐘):想先看實際操作的話可以直接看這支影片,完整步驟、設定與程式碼在下方文字版。
YouTube:https://youtu.be/AZ_RN-PXs_0(winson-DIY 頻道)
把之前舊玩具重新拿出來用AI 玩, telegram 透過tailscale下指令給 Pi4+ESP32
之前文章 ESP32 Grbl Controller

而這篇文章把 ESP32 的韌體升級到了 FluidNC, 直接官網更新


這次把「眼睛」也接上——用一支 USB Webcam 監視 CNC 的運轉,瀏覽器低延時直播、手機可以看、跑 G-code 時自動錄影,影片自動回传到主機。整套東西架在 Raspberry Pi 4 上,全部開機自啟,不佔用 PC。

一、架構總覽

整套系統有四個常駐服務,全跑在 Pi4 上:

服務 作用
mediamtx 串流伺服器,把一路 RTSP 來源重新打包成 WebRTC / RTSP / HLS
cam-push ffmpeg 從相機抓幀,常駐推到 MediaMTX
cnc-monitor Python 腳本盯 FluidNC 狀態:Run 時開錄、Idle 自動收檔
cnc-sync.timer 每 2 分鐘把新錄好的影片 scp 回主機

訊號流:

C310 /dev/video0
    → ffmpeg (H.264 720p15, zerolatency)
        → MediaMTX
            ① WebRTC  瀏覽器 <300ms 低延時
            ② RTSP    手機 VLC
            ③ HLS     瀏覽器備用(有延遲)
    → cnc-monitor 偵測 FluidNC 狀態 → Run 時再錄一份 mp4 → 自動回傳

二、硬體與前置

  • Raspberry Pi 4(64 位 Raspberry Pi OS,本次用 Debian 13 版),有 USB 2.0 口
  • USB Webcam 一支(實測 Logitech C310,C922 也可;720p 等級足夠)
  • FluidNC 韌體的 ESP32(wifi 版,同一台 AP 或同一 tailnet 內可互達)
  • 相機建議斜上方 45 度拍機頭,距離 50–80 cm,避免正對刀頭(反光且看錯切削點)

三、確認相機

插上去之後先確認 OS 認得到:

lsusb | grep -i logitech        # 確認 UVC 相機在線
ls /dev/video*                  # 會有 video0(主流)video1(副流)
v4l2-ctl -d /dev/video0 --info  # 卡型/驅動確認

抓一幀確認能出圖:

ffmpeg -f v4l2 -video_size 1280x720 -i /dev/video0 -frames:v 1 /tmp/t.jpg

C922 的 video0/video1 是雙路串流(一路 1080p、一路 720p),C310 是 1280×720 單路。

四、安裝 MediaMTX

MediaMTX 是輕量串流伺服器,單檔二進位即可,aarch64 有官方包:

mkdir -p ~/cnc-monitor && cd ~/cnc-monitor
curl -fsSL https://github.com/bluenviron/mediamtx/releases/download/v1.21.2/mediamtx_v1.21.2_linux_arm64.tar.gz | tar xz
./mediamtx -h   # 會自動產生默認設定與憑證

最小設定檔 mediamtx.yml(注意欄位要寫對,別亂加會 unmarshal 失敗):

logLevel: info
rtspAddress: :8554
webrtcAddress: :8889
hlsAddress: :8888
paths:
  cnc:
    source: publisher

做成 user systemd 服務:

# ~/.config/systemd/user/mediamtx.service
[Unit]
Description=MediaMTX (CNC monitor streaming)
After=network-online.target

[Service]
Type=simple
WorkingDirectory=%h/cnc-monitor
ExecStart=%h/cnc-monitor/mediamtx %h/cnc-monitor/mediamtx.yml
Restart=on-failure

[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user enable --now mediamtx

五、常駐推播(cam-push)

# ~/cnc-monitor/push_cam.sh
#!/bin/bash
while true; do
  ffmpeg -loglevel warning -f v4l2 -video_size 1280x720 -framerate 15 -i /dev/video0 \
    -c:v libx264 -tune zerolatency -preset ultrafast -pix_fmt yuv420p -b:v 2500k -g 30 \
    -f rtsp -rtsp_transport tcp rtsp://localhost:8554/cnc
  echo "push ended, reconnect in 2s"; sleep 2
done

重點參數:
– -tune zerolatency -preset ultrafast:把編碼延時壓到最低,4 核 Pi 推 720p15 沒壓力
– -rtsp_transport tcp:避免 UDP 在 AP/WiFi 環境掉幀
– 外層 while true 做斷線自動重推

同樣做成 user 服務,記得 After=mediamtx.service、Requires=mediamtx.service。

六、三條收看路徑

用途 位址(把 IP 換成你的 Pi)
PC 瀏覽器(WebRTC,延時最低) http://<PI_IP>:8889/cnc/
手機 VLC(RTSP,轉運選 TCP) rtsp://<PI_IP>:8554/cnc
瀏覽器備用(HLS,有秒級延遲) http://<PI_IP>:8888/cnc/index.m3u8

Pi 在 AP 的 WiFi 網段內直連即可;跨網段或在外就用 Tailscale IP(tailnet 內 100.x 位址,三端口都能通,不需穿透)。

七、自動錄影(cnc-monitor)

這支腳本是整套裡最吃螺絲的一部分,把實踩的坑都寫出來。

FluidNC 的 WebSocket 協定(最大的坑)

  • Endpoint:ws://<FLUIDNC_IP>(port 80,不是 81),subprotocol 要帶 arduino
  • 送 G-code 指令,字串結尾一定要帶換行 \n——FluidNC 的 WSChannel 對沒有 \n 的輸入會緩存不執行,你只收得到 PING 永遠等不到 ok/error
  • 狀態行格式:<Run|MPos:x,y,z|FS:feedrpm> / <Idle|...>,狀態偵測用正規表達式抓 <Run|<Idle|<Alarm
  • 同一 time 只有「最新一個」WebSocket 客戶端是被當 primary terminal 的(它會收到 activeID: push,晚到的客端被踢成 passive,passive 送指令不會回 ok)。所以設計上是:monitor 常駐佔住這個 ws session,其他需要發指令的地方(比如手機端想下指令)走文件轉發:寫一行指令到 /home/pi/cnc-monitor/cmd.txt,monitor 看到就往自己的 ws 送出——這是唯一不會搶 session 的方式。UGS 從 PC 走 USB 序列口完全不受影響。

錄影觸發邏輯

  1. 輪詢 ? 拿狀態(0.4~1 秒一次)
  2. 連續 6 次收不到任何回應 → 判定 ws 失效,重連(防止 FluidNC 重啟或 session 被搶後卡死)
  3. 狀態從 Idle 轉 Run/Hold → 啓動 ffmpeg 錄 MediaMTX 的 rtsp://localhost:8554/cnc,檔名 run_YYYYmmdd_HHMMSS.mp4,同時寫一個 recorder_active 標記檔
  4. 狀態回 Idle 並持續 8 秒→ 收檔(防止 G-code 行與行之間短暫 Idle 就誤收)
  5. 安全上限:錄超過 10 分鐘強制收檔
  6. 收檔判斷要依賴「recorder_active 標記」,不要用 state 變數——實測過用 state 變數做 elif state=='Run' 的判斷会因第一次 Idle 就把 state 改掉而永遠不走收檔分支,ffmpeg 無限錄下去

ffmpeg 參數(錄製版,比推播版可放寬):

ffmpeg -rtsp_transport tcp -i rtsp://localhost:8554/cnc \
  -c:v libx264 -preset veryfast -tune zerolatency -b:v 2500k \
  -pix_fmt yuv420p -movflags frag_keyframe+empty_moov run_xxx.mp4

八、自動回傳主機(cnc-sync)

用 user timer 每 2 分鐘掃一次 recordings 目錄:

systemctl --user enable --now cnc-sync.timer

腳本要防三個坑:
1. 檔身還在寫——mp4 錄好前 size 會一直長,要檢查「距上次修改 ≥20 秒」且「兩秒間 size 不變」才動手
2. 傳半截——先 scp 成 xxx.mp4.part,傳完比對远端 size 一致才 mv 改回正式檔名
3. 本地 size 對遠端 size 不一致——以遠端為準重新傳,別直接 mv

回傳目標例:主機的 /home/<user>/cnc_records/(走 Tailscale IP,免開洞)。

九、必做:啟用 lingering

systemctl --user 的服務只活著在登入 session 裡面。Pi 上通常是用 VNC/SSH 登進去操作的,一登出(或重開機)這些服務就都沒了。必跑一次:

sudo loginctl enable-linger pi
loginctl show-user pi -p Linger   # 應回 Linger=yes

(注意是 enable-linger 不是 enable-lingering,多一個 g 會報 invalid choice。)

十、注意事项清單

  1. 相機角度:斜上方拍機頭,避直對刀尖(反光 + 看不到實際切削位置)
  2. 延時:看走 WebRTC(8889),別走 HLS(8888 有秒級延遲,看切削對位会慢半拍)
  3. Pi 4 USB 2.0 帶寬:720p 推播 + 720p 錄製并行实测沒問題,但別再堆更多路;若要 1080p 建議 Pi4 升 8GB 或 Pi5
  4. Webcam 被佔:/dev/video0 同一 time 只能被一個 ffmpeg 讀,cam-push 死掉重連時如果舊 process 還在,新 process 會 Device or resource busy——重前用 fuser -v /dev/video0 查
  5. FluidNC ws 單一 primary:別同時開 dashboard 和 monitor 又發指令,指令會被 passive 客端吞掉不回 ok;要發指令就走 cmd.txt 轉發
  6. 指令一定要帶 \n 結尾,否則不會執行
  7. 自動收檔要留 idle buffer(8 秒),不然 G-code 行間的微 Idle 會把影片切得七零八落
  8. lingering 必開,不然重開/登出後整套服務消失
  9. 錄製檔要等它「穩」再回傳,size 快照 + .part 暫存 + 遠端比對,三層防護
  10. 看片與看 FluidNC 狀態分屏:左邊 WebRTC 看切削,右邊 FluidNC dashboard 看 position/feed,跑長 G-code 時比單看影片可靠

十一、故障排查快速表

症狀 原因 解法
瀏覽器連 8889 沒畫面 cam-push 沒在推 pgrep -af ffmpeg|cam,重啟 cam-push
VLC 連 8554 卡住 UDP 丢幀 VLC 轉運改 TCP / 命令加 -rtsp_transport tcp
發指令只收 PING 字串沒帶 \n 結尾 ws.send(cmd + '\n')
錄影片無止境長大 收檔判斷用了 state 變數 用 recorder_active 標記檔判
重開 Pi 後串流沒了 lingering 沒開 sudo loginctl enable-linger pi
相機 Device or resource busy 舊 ffmpeg 殘留 fuser -v /dev/video0 找到 pid 殺掉

結語

這套東西的最大價值不是「能看」,而是「自動留證據」——每段 Run 的運動都對應一個 run_xxx.mp4,事後回放配 FluidNC 的狀態流,能精確對到是哪一行 G-code 出了問題。整套 Pi4 常駐之後完全免維護,跑完機就回家看影片。

下次接好馬_driver、寫完設定,第一刀跑完就能收到自己的 run_20261013_xxxxxx.mp4。



延伸閱讀

2026年10月9日 星期五

用本地 ComfyUI 生成圖片與影片:完整流程與注意事項

本文環境:GX10(NVIDIA GB10、121GB 統一記憶體、Linux)+ ComfyUI v0.34.2,全部本地推理,不依賴任何雲端 API。流程已實測:落日黃昏圖片 16 秒、LTX-2.3 22B 影片 75 秒。

一、隨開隨用:用完即停的資源模式

ComfyUI 常駐會吃約 30GB 系統記憶體(GB10 統一記憶體,VRAM 外溢)。為了平常不佔資源,我做了兩個指令:

comfy-on    # 啟動 service + 輪詢 API 就緒(實測 8 秒)
comfy-off   # 停止 service + 確認 port 8188 釋放 + 回報可用 RAM

使用節奏:comfy-on → 生成 → comfy-off。平常狀態 72-75GB 可用,開著時剩 40GB 左右,121GB 的機器扛得住,但不建議同時跑其他重任務(本地 LLM 推理等)。

二、生圖(SDXL,1024×768)

模型:sd_xl_base_1.0.safetensors(models/checkpoints/)。流程:

  1. comfy-on
  2. 組 workflow JSON(CheckpointLoaderSimple → CLIPTextEncode ×2 → EmptyLatentImage → KSampler → VAEDecode → SaveImage)
  3. POST http://127.0.0.1:8188/prompt 提交,拿到 prompt_id
  4. 輪詢 /history/<prompt_id> 到 completed
  5. 從 outputs 拿 filename,用 /view?filename=...&subfolder=...&type=output 下載
  6. comfy-off

本次實例:seed 20261008、steps 25、cfg 4.0、dpmpp_2m/karras,16 秒出圖 1MB PNG。



三、生影片(LTX-2.3 22B,文字→影片)

這台有 ltx-2.3-22b-dev-fp8.safetensors(44GB)+ SVD-XL。LTX-2 系列是新架構,有以下幾點一定要正確組合,任何一錯都會在執行時報錯:

1. 文字編碼器(最容易踩的坑)

  • LTXV 需要 Gemma3-12B(hidden 3840),不是 SDXL 那套 CLIP,也不是 T5-XXL。
  • ComfyUI 要的是單一 safetensors,內部同時含:權重重映射為 model.* 前綴 + spiece_model(sentencepiece tokenizer 原始 bytes)+ LTX 的 text_embedding_projection.* 四支張量(後者本來在 LTX checkpoint 裡,要撈出來合併)。
  • HF 上 google 官方 repo 是 gated、Lightricks 官方是 12+ 分片;實務上抓公開整包的 bf16(5 分片約 22.7GB),合併、key 重整後變成一台 25GB 的單檔,一次搞定。
  • FP4/NVFP4 量化版會撞 ComfyUI 的權重載入路徑('Linear' object has no attribute 'weight'),用 bf16 版。

2. VAE 用錯 = 影片只剩 1 幀

LTX checkpoint 自帶 VAE(vae.encoder/decoder),VAEDecode 要拿 [checkpoint, 2],不要外接 flux2-vae。接錯的症狀是:影片成功輸出,但 ffprobe 只看到 1 幀、尺寸縮半——很容易誤以為「生成成功」。

3. 參數(本次實測值)

  • EmptyLTXVLatentVideo:768 × 512(32 的倍數);幀數 8n+1(97 ≈ 3.9 秒 @25fps)
  • DualCLIPLoader:type=ltxv,clip1/clip2 都填同一支 Gemma3-12B
  • KSampler:cfg=1.0(LTX-2 是 guidance-free,別用 2-7 的舊值)、steps 20-25、euler/simple
  • SaveVideo:新版節點要求必填 format(mp4)

4. 圖→影片(img2vid)

可用「先生圖 → 上傳 /upload/image(會進 input/ 目錄)→ LoadImage 當第一幀」的做法,起幅最穩定。SVD-XL 也可走 img2vid,但品質舊一代;LTX 走 LTXVAddGuide/img2video 節點。



四、驗證清單(每次生成必做)

  • ffprobe 看 duration / nb_frames / 解析度——幀數=1 或尺寸縮半 = VAE 接錯
  • 抽中間幀肉眼確認(ffmpeg -ss N -frames:v 1)
  • 生成完 comfy-off,確認 RAM 還原

五、產出位置

  • 圖片:~/ComfyUI/output/
  • 影片:~/ComfyUI/output/video/
  • 本文章案例:落日黃昏 768×512 / 97 frames / H.264 MP4,本地生成 75 秒。

本文僅記錄自家環境的本地生成流程;模型版權屬原作者(LTX-2.3: Lightricks,Gemma: Google)。



延伸閱讀

2026年7月3日 星期五

ComfyUI 介紹及與 Telegram 文字生圖的方式

📺 影片版(約 7 分鐘):想先看實際操作的話可以直接看這支影片,完整步驟、設定與程式碼在下方文字版。
YouTube:https://youtu.be/USvokuFVFSA(winson-DIY 頻道)

 ComfyUI 是目前 Stable Diffusion (AI 繪圖) 領域中最強大的可視化介面之一。它的核心特點是「節點式 (Node-based)」操作,這讓使用者可以精確控制整個渲染流程。


以下是關於 ComfyUI 的簡介以及安裝方式:

---

🌟 什麼是 ComfyUI?

ComfyUI 是一個基於 Stable Diffusion 模型的視覺化介面。與 Midjourney (文字輸入) 或 Automatic1111 / SD WebUI (選單下拉框) 不同,ComfyUI 透過「連接節點」來完成工作。

💡 核心優勢
1. 效能極佳:由於採用圖形化資料流,它的 GPU VRAM 佔用率通常比其他介面低約 30%~50%,這讓你在顯示卡有限的情況下也能跑更高分辨率。
2. 完全的控制權:你可以看到模型是如何一步步組合(例如:Text Encoding -> Latent Sampling -> VAE Decode)。
3. 工作流分享 (JSON):你畫好的圖,可以直接把 .png 檔塞回 ComfyUI 的左側,就能還原出它的設定流程,方便學習與修改。

---

🛠️ 安裝方式 (以 Linux / NVIDIA GPU 為例)

您的伺服器有 128GB RAM + NVIDIA GB10 顯示卡,效能非常充裕,但建議還是使用隔離環境來安裝以避免系統衝突。

步驟 1:準備環境
確保您已經安裝了必要的基礎工具:
sudo apt update && sudo apt upgrade -y
sudo apt install git python3 python3-venv python3-pip libgl1-mesa-glx -y


步驟 2:下載 ComfyUI
1. 進入您的工作目錄並克隆官方專案倉庫:
2. 建立一個乾淨的 Python 虛擬環境
 python3 -m venv venv
3. 啟動虛擬環境 
source venv/bin/activate
4. 下載最適合您系統的環境依賴套件
 pip install --upgrade pip pip install -r requirements.txt

步驟 3:直接啟動本機 ComfyUI 服務

cd ~/ComfyUI
source venv/bin/activate
python main.py --listen 0.0.0.0 --port 8188


進入後

開啟範本: 若有錯誤點選元件下載

配置 ComfyUI 的 Z-Image-Turbo 流程,請確保將它們放對資料夾

  • ae.safetensors 放入 ComfyUI/models/vae/
  • qwen_3_4b.safetensors 放入 ComfyUI/models/text_encoders/
  • z_image_turbo_bf16.safetensors 放入 ComfyUI/models/diffusion_models/

利用telegram 文字生圖



補充(2026/10 更新):實際環境、實測速度與 Telegram 生圖的完整做法
這篇寫於 7 月剛裝好 ComfyUI 的時候。用了幾個月後,把實際的版本、安裝步驟的修正、實測速度,以及 Telegram 文字生圖背後到底怎麼運作補上來。以下數字都是在 GX10 本機實際查詢、實際跑出來的。

補充一、這台 GX10 的實際環境
項目實際值
主機ASUS Ascent GX10(NVIDIA GB10,ARM64 架構)
記憶體121 GB 統一記憶體(CPU 與 GPU 共用,ComfyUI 看到的 VRAM 也是 121 GB)
ComfyUIv0.34.2(前端 1.49.6)
Python 環境Anaconda 環境 ComfyUI_env,Python 3.10.20
PyTorch2.12.1 + CUDA 13.0(cu130)
Telegram 套件python-telegram-bot 22.8、httpx 0.28.1

Z-Image-Turbo 這組三個模型檔的大小,下載前可以先確認硬碟空間:
檔案放置資料夾大小
z_image_turbo_bf16.safetensorsmodels/diffusion_models/12 GB
qwen_3_4b.safetensorsmodels/text_encoders/7.5 GB
ae.safetensorsmodels/vae/320 MB

補充二、安裝步驟的幾個修正
上面的步驟照抄有幾個地方會卡住,這裡更正:
1. libgl1-mesa-glx 在 Ubuntu 24.04 已經不存在,apt 會報找不到套件,改裝 libgl1:
sudo apt install git python3 python3-venv python3-pip libgl1 -y
2. pip 那行其實是兩個指令,要分開執行:
pip install --upgrade pip
pip install -r requirements.txt
3. GX10 是 ARM64,PyTorch 要裝 CUDA 13 版,否則會裝到只能用 CPU 的版本。建議在 pip install -r requirements.txt 之前先裝:
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu130
裝完用這行確認 GPU 有抓到(要印出 True):
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"
4. ComfyUI-Manager 要用 git clone 放進 custom_nodes,不是 wget:
cd ~/ComfyUI/custom_nodes
git clone https://github.com/ltdrdata/ComfyUI-Manager.git comfyui-manager
5. venv 或 conda 都可以。我這台後來改用 Anaconda 環境(conda create -n ComfyUI_env python=3.10),好處是 Python 版本跟系統完全分開;用 venv 的話把下面服務檔裡的路徑換成 ~/ComfyUI/venv/bin/python 即可。

補充三、Z-Image-Turbo 工作流的參數與實測速度
範本裡實際用的參數如下,Turbo 版只要 8 步就能出圖:
節點參數
EmptySD3LatentImage1024 × 1024
KSamplersteps 8、cfg 1、sampler res_multistep、scheduler simple
ModelSamplingAuraFlowshift 3
CLIPLoaderqwen_3_4b,type 選 lumina2
ConditioningZeroOut負面提示詞直接歸零(cfg 1 時本來就用不到負面提示詞)

在 GX10 上實測三張 1024×1024:
  • 第一張(冷啟動):18.1 秒,多出來的時間是把約 20 GB 模型載入記憶體
  • 之後每張:約 7.5 秒
提示詞可以直接寫中文(文字編碼器是 Qwen3),例如「夜晚霓虹燈下的台北街頭,賽博龐克風格」就能出圖。不過圖中如果要出現招牌文字,中文字常常會變成看起來像字但其實不是字的筆畫,這是目前模型的限制。

補充四、讓 ComfyUI 開機自動啟動(systemd)
每次手動 python main.py 很麻煩,可以做成 systemd 使用者服務,掛掉 10 秒後還會自動重啟。建立 ~/.config/systemd/user/comfyui.service:
[Unit]
Description=ComfyUI AI Generation Service
After=network.target

[Service]
Type=simple
WorkingDirectory=/home/gx/ComfyUI
ExecStart=/home/gx/anaconda3/envs/ComfyUI_env/bin/python main.py --listen 0.0.0.0 --port 8188
Restart=always
RestartSec=10

[Install]
WantedBy=default.target
啟用與管理:
systemctl --user daemon-reload
systemctl --user enable --now comfyui.service   # 開機自動啟動並立即啟動
systemctl --user status comfyui.service
journalctl --user -u comfyui.service -f         # 看即時 log
sudo loginctl enable-linger $USER                # 沒登入也照樣跑
啟動後約 10 秒,用 curl -s http://127.0.0.1:8188/system_stats 有回 JSON 就代表服務好了。

小提醒:ComfyUI 載入模型後會一直佔著記憶體。GX10 的 GPU 跟 CPU 共用同一塊記憶體,如果同時還要跑 Ollama 大模型,不常生圖的話可以不設開機啟動,要用時再 systemctl --user start comfyui。

補充五、Telegram 文字生圖是怎麼運作的
上面截圖裡在 Telegram 打 /generate 提示詞 就收到圖,背後是一支自己寫的 Python 機器人,流程如下:
手機 Telegram:/generate 一隻橘貓坐在窗台上看雨
        │
        ▼
Telegram Bot(python-telegram-bot,在 GX10 上跑)
  1. 讀取工作流 JSON(API 格式)
  2. 把提示詞塞進 CLIPTextEncode 節點的 text
        │  POST /prompt
        ▼
ComfyUI(127.0.0.1:8188)排隊、生圖 → 回傳 prompt_id
        │  每秒 GET /history/{prompt_id},直到出現結果
        ▼
Bot 用 GET /view 取回 PNG → send_photo 傳回手機
步驟 1:建立 Telegram Bot
  1. 在 Telegram 搜尋 @BotFather,輸入 /newbot,依指示取名,最後會拿到一組 Bot Token
  2. 跟 @userinfobot 說一句話,取得自己的數字 User ID(等一下用來限制只有自己能用)
步驟 2:從 ComfyUI 匯出 API 格式的工作流
在 ComfyUI 網頁打開 Z-Image-Turbo 工作流,確認能正常出圖後,選單「Workflow → Export (API)」存成 image_z_image_turbo.json。
注意一定要匯出 API 格式:一般「Save」存的是編輯器格式(有 nodes、links 陣列),直接送 /prompt 會失敗。API 格式長這樣,每個節點以 ID 為 key:
"57:27": {"class_type": "CLIPTextEncode", "inputs": {"text": "山窮水盡的水彩畫", "clip": ["57:30", 0]}},
"57:3":  {"class_type": "KSampler", "inputs": {"seed": 878164762609716, "steps": 8, "cfg": 1, ...}}
程式要改提示詞就改 57:27 的 text,要換種子就改 57:3 的 seed。

步驟 3:安裝套件
pip install python-telegram-bot httpx
步驟 4:機器人程式(精簡版)
我最早的版本有 180 行,還用了執行緒池;其實 httpx 本身就是非同步的,不需要另外開執行緒。整理後的精簡版如下(核心的 ComfyUI 呼叫在 GX10 上實測可正常取回圖片),存成 comfy_bot.py:
import os, json, random, asyncio, httpx
from pathlib import Path
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes, filters

COMFY = "http://127.0.0.1:8188"
WF = json.loads(Path("image_z_image_turbo.json").read_text())
MY_ID = int(os.environ["TG_MY_USER_ID"])      # 只允許自己使用

async def comfy(prompt: str) -> bytes:
    wf = json.loads(json.dumps(WF))            # 複製一份,避免改到原本的
    wf["57:27"]["inputs"]["text"] = prompt     # 提示詞節點
    wf["57:3"]["inputs"]["seed"] = random.randint(0, 2**48)  # 每次換種子
    async with httpx.AsyncClient(timeout=30) as c:
        pid = (await c.post(f"{COMFY}/prompt", json={"prompt": wf})).json()["prompt_id"]
        for _ in range(180):                   # 最多等 180 秒
            await asyncio.sleep(1)
            h = (await c.get(f"{COMFY}/history/{pid}")).json()
            if pid in h:
                img = next(i for o in h[pid]["outputs"].values() for i in o.get("images", []))
                return (await c.get(f"{COMFY}/view", params=img)).content
    raise TimeoutError("ComfyUI 超過 180 秒沒回應")

async def generate(update: Update, context: ContextTypes.DEFAULT_TYPE):
    prompt = " ".join(context.args)
    if not prompt:
        await update.message.reply_text("用法:/generate 畫面描述")
        return
    msg = await update.message.reply_text("⏳ 生成中,約 10 秒…")
    try:
        png = await comfy(prompt)
        await update.message.reply_photo(png, caption=prompt[:1000])
        await msg.delete()
    except Exception as e:
        await msg.edit_text(f"❌ 生成失敗:{e}")

app = (ApplicationBuilder().token(os.environ["TG_BOT_TOKEN"])
       .concurrent_updates(True).build())      # 生圖時 Bot 仍可回應其他訊息
app.add_handler(CommandHandler("generate", generate,
                               filters=filters.User(MY_ID), block=False))
app.run_polling()
步驟 5:執行
export TG_BOT_TOKEN="從 BotFather 拿到的 token"
export TG_MY_USER_ID="你的數字 User ID"
python comfy_bot.py
接著在 Telegram 對你的 Bot 傳 /generate 清晨山間的茶園,雲霧繚繞,寫實攝影,約 8 秒就會收到圖。

補充六、幾個實際踩過的坑
  • 種子固定,同一句提示詞永遠同一張圖:工作流 JSON 裡的 seed 是固定值,不改的話 ComfyUI 甚至會直接回快取結果。上面程式每次用 random.randint 換種子就解決了。
  • 不要用「有 text 欄位就替換」的寫法:我第一版是掃過所有節點、只要 inputs 有 text 就換掉,如果工作流裡有負面提示詞節點,會連負面提示詞一起被換成正面內容。直接指定節點 ID 最保險。
  • 一個 Bot Token 只能有一支程式在收訊息:如果同一個 token 已經給其他程式(例如 AI Agent)用,兩邊同時 polling 會出現 409 Conflict,訊息會被搶走。生圖機器人請另外跟 BotFather 申請一個。
  • 一定要限制使用者:Bot 是公開的,任何人搜到都能傳指令。沒加 filters.User 等於讓陌生人免費用你的 GPU。
  • --listen 0.0.0.0 等於對整個區網開放:ComfyUI 沒有登入機制,同網段的人都能打開網頁、送工作。如果只有本機的 Bot 會用,改成 --listen 127.0.0.1;要從外面連就透過 Tailscale,不要在路由器開 port。
  • 做影片時要把逾時拉長:生圖 10 秒內就好,但 LTX 等影片模型一支要好幾分鐘,輪詢上限要改大,而且 reply_photo 要換成 reply_video。

補充七、更進一步:讓 AI Agent 直接操作 ComfyUI
自己寫 Bot 的好處是簡單、可控,但只能固定打 /generate。後來我在 GX10 上改用 Hermes Agent 的 comfyui 技能(skill),在 Telegram 用一般對話就行,例如「幫我畫一張水彩風格的貓,畫完再做成 5 秒影片」,Agent 會自己選工作流、改參數、送給同一台 ComfyUI,再把結果傳回來。底層呼叫的一樣是 /prompt、/history、/view 這幾個 API,所以先把這篇的 Bot 做通,對理解 Agent 在做什麼很有幫助。

補充八、問題排查清單
症狀檢查方向
Bot 沒反應token 是否正確、是否被其他程式同時 polling(409 Conflict)、User ID 是否填對
回「生成失敗」連線被拒ComfyUI 沒在跑:systemctl --user status comfyui、curl 127.0.0.1:8188/system_stats
POST /prompt 回 400工作流不是 API 格式,或模型檔名、資料夾放錯(看回應裡的 node_errors)
第一張特別慢正常,冷啟動要載入模型,之後就快了
出圖很慢、像在用 CPUPyTorch 裝到 CPU 版,torch.cuda.is_available() 是 False,重裝 cu130 版

延伸閱讀

2026年7月1日 星期三

Hermes Agent Profiles:環境搭建、深度解析與擴展指南

Hermes Agent (Nous Research) 允許透過 Profile 來隔離 AI 的行為模式、模型選擇與插件權限。這意味著「同一個軟體,多重人格」。

我現在的系統 - 透過 telegram 詢問

第一部分:您系統現有 Profile 分析

經過全面掃描,您的系統目前搭載了四個核心 Profile,全部指向本機 Ollama (127.0.0.1:11434),硬體為強大的 NVIDIA GB10 GPU。

📊 Profile 綜合比較表

**主要 AI 模型**
• default (預設/主力): qwen3.6:35b-a3b
• designer (設計者): gpt-oss:20b
• tester (測試員): qwen3.6:35b-a3b
• coder (程式碼助手): gemma4:12b-it-q4_K_M

**模型類型**
• default (預設/主力): 密集模型 (A3B)
• designer (設計者): MoE 混合專家
• tester (測試員): 密集模型 (A3B)
• coder (程式碼助手): Q4 壓縮 (節省記憶體)

**定位**
• default (預設/主力): 🌟 全能主力、日常對話、全盤掌控
• designer (設計者): 🎨 多模態設計、圖面分析、創意發想
• tester (測試員): 🔧 系統管理、程式檢查、測試報告
• coder (程式碼助手): 💻 快速編碼、語義理解與輕量指令執行

**硬體需求**
• default (預設/主力): ~29 GB VRAM
• designer (設計者): ~14 GB VRAM
• tester (測試員): ~29 GB VRAM
• coder (程式碼助手): ~8 GB VRAM (最省資源)

**SOUL.md 風格**
• default (預設/主力): 高效能工具型人格
• designer (設計者): 視覺導向、創意人格
• tester (測試員): 嚴格邏輯、驗證型人格
• coder (程式碼助手): 程式碼優先、技術型人格

🧠 深度分析與建議

1. 主力模式 (default / tester):
* 兩者皆使用 qwen3.6:35b-a3b。這是目前 Ollama 內最強大的中文模型,您的 SOUL.md 已寫入 「先分析問題、再查數據、後給結論」 的強大指令,適合執行複雜的系統資源檢查與股市分析。
2. 設計模式 (designer):
* 使用 gpt-oss:20b (MoE)。其優勢在跨領域聯想(例如:AI 輔助繪圖、視覺解析),VRAM 需求量僅約一半,適合與主力模型切換運行。
3. 程式開發 (coder):
* 採用 gemma4 的 Q4 壓縮版本。這是極度省 VRAM 的選擇,特別適合在跑著 ComfyUI (佔用近 20 GB) 時,作為輕量代碼輔助而不撐爆顯存。

以下是我安裝 Hermes 及 增加 profile的安裝步驟

1. 🛠️ 安裝前置作業

請確保系統已安裝 git 套件。如果沒有,請執行以下指令安裝:

sudo apt update && sudo apt install git -y

2. 執行安裝腳本:

curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash

3. 重新載入 Shell:讓終端機環境變數生效:

source ~/.bashrc

4. 依據安裝畫面進行安裝

5. 安裝時請同意啟用背景service 服務

6. 新增profile隔離 各個agent gateway 服務
hermes profile create designer
hermes profile create coder
hermes profile create tester

以coder profile為例, 其他皆相同
model:
  default: qwen3.6:35b-a3b
  provider: custom
  base_url: http://127.0.0.1:11434/v1/
  api_key: ollama
7.為新 Profile 配置 LLM 模型
a. 切換到新 Profile:hermes profile use coder
b. 啟動互動式設定: hermes setup
c. 綁定獨立的 Telegram 網關(Gateway): hermes gateway setup
d. 重啟服務 hermes -p coder gateway restart

6. 對應三個 telegram bot 加入同一個群組



~~~  開始你的 Hermes 之旅!!!




延伸閱讀

2026年3月25日 星期三

利用telegram來控制 crontab 任務的啟動或關閉

既然用了telegram bot就想說用這個tool來控制系統中的crontab任務列表
先查看crontab -l
再利用crontab -e

再透過 python code:
import logging
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes
from crontab import CronTab

# --- 設定區 ---
TOKEN = 'your bot token'
USER_ID = your chat id 不是字串

# 初始化日誌
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)

def get_cron_keyboard():
    cron = CronTab(user=True)
    keyboard = []
   
    for index, job in enumerate(cron):
        status_icon = "🟢 啟用中" if job.is_enabled() else "🔴 已暫停"
       
        # --- 自定義名稱邏輯 ---
        cmd = job.command
        if "email_notify_v2.py" in cmd:
            display_name = "任務通知 V2"
        elif "email_notify.py" in cmd:
            display_name = "任務通知 V1"
        else:
            # 如果不是以上兩個,就顯示指令的前 20 個字
            display_name = cmd.split('/')[-1][:20]
        # ---------------------

        label = f"{status_icon} | {display_name}"
        keyboard.append([InlineKeyboardButton(label, callback_data=str(index))])
   
    return InlineKeyboardMarkup(keyboard) if keyboard else None

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """驗證身分並列出所有任務清單"""
    if update.effective_user.id != USER_ID:
        return
   
    markup = get_cron_keyboard()
    if markup:
        await update.message.reply_text("📋 **當前系統排程清單**\n點擊按鈕即可切換 啟動/暫停:",
                                      reply_markup=markup, parse_mode='Markdown')
    else:
        await update.message.reply_text("目前系統中沒有任何 Crontab 任務。")

async def button_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """處理按鈕點擊:切換任務狀態"""
    query = update.callback_query
    if query.from_user.id != USER_ID:
        await query.answer("權限不足")
        return

    await query.answer()
    job_index = int(query.data)
   
    # 重新讀取並操作
    cron = CronTab(user=True)
    try:
        job = cron[job_index]
        # 切換開關
        job.enable(not job.is_enabled())
        # 寫回系統
        cron.write()
       
        # 更新訊息與按鈕狀態
        await query.edit_message_text(
            text="✅ **狀態已更新**\n點擊下方按鈕繼續管理:",
            reply_markup=get_cron_keyboard(),
            parse_mode='Markdown'
        )
    except IndexError:
        await query.edit_message_text("❌ 找不到該任務,可能已被手動刪除。")

if __name__ == '__main__':
    app = Application.builder().token(TOKEN).build()
   
    app.add_handler(CommandHandler("start", start))
    app.add_handler(CallbackQueryHandler(button_callback))
   
    print("Bot 啟動中... 請在 Telegram 輸入 /start")
    app.run_polling()

運作情形

這樣控制定時任務就方便了!!!


延伸閱讀

2026年2月26日 星期四

Raspberry Pi4 與小龍蝦 openclaw 的連接問題 (應用 telegram)

首先在raspberryPi4安裝Openclaw
# 更新系統
sudo apt update && sudo apt upgrade -y
sudo apt install -y git curl build-essential
# 安裝 Node.js 22
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
# 安裝 openclaw
curl -fsSL https://openclaw.ai/install.sh | bash

安裝完後接著去設定telegram的APP
這些網路上教學很多可參考!!

以下是我安裝完後跟 telegram 在溝通時的問題與解決方法!!
問題1. 在telegram上, 下 /start 沒有回應
很多 Pi 都是卡 IPv6。
sudo nano /etc/dhcpcd.conf
在最後一行添加noipv6
sudo nano /boot/firmware/cmdline.txt
在最後一行添加ipv6.disable=1
然後在/home/pi/.openclaw的openclaw.json修改
"channels": {
    "telegram": {
      "enabled": true,
      "dmPolicy": "open",
      "botToken": "your telegram token",
      "allowFrom": [
        "*"
      ],
      "groupPolicy": "allowlist",
      "streaming": "partial"
    }
  },
  "gateway": {
    "port": 18789,
    "mode": "local",
    "bind": "lan",
    "controlUi": {
      "enabled": true,
      "allowedOrigins": [
        "http://192.168.1.41:port",
        "http://192.168.1.104:port"
      ]
    },

問題2. ⚠️ API rate limit reached. Please try again later. (rate_limit)
這個問題出在telegram 下指令給 openclaw時, 去找尋答案時還是會用到 model上的token
所以要執行順利一點還是要花點錢!!






應用例:從telegram下達指令, 透過 openclaw 來控制raspberryPi的IO做家電控制
在/home/pi/.openclaw/workspace建立skills目錄跟skills/gpio-control
/home/pi/.openclaw/workspace/skills/gpio-control













led_control.py
import RPi.GPIO as GPIO
import sys

def set_led(pin, state):
    GPIO.setmode(GPIO.BCM) # 使用 BCM 編號
    GPIO.setup(pin, GPIO.OUT)
    if state == "on":
        GPIO.output(pin, GPIO.HIGH)
        print(f"Pin {pin} is now ON")
    else:
        GPIO.output(pin, GPIO.LOW)
        print(f"Pin {pin} is now OFF")
    # 注意:在 AI 持續控制場景下,通常不立即執行 GPIO.cleanup()
    # 以免狀態重置,但在程式退出前應呼叫。

if __name__ == "__main__":
    # 從命令列接收參數:python led_control.py 17 on
    pin_num = int(sys.argv[1])
    target_state = sys.argv[2]
    set_led(pin_num, target_state)
skill.json
{
  "name": "control_raspberry_pi_gpio",
  "description": "控制樹莓派的 GPIO 針腳開關(例如開關燈或馬達)",
  "parameters": {
    "type": "object",
    "properties": {
      "pin": { "type": "number", "description": "GPIO 針腳編號 (BCM)" },
      "state": { "type": "string", "enum": ["on", "off"], "description": "開啟或關閉" }
    },
    "required": ["pin", "state"]
  },
  "handler": "python3 path/to/led_control.py {{pin}} {{state}}"
}

















延伸閱讀