跳转到内容

03 - 多模型适配:12+ 提供商的动态路由与工具增强

学习目标

理解 CowAgent 如何通过 AgentLLMModel 实现 12+ 模型提供商的动态路由,以及 add_openai_compatible_support() 如何为任意 bot 动态注入工具调用能力。

前置知识

多模型适配的通用原理见 多模型适配架构。本章聚焦 CowAgent 的具体实现。

项目实践

模型路由逻辑

CowAgent 的 AgentLLMModel._resolve_bot_type() 实现了基于模型名前缀的动态路由:

映射表包含两层:

  • 精确匹配wenxin → BAIDU, xunfei → XUNFEI 等
  • 前缀匹配qwen* → QWEN_DASHSCOPE, gemini* → GEMINI, claude* → CLAUDEAPI, glm* → ZHIPU_AI 等

动态工具增强

CowAgent 的关键创新是 add_openai_compatible_support() ——动态 mixin 机制:

# 伪代码
def add_openai_compatible_support(bot_instance):
if hasattr(bot_instance, 'call_with_tools'):
return bot_instance # 已有工具调用能力(如 ZHIPUAIBot 原生支持)
# 动态创建一个增强类,继承自原 bot 和 OpenAICompatibleBot
class EnhancedBot(bot_instance.__class__, OpenAICompatibleBot):
def get_api_config(self):
# 从 config.json 推断 API 配置
return {...}
# 运行时修改实例的类
bot_instance.__class__ = EnhancedBot
return bot_instance

这种设计允许任何 OpenAI 兼容格式的 bot 获得工具调用能力,无需修改原 bot 的代码。

思考模式(Thinking)集成

思考模式作为全局开关独立于渠道:

thinking_enabled = bool(conf().get("enable_thinking", False))
kwargs['thinking'] = {"type": "enabled"} if thinking_enabled else {"type": "disabled"}
if thinking_enabled:
effort = conf().get("reasoning_effort", "high")
if effort in ("high", "max"):
kwargs['reasoning_effort'] = effort

IM 渠道(微信/钉钉/飞书)不会渲染推理轨迹,但仍然受益于思考模式产生的更高质量回答。

模型上下文窗口自动检测

Agent._get_model_context_window() 根据模型名称自动推断上下文窗口大小:

模型系列上下文窗口
Claude 3.x Sonnet/Opus200K tokens
GPT-4 Turbo/128K128K tokens
GPT-48K-32K tokens
Gemini 2.02M tokens
Gemini 1.51M tokens
DeepSeek64K tokens

问题与规避

问题CowAgent 的规避策略
新模型未在映射表中前缀匹配兜底(claude* → CLAUDEAPI),加上 bot_type 显式配置
模型不支持工具调用add_openai_compatible_support() 动态注入,但有 native 检查避免重复
思考模式在不支持的平台报错bot 不理解 thinking kwarg 时静默丢弃,不影响正常调用
渠道切换后模型未更新AgentLLMModel 懒加载,模型变化时自动重建 bot 实例

设计取舍

为什么使用动态 mixin 而非为每个 bot 单独实现工具调用?

CowAgent 支持 12+ 模型提供商,如果为每个 bot 单独实现工具调用,需要 12 份不同的代码。通过 add_openai_compatible_support() 统一注入 OpenAI 兼容格式的工具调用,只需维护一份实现。代价是所有提供商必须符合 OpenAI 的 tool calling 格式。

为什么 bot 实例的 __class__ 可以在运行时修改?

这是 Python 的动态特性。修改 __class__ 会改变实例的方法解析顺序(MRO),使其获得新类的全部方法。这与 Ruby 的 “singleton class” 类似。风险是如果原 bot 和 OpenAICompatibleBot 有同名方法,MRO 决定了调用哪一个。

参考来源