🚀 DeepSeek 救场 —— Cappy 真活了,小菠萝当晚就唱起了 ABC
5/15 那天跟 Claude 敲定的方案 5/17 落地。让豆包停止生成回复,启用 ChatTtsText 模式把内容生成层换成 DeepSeek。Codex 一上午改完,prompt 终于生效。当晚给小菠萝试 —— 他用英语跟 Cappy 来回聊了好几轮、说出了 "I like chocolate ice cream" 这样的完整句、还主动唱起了 ABC 英文歌。整个项目里"让小朋友愿意张口"这个最核心的目标,**在这一晚达成了**。
接着 上一篇 的话。
5/15 那天跟 Claude 把方案敲定了:保留豆包 ASR + TTS,中间塞一层我自己完全可控的 LLM。5/17 落地。
这一篇讲三件事 —— 怎么塞的、为什么选 DeepSeek、还有当晚我第一次让目标用户上手的几分钟。
架构改造长什么样
原来 v1(端到端在豆包内部跑):
[麦克风] → 豆包 ASR → 豆包 LLM → 豆包 TTS → [扬声器]
↑——— 一气呵成,黑盒 ———↑
改造后 v2(豆包只听只说,中间换成自己的 LLM):
[麦克风] → 豆包 ASR ─── HTTP ───→ DeepSeek
↓
[扬声器] ← 豆包 TTS ←── ChatTtsText 模式接收文本 ──
代价:客户端多走两次外部网络(豆包→DeepSeek→豆包),整体延迟从 ~800ms 涨到 1.5-2 秒。不无感,但能接受 —— 对孩子来说,这个节奏反而更像真人对话里”想一下再答”的感觉。
关键技术点:ChatTtsText 模式
豆包 Realtime SDK 有一个模式叫 SEDialogWorkModeDelegateChatTtsText。它的作用是让豆包”闭嘴” —— 不再用自己内置的 LLM 生成回复,而是等客户端通过指令告诉它”该说这段文本”,然后豆包只负责把这段文本用指定音色 TTS 播报出来。
一行代码切换:
engine.setIntParam(
Int(SEDialogWorkModeDelegateChatTtsText.rawValue),
forKey: SE_PARAMS_KEY_DIALOG_WORK_MODE_INT
)
就是这一行 —— 把豆包从”端到端对话产品”降级成”我专属的语音转文字 + 文字转语音工具”。这是整个 v2 救场能成立的核心 API。
这个模式藏在豆包文档里一个不显眼的小章节。是 Claude 翻文档时帮我挖出来的。我自己读 SDK 文档大概率会跳过这一节。
为什么 LLM 选 DeepSeek
我没去比较一堆候选 —— 评估了一下,直接锁定 DeepSeek,开始接入看效果。
理由非常朴素:
| 维度 | 为什么 |
|---|---|
| 💰 便宜 | 自用产品,便宜是第一优先。DeepSeek 是市场上性价比最高的一档,跑一天的成本可能就几毛 |
| 🪪 方便 | 后台申请一个 API key 直接就能用,没有复杂的开通流程、不用走企业认证、不用 VPN |
| 🚀 能力强 | DeepSeek v4 刚出来,能力已经很硬,特别是对结构化输出(JSON)、prompt 服从度 |
| 🇨🇳 中文牛 | 我自己平时用 DeepSeek 聊天就觉得它中文水平完胜 ChatGPT —— 这对一个”会经常切中文兜底”的孩子陪练 app 很重要 |
不是因为 “DeepSeek 是技术上最优解”,而是因为 它能让我用最低的门槛 + 最低的成本,把一个原型跑起来。
中间埋了一个伏笔:让 DeepSeek 不只输出
reply字段,还输出{evaluate, reply, memory_note}。这个伏笔下一篇讲教学层时再展开。
Codex 一上午改完
Claude 输出 v2 工程任务书 → Codex 接管 → 一上午跑完。涉及的改造:
CappyEngine启用 ChatTtsText 模式、接管对话主循环- 新建
DeepSeekClient走 HTTP 非流式请求 - 新建
PromptComposer装配多层 prompt(人格 / 词汇约束 / JSON schema / 上下文) - 新建
Prefilter短路层(空白/单字中文/极短输入直接给预设回复,省一次 DeepSeek 调用) - 改
EvaluationResult解析 JSON
中间报了几次错(主要是 ChatTtsText 模式的指令时序细节),我截图给 Claude,Claude 告诉 Codex 怎么修。又跑通了。
自测的那一刻 — prompt 终于在听了
下午跑通自测。我对着手机说话,听到 Cappy 回来:
- 句子短了。 10-30 词的长句不见了,变成 3-5 词的短句。
- 词变了。
awesome / chill / curious这种成人词不见了,换成cool / fun / yes这种 4 岁孩子词汇。 - 风格变了。 心理咨询师式的反问不见了,变成”问完一句、等我回”的节奏。
prompt 终于在听了。
我熬过的所有 4000 字符上限的夜、所有 STRICT REQUIREMENT 大写指令的徒劳 —— 全部清零。换一个真的能听 prompt 的 LLM,这些约束突然就是有用的。
当晚拿给小菠萝试 —— 第一次面对真实用户
当天晚上我把这一版给小菠萝试。
这是 v1 自测否决之后,我第一次让他真的用 Cappy。心里挺期待 —— 想看看目标用户的反馈。这种感觉就像产品发布后第一次面对用户:万一他听不懂、或者觉得无聊立刻就放弃,整个 v2 救场也就失败了。
他点 ▶,对着 iPhone 说话。
然后这几件事陆续发生了:
🎤 他用英语跟 Cappy 打招呼介绍自己 —— “I am Pablo.”
Pablo 是小菠萝的英文名。这句话他平时就会说 —— 但这是第一次在一个真实对话场景里,他愿意主动用英语开口。会说是一回事,愿意张嘴是另一回事。这条路从立项第一天我就在等。
🍦 他完整说出 “I like chocolate ice cream.”
5 个词的完整句。结构对、发音对、语境对。他平时英语其实还行 —— 但平时没人陪他用英语聊天。Cappy 听懂了、合适地回应了 —— 这个反馈循环闭合了,他就愿意继续说。
🔤 他突然就唱起了 ABC 英文歌
这不是 Cappy 引导的,是他自己冒出来唱的。完整一首,从头到尾。我从来没听过他在没有音乐伴奏的情况下完整唱英文歌。
🤔 他困惑时自己切中文 —— Cappy 也跟着切,问完再切回英语。
来回聊了几轮,他比较流畅地用英语在对话。他觉得好玩。 没说”我累了”、没说”我不玩了”、没把手机还给我。他在用英语跟一个 AI 来回交流。
这就是我想要的效果。
至少 小菠萝能够跟 AI 进行对话聊天了。让小朋友愿意张口说英语 —— 这是整个项目最核心、也是最难的目标。这一刻它达成了。
v2 当然远不完美 — 但 0 → 1 已经发生了
我心里很清楚 v2 还有一堆问题:
- 实时性比 v1 慢了一截
- AI 评估孩子能力还很粗糙
- 没有长期记忆,每次会话从零开始
- 家长完全没有视角看孩子学得怎么样
- 对话内容还不够”有教学法”,更像是闲聊
后面要做的事还非常多。
但从”完全不能用”到”能用了”这个跳变,是整个项目里最贵的一步。剩下的全是 1 → 100 的迭代,比 0 → 1 容易十倍。
那一晚我没急着继续 debug、没急着规划下一版。我就坐那看着小菠萝拿着 iPhone 唱 ABC 歌、跟 Cappy 比较流畅地用英语来回聊。这就是产品发布后第一次面对目标用户的感觉。它真的发生了。
几个小总结
自用产品:先”能用”再”好用”。 我在 v2 阶段没纠结实时性、没纠结 UI、没纠结动画。只追一件事 —— 让小菠萝愿意张口。其他都可以等。这种聚焦让 0 → 1 真的可以一周内做到。
选型时”便宜 + 方便”经常打败”最优”。 我没选最强的 LLM,没选最贵的 LLM。选了 DeepSeek 是因为它能让我用最低的门槛 + 最低的成本把原型跑起来。对自用 / MVP 产品,这两个维度的权重应该排在能力维度之前。
架构师 (Claude) + 工程师 (Codex) + 判断者 (我) 这套分工,第一次完整跑通。 v1 阶段虽然也是这套分工,但 v1 失败了。v2 是这套分工的第一次成功 —— Claude 给架构方案、Codex 实现、我做产品决策和真机验证。这一刻我才真正相信”不写代码的人也能做产品”。
AI 处理细节比人快 —— 这是 AI 真正高效的地方。 ChatTtsText 在豆包文档里只占一个不起眼的小章节。我自己读大概率会跳过 —— 但我把上下文铺给 Claude 之后,它几秒钟就把这个细节挖出来给我了。AI 不是替我做架构决策,是替我快速消化所有相关细节,让我能在完整信息上做决策。这种”处理细节的速度差异”是 AI 协作里最容易被低估的价值。
0 → 1 那一晚,比之后所有里程碑都贵。 小菠萝当晚拿着 iPhone 唱 ABC 歌的几分钟,是整个项目里信号最强的几分钟。如果他立刻放弃,这个项目大概率就停了。但他没放弃 —— 后面所有的 v2.x / v2.5 / v2.6 迭代都是从这几分钟长出来的。
下一篇:光会聊天还不够 —— Cappy 怎么开始”记住”和”评估”小菠萝。