前情提要:前面幾篇我們把腦袋練起來(Prompt、記憶、RAG)。接下來要把它變成 能佈署、能監控、能管成本 的雲端系統。這篇先不急著寫程式,先用 GCP 的觀念把整體藍圖定下來。
flowchart LR
  subgraph Client ["前端 / 第三方服務"]
    F1[Web / App (BFF)] -- WebSocket/HTTP --> GW
  end
  subgraph CloudRun ["Cloud Run (多服務)"]
    GW[api-gateway\n(認證/流量控管/路由)] -->|同步| CHAT[chat-service\n(會話控制/個人化)]
    CHAT -->|查詢| MEM[mem-service\n(短/長期記憶)]
    CHAT -->|檢索| RAG[rag-service\n(檔案/RAG/向量查詢)]
    CHAT -->|工具| TOOL[tool-service\n(外部API/任務調度)]
    CHAT -->|非同步事件| BUS[(Pub/Sub)]
    BUS --> WORK[worker-service\n(重計算/回呼/回補)]
  end
  subgraph Data ["資料與AI"]
    VDB[(向量DB/BigQuery/Datastore)]
    SQL[(Cloud SQL/PostgreSQL)]
    SECRETS[(Secret Manager/KMS)]
    GEM[Vertex AI Gemini API]
    APP[AI Applications\n(原 Agent Builder)]
  end
  RAG <--> VDB
  MEM <--> SQL
  TOOL -->|受控| 外部API[(內外部系統)]
  CHAT -->|同步/非同步| GEM
  CHAT --> APP
  GW --> SECRETS
  CHAT --> SECRETS
核心重點
Client → Gateway → chat-service 最多兩跳內回應(ACK/快速答案/查快取)。Pub/Sub,由 worker-service 回補(WS 推播或回呼)。同步:使用者可感知的即時回饋(< 1~2 秒)。
例:回「我收到你的問題、正在處理」、簡短答案、快取命中。
非同步:資料檢索、工具呼叫、向量查詢、模型長回應。
例:RAG 完整答案、跨系統工作流程、LLM 產製多步結果。
記憶(mem-service):個人資料、偏好、最近對話摘要。
知識庫(rag-service):文件、FAQ、規範、手冊。
兩者都會影響回答,但存放與生命週期不同,不可混用一個 DB。
| 服務 | 單一責任 | 不該做的事 | 
|---|---|---|
| api-gateway | 認證、配額、路由、觀測性標頭 | 寫商業邏輯 | 
| chat-service | 會話協調、個人化、決定走同步/非同步 | 自己查 DB/向量庫 | 
| mem-service | 短/長期記憶 CRUD、摘要策略 | 做 RAG/文件解析 | 
| rag-service | Chunking、Embedding、向量檢索、重排序 | 管會話狀態 | 
| tool-service | 受控的 Function/Action(查單據、發通知…) | 直接回傳模型答案 | 
| worker-service | Pub/Sub 消費、重計算、回補、重試 | 提供同步 API | 
原則:每個服務都有「你只做這件事」的一句話。模糊就會耦合。
同步(ACK + 快速回應)
sequenceDiagram
  participant C as Client
  participant GW as Gateway
  participant CH as chat-service
  participant MEM as mem-service
  participant R as rag-service
  C->>GW: POST /chat
  GW->>CH: 轉發(已驗證)
  CH->>MEM: 拉短期記憶摘要
  CH-->>C: 200 OK (ack/busy/短答)
  CH->>R: 非同步查詢 (透過 Pub/Sub 交給 worker)
非同步
sequenceDiagram
  participant BUS as Pub/Sub
  participant WK as worker-service
  participant GEM as Vertex AI
  participant APP as AI Applications
  participant C as Client
  BUS->>WK: chat.task
  WK->>APP: 流程/工具/檢索
  WK->>GEM: 產生最終回答
  WK->>C: WebSocket 推播 / callback webhook
cloudbuild.yaml(精簡骨架)
steps:
  - name: gcr.io/cloud-builders/docker
    args: ["build","-t","$_REGION-docker.pkg.dev/$PROJECT_ID/$_REPO/chat-service:$SHORT_SHA","-f","Dockerfile","."]
  - name: gcr.io/cloud-builders/docker
    args: ["push","$_REGION-docker.pkg.dev/$PROJECT_ID/$_REPO/chat-service:$SHORT_SHA"]
  - name: gcr.io/google.com/cloudsdktool/cloud-sdk
    entrypoint: gcloud
    args:
      - run
      - deploy
      - chat-service
      - --image=$_REGION-docker.pkg.dev/$PROJECT_ID/$_REPO/chat-service:$SHORT_SHA
      - --region=$_REGION
      - --platform=managed
      - --service-account=chat-sa@${PROJECT_ID}.iam.gserviceaccount.com
      - --set-secrets=JWT_KEY=projects/${PROJECT_ID}/secrets/jwt-key:latest
      - --set-env-vars=RAG_ENDPOINT=http://rag-service
      - --min-instances=1
      - --max-instances=10
      - --cpu=1
      - --memory=512Mi
substitutions:
  _REGION: asia-east1
  _REPO: ai-assistant
環境變數規格(範例)
| Key | 說明 | 來源 | 
|---|---|---|
JWT_KEY | 
簽章或驗章金鑰(建議用 KMS) | Secret Manager | 
DB_URL | 
PostgreSQL 連線 | Secret/Env | 
VECTOR_ENDPOINT | 
向量庫服務位址 | Env | 
VERTEX_LOCATION | 
例:asia-east1 | Env | 
ALLOWED_ORIGINS | 
CORS 白名單 | Env | 
原則:任何會換環境就會換值的東西,都放環境變數或 Secret。程式碼零硬編。
kid 對應 JWKS),BFF/前端送 Bearer。chatId/requestId 進入 Pub/Sub 前先上鎖,避免重送重算。worker-service 對可重試錯誤(429/5xx)做指數退避;對不可重試錯誤直接 DLQ(死信佇列)。min-instances 不是越多越好,先 0~1;高耗服務分出來。