iT邦幫忙

0

# Day 27-28 Token 用量與 Google API 成本盤點與極致優化指南、使用 GitHub Actions 打造 CI/CD 測試與部署檢查流水線

n8n
  • 分享至 

  • xImage
  •  

在昨天的文章中,我們整合了 Loguru 與 Sentry,為系統建立了即時可觀測性與自動告警機制。

當系統穩定運行在雲端並陸續開放給親朋好友使用後,除了系統有沒有掛掉之外,身為獨立開發者最關心的莫過於第二個靈魂拷問:

「這隻 LINE 機器人每個月到底要花我多少錢?免費額度用完後會不會突然收到天價帳單?」

一個優秀的工程專案,不只要把功能做出來,更要在**「使用者體驗」與「維運預算」**之間取得絕佳平衡。

今天 Day 27,我們將全面盤點這套 AI 美食推薦架構中的主要成本來源,透過真實數據分析 Gemini 3.5 Flash-Lite 與 Google Maps Places API 的花費結構,並分享 4 個立竿見影的成本收斂與最佳化技巧!


一、系統兩大計費核心與費用模型盤點

在我們的架構中,除了 Render、Zeabur 與 Upstash 提供充裕的免費層(Free Tier)之外,真正會隨使用量動態計費的只有兩項:

1. Google Gemini 3.5 Flash-Lite(LLM 推論成本)

  • 計費單位:每 100 萬個 Tokens(Input / Output Tokens)。
  • 特點:Flash-Lite 模型主打極低延遲與極親民定價,比 Pro 系列便宜數十倍。
  • 單次對話用量估算:
    • 輸入提示詞(System Prompt + 標籤包裹口語):約 350 ~ 500 Tokens。
    • 輸出結構化 JSON:約 50 ~ 80 Tokens。
    • 單次推論總成本:不到 $0.00005 美元(幾乎可以忽略不計,1 美元約可支援 2 萬次以上查詢)。

2. Google Maps Places API (New)(地理資訊成本)

  • 計費單位:每 1,000 次 Text Search 請求。
  • 特點:這是整個系統中單價最高的環節!Google Cloud Places Text Search (New) 基本定價約為每 1,000 次呼叫 $5.00 ~ $17.00 美元(取決於索取的欄位層級 Basic / Advanced / Preferred)。
  • Google 優惠:每個 Google Cloud 帳號每月享有常態性免費抵扣額度(通常足以支撐個人開發每月數百至上千次查詢),但一旦超出,費用累積非常迅速。

二、4 大成本收斂與極致優化策略

為了讓機器人即使面對流量爆發也能安穩運行在免費額度內,我們在過去幾天陸續埋下了關鍵的「防禦性省錢架構」:

策略 1:嚴格使用 Field Masking 避免無效收費

Google Places API (New) 採用「依索取欄位階層計費」。如果沒有加上 X-Goog-FieldMask 標頭,或者填入 *,Google 會回傳包含所有評分、完整營業時間甚至評論文字的高級資訊,以最貴的費率計價!

我們在 app/services/places.py 中明確宣告只索取必要欄位:

headers = {
    "X-Goog-Api-Key": GOOGLE_MAPS_API_KEY,
    "X-Goog-FieldMask": "places.id,places.displayName,places.formattedAddress,places.rating,places.priceLevel,places.editorialSummary"
}

策略 2:利用 Redis 快取吸收重複查詢(Day 24 落地成果)

熱門商圈(例如「板橋 甜點」、「西門町 日式」)的查詢重複率高達 40% ~ 60%。

我們設定了 6 小時的快取存活時間(TTL):

只要有人在 6 小時內問過相同商圈與料理類型,後續查詢全部由 Redis 記憶體快取秒回。

成果數據:以每日 100 次查詢為例,快取命中率約 50%,直接讓 Google Places API 呼叫次數腰斬 50%,同時平均延遲從 800ms 降至 10ms!

策略 3:GPS 定位與抽籤模式「繞過 LLM」(Token 零消耗)

並不是所有的使用者操作都需要經過大語言模型:

傳送 GPS 位置訊息時(Day 16):使用者已經提供經緯度,n8n Switch 節點直接分流至微服務,完全跳過 Dify。

點擊 Quick Reply 抽籤時(Day 18):點擊「🎲 再抽一次」屬於明確的系統指令,微服務直接執行隨機選取,跳過 Dify。

點擊「❤️ 收藏」時(Day 19):LINE Postback 事件直接打到後端資料庫寫入,跳過 Dify。

💡 效果:只在「使用者輸入自訂口語」時才動用 Gemini 大腦,省下了超過 60% 的 LLM Token 消耗!

策略 4:提示詞精簡化(Prompt Compression)

長篇大論的 System Prompt 會在每次對話重複消耗 Input Token。
我們在 Day 22 加固提示詞時,將範例精簡化,並直接規定「只輸出標準 JSON,不附帶任何客套話」,有效將 Input 抑制在 400 Tokens 內、Output 抑制在 60 Tokens 內,確保每一分 Token 都花在刀口上。

三、真實月營運成本試算表

假設機器人擁有 50 位活躍好友,平均每人每月查詢 10 次(全月共 500 次查詢):

服務項目 免費額度 / 方案 實際預估消耗 實際預估支出 (USD)
Render Web Service 每月 750 小時免費運行 運行 1 個 FastAPI 實例 $0.00
Zeabur / PaaS 包含基礎免費額度 運行 1 個 n8n 實例 $0.00
Upstash Redis 每日免費 10,000 次讀寫 每月約 3,000 次快取存取 $0.00
LINE Messaging API 每月 200 則免費 Push,Reply 免費 150 則主動推播,其餘全用 Reply $0.00
Google Gemini 3.5 Flash-Lite Google AI Studio 每月免費額度 約 10 萬 Tokens $0.00
Google Maps Places API Google Cloud 平台常態每月免費抵扣 約 250 次(快取吸收後) $0.00(全數由額度折抵)
總計費用 — — $0.00 元(完全免費運轉)

四、在 Google Cloud Console 設定預算警報(防爆卡關鍵步驟)

為了防範萬一程式寫出無窮迴圈或突發流量導致扣款,務必在 Google Cloud Console 啟用預算警報(Budget Alerts):

1.前往 Google Cloud Console - 帳單儀表板。

2.進入「預算與快訊」➜ 點擊「設定預算」。

3.預算金額設定為 $1.00 美元(或你可接受的最低上限)。

4.設定快訊觸發比例:50%、90%、100%。

5.勾選「傳送電子郵件快訊給帳單管理員」。

6.這樣一來,只要當月花費接近或達到 1 美元,你就會在第一時間收到警告郵件,絕不當冤大頭!

Day 28 使用 GitHub Actions 打造 CI/CD 測試與部署檢查流水線

在昨天的文章中,我們完成了系統營運成本與 Token 用量的全面盤點,透過 Field Masking、快取與邏輯分流,達成了零成本運轉的目標。

隨著專案進入尾聲,我們的程式庫已經累積了完整的微服務、資料庫模型、安全性防禦與測試程式碼。然而,在日常開發中,人總有疏忽的時候:

  • 修改了一個小邏輯,忘了在本地跑 pytest 就直接推送到遠端倉庫
  • 依賴套件更新後,雲端建置(Docker Build)時才噴出版本相容性錯誤
  • 程式碼風格不一致,導致協作與後續維護困難

為了避免「把有問題的程式碼推上正式環境」,現代軟體工程必備的核心利器就是 CI/CD(持續整合與持續交付)!

今天 Day 28,我們將利用 GitHub Actions 打造一條自動化流水線:每當我們執行 git push 或發起 Pull Request 時,雲端自動觸發虛擬機安裝環境、檢查程式碼規範並執行 Pytest 自動化測試,確保每一行交付的代碼都具備生產級的高品質!


一、GitHub Actions 核心概念解析

GitHub Actions 是 GitHub 原生提供的 CI/CD 平台,其架構主要由以下元素組成:

  1. Workflow(工作流程):由觸發條件(Trigger)與多個 Job 組成的自動化過程,存放在專案的 .github/workflows/ 目錄下(使用 YAML 格式定義)。
  2. Event(觸發事件):觸發流程執行的事件,例如 push(推送代碼)或 pull_request(合併請求)。
  3. Jobs(任務)與 Steps(步驟):一個 Workflow 包含多個 Job,每個 Job 在獨立的虛擬環境(Runner)中運行,由多個依序執行的 Step 組成(如簽出代碼、安裝 Python、執行測試)。

我們的 CI 流程規劃:

[開發者推送代碼 (git push main)]
       │
       ▼ (觸發 GitHub Actions)
[Ubuntu 雲端虛擬環境]
       ├─ 1. Checkout 原始碼 (actions/checkout)
       ├─ 2. 設定 Python 3.11 環境 (actions/setup-python)
       ├─ 3. 快取與安裝 requirements.txt 依賴
       ├─ 4. 程式碼靜態排版與語法檢查 (Flake8)
       └─ 5. 執行自動化測試 (Pytest)
             │
             ├───> ✅ 測試全數通過 ──> 標記綠色打勾(Render 放心拉取自動部署)
             └───> ❌ 任一測試失敗 ──> 標記紅色叉叉並發送 Email 警報,終止交付!

二、建立 CI 工作流程設定檔 (.github/workflows/ci.yml)

在本地專案根目錄下建立 .github/workflows/ 資料夾層級,並新增 ci.yml 檔案:

agent-backend/
├── .github/
│   └── workflows/
│       └── ci.yml        # GitHub Actions 流程定義檔
├── app/
├── tests/
├── Dockerfile
└── requirements.txt

撰寫 ci.yml 內容:

name: Food Agent Backend CI Pipeline

# 觸發條件:當 main 分支收到 push 或 pull_request 時自動執行
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  test-and-lint:
    # 執行環境:最新穩定的 Ubuntu 虛擬機
    runs-on: ubuntu-latest

    steps:
      # Step 1: 簽出(拉取)儲存庫程式碼
      - name: Checkout Code
        uses: actions/checkout@v4

      # Step 2: 設定 Python 3.11 執行環境
      - name: Set up Python 3.11
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"
          cache: "pip" # 自動快取 pip 下載套件以加速後續執行

      # Step 3: 安裝專案相依套件與測試工具
      - name: Install Dependencies
        run: |
          python -m pip install --upgrade pip
          pip install flake8 pytest pytest-asyncio
          if [ -f requirements.txt ]; then pip install -r requirements.txt; fi

      # Step 4: 執行靜態語法檢查 (Linting)
      - name: Lint Code with Flake8
        run: |
          # 檢查關鍵語法錯誤或未定義變數(若有錯誤則終止)
          flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
          # 一般風格警告(可放寬行寬至 120 字元,避免中文字串過長報錯)
          flake8 . --count --exit-zero --max-complexity=10 --max-line-length=120 --statistics

      # Step 5: 執行 Pytest 自動化測試
      - name: Run Unit & Integration Tests
        env:
          # 提供虛擬的測試環境變數,避免讀取不到 Key 拋錯
          GOOGLE_MAPS_API_KEY: "mock_test_key_for_ci"
          LINE_CHANNEL_SECRET: "mock_channel_secret_for_ci"
          ENVIRONMENT: "testing"
        run: |
          pytest -v --tb=short

三、設定 Flake8 程式碼檢查規則 (.flake8)

為了避免 Flake8 對虛擬環境(.venv)或快取目錄進行無謂的掃描,我們在專案根目錄建立 .flake8 設定檔:

[flake8]
# 忽略檢查的目錄
exclude =
    .git,
    __pycache__,
    .venv,
    logs,
    build,
    dist

# 允許的最大單行字元長度
max-line-length = 120

# 忽略少數與 Black / 排版工具衝突的代碼
ignore = E203, W503

四、本地驗證並推送觸發 CI

在將代碼推送到 GitHub 之前,我們先在本地確保測試全部通過:

pytest -v

確認所有測試呈現綠色 PASSED 後,使用 Git 提交並推送到 GitHub 遠端倉庫:

# 將新建立的 .github 目錄與設定檔加入暫存
git add .github/ .flake8

# 提交變更
git commit -m "ci: add GitHub Actions workflow for linting and pytest"

# 推送到 GitHub main 分支
git push origin main

五、在 GitHub 儀表板檢視 CI 執行成果

1.打開瀏覽器,進入你的 GitHub 專案儲存庫(https://github.com/<你的帳號>/food-agent-backend)。

2.點擊頂部的 Actions 分頁標籤。

3.你會看到一個名為 Food Agent Backend CI Pipeline 的工作流程正在自動運轉。

4.點擊進入該次 Run,可以看到虛擬機各步驟的即時執行日誌:

Set up Python 3.11 ➜ 成功

Install Dependencies ➜ 成功安裝依賴套件

Lint Code with Flake8 ➜ 語法檢查通過

Run Unit & Integration Tests ➜ 5 個 Pytest 測試全數通過!

5.流程左側將亮起醒目的綠色打勾標章(Passed)!

六、自動化守門員實測:刻意推送錯誤代碼

為了驗證這條防線是否真的能幫我們把關,我們來做一個「破壞性實驗」:

1.刻意修改 app/core/security.py,故意改壞越獄字串的判斷邏輯。

2.執行 git commit -am "test: break security detector" 並 git push。

3.回到 GitHub Actions 頁面:

流程跑到 Step 5 時,tests/test_schemas.py 拋出 AssertionError。

GitHub Actions 立即亮起紅色叉叉(Failed)!

同時你的 GitHub 註冊信箱會收到警報通知:「Run failed: Food Agent Backend CI Pipeline」。

4.守門成果:
因為 CI 失敗,這份壞掉的代碼將無法通過 Pull Request 審查,有效杜絕了劣質代碼偷偷溜進生產線的風險!


*提醒邦友,使用第三方服務/API 時,請務必評估資安風險與隱私保護
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言