Appearance
L01-02 第一个 ChatClient
全局中心内容:模型调用在 Spring 里就是一个 Bean,别写得比 JDBC 还原始。 全局讲解主线:先让人看清「裸写会多惨」,再给「框架给了什么」,最后跑通一次真实调用。
P1 · 产出页
中心内容:这节结束时,你会有一个能回答问题的 HTTP 接口。
页面内容
POST /api/chat→ 返回模型回答- 30 行以内代码
- 可扩展到任何业务场景的最小骨架
讲解技巧
- 先放结果:直接 curl 演示一遍最终效果(用预跑好的终端录像也行),让学员先看到「这就是目标」,再回头讲怎么做。
- 不要在这页讲任何原理,超过 20 秒就是浪费。
时长:20s
P2 · 痛点页:裸调 API 的四个坑
中心内容:能跑通的代码,不等于能维护的代码。
页面内容
- 手写 JSON 拼字符串 → 引号/换行就崩,还顺便开了注入口子
- 错误处理缺失 → 401/429/500 都走进成功分支
- 厂商强耦合 → 换模型要改代码
- 治理无处安放 → 超时、重试、日志各写各的
讲解技巧
- 类比迁移:「这就是你在用 JDBC 手写连接关闭的那一年」的老代码,贴出来让人认出来。
- 现场演一遍反例:真的在 IDE 里贴那段字符串拼接的代码,问「用户输入带个双引号会发生什么」。让他们答,不要代答。
时长:2min
P3 · 原理图:两层 API
中心内容:ChatClient 是 JdbcTemplate,ChatModel 是 JDBC。
页面内容
- 三层框:ChatClient → ChatModel → HTTP/JSON
- ChatClient:链式、带默认配置、Advisor 挂载点
- ChatModel:底层收发,精细控制时才用
- 判断标准:90% 用 ChatClient
讲解技巧
- 黑板留一张图:整节课白板上只留这三层,后面每次说到「在哪一层」就指一下它。这一张贴图的复现率决定本节记忆留存。
- 提问留白:问「什么情况下必须下沉到 ChatModel?」让沉默 3 秒再答(自定义元数据传输场景)。
时长:3min
P4 · 选型表:三件套
中心内容:builder 上只有三件事要配,配完就收工。
页面内容
defaultSystem≈@ControllerAdvicedefaultAdvisors≈ Filter 链defaultOptions≈ 连接池默认参数
讲解技巧
- 用「三张类比卡」一次性给出,不要逐条念。念完接一句:「这三样以后都不用再碰,业务代码只需要
.prompt().user().call()」。 - 强调默认值思维:「好框架的标志是默认值就够用」。
- 用「三张类比卡」一次性给出,不要逐条念。念完接一句:「这三样以后都不用再碰,业务代码只需要
时长:2min
P5 · 编码:依赖与配置
中心内容:版本交给 BOM,密钥交给环境变量。
页面内容
spring-ai-starter-model-openai(不写 version)- 为什么用 OpenAI starter 对接国内厂商:协议兼容
api-key: ${API_KEY}、base-url、model
讲解技巧
- 这是本节最容易出彩的一分钟:问「我们明明用国产模型,为什么依赖叫 openai?」停顿,答「因为它是一套通行协议,就像 JDBC 驱动——厂商遵循同一份协议」。这个类比一次讲清后面所有多模型章节。
- 顺手演示
.gitignore里已经有.env,强化习惯。
时长:4min
P6 · 编码:ChatConfig
中心内容:ChatClient 是 Bean,注入一次,到处复用。
页面内容
@Bean ChatClient chatClient(ChatClient.Builder builder)defaultSystem(...)设角色- builder 由 starter 提供,别自己 new
讲解技巧
- 先翻车:故意在 Controller 里 new 一个,跑一次能成功;然后逐个数它丢掉了什么(系统提示、默认参数、Advisor 链)。让人意识到「能跑」和「对」是两件事。
- 强调:「ChatClient.Builder 一定来自容器,因为 ChatModel 是自动配置出来的」。
时长:6min
P7 · 编码:Controller
中心内容:三行链式调用就是全部业务代码。
页面内容
- 构造器注入
chatClient.prompt().user(question).call().content()
讲解技巧
- 可视化反差:把 P2 那段手写 HTTP 代码和这两行并排放在屏幕上,什么都不说,停 3 秒。
- 顺带指出这个 Controller 目前没有异常处理——「这是我们后面要填的第一个坑」,埋 01-10 章节的伏笔。
时长:4min
P8 · 运行:跑通并排错
中心内容:第一次跑不起来,八成是这三件事之一。
页面内容
- 401 → Key 无效
- 404 → base-url 里多了个
/v1 - 超时 → 代理拦截 / 网络策略
- 期望输出截图 +
usage字段
讲解技巧
- 必须提前备好失败录像。现场翻车会让新手怀疑是自己操作错了,反而适得其反。做法是:提前录三段报错,现场播放并逐个拆。
- 演示真正的成功响应时,顺带指出 JSON 里的
usage字段:「这是我们下节课算钱的依据」。
时长:4min
P9 · 避坑页
中心内容:三条记牢,能省你半天。
页面内容
- 别在方法里 new ChatClient
- 配置项拼错不报错,只在调用时炸
- 默认超时不适合你的场景,先记下这个值
讲解技巧
- **用「我踩过」而不是「要注意」**开头。每条坑给一个具体场景(比如「我把
base-url写成baseurl,沉默了两小时」),可信度完全不同。 - 第三条是钩子:「下一节我们会正式治理它」。
- **用「我踩过」而不是「要注意」**开头。每条坑给一个具体场景(比如「我把
时长:2min
P10 · 小结页
中心内容:模型调用就是 Bean,下一步让它支持多种厂商。
页面内容
- 本节三件事:抽象分层 / Bean 注入 / 环境隔离
- 下一节:多模型统一适配层
- 仓库链接 + tag:
ch01-02-first-chatclient
讲解技巧
- 一定念出 git 检出命令,让学员当场能还原本节代码状态(这是完课率的关键动作)。
- 结尾留一个悬念式提问:「如果公司要求同时接两家厂商做灾备,你要改几行?」——答案在 04-02 模型网关。
时长:1min
讲师备忘
| 项 | 内容 |
|---|---|
| 课前必做 | 检出 ch01-02-first-chatclient,本地跑通,记录真实输出;准备 3 段报错录像 |
| 最容易超时处 | P6 的「为什么不能 new」,讲的时候容易展开到 Spring 生命周期 |
| 学员最常问 | 「国内模型这样接会不会有限制?」答:协议兼容,但工具调用支持度要逐个确认 |
| 现场备用 | Key 失效 → 切预录成功视频;网络不通 → 切本地 mock 配置 |