关于 blog-audio
blog-audio 是将博客文章转换为 Gemini TTS 朗读音频的技能,支持口述摘要、全文朗读和双人对话。它提供预设音色选择,并可生成 HTML5 音频嵌入指导。
主要功能
- 01摘要、全文和对话模式
- 02Gemini TTS 语音生成
- 03预设音色选择
- 04长文本分块并限制数量
- 05HTML5 音频嵌入指导
适用场景
- 01制作文章的简短音频摘要
- 02将全文制作成朗读音频
- 03生成双人播客风格版本
- 04在博客中添加音频播放器
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agricidaniel/ claude-blog --skill blog-audio安装后在 Claude Code 中输入/blog-audio确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/voices.mdscripts/__init__.pygenerate_audio.pyrequirements.lockrequirements.txtrun.pyruntime_paths.pysetup_environment.py详细介绍
博客音频:使用 Gemini TTS 为博客文章配音
以下捆绑路径使用主机 Markdown 对 ${CLAUDE_SKILL_DIR} 进行替换;
它不是导出的 shell 变量。执行前先解析路径并为路径加引号,
并拒绝非绝对路径的覆盖值。将从 ${CLAUDE_SKILL_DIR}/../blog/references
解析出的 blog_reference_root、从 ${CLAUDE_SKILL_DIR}/../blog/templates
解析出的 blog_template_root 以及所需的同级根目录传递给 Agent。
加载项目上下文前,先阅读主参考文件 orchestration-details.md;
只将其由 helper 围栏限定的输出传递给下游 Agent。
使用 Google 的 Gemini TTS 为博客内容生成专业的音频配音。 提供三种模式:摘要(200-300 字的口语概述)、全文朗读,或双人播客对话。 Gemini 3.8 Flash TTS 支持 30 种预置语音和 130 多种语言(Flash-Lite 支持 100 多种语言;Google 的语音生成页面,核查日期为 2026-10-12),并输出 HTML5 嵌入内容。
费用与授权门槛(必读)
此技能会消耗用户的 Google AI 额度并写入音频文件。
- 费用说明: 每个文本块会调用一次付费 Gemini TTS。长文本会拆分成多个文本块,因此全文朗读会产生多次调用。脚本的
--max-chunks标志会限制每次运行的文本块数量(默认值为 12);未告知用户新的最高费用前,绝不能调高此值。 - 费用依据: 音频输出每秒消耗 25 个 token。2026-10-12,https://ai.google.dev/gemini-api/docs/pricing 列出的价格为:
gemini-3.8-flash-tts每 100 万个音频 token 收费 $9.00(每 10 秒约 $0.00225),gemini-3.8-flash-lite-tts收费 $6.00(每 10 秒约 $0.0015);这两个价格均适用至 2026-12-31,2027-01-01 起所列价格更高。先运行--dry-run --json,查看脚本的费用估算。 - 写入内容: 写入
audio/下的音频文件(或写入--output指定的位置),并且仅在设置时写入托管的.venv。 - 授权: 会话中的首次付费调用前,先说明模式、模型、文本块数量和预估费用,然后等待用户明确同意。同意仅适用于本次运行。用户撰写博客文章不代表同意生成音频。
- 禁止无上限支出: 付费调用不得超过上限后重试或循环;遇到暂时性错误时可重试一次,然后报告结果。
- 跳过情形: 如果用户拒绝、缺少密钥或未完成设置,则跳过,并返回一行内容供协调器填写在“回退与跳过”摘要中,例如“音频:未设置 API 密钥,已跳过”。
快速参考
| 命令 | 功能 |
|---|---|
/blog audio generate <file> | 为博客文章生成音频配音 |
/blog audio voices | 显示可用语音及其特点 |
/blog audio setup | 检查/配置 Gemini TTS 的 API 密钥 |
前提条件
- Python 3.11+;托管虚拟环境由显式设置操作管理
GOOGLE_AI_API_KEY或GEMINI_API_KEY环境变量(blog-image 使用GOOGLE_AI_API_KEY;音频脚本接受任意一个)- FFmpeg(用于将 WAV 转换为 MP3;缺少时回退到 WAV)。绝不能隐式安装;告知用户并由用户自行安装
持久运行时目录
CLAUDE_BLOG_RUNTIME_DIR 可选,用于指定一个可信的绝对持久化根目录。此集成使用 <root>/blog-audio/.venv;NotebookLM 还会使用 <root>/blog-notebooklm/data 存储其资料库、身份验证和浏览器状态。未设置此覆盖项时,继续使用现有的技能本地默认路径。
Google 的共享凭据配置/令牌路径保持不变。不会自动复制或迁移任何状态,常规运行也绝不会执行设置。
保留现有且已验证的运维人员覆盖项。对于没有覆盖项的插件,在设置时以及每次后续运行器调用时,都显式传入由宿主替换的 ${CLAUDE_PLUGIN_DATA}/claude-blog-runtime 路径。这是 Markdown 替换,不是导出的 shell 变量。只有在路径已解析为绝对路径后才执行。单引号会让未解析的占位符保持原样,从而使校验拒绝该路径,而不是将其展开为不同的 shell 路径:
CLAUDE_BLOG_RUNTIME_DIR='${CLAUDE_PLUGIN_DATA}/claude-blog-runtime' python3 "${CLAUDE_SKILL_DIR}/scripts/setup_environment.py" --check --json
CLAUDE_BLOG_RUNTIME_DIR='${CLAUDE_PLUGIN_DATA}/claude-blog-runtime' python3 "${CLAUDE_SKILL_DIR}/scripts/run.py" generate_audio.py --help
只有不带 --check 的显式设置才会安装依赖。独立用户继续使用默认路径,除非他们显式选择一个绝对运行时根目录。相对路径、未解析路径或链接的托管子目录都会被安全拒绝。更改根目录会选择独立的状态;这绝不授权迁移或清理旧根目录。插件持久数据遵循宿主的卸载保留规则;如果卸载时仍需要这些数据,请显式保留。
使用已安装的 run.py 包装器
BLOG_SKILLS_DIR="${CLAUDE_BLOG_SKILLS_DIR:-${CLAUDE_SKILL_DIR}/..}"
case "$BLOG_SKILLS_DIR" in /*) ;; *) echo "ERROR: skills dir must be absolute" >&2; exit 1 ;; esac
AUDIO_RUN="$BLOG_SKILLS_DIR/blog-audio/scripts/run.py"
python3 "$AUDIO_RUN" generate_audio.py --text "..." --voice Charon --model flash38 --json
常规的 generate 和 voices 命令会执行不修改状态的功能检查。如果托管环境不存在,则返回需要设置的结果。只有 /blog audio setup 可以创建 .venv 或安装依赖。切勿从当前项目的 scripts/ 目录解析包装器。
API 密钥检查(门控模式)
生成音频前,检查 API 密钥(脚本会先读取
GOOGLE_AI_API_KEY,再读取 GEMINI_API_KEY):
if [ -n "${GOOGLE_AI_API_KEY:-}" ] || [ -n "${GEMINI_API_KEY:-}" ]; then echo "API key is set"; else echo "API key is not set"; fi
- 如果其中一个已设置:继续执行同意门控,然后生成
- 如果两者都未设置:引导用户:
“生成音频需要 Google AI API 密钥。请前往 https://aistudio.google.com/apikey 获取。
然后在 shell 中导出该密钥:
export GOOGLE_AI_API_KEY=your-key(或GEMINI_API_KEY)。 这可以与/blog image使用同一个密钥。” - 内部调用时(来自 blog-write):如果缺少密钥,返回一行跳过说明(说明跳过什么以及原因),供调用方写入最终摘要。绝不阻止写作工作流。
设置
对于 /blog audio setup:
- 检查环境中是否设置了
GOOGLE_AI_API_KEY或GEMINI_API_KEY - 如果 blog-image 使用项目
.mcp.json,确认其中引用的环境变量已导出 - 如果没有,引导用户前往 https://aistudio.google.com/apikey
- 经用户同意后,创建或刷新托管环境。这是唯一会安装依赖的步骤:
python3 "$BLOG_SKILLS_DIR/blog-audio/scripts/setup_environment.py"(添加--check --json可在不安装的情况下进行检查) - 使用试运行进行验证(不调用 API):
python3 "$AUDIO_RUN" generate_audio.py --text "Test" --dry-run --json
语音选择
对于 /blog audio voices:
加载 ${CLAUDE_SKILL_DIR}/references/voices.md,并向用户展示语音目录。
询问用户偏好的语音,或根据内容类型进行推荐:
- 文章旁白:Charon(Informative)或 Sadaltager(Knowledgeable)
- 教程/操作指南:Achird(Friendly)或 Sulafat(Warm)
- 新闻/分析:Rasalgethi(Informative)或 Schedar(Even)
- 生活方式/健康:Aoede(Breezy)或 Vindemiatrix(Gentle)
- 对话主持人:Puck(Upbeat)或 Laomedeia(Upbeat)
- 对话专家:Kore(Firm)或 Charon(Informative)
生成工作流
对于 /blog audio generate <file>:
步骤 1:读取博客文章
读取文件并提取:
- 标题(来自 H1 或 frontmatter)
- 完整内容(Markdown 正文)
- 大致字数
步骤 2:选择模式
询问用户(如果用户指定了 --mode,则自动选择):
| 模式 | 适用场景 | 输出 |
|---|---|---|
| Summary | 快速音频概览(1-2 分钟) | 200-300 词的口语化摘要 |
| Full | 完整朗读(5-15 分钟) | 将全文改写为自然口语 |
| Dialogue | 播客风格(3-8 分钟) | 两人围绕文章展开对话 |
步骤 3:准备文本
由 Claude 准备文本;脚本只负责 TTS。
Summary 模式: 撰写一份 200-300 词的文章口语化摘要。规则:
- 写得像自然对话,而非书面文字
- 以文章的关键发现或答案开篇
- 涵盖 3-5 个主要要点
- 以可执行的建议收尾
- 不要使用 Markdown,不要写“In this article...”,也不要添加元评论
- 使用对话式过渡语(“重点在于……”“关键发现是……”)
Full 模式: 将 Markdown 内容整理为干净、适合朗读的文本:
- 将标题改写为自然过渡语(“接下来,我们来看看……”)
- 将链接改为纯文本(删除 URL,保留锚文本)
- 图片和图表:省略或简要描述(“从数据中可以看出……”)
- 代码块:用口语描述(“这段代码使用了一个 for 循环……”)
- 将列表改写为自然句子
- 删除 frontmatter、schema 标记和 HTML 标签
- 添加简短开场白:“本文是[标题],发表于[日期]。”
Dialogue 模式: 围绕文章撰写双人对话脚本:
- Speaker1 = 主持人(充满好奇,善于提问)
- Speaker2 = 专家(知识渊博,回答清晰)
- 每行格式为:
Speaker1: 这里最重要的启示是什么? - 以对话形式涵盖文章的主要内容
- 进行 15-25 轮对话(生成约 3-8 分钟的音频)
- 表达自然,不要生硬(使用“这是个很好的观点”,而不是“确实,正如研究所表明的那样”)
步骤 4:选择语音
如果用户选择了语音,则使用该语音。否则,根据模式推荐:
- Summary/Full:默认使用 Charon(Informative)
- Dialogue:默认使用 Puck(Host)+ Kore(Expert)
步骤 5:生成音频
将准备好的文本写入工作目录下的文件,然后调用:
# Single voice (summary or full mode)
python3 "$AUDIO_RUN" generate_audio.py \
--text-file blog_audio_prepared.txt \
--voice Charon \
--model flash38 \
--output audio/post-slug.mp3 \
--json
# Two voices (dialogue mode)
python3 "$AUDIO_RUN" generate_audio.py \
--text-file blog_audio_dialogue.txt \
--voice Puck \
--voice2 Kore \
--model flash38 \
--output audio/post-slug-dialogue.mp3 \
--json
模型选择(使用明确的 3.8 ID):
flash38:gemini-3.8-flash-tts,稳定的通用模型,也是上述示例中使用的模型。支持 130 多种语言。flash-lite38:gemini-3.8-flash-lite-tts,成本更低的稳定模型。支持 100 多种语言;少数语言(例如芬兰语和泰语)仅 Flash 支持。- 旧版别名
flash、flash31、pro、legacy-flash25、legacy-pro25分别映射到gemini-3.1-flash-tts-preview、gemini-2.5-pro-preview-tts和gemini-2.5-flash-preview-tts。Google 的弃用页面(于 2026-10-12 核查)列出的最早停用日期为 2026-11-17,因此不要将它们用于新工作。开始处理前,请在--dry-run --json输出中确认解析后的模型 ID。
3.8 别名已通过离线 SDK 模式和 WAV 解析检查;通过身份验证后的可用性和语音质量仍需通过服务提供商测试。使用 3.8 时,请传入逐字稿,不要嵌入旁白指示。Dialogue 必须使用明确的 Speaker1: 和 Speaker2: 行;辅助程序不会推断说话人。费用估算采用截至 12 月 31 日经过核实的 2026 年费率,以及从 1 月 1 日起公布的 2027 年费率。这些仍是估算,并非账单报价。
步骤 6:交付
向用户提供结果:
- 文件路径:音频的保存位置
- 时长:便于阅读的格式(例如“3:42”)
- 嵌入代码:可直接粘贴的 HTML5 audio 标签
- 费用:预估 API 费用
- 放置建议:建议在博客文章中的插入位置
嵌入指南
标准 HTML(Hugo、Jekyll、静态网站)
<audio controls preload="metadata">
<source src="audio/post-slug.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
MDX(Next.js、Gatsby)
<audio controls preload="metadata">
<source src="/audio/post-slug.mp3" type="audio/mpeg" />
</audio>
WordPress
[audio src="audio/post-slug.mp3"]
放置位置
将音频播放器插入引言之后(第一个 H2 标题下方),或放在文章最顶部,并添加标签:“收听本文”或“音频版本”。
内部 API(用于 blog-write)
从 blog-write 内部调用时:
输入:
text:准备好的文本(已由 Claude 清理)voice:语音名称(默认:Charon)voice2:对话中的第二种语音(可选)model:flash38(新任务的默认值)或flash-lite38;旧版别名(flash、flash31、pro、legacy-*)最早将于 2026-11-17 停用output_path:保存文件的位置
输出:
### Audio Narration
- **Path:** /path/to/audio/post-slug.mp3
- **Duration:** 3:42
- **Voice:** Charon
- **Embed:** `<audio controls preload="metadata"><source src="audio/post-slug.mp3" type="audio/mpeg"></audio>`
**优雅回退:**如果未设置 GOOGLE_AI_API_KEY 和 GEMINI_API_KEY,或用户拒绝费用确认,则立即返回,不报错,并向调用方的最终摘要提供一行跳过说明。写作工作流会在没有音频的情况下继续。音频生成不可用时,绝不要阻止 blog-write。
错误处理
| 错误 | 解决方法 |
|---|---|
未设置 API 密钥(GOOGLE_AI_API_KEY 或 GEMINI_API_KEY) | 在 https://aistudio.google.com/apikey 获取密钥 |
| 未找到 FFmpeg | 回退为 WAV 输出。告知用户;不要替他们安装。 |
| 请求过多 | 等待并重试。在 https://aistudio.google.com/rate-limit 查看限制 |
| 文本过长(>8,192 个输入 token) | 拆分为约 7,800 个 token 的片段;脚本会对准备好的文本分块并拼接,每次运行最多处理 --max-chunks(默认值为 12)个块。超过上限时,停止并在提高上限前询问用户 |
| 未知语音名称 | 运行 /blog audio voices 查看有效选项 |
| API 错误 | 检查密钥有效性和模型可用性 |
| API 密钥缺失(内部调用) | 返回一行跳过说明:写作工作流将继续并报告此情况 |
参考文档
按需加载:启动时不要加载全部内容:
${CLAUDE_SKILL_DIR}/references/voices.md:完整的 30 种语音目录、按内容类型划分的推荐及对话配对