AutoGPT Platform CoPilot Bot 架构与实践:把 AutoGPT 接入 Discord、Slack 与 Telegram
本文基于 AutoGPT Platform 仓库中 copilot/bot 模块的官方文档(AGENTS.md 直接引用同目录 README.md 的全部内容)并结合实际源码编写,系统讲解 CoPilot Bot 的运行方式、必需环境变量、双类型适配器架构、消息处理与链接(linking)流程,以及接入新聊天平台的完整扩展路径。读完后你将能够:在本地或 Docker 环境中部署并调试该聊天桥接服务,理解 SocketAdapter 与 WebhookAdapter 的设计边界,并知道新增一个聊天平台适配器需要改动哪些文件。
CoPilot Bot 是什么,如何运行
CoPilot Bot 是一个多平台聊天机器人,把 AutoGPT(具体来说是 AutoPilot 会话能力)桥接到 Discord、Slack 和 Telegram 上,文档同时预告了 Teams/WhatsApp 的后续支持计划。它的核心价值在于:用户在熟悉的聊天软件里 @ 机器人或私聊,即可直接与 AutoGPT 对话、上传附件并流式接收回复,同时用量会计费到已链接的 AutoGPT 账户。
官方文档给出的两种运行方式是:
# 作为独立服务运行
poetry run copilot-bot
# 或者随整个平台一起自动启动
# (当 AUTOPILOT_BOT_DISCORD_TOKEN 已设置时,会一并把 bot 拉起来)
poetry run app
从源码看,poetry run copilot-bot 最终进入 入口文件,其 main() 直接调用 run_processes(CoPilotChatBridge()),即以 CoPilotChatBridge 这个 AppService 作为唯一进程入口;也可以等价的用 python -m backend.copilot.bot 启动。服务端口由 Settings().config.copilot_chat_bridge_port 决定(见 app.py)。
一个值得注意的健壮性设计:当没有任何 socket 适配器被配置(比如只部署了 webhook 平台、没设 Discord token)时,CoPilotChatBridge 不会退出,而是进入一个每 3600 秒睡一次的空转循环,保持健康检查端点可用,以便编排器做健康探测与运行时重配置(app.py)。同时 _on_adapters_exit 回调会把适配器任务崩溃时的健康标志置为 False 并清空适配器句柄,使后续出站 RPC 抛出 UnhealthyServiceError 而不是把消息派发到已经断开的网关连接上(app.py)。
本地 Docker 开发:platform_linking_manager 依赖
文档特别强调了一个容易被忽略的前置依赖:bot 的 link/unlink 流程需要通过集群内部 RPC 与 platform_linking_manager 服务通信。该服务在开发/生产环境中作为独立 pod 运行,但在本地是**可选开启(opt-in)**的——通过 bot 这个 Docker Compose profile 控制,避免拖慢常规的 docker compose up:
# 方式一:在现有栈旁边单独拉起 linking manager
docker compose up -d platform_linking_manager
# 方式二:通过 profile 标志启动(会一并拉起所有打了 bot 标签的服务)
docker compose --profile bot up -d
文档明确指出:如果不运行该服务,/setup 等发放 token 的流程会失败——后端尝试访问一个不存在的主机,报错表现为 httpx.ConnectError。因此本地调试 /setup、/unlink 或链接确认页流程时,先把 platform_linking_manager 拉起来是排障第一步。
必需环境变量与配置
官方文档要求参考 backend/.env.default 获取带完整注释的变量清单,其最小配置如下:
| 变量 | 用途 |
|---|---|
AUTOPILOT_BOT_DISCORD_TOKEN |
Discord 机器人 token——启用 Discord(socket 网关)适配器 |
AUTOPILOT_BOT_SLACK_TOKEN + AUTOPILOT_BOT_SLACK_SIGNING_SECRET |
Slack 机器人 token + 签名密钥——两者都设置后,Slack(webhook / Events API)适配器才会挂载到主后端 API 上 |
AUTOPILOT_BOT_TELEGRAM_TOKEN + AUTOPILOT_BOT_TELEGRAM_WEBHOOK_SECRET |
Telegram BotFather token + webhook 密钥——两者都设置后,Telegram(webhook / Bot API)适配器才会挂载,之后还需用 setWebhook 一次性注册 webhook(见下);同时需通过 BotFather 的 /setprivacy 关闭群组隐私模式,否则机器人在群组中看不到 @ 提及 |
FRONTEND_BASE_URL |
前端基础 URL,用于链接确认页面(与后端其余部分共享) |
REDIS_HOST / REDIS_PORT |
会话 + 线程订阅状态 + copilot 流订阅(继承自共享的后端配置) |
PLATFORMLINKINGMANAGER_HOST |
PlatformLinkingManager 服务 pod 的 DNS 名称(集群内部 RPC) |
对照 .env.default 中的实际条目,还有几个文档表格之外的补充项值得了解:
AUTOPILOT_BOT_SLACK_CLIENT_ID/AUTOPILOT_BOT_SLACK_CLIENT_SECRET:Slack 的 OAuth 应用凭据,用于多工作区的 “Add to Slack” 安装流程;注释说明静态 token 只是开发工作区的可选兜底,其他工作区安装时会各自通过 OAuth 获得 per-team token。这也解释了为什么 webhook_routes.py 中 Slack 适配器的挂载条件是“signing secret +(client id/secret 或 bot token)”三者满足其一组合。AUTOPILOT_BOT_TELEGRAM_USERNAME:不带 @ 的机器人名,用于生成 “Add bot to Telegram” 的 t.me 链接。.env.default中直接给出了 Telegram webhook 的一次性注册命令(注释形式):
curl "https://api.telegram.org/bot<TOKEN>/setWebhook" \
-d "url=<PLATFORM_BASE_URL>/api/copilot-webhooks/telegram/updates" \
-d "secret_token=<AUTOPILOT_BOT_TELEGRAM_WEBHOOK_SECRET>" \
-d 'allowed_updates=["message","my_chat_member"]'
- 同文件还定义了
PLATFORM_LINK_BASE_URL(默认http://localhost:3000/link),即链接确认页所指向的前端路由。
另外,两个与“token 成对出现”相对应的适配器启用逻辑可以直接在工厂函数中验证:Discord 由 _build_socket_adapters 中的 discord_config.get_bot_token() 门控(app.py),Slack/Telegram 由 build_webhook_adapters 中的凭据判断门控(webhook_routes.py)。
目录结构与模块职责
官方文档给出的 bot/ 目录架构如下,基本与仓库现状一一对应:
bot/
├── app.py # CoPilotChatBridge(AppService),适配器工厂,出站 @expose RPC
├── config.py # 共享的(与平台无关的)配置
├── handler.py # 编排器:路由、链接检查、附件摄取、批处理
├── turn_stream.py # 流式发送一个批次化回合:分块发送、产物、改名
├── prompt.py # Prompt + 线程命名组装
├── attachments.py # 附件上传 + 失败说明
├── command_core.py # 共享的 /setup + /unlink 策略(各适配器负责渲染)
├── bot_backend.py # PlatformLinkingManagerClient + stream_registry 之上的薄门面
├── text.py # 文本切分 + 批次格式化
├── threads.py # 基于 Redis 的线程订阅跟踪
├── webhook_routes.py # 把 webhook 适配器的入站路由挂载到主 API 上
└── adapters/
├── base.py # PlatformAdapter(出站契约)+ SocketAdapter / WebhookAdapter + MessageContext
├── shared.py # 平台无关的适配器辅助函数(附件、历史预算、防机器人死循环)
├── discord/ # SocketAdapter — Discord Gateway
│ ├── adapter.py # 网关连接、事件、发送、创建线程
│ ├── commands.py # 斜杠命令(/setup、/help、/unlink)
│ └── config.py # Discord token + 平台限制参数
├── slack/ # WebhookAdapter — Slack Events API
│ ├── adapter.py # 入站事件/命令路由、发送、mrkdwn、附件
│ ├── commands.py # 斜杠命令(/setup、/help、/unlink)
│ ├── config.py # Slack token + 签名密钥 + 平台限制参数
│ ├── signing.py # HMAC-SHA256 请求签名验证
│ ├── text.py # CommonMark → Slack mrkdwn
│ └── app-manifest.yaml # 可导入的 Slack 应用定义(scopes、events、commands)
└── telegram/ # WebhookAdapter — Telegram Bot API
├── adapter.py # 入站 updates 路由、发送、chat 模型映射
├── api_client.py # 薄封装的 httpx Bot API 客户端(JSON + multipart + getFile)
├── commands.py # Bot 命令(/setup、/help、/unlink)
├── config.py # BotFather token + webhook 密钥 + 平台限制参数
└── text.py # CommonMark → Telegram HTML
实际仓库中还包含若干文档树未列出的文件,如 outbound.py(出站投递的实现,被 app.py 的 RPC 端点调用)、sessions.py(会话缓存)以及各平台/模块的 *_test.py 测试文件,可作为延伸阅读。
连接器分类学(Connector taxonomy)
这是该模块最核心的设计决策。PlatformAdapter 是核心 handler 所对话的出站契约,它从不出现具体平台名。具体适配器按入站事件到达方式分为两个子类型:
SocketAdapter——持有长连接(Discord Gateway、Slack Socket Mode)。由start/stop驱动,运行在copilot-botpod 中,由 app.py 的_build_socket_adapters工厂构建;WebhookAdapter——接收入站 HTTPS POST(Slack Events API、Telegram、以及未来的 Teams、WhatsApp)。它是无状态的,register_routes(app)会把它挂载到主后端 API 上(经 webhook_routes.py 的register_webhook_adapters),从而直接复用现有的 N 副本部署——不需要专门 pod,天然获得水平扩展。
文档同时给出了一条明确的局部性规则(Locality rule):一切平台特定代码都放在 adapters/<platform>/ 之下;整个 bot/ 目录里只有上述两个工厂函数会指名具体平台,它们的职责仅是根据哪些 token 被设置来决定实例化哪些适配器。抽象契约本身可在 adapters/base.py 中查到,包括 MessageCallback 回调签名、ChannelType(dm/channel/thread 三种渠道类型)以及 MessageHistoryEntry、FileAttachment、InboundAttachment 等数据结构。
消息处理流程(How messaging works)
官方文档将消息流程概括为四步,这里完整继承并结合 handler.py 的源码补充细节:
- 用户在某个频道里 @ 机器人;
- 适配器的
on_message回调触发,构造一个MessageContext并交给共享的MessageHandler; - Handler 依次完成:
- 检查用户/服务器是否已链接(经由
bot_backend); - 未链接 → 发送带 “Link Account” 按钮的提示(对 DM 用户走
create_user_link_token,对未链接服务器提示 “Ask a server admin to run/setupfirst”); - 已链接 → 对频道消息创建线程、对 DM/已有线程则直接使用现有目标;
- 在 Redis 中把该线程标记为已订阅(7 天 TTL);
- 以适配器的
chunk_flush_at为分块边界,流式回传 AutoPilot 的响应;
- 检查用户/服务器是否已链接(经由
- 在流式响应进行中到达的消息会被批处理(batched),当前流结束后作为单个后续回合一次性发出。
源码中的几个关键实现细节值得展开:
- 线程订阅语义(threads.py):Redis 键格式为
copilot-bot:thread:{platform}:{thread_id},订阅写入 7 天 TTL 自动过期。handler 中有一条重要的防“劫持”规则——机器人只在自己创建的线程里自动回复;对于被拉进的其他既有线程,每一轮都要求显式 @ 提及,首次被 @ 进线程时会把近期线程历史拉入 prompt 作为上下文,但不订阅该线程(handler.py)。/leave命令或机器人被踢出线程时会调用unsubscribe主动清除。 - 批处理状态机(handler.py):每个回复目标(线程 ID 或 DM 频道 ID)维护一个
TargetState,包含pending消息队列、pending_file_ids和processing标志。若一条消息到达时processing为真,它只被追加进队列并立即返回,由正在执行的流在结束后排空整个批次。批次的文件 ID 上限为MAX_TURN_FILE_IDS = 20,超出部分会被丢弃并记录日志,以避免超过BotChatRequest.file_ids的max_length校验。 - 附件摄取:用户消息里的附件必须由适配器先行下载(受适配器
max_attachment_bytes限制),再上传到该回合会话的工作区文件夹(/sessions/<id>/),且回合必须运行在同一个会话里,AutoGPT 才能像读取 web 上传一样读取这些文件。为此 handler 为每个目标维护一把会话锁(asyncio.Lock),让并发到达的附件消息收敛到同一个会话,而不是各自建会话把文件拆散(handler.py)。此外 config.py 规定单条消息最多拉取MAX_INBOUND_ATTACHMENTS = 10个附件,防止一条带几十个文件的消息扇出成同等数量的上传/扫描任务。 - 会话 TTL 一致性约束:config.py 中
SESSION_TTL(AutoPilot 会话 ID 缓存,每渠道/线程一份)被硬性要求不小于线程订阅 TTL(同为 7 天)。注释解释了原因:若会话缓存比订阅窗口先过期,机器人会在会话已丢失全部对话历史后继续自动回复,转而基于跨会话的陈旧记忆行动,行为是“危险的”;由于每个回合都会刷新该 TTL,活跃对话不会在订阅窗口内丢失上下文。
链接流程:/setup 与 /unlink
/setup(把服务器链接到某个 AutoGPT 账户)与 /unlink 的策略统一放在 command_core.py,各适配器只负责传输(Discord interactions、Slack 表单 POST)与渲染(View 按钮 vs block kit),策略不得在各平台之间漂移。从源码可以看到 setup_reply 的行为:
- 调用
api.create_link_token(...)发放服务器链接 token; - 若抛出
LinkAlreadyExistsError(服务器已链接),返回 “This server is already linked… Run /unlink to manage the link” 的提示; - 成功时生成的文案包含关键提示:“All usage will be billed to your account”(用量计费到发起链接的账户)以及 “This link expires in 30 minutes”(链接 30 分钟过期),并附带确认页按钮——确认页即由
FRONTEND_BASE_URL加/link路由(PLATFORM_LINK_BASE_URL)承载。
handler 侧对 DM 用户未链接的场景也做了竞态防护:若 create_user_link_token 因用户恰好在 resolve_user 与 create 之间完成了链接而报 “already exists”,会重新检查一次链接状态,避免在确实未链接时反复骚扰用户(handler.py)。
出站 RPC:让其他服务主动向聊天平台发消息
除了被动响应聊天消息,CoPilotChatBridge 还通过 @expose 装饰器对外暴露一组出站 RPC 端点,供平台其他服务(如 copilot 工具)把消息推入聊天平台(app.py):
| RPC 方法 | 作用 |
|---|---|
list_channels(platform, user_id) |
列出指定用户可通过 bot 发帖的频道,支撑频道名解析与选择器 |
send_message_to_channel(platform, user_id, channel, content) |
以独立消息向频道(按名称或 ID)发帖,投递会校验该用户已链接的服务器 |
send_dm_to_user(platform, user_id, content) |
向用户自己的 DM 发送——目标从用户的 DM 链接解析,绝不接受调用方传入的收件人,保证用户只能给自己发 DM |
create_thread_in_channel(platform, user_id, channel, thread_name, content) |
在频道中创建独立线程并发布内容 |
对应的客户端类 CoPilotChatBridgeClient 通过 endpoint_to_async 把这些方法包装为异步 RPC 调用。一个实现要点:webhook 类适配器(Slack/Telegram)的出站半边也是无状态 HTTP 发送器,所以 copilot-bot pod 也会构建它们(不带路由、不挂 handler),使主动 RPC 能触达每一个已配置平台(app.py)。
如何添加一个新平台
官方文档给出了接入新聊天平台的四步流程,这是该模块扩展性的直接承诺——核心 handler、文本工具、线程跟踪和平台 API 均无需改动:
-
创建
adapters/<platform>/目录,包含adapter.py、commands.py(如果该平台有命令)和config.py; -
adapter.py继承SocketAdapter(长连接)或WebhookAdapter(入站 HTTPS),并实现全部出站PlatformAdapter方法——max_message_length、chunk_flush_at、send_message、send_link、create_thread、send_file等——外加所属子类型的入站方法(start/stop或register_routes); -
config.py声明该平台的环境变量和平台特定数值(消息长度限制、token 名称等); -
在对应的工厂函数中注册,并以该平台的 token 为门控条件:
- Socket 型 →
app.py::_build_socket_adapters - Webhook 型 →
webhook_routes.py::_build_webhook_adapters
if <platform>_config.get_bot_token(): adapters.append(<Platform>Adapter(api)) - Socket 型 →
这一流程与仓库现状互相印证:当前三个工厂判断点分别对应 Discord(socket)与 Slack、Telegram(webhook),新增平台只需照此模式在相应工厂中追加一个 if 分支。各平台适配器目录下均配有 *_test.py(如 discord/adapter_test.py、slack/signing_test.py、telegram/text_test.py),为新适配器补充测试时可参照这些既有用例的写法。
小结
CoPilot Bot 展示了在 AutoGPT Platform 中实现多平台聊天桥接的一套完整工程范式:以“出站契约 + 两种入站子类型”的适配器分类学隔离平台差异,用两个 token 门控的工厂函数把平台名限定在最少数量的文件里,用 Redis 上的 7 天订阅窗口管理线程自动回复的生命周期,并用共享的 command_core 保证跨平台策略一致。文档、.env.default、源码与测试四层材料在仓库中互相印证,为本地部署(poetry run copilot-bot 或 docker compose --profile bot up -d)和二次扩展都提供了清晰的落点。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00