f7a720204a
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用 - 添加所有子项目的完整源代码 - 保留原始 .git 为 .git.bak 备份
209 lines
14 KiB
Markdown
209 lines
14 KiB
Markdown
# Channels
|
||
|
||
通过 **channel**,你可以从外部聊天应用——Telegram、Lark / 飞书、钉钉、微信——操作 NomiFun 的智能体,而不必坐在桌面客户端前面。你启用一个插件,粘贴它的凭证,用一次性验证码授权一个聊天用户,从此发到你机器人的消息就会被分发到智能体,智能体的回复也会回到同一个会话。
|
||
|
||
Channel 适用于以下场景:
|
||
|
||
- 你想从手机或群聊里给智能体下达指令;
|
||
- 你希望让一个工作区感知的智能体能从团队现有 IM 中触达;
|
||
- 你希望长时任务([AutoWork](./autowork-requirements.zh.md))能从桌面之外被发起,而不必启动 WebUI。
|
||
|
||
> 每个平台插件都是 `nomifun-channel` 上的一个 Cargo feature(`telegram`、`lark`、`dingtalk`、`weixin`)。NomiFun 的默认构建把它们全部打开;如果你用非默认 feature 集合自行构建后端,对应的 tab 就直接消失。
|
||
|
||

|
||
|
||
## 在哪里找
|
||
|
||
打开 Nomi 页面(`/nomi`),选择一只伙伴,然后进入 **Remote** tab(`/nomi?companion=<id>&tab=remote`)。这个 tab 会列出该伙伴可用的远程连接器——内置的(Telegram、Lark、DingTalk、WeChat、WeCom、Slack、Discord、扩展)。每个插件你能看到:
|
||
|
||
- 一个状态药丸(`stopped` / `connected`);
|
||
- 连接成功后的 bot 用户名;
|
||
- 当前已授权用户数;
|
||
- 一个 per-channel 的 **默认 agent** + **默认模型** 选择器。
|
||
|
||
Slack / Discord / WeCom 目前作为内置占位符出现——这两者的后端接线被 feature gate 覆盖且仍在搭建中;今天可用的是 Telegram / Lark / DingTalk / WeChat。
|
||
|
||
## channel 是怎么工作的
|
||
|
||
```
|
||
external IM ──▶ plugin (long-poll / WebSocket)
|
||
│
|
||
▼
|
||
ChannelManager ◀─▶ PairingService
|
||
│
|
||
▼
|
||
SessionManager ──▶ agent / conversation
|
||
```
|
||
|
||
- **Plugin** 持有平台特定连接(Telegram 长轮询带指数退避,Lark / 钉钉 WebSocket,微信通过 SSE 上的 QR-code 登录)。
|
||
- **PairingService** 把"我是 Telegram 上的 John,让我进来"变成一个由你在桌面 UI 上批准的 6 位验证码。
|
||
- **SessionManager** 把 `(platform_user, chat_id)` 映射到一个智能体会话,因此每个外部聊天都是一个稳定 session,后续消息落到同一个智能体。
|
||
- **Orchestrator** 把进入的消息接到智能体流,并把智能体的回复以"对同一条 IM 消息编辑"的形式送回(除微信外都支持消息编辑——微信会回退为发送追加回复)。
|
||
|
||
## 各平台配置步骤
|
||
|
||
### Telegram
|
||
|
||
1. 找 [`@BotFather`](https://t.me/BotFather) 创建一个 bot,保存 token(形如 `123456:ABC-DEF…`)。
|
||
2. 在 **Nomi → Remote → Telegram** 粘入 token。
|
||
3. 点 **Test**——后端会调 `getMe`,成功后显示 bot 用户名。
|
||
4. 点 **Enable**。插件开始长轮询(25 s 超时,指数退避,最多 10 次重连)。
|
||
|
||
为了把 Telegram 用户与桌面端配对:用户给你的 bot 发消息;bot 用一个 6 位验证码(10 分钟 TTL)回复。在桌面端的 **Nomi → Remote → Pending pairings** 中粘入或键入该验证码并点 **Approve**。从此该 Telegram 用户即可与智能体对话。
|
||
|
||
### Lark / 飞书
|
||
|
||
1. 在飞书开发者控制台创建一个自定义 app,开启你需要的事件(文本消息、卡片动作、bot 菜单)。
|
||
2. 复制 **App ID**、**App Secret**,以及(可选)**Encrypt key / Verification token**。
|
||
3. 把它们填入 Channels tab 中的 Lark 表单,点 **Enable**。
|
||
|
||
Lark 插件通过飞书的 WebSocket 长连接接入(无需公网 webhook),带一个 60 秒的事件去重清理循环和分片重组。回复以 **互动卡片** 形式发送,因为飞书 API 只支持编辑卡片消息。
|
||
|
||
### 钉钉
|
||
|
||
1. 在钉钉开发者后台创建一个内部 app,启用 **Stream Mode**。
|
||
2. 把 **Client ID** 与 **Client Secret** 填入 DingTalk 表单并启用。
|
||
|
||
钉钉插件通过标准 stream-mode 握手打开 WebSocket;配对流程与 Telegram 一致。
|
||
|
||
### 微信
|
||
|
||
1. 微信用 QR-code 登录。在 WeChat 插件上点 **Enable**——后端会打开一个 SSE 流(`POST /api/channel/weixin/login/start`)推送 QR-code 刷新事件。
|
||
2. 用微信 app 扫码确认登录,插件转为 `connected`。
|
||
|
||
微信 **不支持** 消息编辑——回复以新消息形式投递到同一聊天,而不是就地编辑。
|
||
|
||
## 配对与授权用户
|
||
|
||
配对请求有两种来源:
|
||
|
||
1. 平台用户首次给 bot 发消息(Telegram / Lark / 钉钉)。插件自动创建一份待处理请求,并把验证码回复给用户。
|
||
2. 你可以在 **Nomi → Remote → Pending pairings** 中批准 / 拒绝待处理请求,或以编程方式调用 `POST /api/channel/pairings/approve` 与 `POST /api/channel/pairings/reject`。
|
||
|
||
已批准用户会出现在 **Authorised users** 中,并显示 `last active`。你可以随时撤销(`POST /api/channel/users/revoke`);服务也会清理该用户的活跃 session,使下一条消息从头开始重新配对。
|
||
|
||

|
||
|
||
## 主 Agent 模式 (Master Agent)
|
||
|
||
默认情况下,每个 channel 会话都运行在 **主 Agent 模式**:远程消息由
|
||
Nomi 伙伴本尊接待。会话继承伙伴的人格与记忆,并且 agent 接上了
|
||
**Desktop Gateway** 工具——所以你在手机上对话的不是一个孤立的聊天
|
||
bot,而是那个掌管你整个桌面的 agent。
|
||
|
||
网关工具(统一前缀 `nomi_*`,目前共 32 个)能替你做的事:
|
||
|
||
- **会话**——列出所有会话及其运行态,查看单个会话(状态 + 最近消息,
|
||
含进行中的流式回复),向任意会话注入消息或任务 prompt,新建会话,
|
||
修改与删除旧会话(`nomi_list_conversations`、`nomi_conversation_status`、
|
||
`nomi_send_to_conversation`、`nomi_create_conversation`、
|
||
`nomi_update_conversation`、`nomi_delete_conversation`)。
|
||
- **定时任务**——列出 / 创建 / 修改 / 删除 cron 任务
|
||
(`nomi_cron_list`、`nomi_cron_create`、`nomi_cron_update`、
|
||
`nomi_cron_delete`)。
|
||
- **长期记忆**——读写伙伴的全局记忆库(`nomi_memory_list`、
|
||
`nomi_memory_save`、`nomi_memory_update`、`nomi_memory_delete`)。
|
||
- **需求平台**——浏览与管理需求平台(`nomi_requirement_list`、
|
||
`nomi_requirement_create`、`nomi_requirement_update`、
|
||
`nomi_requirement_delete`)。
|
||
- **终端与监督**——列出终端会话、创建新终端(可经 `knowledge_base_ids`
|
||
顺带绑定知识库),以及读取 / 切换某个终端的 AutoWork 绑定与 IDMM
|
||
监督(`nomi_list_terminals`、`nomi_create_terminal`、
|
||
`nomi_get_autowork`、`nomi_set_autowork`、`nomi_get_idmm`、
|
||
`nomi_set_idmm`)。
|
||
- **知识库**——浏览知识库与绑定关系,改绑会话 / 终端 / 伙伴,新建
|
||
知识库,向库内写 markdown 文件,触发 AI 梗概生成,或把一个 URL
|
||
抓取为 markdown——伙伴可以自主沉淀知识
|
||
(`nomi_knowledge_list_bases`、`nomi_knowledge_get_binding`、
|
||
`nomi_knowledge_set_binding`、`nomi_knowledge_create_base`、
|
||
`nomi_knowledge_write_file`、`nomi_knowledge_autogen`、
|
||
`nomi_knowledge_fetch_url`)。`nomi_knowledge_create_base` 带
|
||
`urls` 时抓取为后台异步——工具立即返回,等待期间勿重复建库;
|
||
库描述(description)出现即代表抓取与梗概流水线已完成。
|
||
- **Provider**——列出已配置的 LLM provider(`nomi_list_providers`)。
|
||
|
||
于是"把我的日报 cron 改到早上 9 点,再说说现在桌面上有什么在跑"
|
||
只需要一条飞书消息。
|
||
|
||
**如何关闭。** 每个平台面板里,默认模型选择器旁边有一个
|
||
**主 Agent 模式** 开关。默认开启;偏好按平台存为客户端配置中的
|
||
`assistant.<platform>.masterAgent`(无值 = 开启)。关闭后该平台回退
|
||
为旧行为——每个远程聊天只得到一个普通独立会话,没有伙伴人格也没有
|
||
网关工具。与模型选择器一样,切换开关会调
|
||
`POST /api/channel/settings/sync` 并清掉该平台的活跃 session,下一条
|
||
进来的消息会以新模式重新创建会话。
|
||
|
||
**选择由哪只伙伴接待。** 有了[多伙伴](./companions.zh.md)之后,机器人按
|
||
**渠道行**绑定伙伴:`assistant_plugins` 每行代表一个机器人(同一平台
|
||
可以接入多个机器人,比如飞书上为每只伙伴各开一个企业自建应用),行上
|
||
的 `companion_id` 决定由哪只伙伴接待,`UNIQUE(type, bot_key)` 唯一约束从结构
|
||
上保证**同一个机器人永远不会被绑到第二只伙伴**(bot 身份:飞书
|
||
`app_id`、Telegram bot id、钉钉 `client_id`……)。绑定 / 解绑走
|
||
`POST /api/channel/settings/companion`(带 `plugin_id`),一步完成持久化与
|
||
**该渠道** session 的重置——下一条进来的消息由新宠的人格、模型与知识
|
||
库挂载接待(会话带 `extra.companionId`)。在伙伴面板的 **远程连接** tab 里
|
||
为某只伙伴连接机器人,就是「新建渠道行 + 绑定该宠」一步完成。未绑定
|
||
伙伴的渠道行回退到旧的平台级偏好 `assistant.<platform>.companionId`,再回退
|
||
**默认伙伴**;被绑定的伙伴若之后被删除,自动回退默认宠并同样重置
|
||
session。记忆是全家共享的:不管多少个机器人、多少个渠道,会话数据都
|
||
汇入同一套记忆体系,换宠不会丢失任何记忆。
|
||
|
||
**与 agent / 模型配置的关系。** 平台级 **默认 agent** 仍然决定由哪个
|
||
引擎应答;网关工具对任意 agent 类型都会注入,而伙伴人格与记忆搭载在
|
||
Nomi 引擎上。主 Agent 模式下的模型解析顺序:平台 **默认模型**(若已
|
||
设置)优先,否则回退到所绑定伙伴自己的模型。
|
||
|
||
## 选择 agent 和模型
|
||
|
||
每个平台都在它的配置表单里有 **默认 agent** 和 **默认模型** 选择器。平台把它们存为客户端配置中的 `assistant.<platform>.defaultModel`,因此:
|
||
|
||
- 来自 Telegram 的消息路由到你为 Telegram 选的智能体 / 模型;
|
||
- 来自飞书的消息可以路由到一个不同的智能体;
|
||
- 改动选择器会调 `POST /api/channel/settings/sync`,它会清掉该平台的活跃 session——下一条进来的消息会用新的默认值重新创建。
|
||
|
||
模型选择器与桌面端使用的是同一个 Gemini 风味组件,所以你配置过的任何 provider(Anthropic、OpenAI 兼容自定义 URL、带 Google 认证的 Gemini、Bedrock,……)这里都可用。
|
||
|
||
## 从 IM 端能做什么
|
||
|
||
平台无关抽象(`UnifiedIncomingMessage`、`UnifiedOutgoingMessage`、`UnifiedAction`)覆盖:
|
||
|
||
- **纯文本**——双向。
|
||
- **流式编辑回复**——智能体的增量更新会被编辑进正在飞行的 bot 消息(微信除外)。
|
||
- **动作按钮**——确认 prompt、重试动作等等,渲染为 inline keyboard(Telegram)、互动卡片按钮(Lark)或对应平台的等价物。
|
||
- **Bot mention / require-mention**——群聊可配置为只在 bot 被 `@` 时才回应。
|
||
|
||
从 IM 端目前还做不到:
|
||
|
||
- 创建 team(请用桌面 / web UI);
|
||
- 超出平台插件原生能力的文件上传;
|
||
- per-user 工作区选择——智能体的工作区就是它路由到的会话上设的那个。
|
||
|
||
## 路由与 API
|
||
|
||
| 用途 | 位置 |
|
||
| ------------------------------- | ---------------------------------------------------------- |
|
||
| Channels UI | `/nomi?companion=<id>&tab=remote` |
|
||
| 列出插件 / 状态 | `GET /api/channel/plugins` |
|
||
| 启用 / 禁用 | `POST /api/channel/plugins/enable`、`…/disable` |
|
||
| 测试凭证 | `POST /api/channel/plugins/test` |
|
||
| 待处理配对 | `GET /api/channel/pairings` |
|
||
| 批准 / 拒绝配对 | `POST /api/channel/pairings/approve`、`…/reject` |
|
||
| 已授权用户 | `GET /api/channel/users`、`POST .../users/revoke` |
|
||
| 活跃 session | `GET /api/channel/sessions` |
|
||
| 同步(变更时清掉 session) | `POST /api/channel/settings/sync` |
|
||
| 绑定主 Agent 伙伴 | `POST /api/channel/settings/companion` |
|
||
| 微信 QR 登录 SSE | `POST /api/channel/weixin/login/start` |
|
||
|
||
## 注记
|
||
|
||
- 插件生命周期是一个状态机——`Created → Initializing → Ready → Starting → Running → Stopping → Stopped`,每一步都可能转到 `Error`。UI 上的状态药丸就是这个枚举。
|
||
- 撤销用户时,session 会先于该 user row 被拆掉。来自该平台用户的下一条消息会触发新的配对码。
|
||
- 配对码 6 位,由 `getrandom` 生成,TTL 10 分钟。配对服务运行一个周期清扫,把 TTL 已过的待处理码过期掉。
|
||
- 微信单独被 feature gate 控制,因为它的依赖树更重(QR / 登录 / 鉴权流)。如果你用 `--no-default-features` 构建,会看到占位卡片但没有启用按钮。
|
||
|
||
## 相关
|
||
|
||
- [伙伴(Companions)](./companions.zh.md)——多伙伴管理、共享记忆,以及搭载在渠道会话上的每宠知识库绑定。
|
||
- [AutoWork & Requirements](./autowork-requirements.zh.md)——从聊天里登记一条需求,再用 webhook 卡片把通知打回飞书。
|
||
- [Web Server Deployment](./web-server-deployment.zh.md)——当你在服务器上自托管后端时同样能暴露这些 channel。
|