渠道接入(Channel Plugin)
Sira 把每种 IM / 客户端通过 独立的 Python 包 + setuptools entry_points 装载——核心代码不需要修改即可新增渠道。每个 Application 绑定一种渠道,对应一份"应用凭据 + 推送方式"配置。
已支持的渠道
| 渠道 | 包 | License Feature | 状态 |
|---|---|---|---|
| Web 客户端 | 内置 | channel.web_client (TRIAL) | 内嵌网页聊天 / app-chat/[token] |
| 企业微信传统应用 | 内置 | channel.wecom (STANDARD) | /features/wechat-work |
| 企业微信 AI Bot | sira-channel-wecom | channel.wecom (STANDARD) | /features/aibot-config |
| 飞书机器人 | sira-channel-feishu | channel.feishu (STANDARD) | /features/channels/feishu |
| 钉钉机器人 🆕 | sira-channel-dingtalk | — (暂未单独 gate,fail-open) | /features/channels/dingtalk(Stream 长连接,无回调地址) |
| 微信客服 | 内置 (adapter) | channel.weixin_kf (PROFESSIONAL) | /features/channels/wechat-kf/overview |
| OpenAI 兼容 API | 内置 | channel.openai_compat (STANDARD) | /features/api-service |
| LangGraph 兼容 API | 内置 | channel.langgraph_compat (PROFESSIONAL) | /features/api-service |
插件装载机制
Sira 启动时扫描所有声明 sira.channels entry_point 的 Python 包:
# sira-channel-feishu/pyproject.toml
[project.entry-points."sira.channels"]
feishu = "sira_channel_feishu.entrypoint:define_channel_plugin"每个插件实现 ChannelPlugin 抽象基类(位于 sira-channel-base),按需重写:
| 方法 | 用途 |
|---|---|
verify_callback | 校验渠道平台的回调签名 |
decode_inbound | 把渠道平台原始消息解码成 Sira 统一的 ChannelMessage |
push_response | 把编排器输出按渠道格式推送(streaming / 单次) |
send_message | 主动发起的消息(非回复型) |
新增渠道 = 新建包 + 实现这几个方法 + 加 entry_point + pip install -e ./ 后即可识别。
ChannelGatewayBridge(DMZ 中转)
License 提示
此功能需要 deploy.dmz_bridge (ENTERPRISE)。
应用场景:Sira 后端运行在纯内网环境,无公网入口,但渠道(飞书 / 企微)的回调必须从公网到达。解决方案:
公网回调
│
▼
DMZ 区的 channel_gateway_bridge.py 服务(独立部署)
│ WebSocket 反向隧道
▼
内网 Backend 主动出站连接到 DMZ
│
▼
回调被转发,结果反向推回每个 Application 在创建时可选 deployment = "gateway" 走 DMZ 中转,否则默认 local(直接本地处理回调)。
详细部署在 devdocs/CHANNEL_GATEWAY_DEPLOYMENT.md、运维在 devdocs/CHANNEL_GATEWAY_OPS.md。
跨渠道用户解析
不同渠道的用户 ID 会通过 users.external_ids[platform_key] 映射回同一个 Sira 用户。例如:
- 张三在企微的 userid =
zhangsan_wecom_id→users.external_ids.wecom = "zhangsan_wecom_id" - 张三在飞书的 open_id =
ou_xxx→users.external_ids.feishu_bot = "ou_xxx"
两条记录指向同一个 Sira user.id,所以张三在企微装的沙箱 / 创建的定时任务,飞书里也能继续使用。
PlatformKey 在 app/extensions/agent_system/services/channel_platform_keys.py 集中管理,含别名映射:wework → wecom、lark → feishu、lark_bot → feishu_bot、web → webclient、sira → webclient 等。
自动建档
当一个新渠道用户首次和 AI 对话时,平台会自动创建占位 Sira 用户(provider="auto_provisioned",邮箱后缀 .invalid),保证 AI 立即可用,无需员工手动注册。
输出格式归一
ResponseNormalizer 统一处理流式 SSE → 渠道原生格式:
- 文本类:累计 chunk → 按渠道允许的"流式"或"一次性"形式推送
- 错误事件 (
event=='error'):转成普通文本帧 + 友好提示,避免 AI 突然失声 - 沙箱安装链接:错误信息中嵌入
/sandbox-install?token=...短链
对话内命令(斜杠命令)
用户在渠道对话里发送以 / 开头的消息会被当成命令拦截(在路由到编排器之前处理),不消耗 AI 调用。支持渠道:企业微信 AI Bot / 飞书 / 钉钉(经统一渠道管线 + DMZ 中转)。Web 分享页(app-chat)暂不支持斜杠命令。
| 命令 | 作用 |
|---|---|
/help | 列出所有可用命令 |
/clear | 显示当前会话信息 + 二次确认提示(不实际删除) |
/clear confirm | 真正清除当前会话的 AI 记忆(确认有效期 5 分钟) |
/whoami | 显示当前 用户 / 应用 / session_id(排查问题时提供给客服) |
/wxbind | 发起微信个人号绑定,返回登录二维码(实验性,依赖 iLink) |
/wxunbind | 解除当前用户的微信绑定 |
/wxstatus | 查询微信绑定状态 |
- 命令必须以
/开头;未识别的/xxx会原样交给 AI 处理。 /clear的清除范围仅限 当前应用 × 当前用户 这一组合,不影响该用户在其他应用的上下文(thread_id = {application_id}_{user_id})。- 个微绑定(
/wxbind系列)为实验性功能,受腾讯服务条款限制,可能随政策调整不可用。 - 命令注册表在
app/extensions/agent_system/services/slash_command_dispatcher.py,新增命令只需加一行,/help会自动收录。
AI 发送文件(MEDIA 标签)
AI 要把一个文件真正发给用户(而不只是口头说"已发送"),必须在回复里输出 MEDIA: 标签。运行时会拦截标签、读取文件、按渠道原生形式投递,并把标签从用户可见文本里删除。只说"文件已发送"却没有标签 = 用户收不到任何东西。
两种写法(按字节来源二选一,不要混用):
| 来源 | 写法 | 说明 |
|---|---|---|
| 沙箱本地文件 | MEDIA:/绝对路径 | 只接受沙箱内的绝对文件路径,单独成行(可用反引号包裹)。例:MEDIA:/tmp/report.pdf |
| 已托管的图片 URL |  | 运行时下载该 URL 并作为图片发送。不要把 URL 放到 MEDIA: 后面,会被当成路径而失败 |
各渠道经 MEDIA 标签支持的文件类型:
| 渠道 | 文档 (pdf/docx/xlsx/pptx/txt/md) | 图片 (jpg/png/webp/gif) | 视频 (mp4) | 音频 |
|---|---|---|---|---|
| 飞书 | 附件 | 图片 | 内联播放 | mp3/wav/m4a → 语音 |
| 企业微信 | 附件 | 图片 | 内联播放 | mp3/wav → 语音 |
| 钉钉 | 文件卡片 | 图片 | — | — |
| Web 客户端 | 链接下载 | 内联渲染 | — | — |
- 一条回复最多带一个文件(出现多个标签只取第一个)。
- 单文件默认上限 30 MiB(环境变量
MEDIA_MAX_BYTES_IN_REDIS可调),超限会提示换更小的文件。 - 标签必须指向真实存在的文件;文件没生成时 AI 应先在沙箱里写出文件再输出标签。
给最终用户的小贴士
模型偶尔会只说"文件已发送 / 已发给你"却忘了输出标签,于是你收不到文件。这时直接引导它:
请用
MEDIA:标签把文件发给我(例如MEDIA:/tmp/report.pdf)。
或者:「把结果存成 xlsx 后用 MEDIA 标签发我」。文件能不能发,取决于编排器是否带沙箱/文件系统(DeepAgents、或配了技能包的 Single 编排器)。
