Featured image of post CopilotKit 开源了 OpenTag!AG-UI 协议驱动的跨平台聊天 Agent,一个深入源码的架构拆解

CopilotKit 开源了 OpenTag!AG-UI 协议驱动的跨平台聊天 Agent,一个深入源码的架构拆解

你团队里用 Claude in Slack 吗?每月 $25/人、对话不能持久化、MCP 工具不支持、想改行为还要写 Support Ticket?CopilotKit 刚开源的 OpenTag,就是冲这个痛点的——它让你在自己的基础设施上跑一个和 Claude in Slack 一模一样的 AI Agent,还能接自己的模型、挂自己的 MCP 工具、渲染图表和卡片。

更让我兴奋的是它的架构设计。OpenTag 不是又一个「把 LLM 接入 Slack」的玩具,它背后有一套清晰的四层协议栈和一个可替换的双运行时设计,和之前拆解过的 NanobotPi Agent 走的是完全不同的技术路线。这篇文章从源码出发,拆解 OpenTag 的每一层。

700+ Star 的 Slack Agent 开源替代

先给个全景感:OpenTag 是 CopilotKit 基于其 @copilotkit/channels SDK 构建的参考实现,MIT 协议开源,GitHub 上不到两周就攒了 700+ Star。

它的定位非常明确——一个可以自托管的 Claude in Slack 替代品

  • 在 Slack 频道里 @mention 机器人,它就能读对话、回答问题、调用 MCP 工具
  • 生成的响应不是纯文本,而是原生 UI 组件(Slack Block Kit、Discord Components V2、Telegram HTML)
  • 所有写操作(创建 Linear Issue、写 Notion 文档)都经过人工确认门(Human-in-the-Loop)
  • 支持四种聊天平台:Slack、Discord、Telegram、WhatsApp——同一套代码,一个 createBot 启动全部

听起来和 Nanobot 有点像?确实,都是「把 Agent 放进聊天工具」的路子。但 OpenTag 在架构上走了完全不同的方向——它用了一套叫 AG-UI 的协议把 Bot 层和 Agent 层解耦,而不是像 Nanobot 那样用 asyncio.Queue 内部耦合。

四层架构:从聊天界面到 Agent 大脑

OpenTag 的架构可以拆成四层,每层职责单一、通过协议通信:

用户聊天界面 (Slack/Discord/Telegram/WhatsApp)
       │
       ▼  @mention  /command
┌─────────────────────────────┐
│   Bot 层 (app/)             │   ← createBot({ adapters, tools, components })
│   平台适配器 · 工具转发      │
│   UI 组件渲染 · HITL 门     │
│   Read Thread · Slash 命令  │
└──────────┬──────────────────┘
           │ AG-UI 协议 (POST /api/copilotkit/agent/triage/run)
           ▼
┌─────────────────────────────┐
│   Agent 运行时              │   ← 可替换:runtime.ts 或 agent/
│   BuiltInAgent (TS)         │
│   或 Deep Agent (Python)    │
│   LLM + MCP 工具            │
└──────────┬──────────────────┘
           │ MCP (Streamable HTTP)
           ▼
┌─────────────────────────────┐
│   数据层                    │
│   Linear MCP · Notion MCP   │
│   Web Search (OpenAI)       │
└─────────────────────────────┘

这个分层和 Pi Agent 的「Provider → Steering → Follow-up」三层有点像,但 OpenTag 用的是一个公开协议 (AG-UI) 来连接 Bot 和 Agent,而不是内部函数调用。这意味着你可以把 Bot 层换成任意客户端,只要它实现了 AG-UI 协议。

AG-UI 协议:Bot 和 Agent 之间的「通用语」

AG-UI 是 CopilotKit 定义的一套 Agent-to-UI 协议,本质上是一个 HTTP SSE 流。Bot 层把用户消息、上下文、前端工具声明打包成 AG-UI 请求发给 Agent,Agent 返回一个事件流——LLM 的 tokens、工具调用请求、UI 组件渲染指令都在这个流里传输。

runtime.ts 里,Agent 端只有几十行代码:

const runtime = new CopilotSseRuntime({
  agents: { triage: agent },
});
const listener = createCopilotNodeListener({ runtime, basePath: "/api/copilotkit" });
createServer(listener).listen(8200);

Bot 端用 AGENT_URL 指向这个地址,两边就通了。这意味着你有一个 Agent 运行在 GPU 集群上,Bot 运行在 Slack 旁边——只要网络可达,它们就能通信。这和 Grok Build 的 ACP 协议(参考四大协议详解)思路一致:把 Agent 的「大脑」和「嘴巴」拆开,通过标准协议连接。

双运行时设计:简单场景 vs 深度研究

OpenTag 最让我感兴趣的设计决策是提供了两个完全不同的 Agent 后端

① TypeScript BuiltInAgent (runtime.ts)

一个单 LLM 调用 + MCP 工具的轻量运行时,适合大多数日常对话场景。核心代码不到 100 行 TypeScript:

const agent = new BuiltInAgent({
  type: "tanstack",
  factory: async (ctx) => {
    return chat({
      adapter: openaiText(model),
      messages,
      systemPrompts: [SYSTEM_PROMPT],
      tools: [webSearchTool({ type: "web_search" }), ...clientTools],
      ...(mcpClients.length > 0 ? { mcp: { clients: mcpClients } } : {}),
    });
  },
});

它用 TanStack AI 的 chat() 做多轮工具循环,MCP 客户端在每次 turn 开始时创建、结束时关闭——临时连接模式,避免长时间持有 MCP 连接。

② Python Deep Agent (agent/)

一个基于 LangGraph + Deep Agents 的深度研究 Agent,用了 write_todos 做任务规划、虚拟文件系统做中间持久化、可选的 Tavily 做网络搜索。它不是简单的一轮 LLM 调用,而是:

  1. write_todos 制定研究计划
  2. 逐个执行研究步骤(通过 research() 工具调用内部子 Agent)
  3. 把发现写入虚拟文件系统
  4. 合成最终报告

切换:改 .env 里的 AGENT_URL 就行——http://localhost:8200 用 TS,http://localhost:8123 用 Python。Bot 层完全不知道背后换了个大脑。

这比 Nanobot 的「单 Agent Loop」更灵活。Nanobot 的 TurnState 只有一套 8 状态机(RESTORE→COMPACT→COMMAND→BUILD→RUN→SAVE→RESPOND→DONE),而 OpenTag 让你在不同 Agent 实现之间切换,甚至可以在生产环境用 TS 运行时做日常对话、用 Python Deep Agent 做深度研究,通过路由层分发。

跨平台 UI 渲染:JSX 编译成原生组件

这是 OpenTag 最亮眼的技术点。@copilotkit/channels-ui 定义了一套跨平台 JSX 组件,编译时自动映射到各平台的原生 UI:

JSX 组件 Slack Discord Telegram
<Table> Block Kit Section Embed HTML <table>
<IssueCard> Block Kit Section Embed Markdown
<Button> Button Element Button Component HTML <button>
<Modal> Modal View Modal 降级为对话流

这个设计的巧妙之处在于:Agent 只需要调用 render_chartrender_tableissue_card 等工具,Bot 层负责把工具调用翻译成对应平台的 UI 渲染。Agent 不关心用户用的是 Slack 还是 Telegram。

对比来看,Nanobot 的 15+ 平台支持也是类似的思路(Channel 接口抽象),但 Nanobot 的输出是纯文本 + Markdown,没有原生 UI 组件渲染。OpenTag 的 JSX 编译方案更接近「一次编写,到处原生渲染」的理想。

MCP 集成与容错设计

OpenTag 的 MCP 集成有两个值得关注的设计:

1. 独立的 MCP 连接超时容错

runtime.ts 里,每个 MCP 服务器独立连接,失败不影响其他服务器:

const settled = await Promise.allSettled(
  transports.map((t) => connectMcp(t.transport)),
);

失败的 MCP 服务器被记录到 unavailable[],系统提示词自动追加一条「数据源状态」说明,告诉模型哪些源当前不可用。模型只在用户明确需要那个源时才告知不可用——不会自己主动说,避免打断对话。

2. 热度超时 + 泄漏防护

connectMcp() 有一个 8 秒超时机制。如果超时先到达,它会确保后续返回的 MCP 客户端被立即关闭:

connecting.then(
  (client) => { if (timedOut) void client.close().catch(() => {}); },
  () => {}, // 延迟拒绝不能 crash 进程
);

这是个教科书级的异步容错模式——我觉得 MCPZERO 的 MCP 连接池也可以借鉴这个思路。

Human-in-the-Loop:写操作必须先确认

所有写操作(创建 Linear Issue、写 Notion 文档)都经过一个 confirm_write 阻塞门。Agent 在写操作前必须调用 confirm_write 工具,等待用户在聊天界面点击 CreateCancel

这个门在 app/human-in-the-loop/confirm-write.tsx 里实现为阻塞 UI 组件——Agent 无法继续,直到用户做出决定。不像某些 Agent 框架的「先写再确认」,OpenTag 是「确认了才能写」,避免了误操作。

三方横向对比

OpenTag、Nanobot 和 Pi Agent 都算是「让 Agent 接入聊天工具」的方向,但技术路线差异很大:

维度 OpenTag Nanobot Pi Agent
语言 TypeScript (+ Python 可选) Python 3.11+ TypeScript
核心协议 AG-UI (HTTP SSE) asyncio.Queue (内存) 无标准化协议
Agent 运行时 CopilotKit BuiltInAgent / LangGraph Deep Agent AgentLoop + AgentRunner Steering + Follow-up 双队列
多平台 Slack/Discord/Telegram/WhatsApp 15+ 聊天平台 30+ Provider (非聊天平台)
UI 渲染 JSX → 原生组件编译 纯文本/Markdown 纯文本/Markdown
工具系统 MCP (Streamable HTTP) Python Tool 类 Plugin 系统
人工确认 阻塞式 confirm_write 无内置 HITL 无内置 HITL
模型 OpenAI-only (当前) 多 Provider 抽象 30+ Provider 抽象
部署 Railway one-click 传统部署 传统部署
记忆系统 无(对话级) Dream 两阶段记忆合并 JSONL 树形分支会话

核心差异点:OpenTag 走的是协议驱动架构(AG-UI 解耦 Bot 和 Agent),Nanobot 走的是事件驱动架构(MessageBus 解耦模块),Pi Agent 走的是队列驱动架构(Steering + Follow-up 流水线)。三个方案都能把 Agent 放进聊天工具,但弹性和复杂度取舍不同。

安全隐患

OpenTag 的自托管模式要求 Bot 进程持有平台 Token(Slack Bot Token、Discord Bot Token 等),这意味着如果 Bot 进程被攻破,攻击者可以用这个 Token 冒充机器人在聊天工具里做任何事。

CopilotKit 对此的解决方案是 Intelligence Gateway 模式:Bot 进程不持有任何平台 Token,Token 由 CopilotKit Intelligence 平台保管,Bot 通过 WebSocket 连接到 Intelligence 的 Realtime Gateway。但这个模式目前还是 CopilotKit 的托管服务,不是开源方案——自托管的用户只能用 self-hosted 模式,把 Token 暴露在进程环境变量里。

另外,OpenTag 的 MCP 连接使用了 Bearer Token 认证(Authorization: Bearer),但通信没有端到端加密。如果 MCP 服务器和 Bot 进程之间的网络被嗅探,Token 和数据都可能泄露。这对 Notion MCP 侧车尤其关键——Notion 的 Integration Token 对所有 workspace 内容有读写权限。

相比之下,Nanobot 的 15+ 平台支持同样面临 Token 管理问题,但 Nanobot 的 Token 是每个 channel 实例化的,影响范围更小。Pi Agent 不直接处理聊天平台 Token,风险面更窄。

写在最后

OpenTag 的出现让我看到 Agent 聊天工具这个赛道正在快速成熟。CopilotKit 用 @copilotkit/channels SDK 把「把 Agent 放进聊天工具」这件事从手动工程变成了框架级能力,而 AG-UI 协议的设计让 Bot 和 Agent 可以独立演化——这是正确的方向。

当然,OpenTag 还远不是 Claude in Slack 的平替。它目前只支持 OpenAI 模型,没有持久化记忆,没有多租户隔离,安全模型也还在早期。但作为一个架构参考实现,它的设计思路——AG-UI 协议、双运行时、跨平台 JSX 渲染、MCP 容错——值得每一个做 Agent 基础设施的人认真看看。

GitHub: https://github.com/CopilotKit/OpenTag

延伸阅读:

By AI博士 万戈