2026年1月31日 星期六

樹莓派學習筆記 : 如何用 VNC 從樹莓派傳送檔案到 PC

今天整理鄉下老家客廳門口的櫃子, 打算把放在上面的 Pi 3 移到酒櫥那邊放, 關機前看到下載資料夾下有以前下載的 Anydesk 32 bit 版, 因 Anydesk 似乎不再維護 32 位元的版本, 所以想將此檔案抓回筆電中保存, 我以前都使用 WinSCP 連線來互傳檔案, 其實這可以用 VNC Cloud 桌面的 File Transfer 功能來達成. 

首先按桌面右上方的 VNC 按鈕, 再按 VNC Server 視窗右上角的三條槓按鈕, 點選彈出選單的 File Transfer 選項 :




在 Transfer Files 視窗中, 按左下角的 Send files 鈕, 於彈出視窗點選要抓取的檔案後, 按 OK 即將該檔案傳送到 PC (預設試桌面) :






傳送完畢後在 PC 桌面即可找到該檔案. 

Gemini CLI 學習筆記 : Python 專案開發的標準工作流程

在前一篇測試中, 我們使用 uv 工具建立 Python 專案後, 手動編輯描語境檔 GEMINI.md, 然後啟動 Gemini CLI, 它會自動讀取 GEMINI.md, 確認它所擬定的執行計畫後, 要求它開始撰寫程式. 本篇旨在摘要整理使用 Gemini CLI 開發 Python 專案時的 SOP.

本系列全部測試文章索引參考 :


後續的測試都會使用 uv 工具來建立 Python 專案結構, 關於 uv 參考 :

在 Windows 下使用 Gemini CLI 進行 Vibe Coding 的工作流程如下 :
  1. 生成語境檔 : 使用 Gemini 或其他 AI 生成 GEMINI.md 內容
    提示詞模板 : "我要用 Gemini CLI 開發一個 Python 專案 (使用 uv 工具), 目標是 {}, 技術堆疊是 {}, 請以繁體中文幫我撰寫 GEMINI.md 語境檔." 
  2. 開啟終端機 : 開啟 PowerShell 視窗
  3. 建立地基 : uv init <專案名稱> (建立資料夾與環境)
  4. 進入專案 : cd <專案名稱>
  5. 建立語境檔 :
    • 直接在 PowerShell 輸入 notepad GEMINI.md
    • 系統會問 "找不到檔案, 是否建立?", 選 "是" 
    • 貼上內容後存檔關閉記事本 (notepad 預設存檔格式 UTF-8)
  6. 啟動 AI : 輸入 gemini 進入 Gemini CLI
  7. 載入與確認 (Prompt 優化) :
    輸入 "請讀取當前目錄下的 GEMINI.md, 根據該文件規範, 你了解這個專案的目標與限制了嗎? 請先列出你的實作計畫 (Step-by-step plan), 不需要先寫程式碼"
    (說明:明確指出要讀取的檔案, 並強調 "根據該文件" 以確保 AI 不會忽略它)
  8. 執行專案 : 
    輸入 "計畫沒問題, 請開始執行第一步 (或是 Go ahead)"

如下圖所示 : 




uv init 建立的專案架構中的 pyproject.toml 是給 Python 執行環境看的專案描述檔, 此流程中我們先用 uv init 把地基打好, 然後再用 gemini 把 Gemin CLI 叫進來幹活. 

2026年1月30日 星期五

Gemini CLI 學習筆記 : 郵遞區號查詢小幫手專案 (三)

在前兩篇的查詢 6 碼郵遞區號的測試中, 我們只利用提示詞與 Gemini CLI 對話便能讓其生成程式碼, 透過 API 正確取得給定地址的郵遞區號. 本篇則要改用語境檔 GEMINI.md 來達成, 它是寫程式用的 PRD (專案需求文件檔), 關於語境檔 GEMINI.md 參考 :


以下測試使用 uv 工具來管理專案與 Python 版本, 關於 uv 參考 : 


本系列全部測試文章索引參考 :



首先開啟 PS 視窗, 用 uv init 建立一個名為 postal-helper-proj3 的 Python 專案資料夾 : 

PS D:\gemini> uv init postal-helper-proj3   
Initialized project `postal-helper-proj3` at `D:\gemini\postal-helper-proj3`

切換到專案資料夾檢視 uv 建立的檔案 : 

PS D:\gemini> cd postal-helper-proj3    
PS D:\gemini\postal-helper-proj3> dir   

    目錄: D:\gemini\postal-helper-proj3

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----       2026/1/30  上午 11:13            109 .gitignore
-a----       2026/1/30  上午 11:13              5 .python-version
-a----       2026/1/30  上午 11:13             97 main.py
-a----       2026/1/30  上午 11:13            165 pyproject.toml
-a----       2026/1/30  上午 11:13              0 README.md

然後開啟記事本編輯 GEMINI.md, 輸入如下內容 (可請 Gemini 產生) :

# 專案設定:台灣郵遞區號查詢工具 (Taiwan ZipCode CLI)

## 1. 角色與語氣
- **角色**: Python 腳本開發者 (Python Scripter)。
- **語氣**: 輕鬆、直觀、實用導向。
- **語言**: 繁體中文 (Traditional Chinese)。

## 2. 技術堆疊與工具 (Tech Stack)
- **專案管理**: **uv** (由 Astral 開發的超快 Python 工具)。
  - 安裝套件請用: `uv add <package>`
  - 執行程式請用: `uv run <script.py>`
  - **禁止使用**: 傳統的 `pip install` 或手動建立 `venv` 指令。
- **語言版本**: Python 3.12+
- **關鍵套件**: 
  - `requests` (若需爬蟲或呼叫 API)
  - `beautifulsoup4` (若需解析 HTML)
  - `typer` 或 `argparse` (用於處理 CLI 指令參數)

## 3. 專案目標 (Project Goal)
開發一個簡單的 Command Line Interface (CLI) 工具,功能如下:
1.  **輸入**: 使用者輸入「縣市」+「行政區」 (例如:`高雄市三民區`) 或 「完整地址」。
2.  **處理**: 程式透過網路查詢 (中華郵政/開放資料 API) 或 內建字典檔。
3.  **輸出**: 回傳對應的 **3+3 郵遞區號** (優先) 或 **3 碼地區號**。

## 4. 程式碼規範
- **結構**: 保持簡單,盡量將功能封裝在 `main.py` 模組中。
- **錯誤處理**: 如果使用者輸入的地址不存在或打錯字,請回傳友善的中文提示 (例如:「找不到此地區,請確認輸入格式」),而非 Python Traceback。
- **註解**: 程式碼中請加入簡單的中文註解,說明資料來源或邏輯。

## 5. 互動範例 (Expected Behavior)
使用者在終端機執行:
```bash
uv run main.py "台北市信義區市府路1號"

以 utf-8 編碼存檔後, 在此專案目錄下輸入 gemini 啟動 Gemini CLI : 




可見 Gemini CLI 一啟動就會先讀取 GEMINI.md 的內容作為對話中持續性的系統提示, 我先問它是否經由此語境檔已完全了解專案目標, 並要求它先提供執行計畫再做 :

> 請問你了解此專案了嗎? 請不要開始寫程式,先告訴我你的計劃

它回答如下, 看來並無問題, 輸入 ok, 好的, 或 go ahead 等授權它撰寫程式 : 





程式撰寫完畢要求寫入 main.py :





要求授權用 uv 執行測試 :




測試 "台北市信義區市府路1號" 結果正確, 要求再授權測試 "高雄市三民區" : 




測試成功取回 3 碼郵遞區號, 要求授權刪除虛擬環境中沒用到的套件 : 




完成專案 : 




最後開啟 main.py 將程式碼抄錄如下 : 

import typer
import requests
import json
import sys
from typing import Optional

app = typer.Typer(help="台灣郵遞區號查詢工具 (Taiwan ZipCode CLI)")

# 3碼郵遞區號備援字典 (部分範例,實務上可擴充或改用完整資料檔)
THREE_DIGIT_ZIPCODES = {
    "台北市": {
        "中正區": "100", "大同區": "103", "中山區": "104", "松山區": "105", 
        "大安區": "106", "萬華區": "108", "信義區": "110", "士林區": "111", 
        "北投區": "112", "內湖區": "114", "南港區": "115", "文山區": "116"
    },
    "高雄市": {
        "新興區": "800", "前金區": "801", "苓雅區": "802", "鹽埕區": "803", 
        "鼓山區": "804", "旗津區": "805", "前鎮區": "806", "三民區": "807", 
        "楠梓區": "811", "小港區": "812", "左營區": "813", "仁武區": "814",
        "大社區": "815", "岡山區": "820", "路竹區": "821"
    },
    "台中市": {
        "中區": "400", "東區": "401", "南區": "402", "西區": "403", "北區": "404",
        "北屯區": "406", "西屯區": "407", "南屯區": "408"
    }
}

def query_zip5_api(address: str) -> Optional[str]:
    """
    使用 zip5.5432.tw API 查詢 3+3 郵遞區號
    """
    try:
        # 使用知名的 zip5 服務 (非官方但穩定)
        url = "https://zip5.5432.tw/zip5json.py"
        params = {"adrs": address}
        response = requests.get(url, params=params, timeout=5)
        response.raise_for_status()
        
        # 該 API 回傳格式為 JSON
        data = response.json()
        
        # 檢查是否有回傳 zipcode
        if data and "zipcode" in data and data["zipcode"]:
            return data["zipcode"]
            
    except requests.RequestException:
        # 網路連線錯誤,靜默失敗,交給備援機制
        return None
    except json.JSONDecodeError:
        return None
    
    return None

def lookup_local_3digit(address: str) -> Optional[str]:
    """
    從內建字典查詢 3 碼郵遞區號
    """
    for city, districts in THREE_DIGIT_ZIPCODES.items():
        if city in address:
            for district, code in districts.items():
                if district in address:
                    return code
    return None

@app.command()
def main(
    address: str = typer.Argument(..., help="請輸入完整地址或縣市行政區,例如:'台北市信義區市府路1號' 或 '高雄市三民區'"),
    show_json: bool = typer.Option(False, "--json", "-j", help="以 JSON 格式輸出")
):
    """
    查詢台灣郵遞區號 (優先嘗試 3+3 碼,失敗則回傳 3 碼)
    """
    result_code = None
    source = "未知"

    # 1. 嘗試網路查詢 (3+3)
    print(f"正在查詢 '{address}' 的郵遞區號...", file=sys.stderr)
    result_code = query_zip5_api(address)
    
    if result_code:
        source = "網路查詢 (3+3碼)"
    else:
        # 2. 失敗則嘗試內建字典 (3碼)
        result_code = lookup_local_3digit(address)
        if result_code:
            source = "內建字典 (3碼地區號)"

    # 3. 輸出結果
    if result_code:
        if show_json:
            print(json.dumps({"address": address, "zipcode": result_code, "source": source}, ensure_ascii=False))
        else:
            print(f"--------------------------------")
            print(f"查詢結果: {result_code}")
            print(f"資料來源: {source}")
            print(f"--------------------------------")
    else:
        print(f"❌ 找不到此地區,請確認輸入格式 (例如:縣市 + 行政區 + 路名)。")
        sys.exit(1)

if __name__ == "__main__":
    app()

可見 Gemini CLI 這次採取自己的方式查詢郵遞區號, 如果在 GEMINI.md 中有限制它使用 zip5.5432.tw 的 API 查詢的話, 實作方式應該會跟前面第一篇測試文章的實作結果差不多. 

Gemini CLI 學習筆記索引

我在 2025 年底趁 58 折的聖誕節特惠價購買了 Google AI Pro, 最主要誘因是在使用 Gemini CLI 時可以選擇 Pro 版, 這樣用它來生成程式碼應該會更好. 自元旦四天台北宜蘭之旅回來後便全心投入 Vibe Coding 的測試學習, 我已被明賢表弟成功洗腦 (他極力敦促我使用 Claude Code), 雖然放棄親自手刻程式碼有損工程師尊嚴, 但那並不重要, 人生苦短, 快速完成專案實現創意才是程式員存在的目的, Vibe Coding 是大勢所趨無法阻擋. 

以下是測試文章的索引 :



~ 進行中 ~

Gemini CLI 學習筆記 : 專案語境檔 GEMINI.md

本篇摘要整理最近利用 AI 學習語境檔 GEMINI.md 用法. 

本系列之前的測試文章參考 : 


1. GEMINI.md 的用途 : 

GEMINI.md 是以 markdown 格式撰寫的語境檔 (context file), 它是一個持續性的常駐系統提示 (system prompt), 用來描述專案結構, 程式風格, 任務模板, 限制和偏好, 以及規範模型思考與回應方式等, 以便 AI 在回應時有一致且具體的行為依據. 在 Vibe coding 專案中, GEMINI.md 扮演的是 "憲法" 的角色. 如果把 AI 看成是為了完成專案所聘請的員工, 則語境檔 GEMINI.md 就是在專案對話開始時先給 AI 的一份 "員工手冊", 也可以說是寫給 AI 看的 PRD (專案需求文件檔, Project Requirement Ducument). 

在專案資料夾下啟動 Gemini CLI 時, GEMINI.md 的內容會先被讀取當作系統提示, 連同使用者提示詞一起提交給模型, 並在隨後的每次對話中都持續提交, 這樣就不用在每個提問中重複說明專案背景, 風格規範, 禁忌事項或個人偏好等. 本質上, 與模型對話的每個請求是彼此獨立的, 模型本身不會跨請求保留記憶, 但 Gemini CLI 會在每回合交談中把合併後的 GEMINI.md 內容與對話歷史一併送入模型, 形成持續且一致的上下文, 因而營造出模型能記憶前後對話的感覺. 


2. GEMINI.md 的儲存位置 : 

GEMINI.md 可放在下列位置 :
  • 專案語境檔 : ~/GEMINI.md (根目錄下)
  • 全域語境檔 : ~/.gemini/GEMINI.md
  • 祖先目錄語境檔
  • 子目錄語境檔
Gemini CLI 啟動時會掃描這些位置的 GEMINI.md 並串接所有內容一起送入模型 (同時會遵守 .gitignore 與 .geminiignore). 可以使用斜線指令 /memory show 來檢視合併後的完整上下文, 或使用 /memory refresh 指令強制重新掃描. 


3. GEMINI.md 的項目 : 

GEMINI.md 支援標準 Markdown 語法, 但它不會渲染 Markdown 內容, 只是把文字原樣送入模型而已. 語境檔使用 Markdown (.md) 而非 .json 或 .yaml 格式的原因是, LLM 模型對 Markdown 的理解能力極佳, 能清楚分辨標題, 列表, 與程式碼區塊, 且對人類而言可讀性高, 開發者可以像寫文件一樣撰寫這些規則, 維護起來非常直觀. 

GEMINI.md 的內容通常包含如下項目 : 


 項目  說明
 角色設定  定義 AI 的身份與專業層級 (例如資深架構師), 確立其思考問題的角度與深度.
 語言與語氣  規範回覆的語言與溝通風格 (簡潔/正式/教學式), 專有名詞是否保留英文等.
 技術堆疊  明確列出專案使用的程式語言版本, 核心框架, 資料庫, ORM 及關鍵函式庫.
 程式風格規範  變數命名規則, 縮排, 註解格式 (Docstrings) 及需遵循的標準 (例如 PEP8). 
 專案架構背景  描述專案的核心功能, 資料夾結構, 特定的設計模式.
 測試與品質要求   定義測試框架工具, 單元測試的要求, 錯誤處理機制及程式碼品質指標.
 安全與禁忌  絕對禁止的操作 (如修改設定檔, 洩露金鑰, 刪除資料) 與隱私的處理原則.
 輸出格式  規範 AI 回覆的結構或特定輸出格式 (先解釋再給程式碼).


GEMINI.md 的內容毋須自行撰寫, 可簡單說明專案內容後利用 AI 幫忙撰寫. 例如我之前手刻的 serverless 平台, 若想改用 Gemini CLI 開發, 可先請 Gemini 幫忙寫 GEMINI.md, 專案描述如下 :

請根據下列專案簡述幫我撰寫 GEMINI.md 檔 : 
架構 : 這是一個使用 Flask 寫的輕量級 FaaS 函式執行平台 (類似 GCF). 
技術 : Flask, SQLite (儲存函式內容)
功能 : 使用者可以線上用 CRUD 撰寫函式腳本, 儲存在 serverless.sqlite 資料庫內, 並透過 /function/<func_name> 來觸發執行函式. 
目標 : 利用 Gemini CLI 開發, 佈署在自建的網站或 render.com 平台. 

Gemini 生成的 GEMINI.md 如下 : 

# Project Context & Rules: Flask FaaS Platform

## 1. 角色與語氣 (Persona)
- **角色**: 資深 Python 後端工程師 (專精 Flask 與 System Design)。
- **特質**: 你擅長處理動態語言特性 (Meta-programming),並對 Web 安全 (如 Code Injection 風險) 有高度敏感性。
- **語氣**: 專業、引導式。在處理潛在危險操作 (如 `exec` 或 `subprocess`) 時,需主動提示風險與防護機制。

## 2. 語言與溝通 (Language)
- **主要語言**: 繁體中文 (Traditional Chinese)。
- **技術術語**: 保留英文 (例如:Route, Decorator, Context Manager, Stdout Capture)。

## 3. 技術堆疊 (Tech Stack)
- **Runtime**: Python 3.10+
- **Web Framework**: Flask 3.0+
- **Database**: SQLite (用於儲存 Function Metadata 與程式碼字串)
- **ORM**: SQLAlchemy (或原生 sqlite3,視需求而定,建議使用 SQLAlchemy 以利維護)
- **Execution**: Python `exec()` 或 `subprocess` (視隔離需求而定)
- **Deployment**: Render.com (Gunicorn + Render Web Service)

## 4. 專案背景與架構 (Project Context)
這是一個輕量級的 FaaS (Function as a Service) 平台,核心邏輯如下:
1.  **管理端 (CRUD)**: 使用者透過 Web 介面或 API 建立函式。
    - 資料結構範例: `Function(id, name, code_body, created_at)`。
2.  **執行端 (Trigger)**:
    - 路徑: `POST /function/<func_name>`
    - 流程: 接收請求 -> 從 SQLite 撈取對應的 Python 程式碼 -> **動態執行**該程式碼 -> 捕捉標準輸出 (stdout) 或 Return 值 -> 回傳給 Client。

## 5. 程式風格規範 (Coding Standards)
- **架構分層**:
  - `models.py`: 定義 DB Schema。
  - `executor.py`: **核心模組**。專門負責處理 `exec()` 的安全性、環境變數注入 (`locals/globals`) 以及 `sys.stdout` 的捕捉。不要將執行邏輯直接寫在 View function 內。
  - `app.py` / `routes.py`: 只負責 HTTP 請求處理。
- **錯誤處理**:
  - 當使用者上傳的程式碼語法錯誤 (SyntaxError) 或執行失敗 (RuntimeError) 時,必須捕捉 Exception 並回傳 HTTP 400/500 及**清楚的錯誤訊息**,而非讓整個 Flask Server 當機。
- **變數命名**: `snake_case`。

## 6. 部署注意事項 (Deployment: Render.com)
- **持久化儲存**: 
  - ⚠️ Render 的 Standard Web Service 檔案系統是 Ephemeral (暫時性) 的。
  - **關鍵指示**: 若專案部署至 Render,必須使用 **Render Disk (Persistent Disk)** 來掛載 SQLite 的儲存路徑 (例如 `/var/data/serverless.db`),否則每次重新部署資料都會遺失。
  - 或者提示改用 PostgreSQL (Render 內建支援) 作為生產環境選項。

## 7. 安全與限制 (Security & Constraints)
- **沙箱限制**: 雖然是簡易版,但請盡量限制 `exec()` 的執行範圍 (例如:禁止 `import os` 進行系統操作,或僅允許引用特定 Standard Libraries)。
- **Timeout**: 需考慮加入執行時間限制 (Timeout),避免使用者寫了 `while True:` 導致 Server 卡死。

## 8. 輸出格式 (Output Format)
- **程式碼**: 使用 Markdown Code Block。
- **檔案標註**: 修改或新增檔案時,請在 Code Block 頂端標註 `# path/to/file.py`。
- **解釋優先**: 針對「如何安全地執行使用者程式碼」這部分,請先解釋你的實作策略 (例如:如何重導 stdout),再給出程式碼。

---
*AI 助理備註:此專案涉及高風險的 Remote Code Execution (RCE) 功能,請在開發時持續提醒使用者相關的資安風險。*

然後根據自己的實際需求加以修改即可. 


4. 一般 Python 專案的 GEMINI.md 範本 : 

GEMINI.md 的內容因專案性質而異, 我請 Gemini 寫了兩份專為 Python 開發量身打造的 GEMINI.md 參考範本 : 


(1). 範本一 : 

# Gemini System Instructions - Python Development

## 1. 角色設定 (Persona)
你是一位擁有 10 年以上經驗的資深 Python 架構師。你的專長包括後端開發、自動化腳本撰寫以及資料處理。你的程式碼風格簡潔、高效且易於維護。

## 2. 溝通準則 (Communication)
- **語言**:除非專有名詞或程式碼字串,否則請**嚴格使用繁體中文 (Traditional Chinese)** 回覆。
- **語氣**:專業、直接、客觀。
- **格式**:
  - 解釋概念時,請使用列點 (Bullet points) 以利閱讀。
  - 程式碼區塊上方請簡述這段程式碼的目的。
  - 若有引用套件,請在回答最後列出 `pip install` 指令。

## 3. Python 程式碼規範 (Coding Standards)
請在產生程式碼時遵守以下規則:

### A. 現代化語法 (Modern Python)
- **版本**:預設使用 Python 3.10+ 語法。
- **型別提示 (Type Hinting)**:所有函式定義**必須**包含型別提示 (Type Hints)。
  - *Good*: `def process_data(items: list[str]) -> dict[str, int]:`
  - *Bad*: `def process_data(items):`
- **字串格式化**:優先使用 **f-strings**,避免使用 `%` 或 `.format()`。
- **路徑處理**:優先使用 `pathlib` 模組,避免使用 `os.path`。

### B. 程式碼品質 (Code Quality)
- **錯誤處理**:對於 I/O 操作、API 請求或資料轉換,必須包含 `try-except` 區塊,並捕捉具體的 Exception (如 `FileNotFoundError` 而非 bare `Exception`)。
- **文件字串 (Docstrings)**:函式應包含 Google Style Docstrings,說明參數 (Args) 與回傳值 (Returns)。
- **變數命名**:嚴格遵守 PEP 8 (變數與函式用 `snake_case`,類別用 `PascalCase`)。

### C. 範例結構 (Example Structure)
提供的 Python 腳本必須是**可執行**的完整範例:
1. 包含必要的 `import`。
2. 使用 `if __name__ == "__main__":` 區塊作為程式入口。
3. 若程式碼較長,請拆分為小的 Helper Functions。

## 4. 專案上下文 (Context) - *[可選:依專案修改]*
*(在此處填寫您目前專案的特殊需求,例如:)*
- 本專案使用 **FastAPI** 作為 Web 框架。
- 資料庫使用 **SQLAlchemy (AsyncIO)**。
- 測試框架請使用 **pytest**。

---
**請記住:你的目標是協助我寫出 Production-Ready 的高品質程式碼。**


(2). 範本二 : 

# 專案語境設定 (Context Profile)

## 1. 角色與語氣 (Persona & Tone)
- **角色**: 你是一位擁有 10 年以上經驗的資深 Python 後端工程師 (Senior Python Backend Engineer)。
- **特質**: 你的程式碼簡潔、高效、且極度重視可讀性與維護性。你擅長架構設計,並能主動指出潛在的效能瓶頸或安全漏洞。
- **語氣**: 專業、客觀、直接。解釋技術概念時請使用清晰的邏輯。

## 2. 語言偏好 (Language Preferences)
- **主要語言**: 繁體中文 (Traditional Chinese)。
- **技術術語**: 保留英文原文 (例如:Middleware, Decorator, Type Hint),不需強制翻譯。
- **程式碼註解**: 使用繁體中文。

## 3. 程式風格規範 (Coding Style & Standards)
- **Python 版本**: 優先使用 Python 3.10+ 語法 (例如:Pattern Matching `match/case`, Union Types `X | Y`)。
- **排版規範**: 嚴格遵循 **PEP 8**。
- **命名慣例**:
  - 變數與函式: `snake_case`
  - 類別 (Class): `PascalCase`
  - 常數: `UPPER_CASE`
  - 私有成員: `_leading_underscore`
- **型別提示 (Type Hints)**: 所有函式參數與回傳值**必須**加上 Type Hints。
- **文件字串 (Docstrings)**: 遵循 **Google Style** 格式。
- **錯誤處理**: 使用 `try-except` 明確捕捉特定 Exception,禁止使用裸露的 `except:`。
- **原則**: 遵循 SOLID 原則與 DRY (Don't Repeat Yourself)。

## 4. 專案背景與技術堆疊 (Project Context & Tech Stack)
> *[請根據實際專案修改此區塊]*
- **框架**: FastAPI (或 Django / Flask)
- **資料庫**: PostgreSQL
- **ORM**: SQLAlchemy 2.0+ (Async mode) / Pydantic V2
- **測試工具**: Pytest
- **套件管理**: Poetry / Pipenv
- **架構模式**: Clean Architecture / MVC

## 5. 安全與禁忌 (Security & Constraints)
- **敏感資料**: **絕對禁止**在程式碼中硬編碼 (Hard-code) 密碼、API Keys 或 Token。請一律使用環境變數 (`.env`)。
- **檔案修改**: 若非必要,請勿修改設定檔 (如 `pyproject.toml`, `Dockerfile`)。
- **破壞性操作**: 禁止建議執行 `rm -rf` 或刪除資料庫的指令,除非使用者明確要求。

## 6. 輸出格式 (Output Format)
- **程式碼**: 必須包含在 markdown code block 中,並指定語言 (例如 ```python)。
- **解釋**: 先提供程式碼,後附上簡短解釋 (除非邏輯非常複雜)。
- **檔案路徑**: 修改檔案時,請在 code block 頂端註明檔案路徑 (例如: `# app/routers/items.py`)。

---
*此檔案由專案負責人維護,AI 助理應優先遵循上述規則。*

2026年1月29日 星期四

蝦皮購買 ESP32-S3 CAM 開發板 3 片

最近打算用 Vibe coding 方式來加速物聯網專案開發速度, 上蝦皮找到這個賣家, 它販售的 ESP32-S3 CAM 開發板有搭配 OV2640 與 OV5640 兩種鏡頭, 前者為定焦鏡頭, 但影片流暢度較佳, 適合即時影像傳輸; 後者為自動對焦, 主打畫質清晰, 兩款均為 N16R8, 8MB PSRAM 讓 ESP32-S3 能夠完美驅動 OV5640 的高畫素拍照功能, 解決舊款 ESP32 記憶體不足的問題 :






OV2640 買兩片, OV5640 的買一片 :




含關稅 58 元免運 852 元, 明華智取店取付. 

也有已焊針腳的 :


這款 Goouuu 的板子似乎品質評價較好, 參考 :


2026年1月28日 星期三

2026年1月27日 星期二

三星 Galaxy Tab 螢幕截圖方法

水某研究計畫結束後留下一台三星 Tab A7 Lite 平板, 之前常用來看電子書, 前陣子因為買 BOOX 閱讀器得知 HyRead 可借閱圖書館的電子書與雜誌, 於是也在平板上安裝, 看雜誌時為了做筆記需截圖, 但每次要同按電源+音量小鍵很麻煩, 上谷歌找到下面這篇 :


其中的 "三星手機截圖方法三、輔助快選截圖" : 

前往「設定」→「協助工具」→「互動與敏銳度」→開啟「輔助快選」功能

完成後桌面會出現一個圓形四小孔的常駐浮動按鈕, 按一下就會出現好幾個選項, 按其中的 "螢幕截圖" 就可以把圖抓下來, 再丟到 Dropbox 即可. 

PS :

這支平板記憶體為 3GB/32GB, 寬度比 iPd mini  小一些, 剛好一手可掌握, 握持手感最佳, 左上角有 SIM 卡槽, 插卡即可上網撥電話, 規格參考 :


蝦皮購買手機平板架x5 + 迷你掛錶x5

最近把水某給我的 iPad Mini 5 與三星 Tab A7 Lite 都找出來用, 主要是安裝 HyRead 後可從市圖借閱電子書與雜誌, 抵厚重的實體書好幾倍. 今天在看雜誌時發現應該買個桌上的平板支架, 這樣就可避免手痠了, 蝦皮一個才 24 元, 為了免運加買了掛錶 : 




裕誠智取店取付 230 元免運. 

Windows 安裝米家 App

鄉下老家目前裡裡外外裝了七支攝影機, 平常都用手機的米家 App 關注爸的安全動態, 今天上網搜尋是否有米家 App 的電腦版, 在 Mobile1 找到這篇 :


原來在小米官網就有提供下載網址 : 





但按右上角的 Download 會得到 404, 需往下拉到步驟說明 3 底下的下載按鈕才行 : 




解壓縮下載的 zip 檔會得到一個 XiaomiCameraViewerInstaller.exe 安裝檔, 點擊執行時把預設的簡體中文改為英文 : 




然後一直按 Next 預設安裝下去即可 : 






安裝完畢執行軟體會先要求登入小米帳號, 我是用手機號碼搭配 ak 密碼登入, 會發一個 email 到谷歌信箱, 輸入驗證碼後就會看到攝影機影像了, 可以選擇 4/6/9 分割顯示畫面 :




與手機米家 App 比起來亮度較暗, 且大門口兩支 CW300 攝影機未入列, Mobile1 底下有人提到似乎 1080p 以上的都讀不到, CW300 是 2.5K 超高解析度 (2560x1440), 小米攝影機 2k 標準版都可以讀到. 

按每支攝影機右下角的照相按鈕可以手動拍照, 預設會將 jpg 檔存在桌面, 可以按右上角的帳號頭像點選 Mahage File 更改儲存位置 (例如建一個 D:\XiaomiCamera 來存圖片) : 





婷婷表妹上周為了處理小舅媽攬的外務 (幫市場阿桑解決手機監控攤位問題) 要買一支小米攝影機, 我順便叫她幫我買兩支 C201, 下標後阿桑說不用了, 我叫婷婷三支都給我 (1597 元), 本周來安裝在車庫, 祖堂, 與庫房. 

2026年1月26日 星期一

2026 年第 4 周記事

轉眼 2026 已走過近一個月, 再過三周就是春節啦! 周日下午有一位在附近田裡工作的伯母來問今年要不要年糕, 每年爸似乎都有跟她訂, 我說那就繼續好了. 

周六姊姊回高雄要跟同學商談下周去沖繩旅行的行程, 二哥則是 1/26 與 1/27 要去成大上 IC 設計課程, 因姐姐有王品禮券, 訂了周六晚上七點中正路王品聚餐, 但她出發時忘記把禮券帶回來, 上了高鐵才發現, 菁菁只好改定至聖路上的知名披薩店波士多 (很多外國人) : 





這個周末因為在推進 Gemini CLI 的學習, 所以老家的整理工作停滯, 但至少把浴室外牆掉落的小太陽能板重新固定 (難怪最近浴室感應燈不亮) 並且將螺絲等上油防鏽. 另外也清理了豬舍前的監控木箱與廁所上方被胡瓜頁遮住的 10W 太陽能板, 等我 Gemini CLI 上手, 用 Vibe coding 流程來完成 IoT 專案. 

周日又去了一趟種子行, 想買九層塔與朝天椒苗, 老闆說九層塔缺貨, 只買了四株朝天椒, 一株種鄉下, 三株帶來高雄種陽台. 原本在菜園到處長的草莓最近快絕跡了, 我打算在曬衣場的牆邊搭角鐵架用盆栽來種 (自動灑水), 這樣就不怕蝸牛來吃. 

下周待辦事項 :
  • 豬舍廁所馬桶蓋換新
  • 修理餐廳後面廁所漏水問題
  • 車庫採光板安裝
  • 小蟻攝影機重設
總之, 要趁年前三周大清理才行. 

2026年1月25日 星期日

蝦皮購買插卡手錶 DW100 Pro+ (黑色 3GB + 32GB)

這兩天做了一些插卡手錶市調, 最終選擇蝦皮賣家數碼盟主 (cutefoodiexinwei) 販售的這支 DW100pro+ 3GB/32GB 黑色款, 主要是透過與賣家溝通, 確定此錶為外貿版 (即國際版), 有內建 GPS 晶片與完整 Google 軟體, 電池為市場主流的 800 mAh, 雖不是大電池款, 但因為是要給爸方便聯絡與 Google 地圖定位追蹤之用, 主要是接電話, 應該不需要每天充電. 賣家做活動有送五項品至今天下午五點 : 






扣掉蝦皮折價券 20 元實付 2668 元, 裕誠至取店取付. 下周去遠傳門市買一張月付 149 元的兒童用 SIM 卡給爸使用. 



2026-02-04  補充 :

2/1 到貨, 2/2 周一晚上尾牙結束去裕誠智取店取貨, 昨天下班後去自由路服務中心申辦月租 199 元新門號 (每月 2GB 網路流量, 網內 20 分鐘, 網外 + 市話 20 分鐘免費), 因我名下已有兩支, 承辦員楊小姐建議用爸名字申辦還可享有 50 元優惠變成月租 149 元. 


2026年1月24日 星期六

Gemini CLI 學習筆記 : 郵遞區號查詢小幫手專案 (二)

在前一篇測試中, 我們利用提示詞讓 Gemini CLI 生成程式碼, 正確地透過 API 取得給定地址的郵遞區號, 但是生成的程式是手動另開 DOS 或 PS 視窗執行的, 其實也可以叫 Gemini CLI 幫我們測試, 它會幫我們尋找本機的 Python 環境來執行, 本篇旨在接續先前靠一張嘴寫程式的方法, 叫 AI 測試它自己寫的程式. 

本系列之前的測試文章參考 : 


PS D:\gemini> cd postal-helper-proj1   
PS D:\gemini\postal-helper-proj1> gemini   

然後輸入 /res 或 /resume 恢復之前的對話, Gemini CLI 會找出之前的 session 列表, 可以上下移動選擇要恢復的對話後按 Enter (此處只有編號 1 的對話) :




輸入 : 

> 請幫我測試程式是否正確

Gemini CLI 這次找了一個台中的地址, 執行結果為找不到, 所以它寫了一個 test_api.py 的測試程式, 要求儲存程式檔 :




接著要求執行測試程式 test_api.py : 




測試程式執行時出現錯誤, 它會自行檢查原因與修正, 它發現需修改 postal-helper.py 原始碼, 要求授權 : 




接著修正測試程式 test_api.py : 




執行結果程式已正常, 但查詢台灣大道 99 號結果仍然找不到, 可能是地址有改, 它打算換別的地址測試來測試 : 




改用中港路二段99號就順利傳回郵遞區號了 :




以上的 Gemini CLI 測試中都是使用系統 Python 來執行程式, 如果 Gemini CLI 找不到 Python 執行環境就會出現錯誤. 目前最新的 Python 版本管理工具是 uv 工具, 它整合了 Python 版本, 套件安裝, 以及虛擬環境管理於一體, 使用上比傳統 pip + venv 方便許多, 參考 :


接下來輸入 "我使用 uv 工具" 提示詞看看 Gemini CLI 如何利用 uv 來執行測試, 首先它會初始化 uv 專案, 建立專案描述檔 pyproject.toml :




接著它會編輯 pyproject.toml, 加入腳本定義, 以便能用 uv run 來執行腳本 :




接下來它要求授權修改 postal-helper.py 腳本 : 




程式已改好, 要求授權用 uv run 執行腳本, 傳入地址參數為台北市信義區市府路 1 號 : 





uv run 指令執行成功, 取得正確 6 碼郵遞區號 110204 : 




最後, 我把 Gemini CLI 修改過的 postal-helper.py 抄錄如下 :

import sys
import json
import urllib.parse
import urllib.request
import io

# 確保在 Windows 上輸出為 UTF-8
if sys.platform == "win32" and isinstance(sys.stdout, io.TextIOWrapper) and sys.stdout.encoding != 'utf-8':
    try:
        sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
        sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
    except (AttributeError, io.UnsupportedOperation):
        pass

def get_postal_code(address):
    base_url = "https://zip5.5432.tw/zip5json.py"
    params = {'adrs': address}
    # Ensure the URL is correctly encoded
    url = f"{base_url}?{urllib.parse.urlencode(params)}"
    
    try:
        # User requested 6-digit postal code
        print(f"正在查詢地址: {address} ...")
        
        req = urllib.request.Request(url)
        # Some APIs require a User-Agent
        req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) PostalHelper/1.0')
        
        with urllib.request.urlopen(req) as response:
            if response.status != 200:
                print(f"錯誤: API 返回狀態碼 {response.status}")
                return

            data = response.read()
            try:
                result = json.loads(data)
            except json.JSONDecodeError:
                print("錯誤: 無法解析 JSON 回應。")
                print(f"回應內容: {data.decode('utf-8', errors='ignore')}")
                return
            
            # The API returns 'zipcode6' for the 6-digit code.
            # Sometimes it might return an empty string or null if not found.
            zip6 = result.get('zipcode6')
            
            if zip6:
                print("-" * 30)
                print(f"地址: {result.get('address', address)}")
                print(f"6碼郵遞區號: {zip6}")
                print("-" * 30)
            else:
                print("找不到該地址的 6 碼郵遞區號。")
                # Show full result just in case useful info is elsewhere
                if 'error' in result:
                    print(f"API 錯誤: {result['error']}")

    except urllib.error.URLError as e:
        print(f"網絡錯誤: {e}")
    except Exception as e:
        print(f"發生未預期的錯誤: {e}")

def main():
    if len(sys.argv) > 1:
        # Join arguments to handle addresses with spaces if not quoted
        address_input = " ".join(sys.argv[1:])
        get_postal_code(address_input)
    else:
        # Interactive mode
        print("台灣 6 碼郵遞區號小幫手 (uv 版)")
        print("用法: uv run postal-helper <地址>")
        print("或在下方輸入地址 (按 Ctrl+C 離開):")
        while True:
            try:
                address_input = input("\n請輸入地址: ").strip()
                if not address_input:
                    continue
                get_postal_code(address_input)
            except KeyboardInterrupt:
                print("\n程式結束。")
                break
            except EOFError:
                break

if __name__ == "__main__":
    main()

Python 學習筆記 : 整合型 Python 版本管理工具 uv

上周胡嘉璽老師的 vibe coding 課堂介紹了uv, 這是比 venv + pip 更好用的新一代整合型 Python 套件與專案管理工具, 使用 Rust 開發故執行速度快. 因為後續 Gemini CLI 與 Claude Code 的學習測試會用到, 所以今天得空來安裝看看. 

uv 的主要功能如下:
  • 可輕鬆且快速地安裝不同的 Python 版本, 並能在這些版本之間進行切換. 
  • 可建立和管理虛擬環境, 以利在不同的專案中使用不同的 Python 版本和套件依賴. 
  • 簡單直觀的命令行介面方便執行版本管理和虛擬環境操作.
  • 可在 Windows, macOS 和 Linux 等系統上跨平台運行.
  • 可在 CI/CD 環境中與其他工具搭配支援自動化工作流程. 
參考官網與 GitHub 說明 : 


網路教學文章參考 : 



1. 安裝 uv :

在 Windows 安裝 uv 的第一個方法是開啟 Power Shell 視窗, 輸入下列指令安裝 :

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

如果已經安裝過軟體管理工具 scoop, 可以用 scoop install uv 來安裝 :

PS C:\Users\tony1> scoop install uv   
Updating Scoop...
Updating Buckets...
... (略) ...
Scoop was updated successfully!
Installing 'uv' (0.9.26) [64bit] from 'main' bucket
uv-x86_64-pc-windows-msvc.zip (21.0 MB) [=====================================================================] 100%
Checking hash of uv-x86_64-pc-windows-msvc.zip ... ok.
Extracting uv-x86_64-pc-windows-msvc.zip ... done.
Linking ~\scoop\apps\uv\current => ~\scoop\apps\uv\0.9.26
Creating shim for 'uv'.
Creating shim for 'uvx'.
Creating shim for 'uvw'.
Making C:\Users\tony1\scoop\shims\uvw.exe a GUI binary.
'uv' (0.9.26) was installed successfully!

安裝完可以用 uv --version 檢查版本 : 

PS C:\Users\tony1> uv --version   
uv 0.9.26 (ee4f00362 2026-01-15)

關於 scoop 安裝參考 :



2. 常用的 uv 指令 :

uv 是一個整合 Python 版本 + 套件 + 虛擬環境管理的工具, 常用指令如下表 : 


 常用 uv 指令  說明
 uv python list   列出本機已安裝及可供 uv 下載安裝的 Python 版本。
 uv python install <version>  下載並安裝指定的 Python 版本 (例如:uv python install 3.12)。
 uv python uninstall <version>  解除安裝指定的 Python 版本 (例如:uv python uninstall 3.12)。
 uv python find <version>  顯示指定的 Python 版本執行檔 python.exe 之安裝路徑。
 uv python dir  顯示 uv 用來存放所有下載安裝之 Python 版本的根目錄。
 uv python pin <version>  鎖定當前專案所使用的 Python 版本。
 uv init  初始化一個新的 Python 專案 (建立 pyproject.toml)。
 uv add <package>  新增套件依賴,並自動更新 lock 檔與環境。
 uv remove <package>  移除套件依賴。
 uv sync  根據 uv.lock 同步專案環境 (安裝/移除套件)。
 uv run <command>  在專案的虛擬環境中執行指令或腳本。
 uv venv  建立一個新的虛擬環境 (預設為 .venv)。
 uv pip install <package>  類似標準 pip install,用於低階套件安裝 (不建議使用)。
 uv tool install <tool>  安裝全域使用的 Python 工具 (如 ruff, black)。
 uv tree  顯示專案依賴關係的樹狀結構圖。
 uv self update  將 uv 更新至最新版本。


注意, 在 uv 專案中不建議使用 uv pip install 來安裝套件, 雖然不會汙染系統 Python (只要不加 --system 參數就很安全), 但不會更新 pyproject.toml, 會使專案依賴清單與實際環境不符. 


3. Python 版本管理 : 

uv python list 會列出目前本機已安裝以及網路上可下載安裝的 Python 版本 :

PS C:\Users\tony1> uv python list  
cpython-3.15.0a5-windows-x86_64-none                 <download available>
cpython-3.15.0a5+freethreaded-windows-x86_64-none    <download available>
cpython-3.14.2-windows-x86_64-none                   <download available>
cpython-3.14.2+freethreaded-windows-x86_64-none      <download available>
cpython-3.13.11-windows-x86_64-none                  <download available>
cpython-3.13.11+freethreaded-windows-x86_64-none     <download available>
cpython-3.12.12-windows-x86_64-none                  <download available>
cpython-3.12.1-windows-x86_64-none                   AppData\Local\Programs\Python\Python312\python.exe
cpython-3.11.14-windows-x86_64-none                  <download available>
cpython-3.10.19-windows-x86_64-none                  <download available>
cpython-3.9.25-windows-x86_64-none                   <download available>
cpython-3.8.20-windows-x86_64-none                   <download available>
pypy-3.11.13-windows-x86_64-none                     <download available>
pypy-3.10.16-windows-x86_64-none                     <download available>
pypy-3.9.19-windows-x86_64-none                      <download available>
pypy-3.8.16-windows-x86_64-none                      <download available>
graalpy-3.12.0-windows-x86_64-none                   <download available>
graalpy-3.11.0-windows-x86_64-none                   <download available>
graalpy-3.10.0-windows-x86_64-none                   <download available>

其中黃底部分即我的 LG Gram 筆電的系統 Python 3.12.1 環境, 其餘為可用 uv python install 下載安裝的版本. 可以在後面用 --only-installed 參數限制僅顯示已安裝的 Python 版本 : 

PS C:\Users\tony1> uv python list --only-installed   
cpython-3.12.1-windows-x86_64-none    AppData\Local\Programs\Python\Python312\python.exe

注意, 使用 uv python install 安裝指定版本的 Python 環境時, uv 會將此 Python 安裝到一個獨立的隔離目錄 (通常在 %LOCALAPPDATA%\uv\python\...), 而不是安裝到 Windows 的系統路徑中, uv 也不會把這個路徑加到系統 PATH 裡. 其次, 可以同時安裝多個 Python 版本, 例如 :

uv python install 3.13 3.10

解除安裝也是可以同時解除安裝多個 Python 版本. 


4. 用 uv run 執行 Python 腳本 : 

先在 PS 中用 notepad 指令啟動記事本, 編輯一個 show_version.py 腳本程式 :

PS C:\Users\tony1> notepad show_version.py   

輸入如下程式碼後存檔 : 

import sys
print(sys.version)

用 uv 安裝的 Python 版本其執行檔位置可用 uv python find 指令找到 :

PS C:\Users\tony1> uv python find 3.12  
C:\Users\tony1\AppData\Local\Programs\Python\Python312\python.exe

然後用此 python.exe 執行腳本 :

PS C:\Users\tony1> C:\Users\tony1\AppData\Local\Programs\Python\Python312\python.exe show_version.py   
3.12.1 (tags/v3.12.1:2305ca5, Dec  7 2023, 22:03:25) [MSC v.1937 64 bit (AMD64)]

但不需要這麼麻煩, 用 uv run 指令即可執行 Python 程式 :

PS C:\Users\tony1> uv run show_version.py   
3.12.1 (tags/v3.12.1:2305ca5, Dec  7 2023, 22:03:25) [MSC v.1937 64 bit (AMD64)]

因為我的筆電目前只有安裝 Python 3.12.1 而已, 所以 uv 會用系統 Python 來執行腳本. 如果用 uv 安裝多個 Python 版本, 則可在 uv run 後面以 --python 參數指定版本, 例如 : 

uv run --python 3.12 show_version.py

PS C:\Users\tony1> uv run --python 3.12 show_version.py   
3.12.1 (tags/v3.12.1:2305ca5, Dec  7 2023, 22:03:25) [MSC v.1937 64 bit (AMD64)]


5. 用 uv init 建立 Python 專案 : 

uv 為了符合 Vibe Coding 的效率提供了兩種建立專案的方式, 第一種是用 mkdir 手動建立專案目錄後進入該目錄, 再用 uv init 指令建立專案檔案 :

mkdir <project_dir>
cd <project_dir>
uv init

例如 :

PS C:\Users\tony1> mkdir my_project

    目錄: C:\Users\tony1

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----       2026/1/24  上午 09:51                my_project

PS C:\Users\tony1> cd my_project   
PS C:\Users\tony1\my_project> uv init   
Initialized project `my-project`
PS C:\Users\tony1\my_project> dir  

    目錄: C:\Users\tony1\my_project

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----       2026/1/24  上午 09:51            109 .gitignore
-a----       2026/1/24  上午 09:51              5 .python-version
-a----       2026/1/24  上午 09:51             88 main.py
-a----       2026/1/24  上午 09:51            156 pyproject.toml
-a----       2026/1/24  上午 09:51              0 README.md

可見 uv init 會在專案資料夾下自動建立下面五個專案檔案 : 
  • pyproject.toml : 專案的核心設定檔 (紀錄依賴套件與專案資訊)
  • .python-version : 紀錄這個專案使用的 Python 版本
  • main.py : 專案主程式範例
  • .gitignore : Git 的忽略清單 (預設會把虛擬環境與快取忽略掉)
  • README.md : 專案的讀我說明文件
主程式 main.py 預設是一個顯示歡迎文字的簡單範例 :

PS C:\Users\tony1\my_project> cat main.py   
def main():
    print("Hello from my-project!")


if __name__ == "__main__":
    main()

可以用 uv run 執行看看 : 

PS C:\Users\tony1\my_project> uv run main.py   
Using CPython 3.12.1 interpreter at: C:\Users\tony1\AppData\Local\Programs\Python\Python312\python.exe
Creating virtual environment at: .venv
Hello from my-project!

也可以用 uv init 讓 uv 自動幫我們建立目錄與專案檔案 :

uv init <project_dir> 

或指定要用的 Python 版本 : 

uv init <project_dir> --python <version>

先用 rm 指令刪除上面手動建立的專案目錄 :

PS C:\Users\tony1> rm my_project -Recurse -Force  

用 uv init 建立專案目錄與檔案 : 

PS C:\Users\tony1> uv init my_project   
Initialized project `my-project` at `C:\Users\tony1\my_project`   
PS C:\Users\tony1> cd my_project  
PS C:\Users\tony1\my_project> dir   

    目錄: C:\Users\tony1\my_project

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----       2026/1/24  上午 10:06            109 .gitignore
-a----       2026/1/24  上午 10:06              5 .python-version
-a----       2026/1/24  上午 10:06             88 main.py
-a----       2026/1/24  上午 10:06            156 pyproject.toml
-a----       2026/1/24  上午 10:06              0 README.md

可見結果一樣. 

如果用 uv init 指令指定 Python 版本建立一個專案, 例如 :

uv init --python 3.13 

那麼在此專案目錄下可以用 uv run xxx.py 執行腳本, 這時就會用此專案的 Python 版本來執行腳本程式. 如果用 uv 安裝了多個版本, 則使用 uv run 指令時 uv 會按照下列順序由上而下依序尋找 python.exe 執行檔 :
  • 專案目錄下 .python-version 檔內設定的 Python 版本
  • 目前啟用的虛擬環境的 Python 版本
  • 專案目錄下 .venv 資料夾內設定的虛擬環境 Python 版本
  • uv 自己安裝的 Python 版本
  • 系統環境變數設定的 Python 版本
可見 .python-version 檔紀錄的 Python 版本是最優先的. 

PS C:\Users\tony1\my_project> cat .python-version      
3.12


6. 用 uv add 安裝套件 :   

先用 uv tree 來檢視專案目錄下的套件依賴關係 :

PS C:\Users\tony1\my_project> uv tree   
Using CPython 3.12.1 interpreter at: C:\Users\tony1\AppData\Local\Programs\Python\Python312\python.exe
Resolved 1 package in 5ms
my-project v0.1.0

由於目前還沒有在專案中安裝套件, 專案內還沒有虛擬環境 (.venv 子目錄不存在), 所以直接讀取了全域安裝 (System Global) 的系統 Python 3.12. 

在 uv 建立的 Python 專案下, 要使用 uv add 來安裝套件, uv 會自動建立一個虛擬環境子目錄 .venv 來放套件與其依賴套件, 例如 :

PS C:\Users\tony1\my_project> uv add requests   
Using CPython 3.12.1 interpreter at: C:\Users\tony1\AppData\Local\Programs\Python\Python312\python.exe
Creating virtual environment at: .venv
Resolved 6 packages in 668ms
Prepared 5 packages in 414ms
Installed 5 packages in 128ms
 + certifi==2026.1.4
 + charset-normalizer==3.4.4
 + idna==3.11
 + requests==2.32.5
 + urllib3==2.6.3

這時再用 uv tree 指令就會顯示此專案下有安裝了那些第三方套件與其依賴套件 : 

PS C:\Users\tony1\my_project> uv tree  
Resolved 6 packages in 2ms
my-project v0.1.0
└── requests v2.32.5
    ├── certifi v2026.1.4
    ├── charset-normalizer v3.4.4
    ├── idna v3.11
    └── urllib3 v2.6.3

可見 requests 的四個依賴套件都掛在它下面, 這比傳統 pip list 一古腦兒列出全部套件還棒. 接下來再安裝 yfinance 與 kbar 套件, 測試一下繪製 K 線圖功能 :

PS C:\Users\tony1\my_project> uv add yfinance   
Resolved 25 packages in 1.48s
      Built multitasking==0.0.12
Prepared 19 packages in 11.09s
Installed 19 packages in 775ms
 + beautifulsoup4==4.14.3
 + cffi==2.0.0
 + curl-cffi==0.13.0
 + frozendict==2.4.7
 + multitasking==0.0.12
 + numpy==2.4.1
 + pandas==3.0.0
 + peewee==3.19.0
 + platformdirs==4.5.1
 + protobuf==6.33.4
 + pycparser==3.0
 + python-dateutil==2.9.0.post0
 + pytz==2025.2
 + six==1.17.0
 + soupsieve==2.8.3
 + typing-extensions==4.15.0
 + tzdata==2025.3
 + websockets==16.0
 + yfinance==1.0

PS C:\Users\tony1\my_project> uv add kbar   
Resolved 35 packages in 2.10s
Prepared 11 packages in 6.29s
Uninstalled 1 package in 134ms
Installed 11 packages in 581ms
 + contourpy==1.3.3
 + cycler==0.12.1
 + fonttools==4.61.1
 + kbar==0.1.5
 + kiwisolver==1.4.9
 + matplotlib==3.10.8
 + mplfinance==0.12.10b0
 + packaging==26.0
 - pandas==3.0.0
 + pandas==2.3.3
 + pillow==12.1.0
 + pyparsing==3.3.2

再次用 uv tree 顯示已安裝套件 : 

PS C:\Users\tony1\my_project> uv tree
Resolved 35 packages in 2ms
my-project v0.1.0
├── kbar v0.1.5
│   ├── matplotlib v3.10.8
│   │   ├── contourpy v1.3.3
│   │   │   └── numpy v2.4.1
│   │   ├── cycler v0.12.1
│   │   ├── fonttools v4.61.1
│   │   ├── kiwisolver v1.4.9
│   │   ├── numpy v2.4.1
│   │   ├── packaging v26.0
│   │   ├── pillow v12.1.0
│   │   ├── pyparsing v3.3.2
│   │   └── python-dateutil v2.9.0.post0
│   │       └── six v1.17.0
│   ├── mplfinance v0.12.10b0
│   │   ├── matplotlib v3.10.8 (*)
│   │   └── pandas v2.3.3
│   │       ├── numpy v2.4.1
│   │       ├── python-dateutil v2.9.0.post0 (*)
│   │       ├── pytz v2025.2
│   │       └── tzdata v2025.3
│   ├── numpy v2.4.1
│   └── pandas v2.3.3 (*)
├── requests v2.32.5
│   ├── certifi v2026.1.4
│   ├── charset-normalizer v3.4.4
│   ├── idna v3.11
│   └── urllib3 v2.6.3
└── yfinance v1.0
    ├── beautifulsoup4 v4.14.3
    │   ├── soupsieve v2.8.3
    │   └── typing-extensions v4.15.0
    ├── curl-cffi v0.13.0
    │   ├── certifi v2026.1.4
    │   └── cffi v2.0.0
    │       └── pycparser v3.0
    ├── frozendict v2.4.7
    ├── multitasking v0.0.12
    ├── numpy v2.4.1
    ├── pandas v2.3.3 (*)
    ├── peewee v3.19.0
    ├── platformdirs v4.5.1
    ├── protobuf v6.33.4
    ├── pytz v2025.2
    ├── requests v2.32.5 (*)
    └── websockets v16.0
(*) Package tree already displayed

執行記事本程式編輯 main.py : 

PS C:\Users\tony1\my_project> notepad main.py   

將 main.py 改成如下 : 

from kbar import KBar   
import yfinance as yf   

def main():
    df=yf.download('0050.TW', start='2024-07-01', end='2024-08-21', auto_adjust=False)    
    df.columns=df.columns.map(lambda x: x[0])    # 改成舊版單層索引
    kb=KBar(df)    # 未傳 font 參數預設使用正黑體
    kb.plot(title='台灣五十(0050.TW)', volume=True)

if __name__ == "__main__":
    main()

用 uv run 執行 main.py :

PS C:\Users\tony1\my_project> uv run main.py  
[*********************100%***********************]  1 of 1 completed
設定字型為: Microsoft JhengHei
使用指定字型: Microsoft JhengHei
字型候選清單: ['Microsoft JhengHei', 'DejaVu Sans', 'Arial']




可見順利繪製了 K 線圖. 


7. 建立 uv 專案的標準流程 :   

最後將 uv 建立 Python 專案的流程摘要整理如下 :


(1). 建立並初始化專案 : 

直接指定專案名稱與 Python 版本一步到位 : 

# 建立一個名為 my_project 的專案, 並指定使用 Python 3.12 

uv init my_project --python 3.12

# 進入專案目錄 

cd my_project


(2). 安裝套件 (關鍵步驟) : 

如果還沒有虛擬環境會自動建立 .venv 子目錄, 然後下載並安裝套件到虛擬環境中 (可以一次安裝多個套件), 最後將套件依賴寫入 pyproject.toml 方便以後管理 :

uv add requests yfinace kbar


(3). 執行程式 : 

不需要先啟動虛擬環境直接用 uv run, 它會自己去用專案裡的環境跑 (假設主程式為 main.py) :

uv run main.py


(4). 同步環境 (可選) : 

如果是從 Github 下載別人的 uv 專案或者換了電腦, 需要執行 uv sync 指令, 它會根據鎖定檔 (uv.lock) 把整個虛擬環境還原回來 : 

uv sync

最後來看一下 .venv 子目錄內容 :

PS C:\Users\tony1\my_project> dir .venv

    目錄: C:\Users\tony1\my_project\.venv

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----       2026/1/24  上午 11:22                Lib 
d-----       2026/1/24  下午 12:36                Scripts  
d-----       2026/1/24  下午 12:36                share
-a----       2026/1/24  上午 11:22              1 .gitignore
-a----       2026/1/24  上午 11:22              0 .lock
-a----       2026/1/24  上午 11:22             43 CACHEDIR.TAG
-a----       2026/1/24  上午 11:22            178 pyvenv.cfg

我們用 uv add 安裝的套件都會放在 Lib\site-packages 裡面.