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,提供ProviderV4、SpeechModelV4、TranscriptionModelV4等核心接口类型;@ai-sdk/provider-utils:从 5.0.23 逐步升级至 5.0.39,提供postJsonToApi、postFormDataToApi、loadApiKey、withUserAgentSuffix等底层工具函数。
从 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.ts | Provider 实例工厂createFishAudio与默认实例fishAudio |
| fish-audio-config.ts | Provider 内部配置类型(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.ts、fish-audio-speech-model.test.ts、fish-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等路径; - headers(
Record<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 不提供语言、嵌入、图像模型,因此源码中languageModel、embeddingModel、imageModel三个方法会直接抛出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-pro | Fish Audio 推荐的默认模型,支持多说话人与normalizeLoudness |
s2.1-pro-free | 免费开发者档位,不保证首音频延迟与数据处理时效,生产环境优先选用s2.1-pro |
注:模型 ID 通过
modelHTTP 请求头(而非请求体字段)发送给 Fish Audio,这一点在源码doGenerate的headers: 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=en、tag=narration做过滤。
4.4 Provider Options 完整参数表
以下参数通过providerOptions.fishAudio传入,均经 fish-audio-speech-model-options.ts 中的 zod schema 校验后映射为 Fish Audio API 字段:
| 参数 | 类型/取值 | 说明 |
|---|---|---|
referenceId | string | string[] | 音色模型 ID;单个 ID 选一个说话人,数组启用多说话人对话(S2-Pro 模型),优先级高于顶层voice |
sampleRate | number(正整数) | 输出采样率(Hz),缺省回退到格式默认值(wav/pcm/mp3为 44100,opus为 48000) |
mp3Bitrate | 64 \| 128 \| 192 | mp3 输出码率(kbps),其他格式忽略 |
opusBitrate | -1000 \| 24000 \| 32000 \| 48000 \| 64000 | opus 输出码率(bps),-1000表示自动,其他格式忽略 |
latency | 'low' \| 'normal' \| 'balanced' | 延迟/质量权衡:normal质量最佳,balanced降低延迟,low最快 |
volume | number | 音量偏移(dB),负值更安静 |
normalizeLoudness | boolean | 响度归一化;S2 家族(s2-pro、s2.1-pro)支持,s1上会被忽略并发出警告 |
temperature | number(0~1) | 控制表现力,值越大变化越丰富 |
topP | number(0~1) | 核采样多样性控制 |
chunkLength | number(100~300) | 文本切分块大小 |
minChunkLength | number(0~100) | 触发新分块的最小字符数 |
normalize | boolean | 中英文文本归一化,对数字稳定性有帮助 |
maxNewTokens | number(正整数) | 每个文本块生成的最大音频 token 数 |
repetitionPenalty | number | 大于 1.0 的值抑制重复音频模式 |
conditionOnPreviousChunks | boolean | 复用前序音频作为上下文以保持跨块音色一致 |
earlyStopThreshold | number(0~1) | 批处理中的早停阈值 |
features | string[] | 透传给推理后端的请求级标志,如['quality-guard'] |
这些参数在 fish-audio-speech-model.ts 的getArgs中被逐一映射为请求体的 snake_case 字段(如sample_rate、mp3_bitrate、condition_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 输出格式与行为约束
- 支持
wav、pcm、mp3、opus四种输出格式;其他值回退为mp3并产生警告(源码中resolveFormat函数与SUPPORTED_FORMATS常量即此逻辑); - Fish Audio 会根据输入文本与所选音色自动推断语言,没有语言参数,因此 AI SDK 的
language与instructions选项均不被支持,传入会产生警告; - 关于
speed:顶层speed选项映射为prosody.speed,源码限定合法区间为 0.5~2.0(MIN_SPEED/MAX_SPEED),超出范围会被忽略并告警; - 当前不支持Fish Audio 的 TTS-live WebSocket 流式端点,也不支持通过
references内联零样本音色克隆(其需要 MessagePack 请求体)。正确做法是先把参考音频上传到 Fish Audio,再把其reference_id通过voice或referenceId传入。
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包含text、segments、language等字段。
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数组映射而来,每项包含text、startSecond、endSecond(源码中从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)以及可观测的request、response元数据(时间戳、模型 ID、响应头与原始响应体); - 语音转录:
doGenerate调用postFormDataToApi,URL 为{baseURL}/v1/asr,请求体为multipart/form-data,成功响应由 zod schema(fishAudioTranscriptionResponseSchema)解析为 JSON。
两个模型类都实现了SpeechModelV4/TranscriptionModelV4的specificationVersion = '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),便于发布流程自动维护。
八、实战要点小结
- 起步最快路径:
npm i @ai-sdk/fish-audio,设置环境变量FISH_AUDIO_API_KEY,直接导入默认实例fishAudio; - 语音合成选型:生产环境优先
s2.1-pro;需要多说话人对话时使用s2-pro/s2.1-pro并配合referenceId数组与<|speaker:N|>标记;开发试玩可用s2.1-pro-free; - 音色管理:先通过 Fish Audio 的模型列表接口查询
reference_id,再通过顶层voice或 provider optionreferenceId传入;需要克隆音色时,先上传参考音频再引用其 ID; - 转录注意点:默认已开启时间戳(
ignoreTimestamps: false),短音频如需更低延迟可显式开启true但会失去segments;程序化语言判断一律使用result.language(ISO-639-1 代码),展示场景才用providerMetadata.fishAudio.language; - 边界认知:不支持流式 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),仅供参考