news 2026/9/12 20:09:27

AI SDK Fish Audio 语音提供方:@ai-sdk/fish-audio 版本演进与语音合成/转录实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI SDK Fish Audio 语音提供方:@ai-sdk/fish-audio 版本演进与语音合成/转录实战指南

AI SDK Fish Audio 语音提供方:@ai-sdk/fish-audio 版本演进与语音合成/转录实战指南

【免费下载链接】aiThe AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and agents项目地址: https://gitcode.com/GitHub_Trending/ai/ai

本篇文章以@ai-sdk/fish-audio包的 CHANGELOG 为脉络骨架,梳理该提供方从 3.0.0 首次引入语音合成(TTS)与语音转录(ASR)能力到 3.0.17 的完整版本演进,并结合仓库内的 官方文档、包内 README 与全部源码实现,深入讲解 Fish Audio 提供方的实例配置、语音生成、多说话人对话、语音转录等完整使用方案。读完本文,你将能基于 AI SDK 直接调用 Fish Audio 的 S1/S2 系列语音模型与transcribe-1转录模型,构建可投入实战的中文语音应用。

一、版本演进概览:从 3.0.0 到 3.0.17

打开 packages/fish-audio/CHANGELOG.md,可以看到该包的全部版本记录,其演进脉络清晰分两类:

1.1 Major Changes:3.0.0 引入语音能力

在 3.0.0 版本中,唯一的 Major Change 记录为:

e1f9b02: feat(fish-audio): add Fish Audio provider with speech and transcription models

这是整个包的核心定位——Fish Audio 提供方同时承载两类能力:

  • speech(语音合成 / Text-to-Speech):调用 Fish Audio TTS 端点,支持 S1 与 S2 系列模型;
  • transcription(语音转录 / Speech-to-Text):调用 Fish Audio ASR 端点,支持transcribe-1模型。

这与包内 README 开头对包的描述完全一致:"contains speech generation (S1 and S2 models) and speech-to-text transcription support"。

1.2 Patch Changes:与底层依赖保持同步

从 3.0.1 到 3.0.17,包持续发布 Patch 版本,内容全部为依赖更新(Updated dependencies),主要涉及两个工作区包:

  • @ai-sdk/provider:从 4.0.6 逐步升级至 4.0.13,提供ProviderV4SpeechModelV4TranscriptionModelV4等核心接口类型;
  • @ai-sdk/provider-utils:从 5.0.23 逐步升级至 5.0.39,提供postJsonToApipostFormDataToApiloadApiKeywithUserAgentSuffix等底层工具函数。

从 package.json 的依赖声明可以看出,@ai-sdk/fish-audio的运行时依赖仅有上述两个包,且都以workspace:*方式引用,与主仓库保持同源构建。这种"薄封装"设计意味着:Fish Audio 提供方本身不携带任何网络请求库,全部 HTTP 能力复用 AI SDK 的 provider-utils 基础设施。

二、安装与包结构

2.1 安装方式

Fish Audio 提供方以独立 npm 模块@ai-sdk/fish-audio发布,包内脚本提供了完整的构建、测试与发布链路:

npm i @ai-sdk/fish-audio

从 package.json 可见,包要求 Node.js>=22,以 ESM 形式导出("type": "module"),并声明zod^3.25.76 || ^4.1.8)为 peer dependency——provider options 的运行时校验正是通过 zod schema 完成的。

2.2 源码文件布局

包的src目录结构清晰,每个模块职责单一:

文件职责
fish-audio-provider.tsProvider 实例工厂createFishAudio与默认实例fishAudio
fish-audio-config.tsProvider 内部配置类型(provider 名、URL 构造、headers、fetch)
fish-audio-speech-model.ts语音合成模型实现(SpeechModelV4
fish-audio-speech-model-options.ts语音合成 provider options 的 zod schema
fish-audio-speech-options.ts语音模型 ID 与 voice ID 类型
fish-audio-transcription-model.ts语音转录模型实现(TranscriptionModelV4
fish-audio-transcription-model-options.ts语音转录 provider options 的 zod schema
fish-audio-transcription-options.ts转录模型 ID 类型
fish-audio-error.ts统一错误响应处理
index.ts公共导出入口

此外包内还配有fish-audio-provider.test.tsfish-audio-speech-model.test.tsfish-audio-transcription-model.test.ts等测试文件(见 vitest.node.config.js 与 vitest.edge.config.js),分 Node 与 Edge 两套环境运行,可作深入阅读的验证入口。

三、Provider 实例:默认实例与自定义配置

3.1 使用默认实例

与 AI SDK 其他提供方一致,包直接导出默认实例fishAudio

import { fishAudio } from '@ai-sdk/fish-audio';

从 fish-audio-provider.ts 源码可见,默认实例即createFishAudio()的空参数调用,API Key 从环境变量FISH_AUDIO_API_KEY读取。

3.2 自定义实例 createFishAudio

需要定制时,可导入createFishAudio创建带配置的实例:

import { createFishAudio } from '@ai-sdk/fish-audio'; const fishAudio = createFishAudio({ // custom settings, e.g. fetch: customFetch, });

3.3 全部配置项说明

结合 源码中FishAudioProviderSettings接口与官方文档,支持以下可选配置:

  • apiKey(string):通过Authorization: Bearer <key>头发送的 API Key,默认读取FISH_AUDIO_API_KEY环境变量;
  • baseURL(string):API 请求的基础地址,默认https://api.fish.audio。源码中通过options.baseURL?.replace(/\/$/, '')去除末尾斜杠,随后拼接/v1/tts/v1/asr等路径;
  • headersRecord<string, string>):附加的自定义请求头,会被合并进所有请求;
  • fetch(input, init) => Promise<Response>):自定义 fetch 实现,可用于拦截请求或提供测试替身,默认使用全局fetch

3.4 鉴权与 UA 细节

从源码看,getHeaders的实现非常值得一提:

const getHeaders = () => withUserAgentSuffix( { Authorization: `Bearer ${loadApiKey({ apiKey: options.apiKey, environmentVariableName: 'FISH_AUDIO_API_KEY', description: 'Fish Audio', })}`, ...options.headers, }, `ai-sdk/fish-audio/${VERSION}`, );

即:每次请求自动附带Authorization头,并通过withUserAgentSuffix在 User-Agent 上追加ai-sdk/fish-audio/<版本号>标识,便于服务端统计与排障。其中VERSION来自 version.ts,由构建期注入的__PACKAGE_VERSION__生成。

3.5 未支持的能力显式报错

FishAudioProvider实现了ProviderV4接口,但 Fish Audio 不提供语言、嵌入、图像模型,因此源码中languageModelembeddingModelimageModel三个方法会直接抛出NoSuchModelError,并附上明确的错误信息(如 "Fish Audio does not provide language models")。这保证了接口完整性的同时,让误用者在第一时间获得清晰反馈。

四、语音合成:Speech Models 实战

语音合成通过.speech()工厂方法创建模型,底层调用 Fish Audio 的文本转语音端点(对应源码 fish-audio-speech-model.ts 中的POST /v1/tts)。

4.1 最小示例

import { fishAudio } from '@ai-sdk/fish-audio'; import { generateSpeech } from 'ai'; const { audio } = await generateSpeech({ model: fishAudio.speech('s1'), text: 'Hello from Fish Audio!', });

4.2 支持的模型 ID

从 fish-audio-speech-options.ts 源码看,FishAudioSpeechModelId为以下联合类型:

模型 ID说明
s1经典单说话人模型,忽略normalizeLoudness
s2-pro支持多说话人对话,支持normalizeLoudness
s2.1-proFish Audio 推荐的默认模型,支持多说话人与normalizeLoudness
s2.1-pro-free免费开发者档位,不保证首音频延迟与数据处理时效,生产环境优先选用s2.1-pro

注:模型 ID 通过modelHTTP 请求头(而非请求体字段)发送给 Fish Audio,这一点在源码doGenerateheaders: combineHeaders(..., { model: this.modelId }, ...)处有明确注释与实现。

4.3 选择音色:voice 与 referenceId

voice选项接受 Fish Audio 音色模型 ID(reference_id),可从 Fish Audio 音色库或自己上传的模型中选取,省略则使用默认音色:

const { audio } = await generateSpeech({ model: fishAudio.speech('s1'), text: 'Hello from Fish Audio!', voice: '933563129e564b19a115bedd57b7406a', outputFormat: 'opus', speed: 1.1, });

音色列表不在 AI SDK 语音模型规范内,需要直接调用 Fish Audio 的模型列表接口获取。按官方文档给出的方式:

const response = await fetch( 'https://api.fish.audio/model?page_size=20&sort_by=task_count', { headers: { Authorization: `Bearer ${process.env.FISH_AUDIO_API_KEY}` } }, ); const { items } = await response.json(); // 每个 item 的 _id 即可以传入 voice 的值

可追加self=true只列出自己上传的模型,或用language=entag=narration做过滤。

4.4 Provider Options 完整参数表

以下参数通过providerOptions.fishAudio传入,均经 fish-audio-speech-model-options.ts 中的 zod schema 校验后映射为 Fish Audio API 字段:

参数类型/取值说明
referenceIdstring | string[]音色模型 ID;单个 ID 选一个说话人,数组启用多说话人对话(S2-Pro 模型),优先级高于顶层voice
sampleRatenumber(正整数)输出采样率(Hz),缺省回退到格式默认值(wav/pcm/mp3为 44100,opus为 48000)
mp3Bitrate64 \| 128 \| 192mp3 输出码率(kbps),其他格式忽略
opusBitrate-1000 \| 24000 \| 32000 \| 48000 \| 64000opus 输出码率(bps),-1000表示自动,其他格式忽略
latency'low' \| 'normal' \| 'balanced'延迟/质量权衡:normal质量最佳,balanced降低延迟,low最快
volumenumber音量偏移(dB),负值更安静
normalizeLoudnessboolean响度归一化;S2 家族(s2-pros2.1-pro)支持,s1上会被忽略并发出警告
temperaturenumber(0~1)控制表现力,值越大变化越丰富
topPnumber(0~1)核采样多样性控制
chunkLengthnumber(100~300)文本切分块大小
minChunkLengthnumber(0~100)触发新分块的最小字符数
normalizeboolean中英文文本归一化,对数字稳定性有帮助
maxNewTokensnumber(正整数)每个文本块生成的最大音频 token 数
repetitionPenaltynumber大于 1.0 的值抑制重复音频模式
conditionOnPreviousChunksboolean复用前序音频作为上下文以保持跨块音色一致
earlyStopThresholdnumber(0~1)批处理中的早停阈值
featuresstring[]透传给推理后端的请求级标志,如['quality-guard']

这些参数在 fish-audio-speech-model.ts 的getArgs中被逐一映射为请求体的 snake_case 字段(如sample_ratemp3_bitratecondition_on_previous_chunks等),并放入prosody对象中提交。

4.5 多说话人对话

S2-Pro 模型支持多说话人对话:通过referenceId传入音色数组,并在文本中用<|speaker:N|>标记轮次,N为数组下标:

const { audio } = await generateSpeech({ model: fishAudio.speech('s2-pro'), text: '<|speaker:0|>Hello!<|speaker:1|>Hi there!', providerOptions: { fishAudio: { referenceId: [ '933563129e564b19a115bedd57b7406a', 'bf322df2096a46f18c579d0baa36f41d', ], }, }, });

这正是 3.0.0 版本引入的 speech 能力的进阶用法,也解释了为什么referenceId在源码中被设计为z.union([z.string(), z.array(z.string())])——数组形态专为多说话人场景服务。

4.6 输出格式与行为约束

  • 支持wavpcmmp3opus四种输出格式;其他值回退为mp3并产生警告(源码中resolveFormat函数与SUPPORTED_FORMATS常量即此逻辑);
  • Fish Audio 会根据输入文本与所选音色自动推断语言,没有语言参数,因此 AI SDK 的languageinstructions选项均不被支持,传入会产生警告;
  • 关于speed:顶层speed选项映射为prosody.speed,源码限定合法区间为 0.5~2.0(MIN_SPEED/MAX_SPEED),超出范围会被忽略并告警;
  • 当前不支持Fish Audio 的 TTS-live WebSocket 流式端点,也不支持通过references内联零样本音色克隆(其需要 MessagePack 请求体)。正确做法是先把参考音频上传到 Fish Audio,再把其reference_id通过voicereferenceId传入。

4.7 模型能力矩阵

模型多说话人备注
s1不支持忽略normalizeLoudness
s2-pro支持支持normalizeLoudness
s2.1-pro支持推荐默认;支持normalizeLoudness
s2.1-pro-free不支持免费开发档;无首音频延迟与数据处理保障

五、语音转录:Transcription Models 实战

语音转录通过.transcription()工厂方法创建模型,底层调用 Fish Audio 语音转文本端点(对应源码 fish-audio-transcription-model.ts 中的POST /v1/asr)。

5.1 最小示例

import { fishAudio } from '@ai-sdk/fish-audio'; import { transcribe } from 'ai'; import { readFile } from 'node:fs/promises'; const result = await transcribe({ model: fishAudio.transcription(), audio: await readFile('audio.mp3'), });

result包含textsegmentslanguage等字段。

5.2 模型 ID 的特殊语义

transcribe-1是转录模型的唯一 ID。但需要注意:当前 Fish Audio 的 ASR 端点不暴露模型选择器,只服务单一模型,因此 fish-audio-transcription-options.ts 中明确注释——该 ID 仅是路由标签,不会发送到 API。这与 TTS 端点通过model请求头选模型的机制不同,/v1/asr目前没有该头;Fish Audio 计划后续增加更多 ASR 模型,并会参照 TTS 端点改用modelHTTP 头选择。

5.3 Provider Options

转录模型仅有两个 provider options,经 fish-audio-transcription-model-options.ts 的 zod schema 校验:

  • language(string):音频语言提示。它只是提示——Fish Audio 会将其传给模型,但自动检测是权威的并会覆盖它,因此既不改变转录文本,也不改变上报的语言
  • ignoreTimestamps(boolean):是否跳过精确时间戳。对应 Fish Audio 的ignore_timestamps参数,其 API 默认值为true;本提供方将其默认值设为false,以保证segments被填充。Fish Audio 文档指出,对短于 30 秒的音频会产生额外延迟成本,若可接受牺牲 segments,可设为true换取更低延迟。
const result = await transcribe({ model: fishAudio.transcription(), audio: await readFile('audio.mp3'), providerOptions: { fishAudio: { language: 'en', ignoreTimestamps: false, }, }, });

从源码看,ignoreTimestamps在请求中始终以字符串形式追加到 FormData(String(fishAudioOptions?.ignoreTimestamps ?? false)),而language仅在显式传入时追加;音频文件则封装为File并以推断出的扩展名(基于 mediaType)命名。

5.4 语言检测结果

  • result.language:上报检测到的语言,为 ISO-639-1 两位字母代码(如en),永远是两位代码而非en-US这类 locale;Fish Audio 未检测出语言时为undefined
  • 人类可读的语言名(如English)通过 provider metadata 提供:
console.log(result.language); // 'en' console.log(result.providerMetadata?.fishAudio?.language); // 'English'

源码中,language_code虽未出现在 Fish Audio 文档化的响应 schema 中,但实际会返回,并反映的是检测到的语言而非请求的语言;providerMetadata.fishAudio.language是展示用名称,其确切形式不保证,因此任何程序化逻辑都应基于result.language判断,不要对其匹配或分支。

5.5 分段结果与时长

segments由响应中的segments数组映射而来,每项包含textstartSecondendSecond(源码中从start/end秒值转换)。将ignoreTimestamps设为true时,Fish Audio 会返回空的segments数组,因此本提供方默认请求时间戳。此外,result.durationInSeconds取自响应中的duration字段。

5.6 转录模型能力

模型转录时长分段语言
transcribe-1支持支持支持支持

六、错误处理与底层调用链

6.1 统一错误响应解析

Fish Audio 的文档化错误响应为{ status, message }结构(如 401 无权限、402 未付费)。fish-audio-error.ts 通过createJsonErrorResponseHandler注册了该 schema:

export const fishAudioErrorDataSchema = z.object({ status: z.number().nullish(), message: z.string().nullish(), });

解析失败时,错误信息回退为'Unknown Fish Audio error'。TTS 与 ASR 两个模型共用这一错误处理器。

6.2 请求链路

  • 语音合成doGenerate调用postJsonToApi,URL 为{baseURL}/v1/tts,请求体为 JSON,成功响应由createBinaryResponseHandler()处理,直接返回二进制音频字节流(result.audio)以及可观测的requestresponse元数据(时间戳、模型 ID、响应头与原始响应体);
  • 语音转录doGenerate调用postFormDataToApi,URL 为{baseURL}/v1/asr,请求体为multipart/form-data,成功响应由 zod schema(fishAudioTranscriptionResponseSchema)解析为 JSON。

两个模型类都实现了SpeechModelV4/TranscriptionModelV4specificationVersion = 'v4',并提供了WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE静态方法,说明它们可被 AI SDK 的 workflow 能力序列化与反序列化(借助serializeModelOptions)。

七、工程化与发布细节

  • 构建:使用tsup打包(见 tsup.config.ts),prepack阶段会把官方文档 190-fish-audio.mdx 复制进包内docs/目录随包发布,postpack后清理;
  • 测试pnpm test会先后运行 Node 与 Edge 两套 vitest 配置(vitest.node.config.js、vitest.edge.config.js),确保提供方在服务端与边缘运行时环境行为一致;
  • 版本策略:以 CHANGELOG 为准,当前最新版本为 3.0.17,与@ai-sdk/provider@4.0.13@ai-sdk/provider-utils@5.0.39对齐;版本号由构建期注入(见 version.ts),便于发布流程自动维护。

八、实战要点小结

  1. 起步最快路径npm i @ai-sdk/fish-audio,设置环境变量FISH_AUDIO_API_KEY,直接导入默认实例fishAudio
  2. 语音合成选型:生产环境优先s2.1-pro;需要多说话人对话时使用s2-pro/s2.1-pro并配合referenceId数组与<|speaker:N|>标记;开发试玩可用s2.1-pro-free
  3. 音色管理:先通过 Fish Audio 的模型列表接口查询reference_id,再通过顶层voice或 provider optionreferenceId传入;需要克隆音色时,先上传参考音频再引用其 ID;
  4. 转录注意点:默认已开启时间戳(ignoreTimestamps: false),短音频如需更低延迟可显式开启true但会失去segments;程序化语言判断一律使用result.language(ISO-639-1 代码),展示场景才用providerMetadata.fishAudio.language
  5. 边界认知:不支持流式 TTS、不支持language/instructions(TTS)参数、不支持的输出格式会回退 mp3 并告警——这些行为均由源码中的warnings机制显式上报,可作为运行时诊断依据。

如需进一步阅读实现细节,可从 fish-audio-provider.ts 入手,配合官方文档 190-fish-audio.mdx 与各模型测试文件逐层深入。

【免费下载链接】aiThe AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and agents项目地址: https://gitcode.com/GitHub_Trending/ai/ai

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 20:08:53

Redis分布式锁原理、实现与生产实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 20:07:40

Python学习【33】:python3 连接mysql 数据库的原理,并举例说明

一、学前花絮跟随着学习的不断深入, 在拥有掌握基础知识这样的前提条件之下, 我们又开展了函数的学习, 类/对象的探索, 还有错误和异常方面的钻研, 以及各种各样文件的处理等等诸多方面。具备了这些基础之后, 我们能够达成许多的工作。针对大数据行业来讲, 核心问题便是针对各类…

作者头像 李华
网站建设 2026/9/12 20:07:19

高分子PVT拟合为何必须用修正双域Tait模型

简介&#xff1a;本资源是一套面向高分子材料科研人员与计算材料学学习者的PVT特性数据高精度拟合程序&#xff0c;聚焦解决实验中温度-压力-比容&#xff08;PVT&#xff09;数据拟合精度不足的共性难题&#xff0c;特别适用于需对修正双域Tait状态方程实施非线性回归与参数优…

作者头像 李华
网站建设 2026/9/12 20:06:58

在线Python少儿编程课哪家好?家长别盲目报课

很多家长在孩子处于小学中高年级阶段时, 就会思索着让孩子去 learn 编程。一方面呢, 它作为人工智能时代里实用性颇为强大的编程语言, 另一方面, 它还是信息学竞赛以及科技特长生升学的关键学习科目哎。然而, 当打开网络进行搜索时, 种类繁多的在线少儿编程机构数量极为庞大。有…

作者头像 李华
网站建设 2026/9/12 20:06:37

小团队管理实战:提升效率的黄金法则

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华