ChannelBase 实现一个渠道类,再把这个类交给 create_app。渠道类是”IM 平台 ↔ 智能体服务”之间的翻译层,平台差异全部收敛在这里,编排逻辑由框架统一处理。
渠道类要做两件事:自描述类型(声明类型标识、凭据与配置),以及实现行为(维持连接、把消息规范化后发出、把回复发回平台)。
类型描述
渠道类把自己的类型信息挂在类上,框架据此渲染前端表单、校验输入、构造实例,无需额外的注册表。自描述的渠道类
(channel_id, credentials, config) 构造每个实例:credentials / config 是已按你声明的 Credentials / Config 校验过的对象。Credentials 装密钥(加密、脱敏、不可变),Config 装非密钥开关(明文、可更新),二者都由用户在管理界面按渠道填写。
必需方法
除构造函数外,ChannelBase 有三个抽象方法必须实现,其余方法都有合理的默认实现(默认空操作或”不支持”)。
接收消息
框架启动渠道时调用start_listening(emit),把入站回调 emit 传进来。渠道类保存为 self._emit,收到平台消息时规范化为 ChannelEvent 并调用它,其余编排交给框架。渠道类不持有、也不 import 框架的编排层。
保存 emit 并规范化入站消息
ChannelEvent 的 content 复用了与 Msg.content 相同的 TextBlock / DataBlock 类型,多模态消息无需额外转换即可交给智能体。
发送回复
框架不会把回复整理好再交给渠道,而是把该次运行的事件流交给send_response,由渠道自行累积、渲染、发送。这样渠道既能一次性发出完整回复,也能像飞书那样边生成边流式更新。它有两个参数:event 是发送目标,只用来取 chat_id 定位要回到哪个聊天;events 才是这次运行产生的智能体事件流。基类提供 _render(),把累积好的回复折叠成可发送的文本 / 数据块,并按渠道的呈现开关决定是否展开思考过程与工具调用。
累积事件流并发回平台
FeishuChannel 与 DiscordChannel。
连接状态
每个渠道实例在__init__ 中创建自己的 self.status = ChannelStatus(),并在连接、断线重连、停止时更新 status.state(取值 stopped / connecting / connected / retrying / failed)。管理界面与 GET /channels/{id}/status 读取的正是这个状态。若首次连接反复失败,可把 state 置为 failed 并驻留,等用户改动配置后再重连。
能力声明
capabilities 是渠道对平台能力的声明,供框架与渠道自身在发送时参考。为你的平台如实声明即可:
声明能力
工具确认
当智能体调用需要审批的工具时,运行会暂停,事件流里出现一个RequireUserConfirmEvent。渠道在 send_response 里识别它,把确认请求呈现给用户:能力强的平台用交互式卡片或按钮,纯文本平台可以发一句”回复 yes/no”的提示。RequireUserConfirmEvent.tool_calls 是待确认的工具列表,每个工具有 id、name、input。
用户做出决定后,渠道把它规范化为一个 ChannelConfirmationResultEvent,走与普通消息相同的 self._emit 入口发出:
呈现确认并回传决定
ChannelConfirmationResultEvent 只携带定位用的键:权威的待确认工具调用在恢复运行时从会话状态读取,从不信任卡片回传的内容。因此这一往返天然是分布式安全的,用户隔多久点击、点击落到哪个节点都能正确处理。
可选方法
以下方法均有默认实现,按平台能力选择性覆盖:启用渠道
把你的渠道类加进create_app 的 channels,服务即允许接入该平台。channels 声明的是整个服务允许接入哪些渠道类型;不传则不启用任何渠道,因此把需要的内置类型和自定义类型一并列出。
在 create_app 中启用
MyChannel 就会出现在管理界面的平台类型列表里,用户即可像内置平台一样填凭据、配路由、创建渠道。
延伸阅读
会话路由
自定义渠道同样复用统一的路由模型。
概览
回到渠道的整体工作方式。