iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0
Build on Google AI

用 Google AI 生態系 30 天從零打造一個全棧 AI SaaS 服務系列 第 12 篇

Day 12 -【快取省成本】實戰 Context Caching 上下文快取:長文本與影音重複分析現省 75%,打造可持續獲利的 AI SaaS!

  • 分享至 

  • xImage
  •  

在前面的章節中,我們陸續解鎖了 Gemini 1.5 令人驚嘆的殺手級功能:吞下數十頁 PDF(Day 08)、直接辨識長篇影音與時間軸(Day 09)、嚴格約束的 JSON 結構化輸出(Day 10),以及具備自主調用外部 API 能力的 Agent(Day 11)。

此時,一個冷酷而現實的商業問題浮上檯面:「Token 帳單誰來買單?」

在 OmniVibe AI 的真實使用場景中,用戶上傳了一份 10 萬 Token 的年度財報或 45 分鐘的訪談影片後,絕不可能只問一個問題就離開。典型的用戶行為是:

  1. 先要求:「產出一份核心摘要」
  2. 接著問:「把裡面的第三點改成 3 篇 Threads 貼文」
  3. 再追問:「為這支影片挑出適合做短影音的時間戳記」
  4. 最後問:「整理出所有涉及財務預測的數據表格」

如果每次追問,系統都把這 10 萬 Token 的原始影音與 PDF 重新打包、重新上傳給模型計算一次:

  • 錢包大失血:每問一次就計費 10 萬 Input Tokens,用戶問 5 次等於消耗 50 萬 Tokens。
  • 首字延遲高(High Latency):模型每次都要重新對龐大的上下文進行注意力矩陣計算(Attention Prefill),使用者每次點擊都要乾等 10 幾秒。

在商業模式上,這種架構會讓你的 SaaS 陷入「用戶用得越多,你虧得越慘」的窘境。

今天,我們將深入 Google Gemini 最具商業價值的省錢神器 —— Context Caching(上下文快取),用代碼實戰將重複分析的 API 成本直接砍掉 75%!


💡 什麼是 Context Caching?底層運作機制解析

在傳統的大模型推論中,當你送出 Prompt 時,伺服器必須對所有文字與多模態 Token 重新計算 KV Cache(Key-Value Cache)。

graph TD
    subgraph 傳統無快取模式 (每次重複計費與計算)
        Q1[問題 1 + 10萬 Token 檔案] --> Engine1[計算 10萬 Token KV Cache] --> A1[產出摘要]
        Q2[問題 2 + 10萬 Token 檔案] --> Engine2[重新計算 10萬 Token KV Cache] --> A2[產出貼文]
        Q3[問題 3 + 10萬 Token 檔案] --> Engine3[重新計算 10萬 Token KV Cache] --> A3[產出腳本]
    end

    subgraph Google Context Caching (一次計算,重複讀取)
        File[10萬 Token 影音/PDF] --> CacheEngine[建立雲端快取 (計算一次 KV Cache)]
        CacheEngine --> CacheStore[(Google 記憶體快取實例 / TTL)]
        
        CacheStore -->|極速快取讀取 (省 75% 成本)| Query1[問題 1] --> Out1[秒級產出]
        CacheStore -->|極速快取讀取 (省 75% 成本)| Query2[問題 2] --> Out2[秒級產出]
        CacheStore -->|極速快取讀取 (省 75% 成本)| Query3[問題 3] --> Out3[秒級產出]
    end

Context Caching 允許開發者將龐大的上下文(長文本、書籍、高解析度 PDF、音訊、長影片或巨量 System Instructions)預先計算並快取在 Google 基礎架構的記憶體中:

  • 輸入成本大幅降低:後續對該快取的提問,Input Token 費用直接享有 75% 的折扣(僅需原本價格的 1/4)。
  • 首字延遲顯著下降(Time-to-First-Token):因為略過了龐大的 Prefill 運算階段,推論延遲往往能從 8~10 秒縮短至 1~2 秒以內。

📊 成本試算:前後差距有多大?

以使用 Gemini 1.5 Flash 處理一份包含長影片與 PDF、合計 200,000 Tokens 的專案為例(假設用戶連續進行 5 次深入提問):

指標 無 Context Caching 啟用 Context Caching 效益提升
輸入 Token 總計費量 200k × 5 = 1,000,000 Tokens 首次寫入:200k

後續 4 次讀取:200k × 4 × 25% = 200k

合計:400,000 Tokens 等效費用 | 節省 60% 總成本 |
| 首字回傳延遲 | 每次提問需重新解析,約 6 ~ 9 秒 | 首次建立需數秒,後續查詢 約 1.2 秒 噴字 | 速度提升 5 倍以上 |
| 用戶操作體驗 | 追問等待時間長,容易跳離網頁 | 即問即答,符合即時生產力工具標準 | 留存率大幅提高 |

(註:快取會依照保留時間收取少許的儲存費用,通常以每小時每百萬 Token 幾美分計,對於頻繁互動的 SaaS 專案而言成本幾乎可以忽略)


⚠️ 使用 Context Caching 的硬性門檻與生命週期(TTL)

在興奮地將代碼全部換成快取前,必須注意 Google 官方的兩大規則:

  1. 最小 Token 門檻:快取內容必須 大於等於 32,768 Tokens。如果你的內容只有幾千字,Google API 會直接拒絕建立快取(因為短文本重新計算反而比快取調度更便宜快速)。
  2. TTL(存活時間,Time-To-Live):快取不是永久儲存。預設通常為 1 小時(3600 秒),你可以根據需求在呼叫時自訂過期時間,或在用戶持續互動時動態延長 TTL。

💻 實戰演練:在 Next.js 後端建立與使用快取

我們將在 src/lib/gemini/cache-manager.ts 中封裝快取管理邏輯,並在 API Route 中使用 GoogleAICacheManager。

1. 封裝快取管理工具 (src/lib/gemini/cache-manager.ts)

// src/lib/gemini/cache-manager.ts
import { GoogleAICacheManager } from '@google/generative-ai/server';
import { GoogleGenerativeAI } from '@google/generative-ai';
import { OMNIVIBE_SYSTEM_INSTRUCTION } from './prompts';

const apiKey = process.env.GEMINI_API_KEY || '';
const cacheManager = new GoogleAICacheManager(apiKey);
const genAI = new GoogleGenerativeAI(apiKey);

export interface CreateProjectCacheParams {
  projectId: string;
  fileUris: Array<{ uri: string; mimeType: string }>;
  ttlMinutes?: number; // 預設保留分鐘數
}

/**
 * 為使用者的專案大檔案建立 Context Cache
 */
export async function createProjectContextCache({
  projectId,
  fileUris,
  ttlMinutes = 60,
}: CreateProjectCacheParams) {
  try {
    const ttlSeconds = ttlMinutes * 60;

    console.log(`[Cache Manager] 正在為專案 ${projectId} 建立快取,TTL: ${ttlMinutes} 分鐘...`);

    const cache = await cacheManager.create({
      model: 'models/gemini-1.5-flash-001',
      displayName: `cache_project_${projectId}`,
      systemInstruction: OMNIVIBE_SYSTEM_INSTRUCTION,
      contents: [
        {
          role: 'user',
          parts: fileUris.map((file) => ({
            fileData: {
              fileUri: file.uri,
              mimeType: file.mimeType,
            },
          })),
        },
      ],
      ttlSeconds: ttlSeconds,
    });

    console.log(`[Cache Manager] 快取建立成功!Cache Name: ${cache.name}`);
    return cache;
  } catch (error: any) {
    console.error('[Cache Manager Error] 建立快取失敗:', error);
    throw error;
  }
}

/**
 * 取得支援快取的 Gemini 模型實例
 */
export function getModelWithCache(cacheName: string) {
  return genAI.getGenerativeModelFromCachedContent({
    name: cacheName,
  });
}


2. 實作基於快取的連續對話端點 (src/app/api/ai/cached-chat/route.ts)

這支 API 接收使用者針對該專案提出的「連續延伸問題」,完全不需要再次傳送原始的大型檔案,直接掛載 cacheName 進行推論:

// src/app/api/ai/cached-chat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { getModelWithCache } from '@/lib/gemini/cache-manager';

export async function POST(req: NextRequest) {
  try {
    const { cacheName, prompt } = await req.json();

    if (!cacheName || !prompt) {
      return NextResponse.json(
        { error: 'Bad Request', message: '必須提供 cacheName 與 prompt' },
        { status: 400 }
      );
    }

    // 1. 直接以快取實例初始化模型(不再需要重複上傳大檔案或重複傳遞 System Prompt)
    const model = getModelWithCache(cacheName);

    // 2. 進行推論
    const result = await model.generateContent(prompt);
    const response = await result.response;
    const usage = response.usageMetadata;

    // 3. 輸出監控驗證:觀察 cachedContentTokenCount
    console.log(`[Cache Hit Monitor] Token 消耗統計:`, {
      promptTokens: usage?.promptTokenCount,
      cachedTokens: usage?.cachedContentTokenCount, // 命中快取的 Token 數量
      candidatesTokens: usage?.candidatesTokenCount,
      totalTokens: usage?.totalTokenCount,
    });

    return NextResponse.json({
      success: true,
      data: {
        output: response.text(),
        usage: {
          totalTokens: usage?.totalTokenCount,
          cachedTokens: usage?.cachedContentTokenCount,
          // 若 cachedContentTokenCount 大於 0,代表成功命中快取並享有極致折扣
          isCacheHit: Boolean(usage?.cachedContentTokenCount && usage.cachedContentTokenCount > 0),
        },
      },
    });
  } catch (error: any) {
    console.error('[Cached Chat Error]:', error);
    return NextResponse.json(
      { error: '快取推論失敗', message: error.message },
      { status: 500 }
    );
  }
}


🧪 實測驗證:檢視伺服器日誌的命中證據

當我們上傳了一支 40 分鐘的演講影片(換算 Token 數約 120,000 Tokens)並建立快取後,連續發送追問請求:

curl -X POST http://localhost:3000/api/ai/cached-chat \
  -H "Content-Type: application/json" \
  -d '{
    "cacheName": "cachedContents/ab12cd34ef56gh78",
    "prompt": "請從這支演講中整理出 3 個最具啟發性的金句,並附上講者說出該句子的情境"
  }'

後端終端機印出的 usageMetadata:

[Cache Hit Monitor] Token 消耗統計: {
  promptTokens: 120140,
  cachedTokens: 120000, 
  candidatesTokens: 380,
  totalTokens: 120520
}

注意看這組數據:

  • cachedTokens: 120000:這 12 萬個 Token 全數從記憶體快取中讀取!
  • 實際需要以標準價格計費的全新 Prompt Token 只有 140 Tokens(也就是我們剛打的那句追問短句)。
  • 回應時間從原先的 7.8 秒直接驟降至 1.1 秒!

這代表你的 SaaS 在每次用戶進行互動追問時,都能以不到四分之一的成本提供極速流暢的即時體驗。


🏁 第二篇章總結:Google AI 核心大腦完全就緒!

今天我們完成了【第二篇:Google AI 核心引擎與 Prompt 工坊】(Day 06 - Day 12)的收官之作。讓我們回顧這 7 天建立起的堅實基礎:

  1. Day 06【AI Studio】:沙盒可視化調優,固化了專業級 System Prompt 與超參數。
  2. Day 07【Gemini API】:Next.js 後端打通 Google Gen AI SDK,架構防禦性隔離端點。
  3. Day 08【長文本魔法】:實戰百萬 Token 原生解析,整份 PDF 直接吞下,告別繁複 RAG。
  4. Day 09【多模態震撼】:捨棄 Whisper 拼裝車,音訊與影片原生雙向識別,精確抓取影音時間軸。
  5. Day 10【結構化資料】:透過 Structured Outputs 與 JSON Schema,徹底消除前端渲染白屏。
  6. Day 11【工具調用】:解鎖 Function Calling,讓模型從被動回答升級為能主動查資料的 Agent。
  7. Day 12【快取省成本】:運用 Context Caching 將重複查詢成本直接砍掉 75%,打通商業獲利路徑。

後端大腦與 AI 引擎已經武裝到牙齒,但終究還停留在終端機與 API 測試工具裡。一個真正的 SaaS 必須擁有令人驚豔、絲滑流暢的使用者介面!

明天開始,我們將正式邁入【第三篇:介面疾速生成與全棧基礎建設(Day 13 - Day 19)】。

明天(Day 13),我們將進入【Google Stitch 介面生成篇】:認識 Google 次世代 AI 介面生成神器 Google Stitch!看我們如何用純自然語言,在數分鐘內生成出極具設計感、現代化且具備生產力質感的 SaaS Dashboard 完整前端原型!

敬請期待,我們明天見!🔥


上一篇
Day 11 -【工具調用】實戰 Function Calling:賦予 Gemini 聯網與外部 API 調用能力,從被動問答進化為自主 Agent!
下一篇
Day 13 -【Google Stitch】AI 介面生成實戰:用自然語言疾速打造 OmniVibe AI 現代化 Dashboard 前端原型
系列文
用 Google AI 生態系 30 天從零打造一個全棧 AI SaaS 服務 共 18 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言