[TOOLS] 15 分鐘閱讀OraCore 編輯部

OpenTag 讓 Slack 對話變動作

我拆 OpenTag 的 Slack agent 模板,重點是讀 thread、接工具、做富結果,最後還要人類按批准才執行。

分享 LinkedIn
OpenTag 讓 Slack 對話變動作

我拆 OpenTag 的 Slack agent 模板,重點是讀 thread、接工具、做富結果,最後還要人類按批准才執行。

我玩 Slack bot 玩到有點煩了。大多數都長一樣:你丟一句,它回一句,最多幫你把 thread 摘要一下,然後就沒了。看起來很像有做事,實際上只是把對話包裝得比較像樣。真正卡人的地方從來不是「會不會回訊息」,而是它能不能讀上下文、去查工具、把結果整理好,最後還知道什麼時候該停下來等人按一下批准。少了這個,bot 不是太膽小,就是太莽。

這次我會拆的是 OpenTag。它不是那種可愛的 Slack 小玩具,而是 CopilotKit 做的一個 self-hosted 模板,目標很明確:讓 agent 在 Slack 裡讀 thread、呼叫工具、渲染更豐富的輸出,然後在真正動手前先卡一個 human approval gate。這個 repo 目前有 554 stars、66 forks,數字不算誇張,但夠我確定一件事:大家都在找同一種工作流,只是大多數人還沒把它做對。

別再把 Slack bot 當自動回信機

訂閱 AI 趨勢週報

每週精選模型發布、工具應用與深度分析,直送信箱。不定期,不騷擾。

不會寄垃圾信,隨時可取消。

Run your own AI agent inside Slack: it reads a thread, answers, calls your tools, and renders rich results right in the conversation.

白話翻譯就是:這不是一個只會吐字的聊天機器人,而是一個住在 Slack 裡的工作流程引擎。它先看 thread,再決定要不要查資料、叫外部系統、或者把結果直接塞回對話裡。這跟那種「/summarize」指令 bot 完全不是同一個層級。

OpenTag 讓 Slack 對話變動作

我以前也踩過這種坑。內部支援頻道想做一個 bot,讓它幫忙看 incident thread,再順手查 Jira。結果它不是亂編 ticket number,就是乾脆裝死。那時候我才認清楚,問題不是 prompt 不夠長,而是架構根本沒給它做事的能力。你把它做成只能回答的東西,它就永遠只會回答。

OpenTag 的重點是 agent loop,不是 chatbot。Slack 訊息只是入口,真正的工作在 runtime 裡。這件事很重要,因為它讓 bot 有機會對流程有意見,而不是只對語言有意見。換句話說,它可以先判斷,再行動,而不是每次都把責任丟回人類。

實操寫法很簡單:先把你的 Slack bot 拆成三件事,摘要、查詢、動作。很多團隊只做到摘要就停了,然後就說「bot 很方便」。不,它只是省掉你複製貼上而已。你要的是能推進工作的 agent,不是更會講話的回覆機器。

self-hosted 不是附加價值,是這套玩法的核心

Think of it as having Claude in your workspace, except open-source and self-hosted: you own the runtime, bring your own model, and wire it to your own tools.

翻譯一下就是:你不是在租一個黑盒服務,你是在把 runtime 掌握在自己手上。模型你自己選,工具你自己接,資料流向也自己管。對很多團隊來說,這才是能不能上線的分水嶺。因為一旦 Slack 裡面開始碰敏感 thread,資安、合規、權限這些麻煩事就會自己冒出來。

我很討厭那種把 self-hosting 講得很輕鬆的文章。好像只要貼一個 docker-compose 就萬事大吉。實際上,agent 的麻煩從來不是模型本身,而是周邊:連線、重試、持久化、權限、觀測、秘密管理。OpenTag 至少沒有裝傻,它很直接地告訴你:要控制權就自己跑,要省 ops 就等別的產品形態。

這種誠實我反而買單。因為我見過太多 starter kit 把部署複雜度藏起來,等你真的接進公司流程,才發現 bot、runtime、storage 全黏在一起,改一個地方就像拆炸彈。self-hosted 的價值不是便宜,是你能決定邊界在哪裡。

  • 如果你們有 compliance 顧慮,self-hosted 是必要條件,不是加分項。
  • 如果你們只想先驗證流程,這個 repo 也可以當 reference implementation。
  • 如果你們之後想換部署方式,先把架構拆開,之後才有退路。

實操寫法:先問自己,這個 bot 是產品還是模板。是產品,就從第一天把 hosting、secret、persistence 一起算進去。是模板,就先複製架構,別急著把所有東西都做成正式服務。

真正值錢的是薄薄一層組裝,不是大而全框架

OpenTag is a thin layer on top of a handful of CopilotKit packages.

也就是說,這 repo 不是把你鎖進一個大框架裡。它比較像把幾個小零件拼起來:bot engine、agent runtime、rich UI、平台 adapter。這種做法我比較舒服,因為你知道每一層在幹嘛,也比較不會被抽象層搞到頭痛。

OpenTag 讓 Slack 對話變動作

它把需要的套件講得很明白,像 @copilotkit/bot@copilotkit/runtime@copilotkit/bot-ui@copilotkit/bot-slack。另外還有 Discord、Telegram、WhatsApp 這類 adapter,還有 Redis persistence 這種你真的需要時才會碰的東西。這種「先把邊界講清楚」的文件,我看了就比較不想翻白眼。

我以前被那種「超簡單 starter」整過。文件寫得像一鍵啟動,結果你一進去才發現 bot 層、UI 層、model 層全部焊死。你想換一個工具?先把整個專案摸懂再說。OpenTag 至少把 seam 留給你看,這很難得。

實操寫法:你自己做 agent starter 時,先不要寫 code,先畫四格。第一格是 transport,第二格是 runtime,第三格是 UI,第四格是 storage。任何功能如果塞不進這四格之一,那它多半應該是獨立模組,不該硬塞進核心。

Slack app 設定很煩,所以要把它做得無聊

Create a Slack app. At api.slack.com/apps → From a manifest → paste slack-app-manifest.yaml.

白話翻譯就是:別再手工點 Slack 後台點到懷疑人生了,直接用 manifest。這招很土,但真的省時間。尤其當你只是想先把 bot 跑起來,不想先被 UI 卡死,manifest-driven setup 就是正解。

OpenTag 的 quickstart 也很務實:先建 Slack app、安裝、拿 Bot User OAuth Token 和 App-Level Token,把幾個 secret 塞進 .env,然後把 agent 和 bot 分成兩個 process 跑。我喜歡這種切法,因為它把 mental model 變乾淨了。bot 負責連 Slack,agent 負責推理和工具呼叫,兩邊分開,出問題才知道該怪誰。

我自己做過一個內部 workspace 的支援 assistant,最痛的不是模型,而是 debug。只要 bot 和 runtime 混在同一個 process,任何錯誤都像鬼打牆。分開之後就好多了:Slack 壞了是 Slack 壞,模型壞了是模型壞,工具壞了是工具壞。工程上這種可切分性,比看起來聰明重要太多。

  • 用 manifest 建 app,少掉一堆手動設定。
  • Token 跟 model key 全部走環境變數,不要寫死。
  • bot 和 runtime 分開跑,debug 會少很多怨氣。

實操寫法:就算你不用 OpenTag,也照這個 operational shape 做。manifest、secrets、separate runtime、separate connector。這套不花俏,但不容易炸。

要能動手,但先讓人類按鈕

It renders a breakdown, a table, and a bar chart inline and files a ticket only after an Approve gate.

翻譯一下就是:bot 可以做事,但不能自己當老大。它先把分析結果攤開,必要時還能把表格、圖表直接塞在 Slack 裡,然後在真的要建立 ticket 之前,先等人按批准。這個順序我很在意,因為它把「有用」跟「亂搞」切開了。

很多 agent demo 都很愛秀自動化,結果一上線就出事。某個 noisy incident thread,bot 一口氣幫你開五張 ticket,然後大家開始花時間清理 bot 的錯,而不是處理原本的問題。這種事我看過太多次,所以我現在只相信一件事:凡是會產生 side effect 的動作,都要先過人類。

OpenTag 的好處是它把 approval gate 做成流程的一部分,不是事後補丁。使用者在 Slack 裡就能看到 bot 找到什麼、打算做什麼、會改什麼,然後再決定要不要放行。這比把人導去另一個系統按確認強很多,因為 context 沒有被切斷。

實操寫法:你只要記住一條線。會改狀態、會發訊息、會建立工單、會通知別人的動作,都先擋一下。只讀查詢可以自動做,真正會留下痕跡的動作,先讓人看過再說。這不是保守,這是避免 bot 把你搞成救火隊。

一個 prompt 檔,比五層抽象更有用

The agent's behavior is steered by a single system prompt in runtime.ts — rewrite it and you have a different agent.

也就是說,這套東西不是把行為藏在一堆 policy 和 callback 裡,而是盡量集中在一個地方。你改 prompt,agent 的個性、範圍、邊界就跟著變。這種設計我很愛,因為它讓實驗成本低很多。

repo 也很直接地說,你可以直接複製 app/ 當起點,而 runtime.ts 就是 agent backend,透過 AG-UI 對外服務。這個切法讓你可以先專注在一個 Slack bot 上,把工具、approval、prompt 都跑順,再慢慢加別的平台。這比一開始就想做全通路 agent 實際多了。

我最怕那種行為散在十幾個檔案的專案。寫的人以為自己很有架構,改的人只會想離職。OpenTag 這種「行為集中、邊界清楚」的做法,才真的有人會去迭代。因為你知道要改哪裡,也知道改了會影響什麼。

實操寫法:第一版先把 agent 做得很醜也沒關係,但一定要可改。只有一個 system prompt、只有一個 runtime entrypoint、只有一層 adapter。你如果連「行為在哪裡」都講不清楚,後面只會越做越亂。

可抄的模板

# OpenTag 風格的 Slack agent 模板(可直接改成你的專案骨架)

## 目標
- 讀 Slack thread
- 摘要上下文
- 需要時呼叫工具
- 在 Slack 內回傳富格式結果
- 任何 side effect 先走 human approval

## 專案結構
app/
  bot/                # Slack adapter、message routing
  runtime/            # LLM runtime、tools、system prompt
  ui/                 # 富訊息元件、表格、圖表
  tools/              # Jira、Linear、Notion、DB 查詢等整合
  config/             # env 讀取與驗證

scripts/
  start-bot.sh
  start-runtime.sh

.env.example
README.md

## 環境變數
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_APP_TOKEN=xapp-your-app-token
OPENAI_API_KEY=your-model-key
# 或 ANTHROPIC_API_KEY=your-model-key
REDIS_URL=redis://localhost:6379

## System prompt
你是一個 Slack workspace agent。

目標:
1. 仔細讀 thread。
2. 摘要相關上下文。
3. 只有在有價值時才呼叫工具。
4. 在 Slack 內用結構化方式回報結果。
5. 任何 side effect 都要先得到明確的人類批准。

規則:
- thread 不清楚時先問澄清問題。
- 回答要短、可執行。
- 提議動作時,先講清楚為什麼需要它。
- 工具失敗時要直接回報,不要硬掰。
- 在批准前,不要建立 ticket、不要送訊息、不要改資料。

## Approval gate
在任何 side effect 前,先渲染:
- 你找到什麼
- 你打算做什麼
- 會改變什麼
- 明確的 Approve / Reject 按鈕

## 工具範例
{
  "name": "create_ticket",
  "description": "建立 issue tracker ticket",
  "input_schema": {
    "type": "object",
    "properties": {
      "title": { "type": "string" },
      "description": { "type": "string" },
      "priority": { "type": "string" }
    },
    "required": ["title", "description"]
  }
}

## Runtime entrypoint
export async function main() {
  const bot = createBot({
    adapters: [createSlackAdapter({ token: process.env.SLACK_BOT_TOKEN })],
    runtime: createRuntime({
      model: process.env.OPENAI_API_KEY ? "openai" : "anthropic",
      systemPrompt: SYSTEM_PROMPT,
      tools: [createTicketTool(), createLookupTool()],
      approvalRequiredFor: ["create_ticket", "send_message", "update_record"],
    }),
    persistence: process.env.REDIS_URL ? createRedisStore(process.env.REDIS_URL) : createMemoryStore(),
  });

  await bot.start();
}

## 我會怎麼用
1. 先把這段貼進新 repo。
2. 先接一個 Slack adapter。
3. 先做一個 read-only tool。
4. 再做一個需要 approval 的 side-effect tool。
5. prompt 保持可編輯。
6. Slack 跑通後,再考慮加其他平台。

## 開工檢查清單
- [ ] 用 manifest 建 Slack app
- [ ] bot 和 runtime 分開 process
- [ ] 一個可改的 system prompt
- [ ] side effect 前先做人類批准
- [ ] Redis persistence 視需要再加
- [ ] 工具邊界清楚
- [ ] 富格式輸出可讀

## 最小啟動命令
pnpm install
pnpm --filter slack-example runtime
pnpm --filter slack-example dev

這份模板我會直接拿去改,原因很單純:它把最難的幾件事都攤在檯面上了。你要換模型、要加 Discord、要改 approval policy,都不需要把整個系統拆掉重來。這才是能在團隊裡活下來的 starter。

OpenTag 是 CopilotKit 的原始作品,我上面這篇是照 repo 結構跟 setup 文件拆出來的讀法,不是官方文案。原始來源在 https://github.com/CopilotKit/OpenTag,setup 細節可看 setup.md。如果你想看它背後用到的組件,也可以對照 CopilotKitSlack app 文件,以及 OpenAI docsAnthropic docs