Appearance
L01-03 国内多模型统一适配层
全局中心内容:厂商差异锁在配置层,业务代码里不该出现任何厂商名。 全局讲解主线:先看「if-else 接厂商」有多糟 → 讲清 OpenAI 兼容协议 → 做出路由 → 三家实测。
P1 · 产出页
中心内容:改一个配置名换厂商,业务代码一行不改。
页面内容
- DeepSeek / 通义 / 智谱 三家可切换
- 业务层只认
ChatClient - 三家都能触发工具调用
讲解技巧
- 当场演示切换:改 yml 一行 → 重启 → 同一个问题三家分别答一遍。眼见为实比任何架构图都有力。
- 强调验收标准不是「能跑」,是「业务代码零改动」。
时长:20s
P2 · 痛点页:if-else 接厂商
中心内容:问题不在 if 多,在业务里出现了厂商概念。
页面内容
- 反面代码:
if ("deepseek".equals(vendor)) ... - 三个后果:无法灰度/灾备、选项类型各自为政、工具能力要写分支
- 反面代码:
讲解技巧
- 先翻车:真的把这段 if-else 写出来跑通,然后问「现在老板要求做灾备自动切换,你要改几个类?」让大家数。
- 类比迁移:「这就是把 MySQL 驱动名写死在 Service 里」——一次讲清依赖倒置。
时长:2min 30s
P3 · 原理图:OpenAI 兼容端点
中心内容:一个 starter 通吃,因为大家都说同一套协议。
页面内容
- 你的代码 → OpenAiChatModel → POST {base-url}/chat/completions
- 下挂四家:DeepSeek / 通义 / 智谱 / OpenAI
- 换厂商 = 换 base-url + key + model
讲解技巧
- JDBC 类比再次使用:「协议就像 JDBC 规范,各家提供驱动。你依赖的是规范,不是某家数据库」。这是贯穿全课的核心类比,要讲透。
- 诚实说明代价:厂商独有能力用不了。什么时候该引专用 starter,给明确判据。
时长:3min
P4 · 选型表:三种装配策略
中心内容:B 打底 + C 收口,本课时用这个组合。
页面内容
- A 单一主模型 / B 多 Bean + Qualifier / C 路由代理
- 推荐:先做 B 让切换可行,再用 C 做默认路由
讲解技巧
- 不要平均用力讲三种。重点讲 C,因为它是生产形态;A 一句话带过,B 说明它是 C 的地基。
- 提问留白:「为什么不让业务代码直接
@Qualifier选模型?」——因为那样业务就知道了厂商,又回到 P2 的坑。
时长:2min 30s
P5 · 版本差异提示
中心内容:starter 命名在 1.x → 2.x 变过,拉不到依赖先查命名。
页面内容
- 旧:
spring-ai-openai-spring-boot-starter - 新:
spring-ai-starter-model-openai
- 旧:
讲解技巧
- 这页是给踩过坑的人的安慰剂:明确说「如果你按老教程引依赖会拉不下来,不是你的错」。这种话能显著降低挫败感。
- 顺带给出排查口诀:依赖拉不到先核名,功能不生效先核版本。
时长:1min
P6 · 编码:配置外置
中心内容:厂商差异全部写进 yml。
页面内容
ai.vendors.{deepseek,qwen,zhipu}各自 base-url / model / key / supports-tools- 通义的兼容端点是
/compatible-mode/v1
讲解技巧
- 重点讲通义那个路径:这是真实会让 80% 的人卡住的坑。现场演示写错
/v1报 404,再改对。 - 强调
supports-tools这个字段的意义:能力差异也要配置化,不要写死在代码里。
- 重点讲通义那个路径:这是真实会让 80% 的人卡住的坑。现场演示写错
时长:4min
P7 · 编码:多 ChatModel 构建
中心内容:每个厂商一个独立 OpenAiApi 实例。
页面内容
OpenAiApi.builder().apiKey().baseUrl().build()OpenAiChatModel.builder().openAiApi(api)...- 放进
Map<String, ChatModel>
讲解技巧
- 这是本节最容易写错的地方,要放慢:很多人试图共用一个 Api 改 url,结果打到错误地址。
- 用一句话钉死:「base-url 和 key 绑在 Api 上,不是绑在 ChatModel 上」。
时长:5min
P8 · 编码:路由代理
中心内容:实现 ChatModel 接口,上层全部无感知。
页面内容
ChatModelRouter implements ChatModelcall()里按默认厂商委派- 注入给
ChatClient.Builder.chatModel(router)
讲解技巧
- 可视化反差:把替换前后的
ChatConfig并排——除了.chatModel(router)多一行,其余一字未改。 - 强调这就是适配层的全部价值:换实现不改调用方。
- 可视化反差:把替换前后的
时长:4min
P9 · 运行:三家逐个体检
中心内容:三家都要跑,不能只跑默认那家。
页面内容
/api/vendor看当前厂商?vendor=qwen指定厂商- 工具调用三家分别测
讲解技巧
- 必须现场翻一次车:故意只配了默认厂商的 Key,切到第二家时报错。然后讲「这就是为什么切换必须实测」。
- 演示
/api/vendor返回值与 yml 一致性校验——这是上线前必查项。
时长:4min
P10 · 避坑与小结
中心内容:工具能力必须逐模型实测,文档不可信。
页面内容
- 业务代码里不出现厂商名(判据:换掉 yml 全部厂商名能跑)
- 超时/重试按厂商分别配置
- 工具支持度逐模型实测,结果写进配置
讲解技巧
- 第三条要说的是「这张实测表是你的资产」。厂商文档写「支持 Function Calling」常指旗舰模型,兼容端点上的轻量模型完全不触发。
- 结尾悬念:「现在换厂商要重启,怎么做到运行时切换?」——04-02 模型网关。
时长:2min
讲师备忘
| 项 | 内容 |
|---|---|
| 课前必做 | 三家 Key 全部确认有效;提前跑一遍 /compatible-mode/v1 的常见错误 |
| 最容易超时处 | P7 的 OpenAiApi 实例化,容易展开讲 Spring AI 的 Api 抽象层 |
| 学员最常问 | 「为什么不用厂商官方 SDK?」答:官方 SDK 意味着每家一套 API,正是我们要避免的 |
| 现场备用 | 某家 Key 失效 → 用预录的该家成功输出;不要现场调试 Key |