在昨天的文章中,我們使用 GitHub Actions 建構了專業級的 CI 自動化測試管線,確保每次程式碼交付都能通過嚴密的品質檢驗。
從 Day 01 到現在,我們一步一腳印地把一套空白架構,打造成涵蓋 LINE Bot、n8n 工作流、Dify 意圖大腦、FastAPI 推薦微服務、Google Places API、SQLite/Redis 記憶庫 的完整雲原生 AI 系統。
然而,在技術文章中光鮮亮麗的成果背後,真實的工程實踐往往充滿了各種「看似莫名其妙、Debug 到深夜」的深坑。
今天 Day 29,我們不寫新功能,而是要進行一場深度技術覆盤(Post-Mortem):系統性盤點這趟旅程中踩過最具代表性的 5 大架構痛點、根本原因以及最佳避坑指南,為你的 AI Agent 工程化之路提供最接地氣的經驗沉澱!
在深入覆盤之前,我們先把整套系統的終極架構藍圖攤開:
┌───────────────────────┐
│ LINE 官方客戶端 │
└───────────┬───────────┘
│ 1. Webhook (HMAC-SHA256 簽章)
▼
┌───────────────────────┐
│ n8n 自動化工作流 │ ◄── [Day 20] Cron 排程推播
└─────┬───────────┬─────┘
│ │
(口語意圖分析) │ │ (已具座標 / 抽籤 / Postback 直接調用)
▼ │
┌──────────────────┐ │
│ Dify (Gemini) │ │
│ 提示詞防護加固 │ │
└─────────┬────────┘ │
│ (結構化 JSON) │
└──────────┬──────┴────────────────┐
▼ ▼
┌──────────────────────────────────────┐
│ FastAPI 後端推薦微服務 │
│ - Slowapi 慢速限流 (429 防刷) │
│ - FSM 多輪對話槽位狀態管理 │
│ - Loguru 結構化日誌 + Sentry 告警 │
└─────┬────────────────────┬───────────┘
│ │
(快取查詢 / 寫入) │ │ (歷史足跡 / 收藏)
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ Redis 快取 (Upstash) │ │ SQLite 資料庫 (ORM) │
└──────────────┬───────┘ └──────────────────────┘
│ (Cache Miss)
▼
┌──────────────────────┐
│ Google Places (New) │ ──> Field Masking 精準索取
└──────────────────────┘
踩坑 1:Docker 容器間的通訊黑洞(localhost 的幻覺)
痛點現象:
在本地開發初期,n8n 與 FastAPI 分別以容器或本地行程運行。當 n8n 的 HTTP Request 節點填寫 http://localhost:8000 時,總是報出 ECONNREFUSED(連線遭拒)的錯誤。
根本原因:
容器內的 localhost 指向的是容器自己,而不是宿主機(Host Machine)!
終極解法:
1.若 n8n 跑在 Docker 容器內、FastAPI 跑在 Windows/Mac 本機:連線網址必須改用 http://host.docker.internal:8000。
2.若兩者都在 Docker 內:必須將兩者掛載到同一個 Docker Network(自訂橋接網路),直接以容器名稱(Container Name)相互呼叫(例如 http://fastapi-backend:8000)。
踩坑 2:大語言模型偶發 503 負載過載與格式崩潰
痛點現象:
Dify 在呼叫 Gemini 或其他 LLM 時,尖峰時段可能遇到 Google 回傳 503 Service Unavailable;或偶爾模型會在 JSON 外面包上一層 json,導致 n8n 的 JSON Parse 節點直接報錯中斷。
根本原因:
雲端模型具備不可控的網路波動與隨機推論特性,若工作流缺乏「重試(Retry)」與「容錯(Defensive Parsing)」機制,脆弱度會極高。
終極解法:
1.n8n 重試機制:在 HTTP Request 節點開啟 Retry on Fail,設定重試 2 次、間隔 2 秒,能平滑化 95% 以上的暫態 503 錯誤。
2.正規防禦解析:在 n8n Code 節點中,不要直接 JSON.parse(),先透過正則表示式 text.match(/{[\s\S]*}/) 抽出純 JSON 核心內容後再解析。
踩坑 3:LINE Flex Message 欄位超載與非法字元炸裂
痛點現象:
後端明明搜尋成功回傳了資料,但 LINE 機器人卻一片死寂,完全不回覆訊息,且 LINE API 回傳了 400 Bad Request: Invalid message format。
根本原因:
LINE Flex Message 對資料結構有嚴格的限制:
文字欄位若為 null 或未定義,整個 Flex JSON 直接無效!
Google Places 回傳的店家簡介若含有特殊未轉義換行符或長度超過上限,卡片渲染引擎會拒絕接收。
終極解法:
在組裝 Flex Bubble 時,對所有外部輸入加上嚴格的保底預設值與長度裁切(Truncate):
const shopName = (shop.name || '精選餐廳').substring(0, 30);
const shopAddress = (shop.location || '地址請參考地圖').substring(0, 45);
踩坑 4:FastAPI 非同步端點中的資料庫阻塞(Blocking DB)
痛點現象:
當多個使用者同時發起請求時,原本宣稱高效能的 FastAPI 竟然出現排隊卡頓的現象。
根本原因:
在 async def 端點中,若呼叫了標準的同步 SQLAlchemy 查詢(如 db.query(...).all()),Python 的單線程 Event Loop 會被該同步 I/O 阻塞住,導致其他請求無法切換處理。
終極解法:
1.對於高併發情境,改用 SQLAlchemy 2.0 的 AsyncSession 搭配 aiosqlite。
2.若維持同步 ORM,端點應宣告為一般的 def recommend_food(...)(FastAPI 會自動丟到外部執行緒池 ThreadPool 執行,避免卡死主事件迴圈)。
踩坑 5:Places API 帳單隱藏炸彈(未受限的欄位索取)
痛點現象:
測試幾十次後,查看 Google Cloud Console 帳單預估,費用增加的速度遠超預期。
根本原因:
呼叫 Google Places API (New) 時,若未加上 X-Goog-FieldMask 標頭,系統預設會抓取最貴的「進階資訊(Atmosphere / Reviews)」,費用直接乘以數倍!
終極解法:
嚴格規範 X-Goog-FieldMask,只勾選系統需要的基礎欄位(Basic SKU),並搭配 Day 24 的 Redis 快取與 Day 27 的 GCP 預算警報,達成零成本運轉。
回顧整個 30 天的開發歷程,這套系統之所以具備生產級實用性,關鍵在於堅持了三項核心原則:
1.職責分離(Separation of Concerns):
溝通互動交給 LINE
工作流程編排交給 n8n
意圖辨識交給 Dify
商業邏輯與記憶交給 FastAPI
每個模組都是獨立解耦的,未來即使想換掉 LINE 改接 Discord 或 Slack,也只需改動工作流前端,核心後端完全不用重寫!
2.防禦性程式設計(Defensive Programming):
從限流(Slowapi)、提示詞加固(Injection Defense)、簽章比對(HMAC)到測試(Pytest),資安與穩定性從一開始就深植在架構中,而非上線後才打補丁。
3.極致的成本意識:
透過記憶體快取與條件繞道(GPS / 抽籤跳過 LLM),讓這套系統即使部署在真實環境中,每個月也幾乎不需要花費任何額外伺服器與 API 開銷。
歷時整整 30 天,每天不間斷的架構設計、實作撰寫、除錯踩坑與覆盤記錄,我們終於抵達了這趟旅程的終點線!
回顧 Day 01,我們從一個最常見的生活痛點出發——「今天吃什麼?」。
如果只把 LLM 當成聊天框,那它永遠只能吐出幻覺連篇或脫離現實的文字;而真正的工程價值,在於如何將 AI 的推理能力,與真實世界的 API、雲原生微服務、工作流編排器、資料庫記憶以及安全性防護網深度結合。
今天 Day 30,我們將完整回顧這 30 天的技術全景、整理標準的開源專案規格與目錄架構,並展望 AI Agent 在未來工程落地上的延伸可能!
這 30 天的旅程可以精準拆解為五大演進階段:
【階段一:地基與架構誕生】(Day 01 ~ Day 07)
├── 釐清 AI Agent 架構與工具鏈選型
├── FastAPI 核心微服務建置與 Pydantic 嚴格型別校驗
└── Dify + Gemini 意圖抽取大腦與本地 Docker 環境成型
【階段二:雲端通訊與介面】(Day 08 ~ Day 15)
├── LINE Messaging API 串接與 ngrok 穿透測試
├── n8n 視覺化工作流自動化編排
├── 精緻美觀的 LINE Flex Message Carousel 輪播卡片
├── Google Maps Places API (New) 真實餐廳資料落地
└── SQLite 記憶庫整合與全雲端 PaaS 部署 (Render / Zeabur)
【階段三:進階互動與功能拓展】(Day 16 ~ Day 20)
├── LINE GPS 座標位置分享與方圓 1 公里雷達式精準搜尋
├── 有限狀態機(FSM)槽位填補與智慧主動追問
├── 隨機盲盒抽籤轉盤與 LINE Quick Reply 快速回覆
├── SQLite 私房名單收藏夾與 Postback 背景靜默操作
└── n8n Cron 排程觸發器與 LINE Push API 午餐靈感主動推播
【階段四:企業級資安與效能】(Day 21 ~ Day 25)
├── Slowapi 慢速限流防護 (429 Too Many Requests)
├── System Prompt 提示詞加固與 Prompt Injection 越獄防禦
├── LINE Webhook HMAC-SHA256 數位簽章驗證 (X-Line-Signature)
├── Redis 快取架構 (Cache-Aside Pattern) 壓低 70 倍延遲與節省成本
└── Pytest + TestClient 建立自動化單元與整合測試防線
【階段五:維運監控與工程交付】(Day 26 ~ Day 30)
├── Loguru 結構化日誌輪轉 + Sentry 實時異常捕獲告警
├── Token 用量與 Google API 成本盤點(達成零成本運轉)
├── GitHub Actions CI/CD 自動化測試與 Lint 檢查管線
├── 五大架構深度踩坑剖析與避坑實務覆盤
└── Day 30 專案總整理與開源發布!
這套系統採用了高內聚、低耦合的模組化目錄結構,確保未來無論是要抽換 LLM 供應商、更換資料庫或擴充前端通訊平台,都能以最小成本無痛抽換:
food-agent-production/
├── .github/
│ └── workflows/
│ └── ci.yml # GitHub Actions 自動化 CI 測試流水線
├── app/
│ ├── core/
│ │ ├── cache.py # Redis 非同步快取服務層
│ │ ├── line_auth.py # HMAC-SHA256 數位簽章驗證依賴
│ │ ├── limiter.py # Slowapi 速率限制配置
│ │ ├── logger.py # Loguru 結構化日誌設定
│ │ └── security.py # 提示詞注入與正則安全過濾器
│ ├── models/
│ │ └── memory.py # SQLAlchemy 資料表模型(使用者、歷史、收藏夾)
│ ├── routers/
│ │ ├── favorite.py # 私房餐廳收藏夾管理 API
│ │ ├── health.py # 容器健康檢查與 Sentry 測試端點
│ │ ├── push.py # 主動推播訂閱名單 API
│ │ └── recommendation.py # 核心推薦、抽籤與 FSM 狀態端點
│ ├── schemas/
│ │ └── food.py # Pydantic 資料模型與列舉規範
│ ├── services/
│ │ └── places.py # Google Places API 串接與快取整合
│ ├── config.py # 全域環境變數配置
│ ├── database.py # 資料庫連線引擎與 Session 管理
│ └── main.py # FastAPI 核心入口與中介軟體掛載
├── n8n/
│ ├── line_core_flow.json # n8n 核心訊息分流工作流匯出檔
│ └── lunch_push_flow.json # n8n 每日定時推播工作流匯出檔
├── tests/
│ ├── conftest.py # In-Memory SQLite 測試沙盒與客戶端
│ ├── test_health.py # 健康檢查端點測試
│ ├── test_recommend.py # 核心商業邏輯 Mock 測試
│ └── test_schemas.py # Schema 驗證與資安過濾單元測試
├── .dockerignore
├── .env.example # 環境變數設定範本檔
├── .flake8 # 程式碼靜態檢查規則檔
├── Dockerfile # 生產級容器打包建置檔
├── README.md # 專案完整架構與部署文檔
└── requirements.txt # 專案 Python 相依清單
如果說這 30 天帶給我最大的收穫是什麼,那就是體會到:「Prompt 只是引信,System 才是炸藥的本體。」
在 AI 浪潮初期,許多人以為做 AI 應用就是寫好一串神奇的咒語(Prompt)。但當你真正要把一個應用交到使用者手中時,你會發現:
LLM 不會幫你管理連線逾時
LLM 不會幫你防禦惡意爬蟲刷量
LLM 不能直接幫你查詢精準的 GPS 經緯度商圈
LLM 更不會在半夜程式報錯時發 Slack 或 Email 叫醒你修 Bug
所有的可靠性、使用者體驗與商業落地價值,全部依賴於傳統軟體工程中最扎實的環節:API 設計、微服務架構、狀態機控制、資料庫關聯、通訊加密、快取優化與自動化測試。
感謝所有在這 30 天裡閱讀、按讚、留言與交流的夥伴們!
是你們的關注與反饋,讓這份原本孤獨的敲鍵盤過程充滿了動力。
這套系統的所有設計思路、架構實踐與原始程式碼,都將作為踏入 AI 工程化領域的一塊基石。
願我們都能在這場 AI 的時代洪流中,不只當一名 Prompt 的使用者,更成為能動手建造完整系統的優秀軟體架構師!