前情提要:前面幾篇我們把腦袋練起來(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;高耗服務分出來。