用 Python 透過 SSH 呼叫 mini PC 上的 Ollama,生成一篇小故事
如何讓 Windows 電腦透過 Python 使用另一台 Ubuntu mini PC 上的本地 AI 模型?本文以 GIGABYTE BRIX GB-BTIP-N150 為例,示範 Python 自動建立 SSH 隧道、呼叫 Ollama API、接收繁體中文故事,並在程式結束時自動關閉連線。
一、測試架構與準備條件
本次由 Ubuntu mini PC 執行 Ollama 與語言模型,Windows 電腦則執行 Python 程式。SSH 負責建立加密連線與本機埠轉送;Python 將問題送到 Windows 本機轉送埠,SSH 再把請求帶到 minPC 的 Ollama。
- mini PC:GIGABYTE GB-BTIP-N150,Ubuntu,IP 位址
192.168.1.114。 - Ollama API:minPC 上的
127.0.0.1:11434。 - 模型:
qwen3:4b-instruct-2507-q4_K_M,需先在 minPC 下載完成。 - Windows 電腦:已安裝 Python 與 OpenSSH,用 CMD 執行程式。
- 帳號:SSH 使用者
nfusrai。
ssh nfusrai@192.168.1.114 登入 mini PC。本程式使用 SSH 隧道,不必將 Ollama API 直接開放到區域網路。二、Python 如何建立 SSH 隧道
程式使用 Python 標準函式庫 subprocess 啟動 Windows 的 ssh 用戶端。SSH 指令的核心如下:
ssh -o ExitOnForwardFailure=yes -N -L 127.0.0.1:11436:127.0.0.1:11434 nfusrai@192.168.1.114
-N:只建立轉送,不在 mini PC 上執行互動式命令。-L 127.0.0.1:11436:127.0.0.1:11434:把 Windows 本機的 11436 埠轉送到 SSH 主機端的 11434 埠。nfusrai@192.168.1.114:指定登入 mini PC 的帳號與 IP。ExitOnForwardFailure=yes:如果本機轉送埠無法建立,就讓 SSH 明確失敗。
程式啟動時,SSH 會在同一個 CMD 視窗要求輸入密碼。通過驗證後,Python 會每隔半秒測試一次 /api/version,最多等待 60 秒;確認 API 可連線後,才開始詢問使用者要生成什麼內容。
三、Python 如何呼叫 Ollama API
SSH 隧道建立後,Python 不直接連到 minPC 的 IP:11434,而是向 Windows 本機的 http://127.0.0.1:11436/api/generate 發送 HTTP 請求。SSH 會自動將這筆資料轉送到 minPC 的 Ollama。
請求內容包含模型名稱、系統提示詞、使用者問題、是否串流,以及輸出 token 上限。程式以 json.dumps(..., ensure_ascii=False) 保留中文,再用 UTF-8 編碼送出。
四、完整 Python 程式
將以下程式存成 test_ollama_ssh_windows.py。這支程式不需安裝額外 Python 套件。
#!/usr/bin/env python3
"""自動建立 SSH 隧道,從 Windows 呼叫 BRIX 上的 Ollama。"""
import json
import subprocess
import sys
import time
import urllib.error
import urllib.request
SSH_HOST = "nfusrai@192.168.1.114"
LOCAL_PORT = 11436 # 避開既有測試常用的 11435
MODEL = "qwen3:4b-instruct-2507-q4_K_M"
API_URL = f"http://127.0.0.1:{LOCAL_PORT}/api/generate"
VERSION_URL = f"http://127.0.0.1:{LOCAL_PORT}/api/version"
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8")
ssh_command = [
"ssh",
"-o", "ExitOnForwardFailure=yes",
"-N",
"-L", f"127.0.0.1:{LOCAL_PORT}:127.0.0.1:11434",
SSH_HOST,
]
ssh_process = None
try:
print("正在建立到 BRIX 的 SSH 隧道;若出現提示,請輸入 SSH 密碼。")
ssh_process = subprocess.Popen(ssh_command)
# 最多等 60 秒,讓 SSH 驗證完成並確認 Ollama API 可連線。
deadline = time.monotonic() + 60
while time.monotonic() < deadline:
if ssh_process.poll() is not None:
raise RuntimeError(
f"SSH 程序已結束,結束碼:{ssh_process.returncode}。"
"請確認 SSH 帳號、密碼與主機位址。"
)
try:
with urllib.request.urlopen(VERSION_URL, timeout=2) as response:
version = json.loads(response.read().decode("utf-8"))
print(f"SSH 隧道已連線;Ollama 版本:{version.get('version', '未知')}")
break
except (urllib.error.URLError, TimeoutError):
time.sleep(0.5)
else:
raise TimeoutError(
"等待 Ollama API 逾時。請確認 BRIX 上的 Ollama 正在執行。"
)
prompt = input("請輸入問題(直接按 Enter 使用預設問題):").strip()
if not prompt:
prompt = "請用繁體中文簡短自我介紹。"
payload = {
"model": MODEL,
"system": "請使用繁體中文與臺灣用語,回答簡短。",
"prompt": prompt,
"stream": False,
"options": {"num_predict": 128},
}
request = urllib.request.Request(
API_URL,
data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
headers={"Content-Type": "application/json; charset=utf-8"},
method="POST",
)
with urllib.request.urlopen(request, timeout=180) as response:
result = json.loads(response.read().decode("utf-8"))
print("\n模型:", result.get("model", MODEL))
print("回答:\n", result.get("response", "(API 沒有回傳 response 欄位)"), sep="")
print("\n完成原因:", result.get("done_reason", "未提供"))
except urllib.error.HTTPError as exc:
print(f"HTTP 錯誤:{exc.code} {exc.reason}")
print(exc.read().decode("utf-8", errors="replace"))
raise SystemExit(1)
except urllib.error.URLError as exc:
print("API 連線失敗:請確認 BRIX 上的 Ollama 正在執行。")
print("詳細資訊:", exc.reason)
raise SystemExit(1)
except (TimeoutError, RuntimeError) as exc:
print("連線問題:", exc)
raise SystemExit(1)
except KeyboardInterrupt:
print("\n已中止測試。")
finally:
if ssh_process is not None and ssh_process.poll() is None:
ssh_process.terminate()
try:
ssh_process.wait(timeout=5)
except subprocess.TimeoutExpired:
ssh_process.kill()
五、執行方式與輸出判讀
先讓 mini PC 的 Ollama 保持運作。在 Windows CMD 切換到 Python 程式所在資料夾,執行:
py test_ollama_ssh_windows.py
輸入 Ubuntu 的 SSH 密碼,看到「SSH 隧道已連線」後,輸入問題。本次測試輸入「請用三句話寫一篇小故事。」
六、實際執行結果
C:\Users\cheng-min>py test_ollama_ssh_windows.py
正在建立到 BRIX 的 SSH 隧道;若出現提示,請輸入 SSH 密碼。
nfusrai@192.168.1.114's password:
SSH 隧道已連線;Ollama 版本:0.35.1
請輸入問題(直接按 Enter 使用預設問題):請用三句話寫一篇小故事。
模型: qwen3:4b-instruct-2507-q4_K_M
回答:
老陳每天清晨都坐在門口,看一隻小貓在花盆邊打滾。
貓總在日落時分,把一朵枯花叼進屋裡,放在他枕邊。
某天他發現,那朵花竟開出細小的紅花,像他早年丟失的夢。
完成原因: stop
完成原因: stop表示本次輸出自然結束,而非達到 token 上限。七、程式結束時自動關閉 SSH
程式最後的 finally 區塊會檢查 SSH 子程序是否還在執行;若仍在,就先呼叫 terminate(),等待最多五秒,必要時再強制停止。這讓使用者只需在一個 CMD 視窗操作,不必另外開一個視窗手動維持隧道。
常見問題
輸入密碼後一直等待,沒有「SSH 隧道已連線」?
請確認 mini PC 上 Ollama 正在執行,且 SSH 帳號、IP 位址正確。也可先在 Windows CMD 使用 ssh nfusrai@192.168.1.114 確認 SSH 登入正常。
顯示本機埠無法綁定或轉送失敗?
程式使用 Windows 的 11436 埠。若另一個程式已占用此埠,請關閉占用它的舊 SSH 隧道,或將程式中的 LOCAL_PORT 改成其他未使用的埠號。
Python 找不到 ssh 指令?
請確認 Windows 已安裝 OpenSSH Client,且在 CMD 執行 ssh -V 能顯示版本資訊。
結語:把本地模型接進生活應用
這次完成了「Windows Python → SSH 加密隧道 → mini PC Ollama → Qwen3 Instruct」的單視窗測試。下一步可在 Windows 端加入麥克風語音辨識,將台語問題轉成文字交由本地模型處理,再透過台語語音合成把回答播出,逐步建立由 mini PC 推論、Windows 收音與播放的台語互動原型。
智慧生活科技專業社群|本地生成式 AI、Python 網路程式與 SSH 實作
沒有留言:
張貼留言