---
title: "讓整個團隊用 AI Agent：訊息從哪進來、模型從哪出去"
url: https://geminixiang.com/posts/team-ai-agent-gateways/
description: "團隊導入 AI agent 時，agent 本身反而不是最難的。記錄我在工作上做訊息 gateway（各家通訊軟體進 Slack）和參與 LLM gateway 的心得：路由要用完整 key、附件不能外洩 token、模型統一出口、每個人一把 key。"
published: 2026-10-19
category: "AI Agent"
tags: ["AI Agent", "LLM Gateway", "Slack", "Go", "平台工程"]
---

# 讓整個團隊用 AI Agent：訊息從哪進來、模型從哪出去

之前寫過 [mikan 的開發心得](/posts/mikan-development-reflection/)，講的是怎麼把 coding agent 搬進聊天室

這篇想講另一面：如果不是自己玩，而是要讓整個團隊每天用，還缺什麼

這些是工作上的東西，程式碼沒有公開，所以只講設計跟踩過的坑

結論先講：agent 本身反而不是最難的，難的是它前後的兩個 gateway

```text
各種通訊軟體 → 訊息 gateway → Slack ← 人和 agent 一起工作
                                       ↓
                               LLM gateway → 各家模型
```

## 訊息 gateway：所有對話都進 Slack

團隊每天要處理的對話散在 LINE、WhatsApp、Telegram、Google Chat……各種地方

人要一直切 app，agent 更慘，每個平台都要接一次

所以我做了一個 Go 寫的 gateway：外面的每一段對話，自動在 Slack 開一個專屬 channel，訊息轉進來；在 Slack 回覆，再送回原本的平台

這樣人跟 agent 都只要看 Slack 就好

agent 只要會 Slack，就等於會所有平台

### 路由不能用名字

一開始 channel 名稱用 `#{platform}-{chatId 前 8 碼}`，看起來很合理

結果 WhatsApp 群組的 ID 前 8 碼會撞，好幾個群組的訊息被合併進同一個 channel

這種 bug 很恐怖，訊息會送給錯的人

後來的規則是：路由只認完整的 `{platform}:{ChatID}`，對應到 Slack 不會變的 channel ID，存在 SQLite

channel 名稱純粹是給人看的，在 Slack 隨便改名都不影響轉發

名字撞到就加 `-2`、`-3`，絕對不會因為名字一樣就當成同一段對話

### 附件不能直接貼連結

最偷懶的做法是把原平台的附件網址直接貼到 Slack

但 [Telegram Bot API 的檔案網址](https://core.telegram.org/bots/api#getfile)裡面帶著 bot token，貼出去等於把 token 公開；Discord 的 CDN 連結還會過期

所以附件一律下載後重新上傳成 Slack 原生檔案，而且用 streaming，不整個讀進記憶體

抓附件的時候還要防 [SSRF](https://owasp.org/www-community/attacks/Server_Side_Request_Forgery)：只允許 HTTPS、預設 port，而且 IP 是在建立連線的那一刻才檢查，不管網址、DNS 還是 redirect 怎麼繞，都連不到內網或 metadata

### 同一則訊息被處理兩次

最近才修的一個：同一個 Slack channel 裡同時有兩個我們的 app 時，一則訊息會被收到兩次，下游也跟著處理兩次

改成所有 Slack adapter 共用一個去重表，用 `(channel, ts)` 當 key，只處理第一份

> p.s. 這種「理論上只會發生一次」的事，上線之後一定會發生，去重跟冪等最好一開始就想

## LLM gateway：模型統一一個出口

這部分是團隊一起做的，我參與其中一部分

一開始大家各自拿 API key，各自打各家的模型，很快就會遇到

- key 散在每個人的電腦、每個服務的設定檔
- 不知道錢花在哪、誰在用、用哪個模型
- 某家掛掉，所有東西一起掛

所以做了一個 OpenAI 相容的 LLM gateway，所有 agent 跟工具都只打它

- 後面接 OpenAI、Anthropic、Gemini 等不同 provider，可以設定 fallback，某家掛了自動換下一家
- 每個人、每個服務一把自己的 virtual key，可以單獨撤銷，不用換掉整組
- 每個 request 都記用量跟成本，可以看到誰用了多少、用哪個模型
- audit log 有保留期限，過期就清掉

因為是 OpenAI 相容的 API，換成用 gateway 只要改 base URL 跟 key，現有工具幾乎不用動

類似的開源方案有 [LiteLLM Proxy](https://docs.litellm.ai/docs/simple_proxy)，需求單純的話直接用它就好

我們自己寫是因為要跟其他內部工具整合

### 錯誤要原文記下來

一開始 gateway 的錯誤訊息是自己整理過的，看起來比較乾淨

結果出事的時候根本查不到上游到底回了什麼

後來改成每次失敗都把上游的回應原文記下來，串流到一半斷掉的也是

gateway 是所有人的必經之路，它的 log 就是整個團隊除錯的起點，不能自作聰明

## 給 agent 寫的程式定規矩

這兩個 gateway 很多程式碼都是 agent 寫的，所以團隊也定了一些原則，其中我覺得最有用的兩條

- 每個功能都要能從 CLI 測試：測試要跑真的 build 出來的 binary，檢查輸出跟結果，不能只測內部函式
- 一般測試不能依賴私人環境：不用個人帳號、不用付費模型，外部服務一律用 fake upstream

agent 很會寫出「看起來會動」的程式，這兩條讓它必須證明真的會動

## 心得

- 團隊導入 agent，先把入口跟出口收斂，agent 才有地方住
- 路由一定要用完整、不會變的 ID，名字只是給人看的
- 任何外部網址都可能帶 token 或指向內網，一律重新上傳、一律防 SSRF
- 模型統一出口，key、成本、fallback、audit 才管得起來
- gateway 的錯誤原文記下來，不要美化

## 參考資料

- [mikan 開發心得](/posts/mikan-development-reflection/)
- [Telegram Bot API: getFile](https://core.telegram.org/bots/api#getfile)
- [OWASP: Server Side Request Forgery](https://owasp.org/www-community/attacks/Server_Side_Request_Forgery)
- [LiteLLM Proxy](https://docs.litellm.ai/docs/simple_proxy)

---

## About the author

**Ying Xiang Zhao** (geminixiang) — DevOps Engineer, Taipei, Taiwan.

Ying Xiang Zhao（geminixiang）是住在台北的 DevOps 工程師，工作橫跨雲原生基礎設施、CI/CD、ML 模型部署與全端開發。這個部落格記錄 infra、資安與 AI coding agent 的實戰筆記；作品包含開源專案 mikan（把 AI coding agent 帶進 Slack / Telegram / Discord）與 tiny-agent（用四種語言實作的極簡 coding agent），瀏覽器內的影片編輯器 Studio，並經營旅遊分帳工具 SentriPay。

**Open to work** — DevOps Engineer / Platform Engineer / Site Reliability Engineer / AI Infrastructure Engineer · Taipei, Taiwan / Remote. Résumé: https://geminixiang.com/about/ (JSON: https://geminixiang.com/resume.json)

Topics: DevOps, Cloud Native, Kubernetes, Docker, Terraform, Google Cloud, CI/CD, Security, AI coding agents, ML model deployment, TypeScript, Python

Projects:
- [mikan](https://github.com/geminixiang/mikan) — Self-hosted AI coding agent for Slack, Telegram, Discord and GitHub with per-conversation sandboxes
- [tiny-agent](https://github.com/geminixiang/tiny-agent) — Minimal AI coding agents in TypeScript, Go, Python and Rust, plus a book on how they work
- [SentriPay](https://sentripay.com) — Travel expense splitter with multi-currency conversion and shareable, live-synced trip ledgers
- [Studio](https://studio.geminixiang.com) — Browser-based video editor built on WebCodecs

[About](https://geminixiang.com/about/) · [Résumé](https://geminixiang.com/about/) · [LinkedIn](https://linkedin.com/in/geminixiang/) · [GitHub](https://github.com/geminixiang) · [Twitter](https://x.com/geminixiang)
