在 nanobot 对话框中实现 Model-Switcher 斜杠命令

技术原理 + 操作手册:即使 AI 模型不可用,也能在对话框中切换模型


1. 背景与问题

1.1 nanobot 的多模型架构

nanobot 支持配置多个 AI 模型提供商(如 OpenAI、DashScope、百度千帆等),并通过 fallback 机制实现自动故障转移。当主模型不可用时,系统会自动切换到备用模型。

1.2 为什么需要在对话框中切换模型

在实际使用中,我们经常遇到以下场景:

  • 模型故障:当前使用的模型 API 返回错误或限流
  • 功能需求:某些任务需要特定模型(如代码生成用 Codex,中文优化用文心一言)
  • 成本优化:根据任务复杂度选择不同价格的模型

1.3 关键挑战

如果通过 AI 模型来处理切换命令(如让 AI 执行 /model switch),会存在一个致命问题:当 AI 模型本身不可用时,切换命令也会失效

这就好比门锁坏了,你却需要打开这扇门去拿备用钥匙。


2. 技术方案对比

2.1 方案 A:AI 层处理(loop.py)

实现方式:在 nanobot/agent/loop.py_process_message() 方法中添加 /model 命令处理。

优点

  • 实现简单,代码集中
  • 与现有 /new/stop 命令处理方式一致

缺点

  • 致命缺陷:如果当前 AI 模型不可用,命令无法执行
  • 响应延迟较高(需要经过 AI 处理流程)

2.2 方案 B:通道层处理(feishu.py / telegram.py)

实现方式:在消息通道层(飞书/Telegram)直接拦截 /model 命令,调用 model_switcher.py 脚本执行,不经过 AI 模型

优点

  • 高可用:即使 AI 模型挂了,命令依然可用
  • 响应快:无需等待 AI 处理
  • 解耦:命令执行与 AI 状态无关

缺点

  • 需要修改多个通道文件
  • 代码分散在各通道实现中

2.3 方案选择

选择方案 B

对于 model-switcher 这种基础设施级别的功能,可靠性优先于代码简洁性。当系统出现故障时,必须保证有可靠的逃生通道。


3. 核心实现原理

3.1 nanobot 消息处理流程

1
2
3
4
5
6
7
8
9
10
11
12
13
用户发送消息

[飞书/Telegram 服务器]

nanobot bridge (WebSocket)

通道层 (feishu.py / telegram.py)

消息总线 (MessageBus)

AI 层 (loop.py)

模型调用

3.2 拦截点选择

飞书通道:在 _on_message() 方法开头拦截

1
2
3
4
5
6
7
8
9
10
11
async def _on_message(self, message_data: dict) -> None:
# 1. 解析消息内容
content = self._extract_content(message_data)

# 2. 【关键】检查是否为 /model 命令
if content.startswith("/model"):
await self._handle_model_command(message_data, content)
return # 直接返回,不进入 AI 处理流程

# 3. 普通消息,进入 AI 处理
await self._publish_to_bus(message_data)

Telegram 通道:使用 CommandHandler 注册 /model 命令

1
self._app.add_handler(CommandHandler("model", self._on_model))

3.3 命令执行流程

1
2
3
4
5
6
7
8
9
10
11
12
用户发送 /model switch dashscope

通道层拦截命令

解析子命令: switch
解析参数: dashscope

调用 model_switcher.py switch dashscope

获取输出结果

格式化并返回给用户

3.4 关键设计决策

  1. 直接调用脚本:通过 subprocess.run() 调用 model_switcher.py,而不是导入 Python 模块

    • 优点:隔离性好,脚本异常不影响主程序
    • 缺点:有进程创建开销(可接受)
  2. 统一输出格式:所有子命令返回 Markdown 格式,便于阅读

  3. 超时保护:设置 30 秒超时,防止脚本卡死


4. 操作手册:重新部署

4.1 修改文件清单

文件路径 修改类型 说明
nanobot/channels/feishu.py 修改 添加 _handle_model_command() 方法
nanobot/channels/telegram.py 修改 添加 _on_model() 方法和 CommandHandler

4.2 关键代码位置

4.2.1 feishu.py

位置_on_message() 方法开头

关键逻辑

1
2
3
4
# 检查是否为 /model 命令
if content.startswith("/model"):
await self._handle_model_command(event_data, content)
return # 直接返回,不进入 AI 处理

新增方法_handle_model_command()

  • 解析命令:/model <subcmd> [args]
  • 有效子命令:list, switch, status, test, fallbacks, watcher, health
  • 执行脚本:python3 skills/model-switcher/model_switcher.py <subcmd> [args]
  • 格式化输出:Markdown 代码块

4.2.2 telegram.py

位置BOT_COMMANDS 列表和 setup() 方法

关键逻辑

1
2
3
4
5
6
7
8
# 注册命令
BOT_COMMANDS = [
BotCommand("model", "Model management..."),
...
]

# 添加 handler
self._app.add_handler(CommandHandler("model", self._on_model))

新增方法_on_model()

  • 与 feishu.py 的 _handle_model_command() 逻辑相同
  • 使用 update.message.reply_text() 返回结果

4.3 验证步骤

  1. 语法检查

    1
    2
    3
    cd /home/admin/nanobot
    python3 -m py_compile nanobot/channels/feishu.py
    python3 -m py_compile nanobot/channels/telegram.py
  2. 重启 nanobot

    1
    nanobot restart
  3. 测试命令

    • 发送 /model — 应显示帮助信息
    • 发送 /model list — 应显示可用模型列表
    • 发送 /model status — 应显示当前模型状态
    • 发送 /model switch <provider> — 应切换模型
  4. 故障场景测试

    • 配置一个无效的 API key
    • 确认 AI 模型无法响应
    • 发送 /model switch <其他provider>
    • 确认命令仍然可以执行

4.4 常见问题排查

问题 原因 解决方案
/model 无响应 命令未正确拦截 检查 _on_message() 中的 startswith 判断
提示 “Model switcher not found” 脚本路径错误 确认 skills/model-switcher/model_switcher.py 存在
命令超时 model_switcher.py 执行慢 检查网络连接或增加超时时间
Telegram 不显示命令 未注册 BotCommand 检查 BOT_COMMANDS 列表和 set_my_commands()

5. 总结与扩展

5.1 设计模式总结

这次实现采用了 “通道层拦截” 模式:

  • 适用场景:需要高可靠性的基础设施命令
  • 核心思想:在消息进入 AI 处理流程之前拦截并处理
  • 权衡:代码分散 vs 可靠性提升

5.2 可扩展的命令

基于同样的模式,可以添加其他基础设施命令:

命令 用途 实现难度
/restart 重启 nanobot 服务
/status 查看系统状态
/backup 触发手动备份
/logs 查看最近日志

5.3 注意事项

  1. 系统升级后需重新应用:这些修改在 nanobot 核心代码中,升级会被覆盖
  2. 备份修改:建议将修改保存为 patch 文件,便于快速恢复
  3. 测试环境先行:在生产环境应用前,先在测试环境验证

附录:快速恢复脚本

如果系统升级后需要快速恢复此功能,可以使用以下 patch:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 备份当前文件
cp nanobot/channels/feishu.py nanobot/channels/feishu.py.bak
cp nanobot/channels/telegram.py nanobot/channels/telegram.py.bak

# 2. 应用修改(假设修改保存在 git patch 中)
git apply model-slash-commands.patch

# 3. 验证语法
python3 -m py_compile nanobot/channels/feishu.py
python3 -m py_compile nanobot/channels/telegram.py

# 4. 重启服务
nanobot restart

文章版本:v1.0
最后更新:2026-03-16
适用 nanobot 版本:v0.x