本地可复用的 AI 儿童英文绘本工作台:React 阅读器负责表单、封面与 12 页阅读体验;Codex 的 moonlit-book-workflow Skill 负责编排故事、统一画风插画、本地授权成人声音克隆、校验与单文件发布。
本仓库采用 MIT License。模型权重不会进入仓库,而是在用户本机从对应模型页面下载;第三方模型及其 tokenizer 仍分别受各自模型页面所列条款约束。
把仓库拉到 Apple Silicon Mac 后,在 Codex 中打开这个文件夹,直接说:
运行这个程序
仓库内的 AGENTS.md 会让 Codex 执行唯一入口。也可以自己在终端运行同一条命令:
npm start首次运行会自动检查环境;检测到 Homebrew 时会补装缺少的 uv 和 FFmpeg,然后安装 Node/Python 依赖、下载约 3.2 GB 的固定 TTS 模型、构建阅读器、安装 Codex Skill、复检服务并自动打开页面。后续运行通过 doctor 后会跳过安装和下载,直接打开本地工作台。
这个入口仍有两个无法安全绕过的前置条件:
- 电脑需要是 Apple Silicon Mac,并已安装符合版本要求的 Node.js/npm;没有 Node 就无法执行
npm start本身。 - 如果电脑既缺少
uv/FFmpeg 又没有 Homebrew,启动器会给出明确安装提示,不会静默执行远程脚本或要求管理员密码。
填完表单后,本地任务会自动保存,并显示一条“复制 Codex 指令”。点击一次复制并发回当前 Codex 任务,它就会继续推进故事、统一画风插画、本地 TTS、12 页合成和单文件发布。流程仍会在两个人工检查点暂停:第 1 页声音试听,以及分享孩子显示名与生成形象的监护人授权。这里仍需一次回到 Codex,因为浏览器不能直接调用用户账户里的 Codex 内置生图能力。
- 浏览器不能直接把 Codex 内置生图当作本地 HTTP API 调用。真实插画任务由 Skill 调用 built-in imagegen,并把返回的确切文件写入可恢复任务清单。
- “本地”只指任务文件、模型与 TTS;内置生图仍由图像生成服务处理提示词和参考图。默认建议只用名字与文字外貌描述,上传真实儿童照片前必须取得监护人同意。
- TTS 在 Apple Silicon Mac 本地运行;模型和声音参考均保存在本机。
- 声音克隆只接受已明确授权的成人讲述者,不克隆儿童、公众人物或无法核实同意的人。
- 没有授权参考音频时,故事和插画仍可完成,但不能把语音部分标记为完成。
.moonlit/、.local-models/、.venv-tts/、dist/、上传目录和本地生成音频均被 Git 忽略。- 公开源码只带虚构的 Luna Demo 和身份中性的画风锚点,不包含真实任务、孩子资料、家长录音、转录稿、授权档案、克隆音频、缓存哈希或模型权重。
- 单文件发布器只内联当前
book.json明确引用的阅读资产,并拒绝private/路径和声音授权元数据。 - 克隆音频即使由本机生成,也应当在取得相应声音权利人与监护人许可后再分享。
本地 TTS 仅支持 Apple Silicon macOS(darwin arm64)。需要 Node.js ^20.19.0 或 >=22.12.0 和 npm。本地声音克隆和公开模型下载不需要 API Key。
推荐始终使用会自动安装、复检、启动并打开页面的一键入口:
npm start如需只查看状态或手动拆分排查,才使用下面的底层命令:
npm start -- --dry-run
npm run setup -- --dry-run
npm run setup
npm run doctornpm run setup 会幂等地完成以下工作:
- 检查 macOS/arm64、Node、npm、
uv、ffmpeg和ffprobe。 - 安装 Node 依赖,创建或同步
.venv-tts。 - 下载精确固定 revision 的 Chatterbox Multilingual V3 和 S3Tokenizer;已完整安装时跳过下载。
- 构建
dist/moonlit-stories-single.html。 - 将仓库内的
moonlit-book-workflow安装到$CODEX_HOME/skills(已设置时)或默认的~/.codex/skills。
安装器不会覆盖同名但来源不同的用户 Skill,也不会抹掉已安装 Skill 上的本地修改;遇到这种情况会报冲突并停止,请先人工核对现有目录。安装后可以执行只读自检:
npm run doctor
npm run --silent doctor -- --json默认输出是简洁文本,--json 输出适合脚本读取的完整 JSON。doctor 只检查平台、命令、Python、固定模型、Codex Skill 和阅读器构建,不会写入文件。
Codex 内置 imagegen 不是这个项目提供的离线 HTTP API。插画仍由已安装的 Skill 在 Codex 会话中调用内置生图能力,可能需要网络和可用的 Codex 账户权限;不需要在本项目里配置 OpenAI API Key。
npm install
npm run dev录入页支持中文填写孩子资料,并额外要求一个只用于英文故事和朗读的“英文故事名”。“家长声音”默认不使用;开启后可以直接使用浏览器录音,或上传 WAV、MP3、M4A 等成人授权录音,并填写授权人和两项明确确认。切回“不使用”会立即清掉前端内存中的原始录音与授权字段;这不会生成所谓默认正式音色,工作流会在 TTS 前暂停等待授权成人声音。
需要注意:静态网页不能自行启动本机工作流或 TTS 模型;正式任务要通过 npm start 打开的本机服务页面进入。点击“创建正式绘本任务”后,页面会创建真实的 .moonlit/jobs/<job-id>,并显示一条可复制给 Codex 的继续指令。浏览器不会用 Luna Demo 冒充正式成品。
任务接口使用 multipart:公开孩子资料和声音授权元数据放在 request JSON,原始录音只放在私有 voice_sample 二进制部分。服务会在临时目录中把录音规范化为任务级 reference.wav、转录稿和授权档案,然后删除原始上传;授权人、同意记录、转录稿和私有声音文件都不会进入 PictureBook、公开状态响应或分享 HTML。未选择家长声音时,可以先完成故事和插画,但 Codex 会在正式 TTS 前暂停,不会把系统预览音色冒充为已完成的本地克隆。
构建默认演示:
npm run build
npm run build:single-preview默认单文件输出为 dist/moonlit-stories-single.html。当前 Luna demo 已停用原先偏生硬的固定 Karen MP3,改用安全筛选后的本机英文系统音色作为预览兜底;正式定制书只使用经过试听确认的本地克隆音频。
家长声音功能不能从 file:// 单文件直接启动模型。正常使用只运行:
npm start启动器会自动完成缺失安装、复检、托管页面与打开浏览器。服务默认只监听 127.0.0.1:4191,不会暴露到局域网;如果默认端口被其他程序占用,会安全尝试 4192–4200,不会结束未知进程。也可以明确指定另一个本机端口:
npm start -- --port 4291npm run local:start 和 npm run tts:service 保留为开发者底层入口;它们只启动已经安装好的服务,不负责自动安装或打开浏览器。
底层服务入口会先检查构建后的单文件阅读器、.venv-tts Python、本地渲染脚本、固定模型及其必要权重、ffmpeg 和 ffprobe。如需手动排查 TTS 安装,可以执行:
npm run tts:setup
npm run tts:model:download使用期间保持该终端运行,按 Ctrl+C 会中止当前本地声音任务、删除任务级临时声音资料并关闭服务。服务不会在终端打印浏览器使用的会话令牌。家长声音仍会先只生成第 1 页供试听,只有确认声音温和、发音正常后才继续其余 11 页。
127.0.0.1 只代表当前这台电脑。将 HTML 发给其他家长并不会让他们自动连上这台 Mac 的 TTS 服务;对外产品化仍需要托管后端或安装在用户电脑上的本地伴随应用。
npm run tts:setup
npm run tts:model:download默认语音链路由两个精确固定的本地组件组成:
mlx-community/chatterbox-multilingual-v3,revision03565773edd72e949572557597af8063bb49a18a,安装到.local-models/chatterbox-multilingual-v3;mlx-community/S3TokenizerV2,revisione0c9886f0e1c35ae85b1f27277416fb19fc72bec,安装到.local-models/s3-tokenizer-v2。
两者合计约 3.2 GB。渲染器会逐页串行复用一个模型实例,并在生成阶段强制使用本地文件;缺少精确 revision、必要权重或 tokenizer 文件时会直接停止,不会静默换模型。建议使用至少 16 GB 内存的 Apple Silicon Mac。
准备一段 7–10 秒、干净且有明确授权的成人英文朗读。请用温暖、连续、有自然起伏的绘本语气,不要逐字慢念或留下长停顿:
npm run voice:prepare -- \
--voice-id family-narrator \
--source /absolute/path/reference.wav \
--transcript "Exact words spoken in the recording." \
--speaker-label "Authorized adult narrator" \
--rights-holder "Rights holder name" \
--consent-confirmed \
--speaker-is-adult声音档案默认保存在 .moonlit/voices/,该目录已忽略,不会进入前端产物。
最短路径是在本地工作台填写表单并点击“创建正式绘本任务”,然后把页面返回的整段指令粘贴给当前项目里的 Codex。指令包含唯一 job ID,Skill 会从该任务继续,不会重复创建或丢失已经批准的页面。
在 Codex 中直接说:
请使用 $moonlit-book-workflow,根据这份孩子资料生成一本新绘本并在每个视觉检查点继续执行。
Skill 会执行以下固定链路:
- 校验中文显示名与英文故事名,生成 12 句英文、12 条中文翻译和 12 个场景提示。
- 建立
.moonlit/jobs/<job-id>/manifest.json,所有任务可中断恢复。 - 始终使用
moonlit-watercolor-v1中性画风锚点。 - 每本书先生成并批准一张专属角色设定图。
- 封面和 12 页都同时引用同一画风锚点与该角色设定图;逐张查看,不合格只重做当前页。
- 先用 Chatterbox 本地克隆第 1 页供试听;确认温柔度、速度和发音后再渲染全部 12 页。默认情感参数是
exaggeration=0.7、cfg_weight=0.3、temperature=0.8,每页使用可复现的独立 seed。 - 校验图片、音频、哈希、页码、英文中无中文字符以及隐私边界。
- 组装
book.json,再输出可直接打开的定制单文件阅读器。
底层命令也可单独使用:
npm run book:workflow -- create --job-id luna-001 --profile /absolute/path/profile.json
npm run book:workflow -- commit-story --job-id luna-001 --story /absolute/path/story.json
npm run book:workflow -- plan --job-id luna-001
npm run book:workflow -- status --job-id luna-001图片任务必须使用 imagegen 返回的确切路径:
npm run book:workflow -- start --job-id luna-001 --kind image --task character-sheet
npm run book:workflow -- record-image --job-id luna-001 --task character-sheet --source /exact/imagegen/output.png --reference-ids style-key
npm run book:workflow -- approve --job-id luna-001 --kind image --task character-sheetTTS 首次试听与全量渲染:
npm run tts:render -- \
--story .moonlit/jobs/luna-001/stories/story-0123456789abcdef.json \
--voice-profile .moonlit/voices/family-narrator \
--model-dir .local-models/chatterbox-multilingual-v3 \
--tokenizer-dir .local-models/s3-tokenizer-v2 \
--output-dir .moonlit/jobs/luna-001/tts \
--exaggeration 0.7 \
--cfg-weight 0.3 \
--temperature 0.8 \
--seed 20260804 \
--pages 1
# 第 1 页试听通过后,去掉 --pages 1 再运行一次。上面的故事文件名只是格式示例;实际路径必须从该任务的 manifest.json 读取,不能猜哈希。
逐页 record-audio 和 approve 后完成组装与发布:
npm run book:workflow -- record-audio \
--job-id luna-001 \
--task page-01 \
--source .moonlit/jobs/luna-001/tts/page-01.mp3 \
--tts-manifest .moonlit/jobs/luna-001/tts/tts-manifest.json
npm run book:workflow -- approve --job-id luna-001 --kind audio --task page-01
npm run book:workflow -- assemble --job-id luna-001
npm run book:workflow -- validate --job-id luna-001
npm run build
node scripts/build-single-preview.mjs \
--book .moonlit/jobs/luna-001/book.json \
--output dist/luna-001.html单文件构建只内联 book.json 精确引用的封面、画风锚点、角色设定图、12 页插画和 12 条 MP3;会拒绝声音参考、转录稿、同意记录及 private/ 路径。
最终书使用 PictureBook schema 1.1,并保留最初要求的核心字段:
{
"book_title": "",
"child_name": "",
"pages": [
{
"page": 1,
"image": "",
"english": "",
"chinese": "",
"audio": ""
}
]
}此外还保存固定的 art_direction、角色参考和每页 image_generation 规范。生产状态、私密声音资料、模型权重和缓存不写入最终书。
AGENTS.md:用户说“运行这个程序”时的 Codex 一键启动与续作规则scripts/start-local.mjs:自动安装、复检、启动、端口复用和打开浏览器的一键入口workflow/styles/moonlit-watercolor-v1/style.json:固定画风契约scripts/book-workflow.mjs:可恢复任务 CLIworkflow/tts/render_cloned_book.py:本地克隆试听与批量渲染scripts/build-single-preview.mjs:定制书单文件发布器src/config/runtimeBook.ts:运行时注入并校验定制book.jsonsrc/services/createBookSubmission.ts:表单到本地生产服务的私有 multipart 契约src/types/narration.ts:只存在于生成请求中的声音数据类型src/data/luna-book.json:12 页 demo 数据skills/moonlit-book-workflow/SKILL.md:仓库内可安装的 Codex 生产 Skillscripts/setup-local.mjs:Apple Silicon 本机安装入口scripts/doctor.mjs:只读环境自检与 JSON 报告
npm test
npm run buildnpm test 覆盖任务恢复/依赖、哈希和路径安全、私密语音隔离、定制书注入、单文件资源内联、英文中混入中文的拒绝以及 TTS 缓存和单页试听选择。