🏭 青工廠 v9.4 客服 AI — 工廠流水線藍圖

repo qgc-v52 · branch main · HEAD b89b2bb「v9.4 token cap test: < 2500 → < 2900(鐵律 8 + 範例 9 / 修 5/6 brief #23 真實踩雷)」· audit 2026-05-06 07:10 UTC
🟦 客戶 / 外部 🟩 通道 / API 出入口 🟧 核心 / 商業邏輯 🟪 倉庫 / DB 🟥 轉人類 / 人工介入
A 區 · 全廠空照圖(Context + Container)

客戶 LINE 訊息進來 → 客戶來電總機簽收 → 進待處理倉 → 訂單處理員按順序處理 → 分流調度員(AI 大腦)查工具書回客戶;遇到 escalate tag 就敲對講機叫青哥來。

flowchart LR classDef client fill:#0e2547,stroke:#3aa0ff,color:#3aa0ff classDef gate fill:#0e2547,stroke:#4cc38a,color:#4cc38a classDef core fill:#0e2547,stroke:#ff8a3d,color:#ff8a3d classDef data fill:#0e2547,stroke:#b07cff,color:#b07cff classDef esc fill:#0e2547,stroke:#ff5e7e,color:#ff5e7e C[🟦 客戶 LINE 訊息]:::client W[🟩 客戶來電總機
webhook :8000]:::gate Q[🟪 待處理倉
webhook_queue]:::data WK[🟧 訂單處理員
worker]:::core AI[🟧 分流調度員
ai_router LLM]:::core TB[🟧 工具書架 6 本工具]:::core H[🟪 對話流水帳
history]:::data CU[🟪 客戶檔案櫃
customers]:::data EB[🟥 對講機調度站
escalate_bridge]:::esc EQ[🟪 轉人類隊伍
escalate_queue]:::data TG1[🟩 青哥 TG
@ching_factory_bot]:::gate OA[🟩 青哥 LINE OA 後台
chat.line.biz]:::gate ED[🟦 青哥 / 小編]:::client C --> W --> Q --> WK WK --> AI AI --> TB AI --> H WK --> CU AI -- 含 TRIGGER_QUOTE / TRANSFER_HUMAN tag --> EB EB --> EQ EB -- inline button:跳 LINE OA 後台 --> TG1 TG1 --> ED ED --> OA --> C

業務閉環:B 方案(5/2 PM 拍板)— TG 推 brief 給青哥,青哥用 LINE OA 後台直接回客戶 / TG 不收回流。

B 區 · 一通客戶訊息的旅程(Sequence)

從客戶按下送出,到青哥收到 TG 報價請求,工廠裡發生的事。

sequenceDiagram autonumber participant 客戶 participant 總機 as 客戶來電總機 participant 倉 as 待處理倉 participant 處理員 as 訂單處理員 participant 大腦 as 分流調度員 participant 工具書 as 工具書架 participant 對講機 as 對講機調度站 participant 青哥 as 青哥 TG / LINE OA 客戶->>總機: 「曼巴想改避震 預算一萬」 總機->>總機: 簽章驗證 (HMAC-SHA256) 總機->>倉: 入庫 message_id idempotency 總機-->>客戶: 200 OK (2 秒內) Note over 倉,處理員: 訂單處理員背景 polling 處理員->>大腦: run(user_id, msg) 大腦->>工具書: search_motorcycles / search_prices ... 工具書-->>大腦: matched data 大腦-->>處理員: reply + tags 處理員->>客戶: line_api.push_text (LINE 回客戶) alt reply 含 [TRIGGER_QUOTE] 或 [TRANSFER_HUMAN] 處理員->>對講機: notify_editor(...) 對講機->>青哥: TG 報價 brief #N + inline button「💬 跳 LINE OA 後台」 青哥->>客戶: 在 LINE OA 後台直接回客戶 end
C 區 · 6 本工具書(白名單情報來源)

分流調度員不能腦補;要回什麼都要先翻工具書(OpenAI tool-use loop / fuzzy match threshold = 80)。

業務名稱code 名 / 用途對應工具書source
車款黑話翻譯機search_motorcycles
查台灣機車車款、別稱、黑話
motorcycles.yamltools.py:60
品牌情報員search_brands
查改裝品牌(避震 / 排氣 / CNC / 燈具)
brands_categories.yamltools.py:137
商品清單員search_products
查青工廠商品 / 服務清單
products.yamltools.py:235
報價白名單員search_prices
查報價白名單 + 規則(白名單外要 push 青哥)
prices_rules.mdtools.py:282
業務規則查詢員search_business_rules
查業務規則 / 紅線 / 細項
business_rules.mdtools.py:345
對講機叫人來escalate_to_human
轉真人(青哥 / 老徐)
—(直接觸發 escalate_bridge)tools.py:402

FUZZY_THRESHOLD = 80 / tools.py:33

D 區 · 分流調度員(AI 大腦)· 內部裝配

OpenRouter Gemini 2.5 Flash 跑 OpenAI tool-use loop,最多 5 輪,每輪 timeout 8 秒。System prompt 分 3 layer 利用 prefix-cache。

模型google/gemini-2.5-flash(env: OPENROUTER_MODEL)
Tool Use 上限5 輪 / ai_router.py:55
每輪 timeout8 秒(env: OPENROUTER_TIMEOUT_S)/ ai_router.py:56
Retry backoff1.0 秒 / ai_router.py:57
3 層 System Prompt
(prefix-cache 設計)
小青人格 persona(穩定 / long-term cache)
業務規則 + 報價白名單(中等 / 教學通道改才動)
客戶個人 active_session + summary(不 cache)
ai_router.py:98-156
Error Fallbacksystem_error / no_data / multi_turn_limit / error_fallback.py
主入口async def run(user_id, user_msg, message_id) -> str / ai_router.py:204
E 區 · 對講機調度站(escalate_bridge)· 轉人類

LLM reply 含 [TRIGGER_QUOTE][TRANSFER_HUMAN] tag → 訂單處理員拍對講機 → 推 brief 給青哥 TG,brief 內附 inline button「💬 跳 LINE OA 後台」

設計原則push only / 不做 reply 回流(B 方案 5/2 PM 拍板)
使用 TG bot@ching_factory_bot(CHING_FACTORY_BOT_TOKEN + CHING_FACTORY_CHAT_ID)
業務閉環brief → 青哥點 inline button → 跳 chat.line.biz LINE OA 後台 → 對話列表找未讀第一筆 → 直接回客戶
核心員工 _line_chat_link(v9.2 件 3 final / Fallback A:跳 OA chat 主頁,不拼 user_id)/ escalate_bridge.py:42
notify_editor(TRIGGER_QUOTE → 報價請求 brief)/ escalate_bridge.py:98
notify_editor_transfer(TRANSFER_HUMAN → 轉真人 brief)/ escalate_bridge.py:152
remind_overdue(cron 30 min / warn 2h / critical 24h / expire 7d)/ escalate_bridge.py:206
佐料 電話遮罩(09xx-xxx-XXX 遮最後 3 碼)/ escalate_state.py:59
Rate limit 60s(同 user × 同類型 60s 內不重推)/ escalate_state.py:72
觸發點_maybe_push_escalate_brief in worker.py:159 / called from worker.py:129
F 區 · 通道清單(誰跟外面講話)
通道方向用 bot / endpointsource
🟩 LINE webhook客戶 → 工廠POST /webhook 或 /line/webhook(uvicorn :8000)/ HMAC-SHA256 簽章驗證 / 必 2 秒內回 200webhook.py:35
🟩 LINE push工廠 → 客戶line_api.push_text(async / 不用 reply token)line_api.py:53
🟥 TG escalate brief工廠 → 青哥@ching_factory_bot(push only / inline button)escalate_bridge.py:59
🟩 TG 教學通道Strider ↔ 工廠@qing_factory_v7_teach_bot(long-polling / 30s)teach_bot.py
🟩 SSH 部署通道M4 Max ↔ M1 Ultrassh ultra-out Tailscale 100.109.236.18 / ssh ultra-home 192.168.0.168 (infra / 不在 v7 code base)

守門員:HMAC 簽章驗證 / 群組事件直推青哥(補4)/ redelivery skip(補2)/ message_id idempotency(補2)。

G 區 · 倉庫(SQLite 6 表)

runtime DB:~/qing_factory_data/qing.db(WAL 模式)

業務名稱code 表名用途
客戶檔案櫃customers每位客戶一張卡:display_name / active_session / 對話總結 summary_md / last_active
對話流水帳history每則訊息一行:user_msg / reply / tool_calls_json / latency / cost
待處理倉webhook_queuewebhook 進來先入庫排隊 → worker 背景拉出來處理(idempotency by message_id)
轉人類隊伍escalate_queueTRIGGER_QUOTE / TRANSFER_HUMAN brief 排隊 / position / status / tg_msg_id
migrations/001:6-21
品檢隊伍review_queueV8_REVIEW_MODE 開時 LLM reply 先排這 / Strider 審核才 push 客戶
migrations/002:9-24
品檢學習語料庫review_corpusStrider 改寫 / 拒絕 / 通過的 reply 全留下 / 未來訓練語料
migrations/002:31-40
H 區 · 廠內常駐員工(launchd 4 個 service)
業務崗位label排班狀態
主生產線(LINE bot)
uvicorn :8000 / 接 webhook + 跑 worker
com.qgc.v7-line-bot常駐 KeepAlive🟢 running (audit 當下 PID 38900)
教學通道值班員
Strider TG 改 persona / 工具書 / 重啟
com.qgc.teach-bot常駐 KeepAlive🟢 running (audit 當下 PID 9747)
逾時提醒員
escalate_queue 還 pending → 提醒青哥(warn 2h / critical 24h / expire 7d)
com.qgc.overdue-reminder每 30 分鐘(StartInterval=1800)cron / loaded
API 額度警報員
OpenRouter credits 快沒 → 預警 / 對應 API 4 護欄
com.qgc.openrouter-alarm每 30 分鐘cron / loaded
I 區 · 環境變數(key 用途,不 dump value)
類別變數用途
LINELINE_CHANNEL_ACCESS_TOKENpush / profile API 認證
LINE_CHANNEL_SECRETwebhook HMAC 簽章驗證
LINE_OA_CHAT_IDLINE OA 後台 chat.line.biz 路徑(v9.2 件 3 inline button URL 用)
TGTG_BOT_TOKEN / TG_EDITOR_CHAT_ID原 ThAutoPost 共用 bot(escalate brief / 5/2 拍板不開新 bot)
CHING_FACTORY_BOT_TOKEN / CHING_FACTORY_CHAT_ID青哥 TG @ching_factory_bot 推 escalate brief 用
TEACH_BOT_TOKEN@qing_factory_v7_teach_bot 教學通道
V8_REVIEW_BOT_TOKEN / V8_REVIEW_MODE / REVIEW_TG_CHAT_ID品檢站開關(Stage A → B cutover)
OpenRouterOPENROUTER_API_KEYLLM 認證
OPENROUTER_MODEL預設 google/gemini-2.5-flash
OPENROUTER_BASE_URL預設 https://openrouter.ai/api/v1
OPENROUTER_TIMEOUT_S每輪 LLM call timeout,預設 8 秒
OPENROUTER_REFERER / OPENROUTER_TITLEOpenRouter 流量來源 metadata
資料 / 路徑QING_DB_PATHSQLite 路徑(預設 ~/qing_factory_data/qing.db)
QING_TOOLS_DIR工具書資料夾(預設 ~/qing_factory_tools/)
SYSTEM_PROMPT_PATHpersona_xiaoqing.md 路徑
Worker / KPIWORKER_BATCH_SIZE / WORKER_POLL_INTERVALworker 拉佇列頻率
JUDGE_LOOKBACK_HOURS / JUDGE_SAMPLE_RATEclassify_batch 自動分類抽樣
HUMAN_RATE_TWD_PER_HOUR / MIN_PER_TICKETKPI 試算(節省人工成本)
J 區 · 觸發機制 + 配套(Trigger → Reaction)
觸發事件反應
客戶送 LINE 訊息HMAC 簽章驗證 → 入待處理倉 → 200 OK 給 LINE → worker 背景拉 → AI 大腦 run → push 客戶
LLM reply 含 [TRIGGER_QUOTE]對講機 notify_editor → escalate_queue 入隊 quote → @ching_factory_bot push brief(inline button「💬 跳 LINE OA 後台」)
LLM reply 含 [TRANSFER_HUMAN]對講機 notify_editor_transfer → escalate_queue 入隊 transfer → @ching_factory_bot push 轉真人 brief
LLM reply 含 [BRIEF_SUMMARY:...]worker 抽 tag 內容當 escalate brief 的「需求摘要」(v9.1 件 1 / 4 行 label 格式)
LINE 群組訊息(補4)不寫客戶資料夾 / 直接 push 青哥
同 message_id 重來(補2)history / queue idempotency check → skip
每 30 分鐘 cron逾時提醒員掃 escalate_queue:warn 2h / critical 24h / 7 天 expired
每 30 分鐘 cronOpenRouter credits 警報員(API 4 護欄)
customers.display_name 沒v9.2 件 3:worker 即時 call line_api.fetch_display_name → 寫回 cache(brief 顯示真名)
K 區 · 已知設計選擇 / 拍板(PM 看的決策史)
  • B 方案 escalate brief(5/2 PM 拍):TG 推 brief / 青哥用 LINE OA 後台直接回客戶 / TG 不收 reply 回流。
  • 教學通道 TG only:Strider 改 persona / 工具書一律走 @qing_factory_v7_teach_bot / 不混 LINE。
  • v9.2 件 3 deep link Fallback A(5/5 search-confirmed):LINE Messaging API userId ≠ OAM userId,LINE 沒公開 mapping API → button URL 砍 user_id 後綴 / 跳 OA chat 主頁 / 小編在未讀第一筆找剛 escalate 的客戶。
  • v9.3 worker sanity check trigger 改 escalate-tag-driven(5/5 round 5 audit 修 3 紅):worker 偵測 `[TRIGGER_QUOTE]` / `[TRANSFER_HUMAN]` 任一 → 必含 `[BRIEF_SUMMARY:]`;舊版用「push 字眼」trigger 改成 escalate tag 自身 trigger。
  • v9.4 鐵律 8 資訊量底線(5/6 修 brief #23 真實踩雷):brief 至少 1 件具體(車款 / 想改部位 / 預算)才能 push escalate;模糊意圖(「我要改車 / 想改」)→ 純釐清不 push 空 brief。
  • 3 層 system prompt cache:persona / business_rules / customer-specific 分離,吃 OpenRouter prefix-cache。
  • API 4 護欄:prepaid only / 月 hard cap / 80%-100% 預警 / API key 隔離(對應 SHARED_RULES)。