☰
【SenseNova U1.5 Lite 实战】少儿外教培训场景下的AI情景对话工作流——从出图、配音到离线绘本的完整落地
2026/9/30 23:52:10 网站建设 项目流程

1. 少儿外教培训的“回家就忘”困局,到底卡在哪一步

如果你在做少儿英语启蒙,大概率见过这个画面:外教课上孩子跟着老师喊 “Good morning” 喊得挺响,下课回家家长问“今天学了啥”,孩子憋半天蹦出个 “morning”,再问就摇头。不是孩子记性差,是课堂那个“情景”没有被带回家。

传统解法是课本加点读笔。点读笔能出声,但孩子看不到“谁在什么场景下说这句话”,画面感是缺的;而且只有一个声音在念,没有对话的来回感;更麻烦的是更新成本,一套教材录制动辄几周,教学进度一变材料就废了。我试过用通用文生图工具补画面,结果经常画出“两只动物各看各的”,完全没有对话感,老师还得手动裁图配字,一个场景折腾半小时。

所以真正卡住的不是“能不能生成图”,而是三件事没打通:第一,画面里两个角色得有互动,相视、挥手、递东西,孩子才能理解“这是对话”;第二,每个角色得有独立音色,A 说完 B 接,节奏像真对话;第三,成品要能离线分发,家长微信点开就能看能听,不能要求装 App 或者连服务器。

这篇就按这个思路,用 SenseNova U1.5 Lite 做视觉引擎,串上 Edge-TTS 配音和 PIL 排版,最后打包成一个单 HTML 离线绘本。整条链路我按“教师只维护一份 JSON 场景配置”来设计,出图、配音、排版、打包全自动。下面每个环节都给可复制的配置和代码,你照着跑就能复现。

先说清楚适合谁:少儿外教机构的教研老师、做英语启蒙内容的独立开发者、想给孩子做定制绘本的家长。不需要 GPU,一台普通 Windows 笔记本就能跑通,因为出图走的是云端 API。

2. 前置准备:SenseNova U1.5 Lite 接入与 TaoToken 配置

在动手写工作流之前,得先把模型调用这条线打通。这里分两块:一块是 SenseNova U1.5 Lite 本身的接入,另一块是我实际项目里用来统一管理多模型调用的 TaoToken 配置。两块都讲清楚,你按自己情况选。

先说 SenseNova U1.5 Lite。它是商汤开源的 8B 参数文生图模型,支持文生图和图像编辑两种能力,走的是 OpenAI 兼容协议。这意味着你可以直接复用 OpenAI 的 Python SDK,不用自己写 HTTP 请求。接入方式有两种:本地部署(下载权重 + GPU 推理)和云端 API。少儿教培场景我强烈建议走 API——机构不需要买 GPU 服务器,按量计费,免维护,出图 22 到 28 秒一张,4 张图两分钟出完。

申请流程很直接:打开 SenseNova 开放平台注册账号,进控制台找到密钥管理,创建一个 API Key。新用户有免费额度,文生图和图像编辑都覆盖,跑通整个项目绰绰有余。拿到 Key 之后,项目根目录建一个.env文件:

SENSENOVA_API_KEY=sk-xxxxxxxxxxxxxxxx SENSENOVA_BASE_URL=https://token.sensenova.cn/v1 SENSENOVA_MODEL=sensenova-u1.5-lite

代码里用python-dotenv自动加载,绝对不要把 Key 硬编码进源码。核心调用封装成一个SenseNovaClient类,底层直接用 OpenAI SDK:

from openai import OpenAI client = OpenAI( api_key="sk-xxxxxxxx", base_url="https://token.sensenova.cn/v1" ) resp = client.images.generate( model="sensenova-u1.5-lite", prompt="一只穿黄色背心的小熊 and 穿粉色连衣裙的小兔子 in 温馨的儿童卧室...", size="2048x2048", n=1, ) image_data = resp.data[0].b64_json

关键理解就一句:base_url决定请求发到哪,模型名sensenova-u1.5-lite决定用哪个模型处理。把这两行配对,SDK 就自动路由过去了。

再说 TaoToken。我项目里同时要调文生图模型和对话模型(用来生成老师讲解文案),如果每个模型都单独配一套 Key 和 base_url,代码里到处是分支,维护起来很烦。TaoToken 的作用就是把这些调用统一到一个入口,用一套 OpenAI 兼容协议管理多个模型。它的 API 地址是https://taotoken.net/api,控制台在https://taotoken.net/console,API Key 在https://taotoken.net/api-keys创建。

配置方式还是 OpenAI SDK 那套,只是 base_url 换成 TaoToken 的:

from openai import OpenAI client = OpenAI( api_key="你的TaoToken Key", base_url="https://taotoken.net/api" )

这样你调文生图、调对话模型,都走同一个 client,只是 model 参数不同。对于要长期跑编码类任务或者搭 Agent 的场景,TaoToken 还有个 Coding Plan 可以看,地址是https://taotoken.net/coding-plan。如果你只是想先验证模型效果,可以直接用模型对话页面https://taotoken.net/chat试,不用写代码。

这里要提醒一句:TaoToken 是合规的 API 聚合入口,不是那种灰色中转,你按正常开发者工具用就行。接入文档在https://taotoken.net/doc,遇到配置问题先翻文档。

两块配置讲完,你至少有了一个能调通的模型入口。接下来进入正题,把出图、配音、排版、打包串起来。

3. 可复制配置:从 JSON 场景到出图、配音、PIL 排版全链路

这一节是整篇的核心,我把每个环节的配置和代码都摊开。你复制过去改改路径就能跑。

3.1 场景配置层:教师只填这一份 JSON

整个工作流的输入是一份场景配置。我用 dataclass 描述,字段设计完全对齐教学逻辑:

from dataclasses import dataclass @dataclass class Scene: id: str # 场景唯一标识,如 "greeting_morning" title_en: str # 英文标题,如 "Good Morning!" title_zh: str # 中文标题,如 "早上好!" speaker_a: str # 角色A视觉描述(给 SenseNova 出图用) speaker_b: str # 角色B视觉描述 line_a: str # 角色A台词 line_b: str # 角色B台词 scene_setting: str # 场景环境描述 teacher_notes: str # 老师讲解(发音重点、用法说明) follow_up: str # 跟读教学设计 question: str # 课后小问题 voice_a: str # 角色A的 TTS 音色 voice_b: str # 角色B的 TTS 音色

一个不懂代码的外教老师,只要会写 JSON,就能生成自己的教学材料。这是整个设计里我最满意的一点——把技术复杂度全部收进工作流,暴露给教师的只有一份结构化文本。

3.2 出图模块:提示词模板是成败关键

把 Scene 转成出图 prompt 的函数:

STYLE_PROMPT = ( "Children's picture book illustration, warm pastel colors, " "rounded soft shapes, large expressive eyes, " "friendly and educational tone, " "no text in image, no watermark, " "4K, high detail" ) def _build_scene_prompt(scene: Scene) -> str: return ( f"{scene.speaker_a} and {scene.speaker_b} in {scene.scene_setting}. " f"The two characters are facing each other, smiling, with friendly body " f"language — one is waving or extending hand. {STYLE_PROMPT}" )

这里有个我踩了 20 多次坑才总结出来的点:prompt 里必须强制写facing each other, smiling, friendly body language。不加这句,双角色互动成功率大概 50%,经常画出“两只动物各看各的”;加上之后成功率提到 85% 以上。原因也好理解,文生图模型默认会把多个主体平铺在画面里,你不明确要求“相视、互动”,它就不知道这俩角色是在对话。

STYLE_PROMPT统一控制画风,no text in image和no watermark一定要加,否则模型会在图里乱塞文字,后面 PIL 排版时你还要裁掉。

3.3 TTS 配音模块:双引擎加音色降级链

这是整个工作流里踩坑最多的部分。Edge-TTS 的在线真人音色质量很好,但微软的服务偶尔会返回空音频,报 “No audio was received”,不报错但给你一个 10KB 的机械音文件。我设计了一套三级降级策略:

def synth(text: str, out, voice: str = "en-US-AnaNeural", rate: str = "+0%") -> Path: """先尝试 Edge-TTS,失败再降级到 pyttsx3。""" for engine in ["edge", "pyttsx3"]: try: if engine == "edge": _edge_tts(text, out, voice, rate) if out.exists() and out.stat().st_size > 1000: return out elif engine == "pyttsx3": _pyttsx3_tts(text, out, voice) if out.exists() and out.stat().st_size > 1000: return out except Exception: continue raise RuntimeError(f"TTS 全部引擎失败:{text[:60]}")

音色选型直接对应教学角色:女童用en-US-AnaNeural(温柔),外教用en-US-MichelleNeural(标准清晰)。降级链是主音色失败切备选音色,每个音色重试 2 次加指数退避,全失败才回退到 pyttsx3 离线引擎。实测 8 段配音全部走 Edge-TTS 真人音色,单段 70 到 120KB,音质清晰。

3.4 PIL 排版模块:对话卡自动渲染

把 SenseNova 出的图做成卡通单元式学习卡片:

def render_scene_card(scene: Scene, base_img_path: Path, out_path: Path): img = _crop_watermark( Image.open(base_img_path).convert("RGB") ).resize((1024, 1024)) canvas = Image.new("RGB", (1280, 1600), (252, 248, 240)) canvas.paste(img, (128, 80)) d = ImageDraw.Draw(canvas) _draw_text(d, (128, 1216), f" {scene.title_en}", f_title, fill=(28, 36, 56), emoji_font=f_emoji) d.text((128, 1284), scene.title_zh, fill=(56, 189, 248), font=f_zh) bubble_a = _make_bubble(scene.line_a, "left", f_en) bubble_b = _make_bubble(scene.line_b, "right", f_en) canvas.paste(bubble_a, (24, 720), bubble_a) canvas.paste(bubble_b, (canvas.width - bubble_b.width - 24, 880), bubble_b) _draw_wrapped(d, (BOX_X1 + PAD, BOX_Y1 + PAD + f_zh.size + 8), scene.teacher_notes, f_small, fill=(51, 65, 85), max_width=text_w) canvas.save(out_path, quality=92)

对话气泡左右分布,模拟真实对话的来回感。老师讲解框用_draw_wrapped自动换行,因为 PIL 的d.text不会自动换行,中文讲解一长就溢出边框。

3.5 HTML 绘本打包:base64 内嵌实现离线分发

最后把所有资源内嵌进单个 HTML:

def build_html(scenes: List, out_path: Path): items_js = [] for s in scenes: items_js.append({ "card": _to_data_url("runs_kids_en/" + s.id + "_card.jpg", "image/jpeg"), "audio_a": _to_data_url("runs_kids_en/" + s.id + "_a.mp3", "audio/mpeg"), "audio_b": _to_data_url("runs_kids_en/" + s.id + "_b.mp3", "audio/mpeg"), "teacher_notes": s.teacher_notes, "follow_up": s.follow_up, "question": s.question, }) js = JS_TEMPLATE.replace("ITEMS_JSON_PLACEHOLDER", json.dumps(items_js)) out_path.write_text(html_content, encoding="utf-8")

最终产物是一个 2.7MB 的单 HTML 文件,图片和音频全部 base64 内嵌。发家长微信群、当邮件附件、拷 U 盘、放公众号下载页都行,不需要服务器、不需要联网、不需要装 App,打开浏览器就能用。

3.6 ComfyUI 自定义节点:让设计师也能用

对不写代码的教学设计师,我封装了标准 ComfyUI 节点:

class SenseNovaU1LiteT2I: @classmethod def INPUT_TYPES(cls): return { "required": { "prompt": ("STRING", {"multiline": True}), "size": (["1024x1024", "2048x2048", "4K"], {"default": "1024x1024"}), "seed": ("INT", {"default": 0, "min": 0, "max": 2**31 - 1}), }, "optional": { "negative_prompt": ("STRING", {"multiline": True, "default": ""}), }, } RETURN_TYPES = ("IMAGE", "STRING") RETURN_NAMES = ("image", "revised_prompt") FUNCTION = "generate" CATEGORY = "SenseNova/U1.5 Lite"

把workflows/report_with_cover.json拖进 ComfyUI(File → Import),就能看到完整节点图,设计师直接拖拽使用。

4. 验证请求:跑通第一组问候类教学材料

配置写完,得验证整条链路真的能跑。我按“先最小验证,再全量跑”的顺序来。

第一步,先在浏览器里打开模型对话页面手动试一次出图,确认 API Key 有效、额度充足。这一步别省,很多人卡在 Key 没生效上。

第二步,写一个最小脚本,只用 10 行代码调一次images.generate,打印返回的 base64 长度:

from openai import OpenAI client = OpenAI(api_key="sk-xxx", base_url="https://token.sensenova.cn/v1") resp = client.images.generate( model="sensenova-u1.5-lite", prompt="a bear and a rabbit facing each other, smiling, children's book style", size="1024x1024", n=1, ) print(len(resp.data[0].b64_json))

返回一个几万长度的字符串,说明网络通了、鉴权过了。

第三步,跑完整工作流。我用 4 个问候类场景做验证,这也是“英语启蒙 Level 1”前 4 节课的核心内容:

# 1) 克隆仓库 git clone https://atomgit.com/wdracky/nova.git cd nova # 2) 安装依赖 pip install -r requirements.txt # 3) 配置 API Key cp .env.example .env # 编辑 .env 填入 SENSENOVA_API_KEY=sk-xxx # 4) 生成 4 张情景对话卡(出图 + 配音) python -m pipelines.english_kids # 5) 生成可点击的离线 HTML 绘本(2.7 MB) python -m pipelines.english_kids_html # 6) 启动 Web UI(http://127.0.0.1:7865) python -m webui.app

跑完看输出目录,应该拿到 4 张对话卡加 8 段配音。出图参数model=sensenova-u1.5-lite, size=2048x2048, n=1,单张耗时约 28 秒,4 张图总共约 2 分钟。本地处理(PIL 排版加 TTS 配音)只要 10 秒,主要等待时间在 API 推理。

生成的第一组材料长这样:

场景一 “Good Morning!”,小熊(学生角色)说 “Good morning!”,小兔子(外教角色)回 “Good morning!”。发音重点标注 morning 的 /ɔː/ 要圆唇,句尾语调上扬,跟读设计是老师领读两遍、孩子跟读、角色互换。

场景二 “How are you? — Fine, thank you.”,小狐狸老师问,小男孩答。回答拓展给了 Fine, thank you. / I'm good. / Not bad. 三种,发音重点 thank 的 /θ/ 要咬舌送气。

场景三 “What's your name? — My name is…”,小鸟问,小熊猫答 “My name is Pipi.”。发音重点 name 的 /eɪ/ 双元音要饱满,跟读设计让孩子给自己起英文名轮流上台。

场景四 “Nice to meet you!”,小女孩和小男孩互相说 “Nice to meet you!” 和 “Nice to meet you, too!”。发音重点 meet 的 /iː/ 要拖长,too 句尾语调略降,跟读时加入握手动作。

HTML 绘本的交互功能:点击图片,A 角色说完自动播 B 角色,间隔 400ms 模拟真实对话节奏;键盘左右键翻页;空格键触发连读模式(睡前复习);单独播放 A 或 B 按钮可以只听某一角色。

实际教学用法:课前 5 分钟老师在教室大屏打开绘本带孩子过一遍;课后作业家长在手机上打开文件让孩子跟读 3 遍;周末复习孩子自己翻看点击听发音。这种“看图加听音加跟读”的三感联动,比单纯放录音效果好得多,因为孩子不是在“听”英语,而是在“参与”一段情景对话。

5. 本篇常见错排查:401、空音频、文字溢出、emoji 方框

跑这条工作流,我踩过的坑基本集中在四类。你对照真实报错看。

第一类:401 Unauthorized 或鉴权失败。报错长这样:openai.AuthenticationError: Error code: 401 - {'error': {'message': 'Invalid API key'}}。原因通常是.env没被加载,或者 Key 复制时带了空格。排查动作:先确认python-dotenv在代码入口处调用了load_dotenv();再打印os.getenv("SENSENOVA_API_KEY")[:8]看前 8 位对不对。如果你走的是 TaoToken 统一入口,检查 base_url 是不是https://taotoken.net/api,Key 是不是在https://taotoken.net/api-keys创建的那个。三件套要配对:Base URL、Key、Model ID,缺一个都会 401。

第二类:Edge-TTS 返回空音频,一半变机械音。现象是 4 段里有 2 段 MP3 只有 10KB,打开是机械音。这是 Edge-TTS 偶发的 “No audio was received” 错误,微软的 voice 服务对某些组合会静默失败,不报错但返回空数据。解决方案就是前面写的三级降级策略。另外建议在生成前先跑edge-tts --list-voices缓存可用音色列表,避免运行时才发现某个音色不可用。每次降级切换时打印日志并记录到清单文件的tts_status字段,方便排查哪些音色组合容易触发失败。

第三类:老师讲解文字溢出边框。第一版我直接用d.text(...)画中文讲解,结果“发音重点:morning 的 /ɔː/ 要圆唇,句尾语调上扬”超出卡片右边界。PIL 的d.text不会自动换行。解决是写_wrap_text函数按像素宽度逐字符累加。但这里有个细节:纯按字符累加会把英文长单词从中间截断,所以断行逻辑里要加“遇到空格优先在空格处断行”的判断,只有单个单词本身就超宽时才强制切断。容器宽度也别硬编码,改成相对画布宽度的百分比,适配不同尺寸卡片。

第四类:emoji 显示成方框。PIL 默认的msyh.ttc不带 emoji 字体,画 和 出来都是 □。Windows 自带彩色 emoji 字体seguiemj.ttf,但需要按 Unicode 码位逐字符判断并切换字体。这里也有个坑:只判断>= 0x1F000覆盖不全,emoji 码位范围更广,比如 U+2600 到 U+27BF 的杂项符号、U+1F900 到 U+1F9FF 的补充符号。稳妥做法是用 regex 库的\p{Emoji}属性精确匹配,或者维护一个 emoji 码位区间表。

第五类:图像编辑换背景失败。我一开始只写 “把背景换成傍晚场景”,结果模型把原图内容也改了。原因是图像编辑的 prompt 需要带上原图内容描述,不能只写改动部分。正确写法是把原场景描述加改动指令一起给,比如 “一只穿黄色背心的小熊和穿粉色连衣裙的小兔子在儿童卧室,把背景换成傍晚场景”。走的是/v1/images/edits端点,因为 OpenAI SDK 的images.edit()参数格式和 SenseNova 不完全一致,项目里用 requests 直接发 HTTP POST:

import requests url = "https://token.sensenova.cn/v1/images/edits" headers = {"Authorization": f"Bearer {api_key}"} body = { "model": "sensenova-u1.5-lite", "images": [{"image_url": f"data:image/png;base64,{img_b64}"}], "prompt": "把背景换成傍晚场景", "size": "1024x1024", "n": 1, } resp = requests.post(url, json=body, headers=headers, timeout=120)

第六类:超长指令被网关截断。测试发现 API 网关对 prompt 长度有限制,400 到 800 字符内稳定,超过就可能被截。所以场景描述别写太长,把核心信息(角色、场景、互动指令、画风)压进 400 字符内最稳。

排障时如果遇到配置类问题,先翻接入文档https://taotoken.net/doc;如果是 Key 或额度问题,去https://taotoken.net/api-keys和https://taotoken.net/console看。验证模型本身能不能出图,用https://taotoken.net/chat最快。

6. 长期跑这条工作流,我的 CTA 分流建议

这条工作流跑通之后,你大概率会想扩展:加更多场景、接对话模型自动生成老师讲解、或者把它做成机构内部的教学材料生产工具。这时候模型调用的稳定性和成本就变成长期问题。

我的建议是按用途分流。如果你主要是排障和接入调试,先把 API Key 和接入文档这两个入口存好:API Key 在https://taotoken.net/api-keys,接入文档在https://taotoken.net/doc。这两个是基础,配置类问题九成能在这找到答案。

如果你只是想快速验证某个模型出图效果、试提示词,直接用模型对话页面https://taotoken.net/chat,不用写代码,改提示词看结果最快。

如果你要长期跑编码类任务、搭 Agent 自动生成教学讲解,或者把这条工作流做成机构的生产管线,那 Coding Plan 更合适,地址是https://taotoken.net/coding-plan。它针对的是持续性的开发任务,比按次调用更划算。

回到这条工作流本身,它的核心价值是把教学材料生产从“周”压到“分钟”。教师写好 SCENES 配置,一条命令,自动出图加配音加排版加打包,一组材料约 5 分钟,主要等待在 API 出图。这不是“AI 替代老师”,AI 替代的是找图、录音、排版、打包这些重复性机械工作。老师的创造力应该用在教学设计、课堂互动、个性化辅导上。

SenseNova U1.5 Lite 在这条链路里承担视觉引擎的角色,负责把教师脑海里的教学场景变成孩子看得到的画面。8B 参数、单卡可跑、中英双语理解、双角色对话感好,这些特性让它在少儿教学材料生成场景里很能打。你把这套配置跑一遍,改改 SCENES 里的文字,一个周末下午就能生成一整个学期的教学材料。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询