Heidi Labs
Hugging FaceAI 领域最新发展约 21 分钟8,582 字

AnySpeechRecognizer 与 AnyDecisionModel:Apple 平台上的开源语音与决策模型

本文目录

作者:Mattt

发布于 2026 年 10 月 29 日

语言模型能做很多事。它们写作、总结、翻译、调用工具。但它们只是机器学习所能做的事情中的一小部分。

假设一位客户打来你的客服电话。首先,你想知道他说了什么(语音识别)。然后,你想决定把他转给谁(决策)。你可以把这两件事都交给语言模型,但有一些专门为此构建的模型更快、更好、也更便宜。

在 AnyLanguageModel 1.0 的回顾中,我们梳理了 Swift 生态中一整年的本地与托管模型。如今,AnyLanguageModel 和 swift-huggingface 都已进入 1.x 版本,我们把同样的思路延伸到语音和决策:熟悉的 Swift API,加上可选的模型。

今天我们宣布两个软件包:

AnySpeechRecognizer 实现了 Apple 新的语音转文本 API,并允许选择识别引擎,包括在 Core ML 上本地运行的 NVIDIA Parakeet 和阿里巴巴 Qwen3-ASR,同时新增说话人分离(speaker diarization)以跟踪谁在何时说话。

AnyDecisionModel 回答是/否、多选题和评分量表类问题,返回带类型的结果和概率。它从模型的概率推导出每个答案,而非生成文本;所用模型可运行在你的设备上(通过 MLX、Core AI 或 llama.cpp),也可使用来自 Jev 或 Ollama 的决策模型。

两个软件包都能从 Hugging Face Hub 下载开放模型并在设备上运行。

AnySpeechRecognizer#

计算机转写语音已有数十年,而过去几年里它们已经做得非常好。Apple 平台本就有相应的 API。macOS 26 和 iOS 26 引入了 SpeechAnalyzer——Apple 新的语音转文本 API。你向分析器中添加 SpeechTranscriber、SpeechDetector 等模块,交给它一个文件或一串音频缓冲区,然后从异步序列中读取陆续到达的结果。AssetInventory 负责安装模型,因此何时下载由你的应用决定。

这是一个设计良好的 API,但它背后只有 Apple 自家的引擎。AnySpeechRecognizer 保留这套 API,同时让你选择引擎:

- import Speech
+ import AnySpeechRecognizer

该软件包对应实现了 SpeechAnalyzer、SpeechTranscriber、DictationTranscriber、SpeechDetector 和 AssetInventory,并以 Apple 的 Speech 框架作为默认引擎。如果你的代码使用这套共享 API,只改变 import 就够了。要改用 Parakeet,只需改变创建转写器的方式:

let transcriber = SpeechTranscriber(
    model: ParakeetSpeechModel(variant: .v3),
    transcriptionOptions: [],
    reportingOptions: [.volatileResults],
    attributeOptions: [.audioTimeRange]
)

安装资源、运行分析器、读取结果的步骤保持不变。README 中有一个完整示例,并列出了与 Apple 框架的差异。

认识这些模型#

Parakeet v3 是 NVIDIA 的 Parakeet TDT 0.6B v3。它是个多语通:能识别 25 种欧洲语言,并自行判断听到的是哪一种,因此你无需传入 locale。

Parakeet Ultra 是 Moondream 对 v3 做后训练(post-training)得到的版本。在我们大多数测量中它更准确。

Parakeet Unified 是 NVIDIA 仅支持英语的 Parakeet。

三个 Parakeet 版本都运行在该软件包自有的 Core ML 运行时上,支持文件输入与实时输入、易变结果(volatile results)、词级时间戳和词级置信度。

Qwen3-ASR 是阿里巴巴的语音模型,有 0.6B 和 1.7B 两种规模。它能识别 30 种语言和 22 种中文方言,包括普通话、粤语、日语和韩语——这些是 Parakeet 不覆盖的。它会自动检测语言,你也可以指定一种:

let transcriber = SpeechTranscriber(
    model: QwenSpeechModel(variant: .large, language: Locale.Language(identifier: "yue")),
    transcriptionOptions: [],
    reportingOptions: [],
    attributeOptions: []
)

Qwen3-ASR 为每段最长 40 秒的音频给出一个最终结果,不支持易变结果、词级时间戳或置信度。它运行在 GPU 上,比 Parakeet 慢,因此更适合转写文件而非实时输入。

要添加另一个引擎,需为其配置实现 SpeechRecognitionModel,并为单个模块的会话实现 SpeechRecognitionExecutor。README 中有一个小型示例提供方,会返回占位文本,帮你上手。

Hub 上的 Core ML 模型#

为了在应用中使用这些模型,我们把它们转换为 Core ML 并发布到 Hub 上:

模型Core ML 转换版本原始检查点许可证
Parakeet TDT 0.6B v3mattt/parakeet-tdt-0.6b-v3-coremlnvidia/parakeet-tdt-0.6b-v3CC BY 4.0
Parakeet Ultramattt/parakeet-ultra-coremlmoondream/parakeet-ultraCC BY 4.0
Parakeet Unified EN 0.6Bmattt/parakeet-unified-en-0.6b-coremlnvidia/parakeet-unified-en-0.6bNVIDIA Open Model License
Qwen3-ASR 0.6Bmattt/qwen3-asr-0.6b-coremlQwen/Qwen3-ASR-0.6B-hfApache 2.0
Qwen3-ASR 1.7Bmattt/qwen3-asr-1.7b-coremlQwen/Qwen3-ASR-1.7B-hfApache 2.0
Streaming Sortformer 4spk v2mattt/diar-streaming-sortformer-4spk-v2-coremlnvidia/diar_streaming_sortformer_4spk-v2CC BY 4.0
Streaming Sortformer 4spk v2.1mattt/diar-streaming-sortformer-4spk-v2.1-coremlnvidia/diar_streaming_sortformer_4spk-v2.1NVIDIA Open Model License

每个仓库都记录了源检查点以及每个文件的校验和(checksum),软件包将其固定到某个特定修订版本(revision)。Parakeet 的仓库额外提供了一个用于实时输入的 8 秒编码器,它把实时分析时间减少约三分之一,代价是词错误率略高。仓库中还有转换工作流,其中大部分沿用 FluidInference 的 mobius 脚本,并把每次转换的结果与原模型进行核对。

下载只在你的应用请求时发生:每个 Parakeet 约 600 MB,实时编码器另需约 590 MB,Qwen3-ASR 为 0.9 到 2.4 GB,Sortformer 约 250 MB。文件存放在标准 Hugging Face 缓存中,因此沙盒之外的 Mac 应用可以与 Python 和其他 Hugging Face 工具共享它们。

说话人分离#

当你转写一段对话时,往往想知道谁说了什么。这就是说话人分离,而 Apple 的 Speech 框架没有对应模块,因此 AnySpeechRecognizer 增加了一个。SpeakerDiarizer 在与 Apple 或 Parakeet 转写器相同的同一时间线上,于 Core ML 上运行 NVIDIA 的 Streaming Sortformer:

let diarizer = SpeakerDiarizer(model: SortformerDiarizationModel())
if let request = try await AssetInventory.assetInstallationRequest(
    supporting: [transcriber, diarizer]
) {
    try await request.downloadAndInstall()
}
let analyzer = SpeechAnalyzer(modules: [transcriber, diarizer])

分离器最多跟踪四位说话人,即使他们同时说话。SpeakerTimeline 收集其结果,把每个带时间戳的词归属到某位说话人,因此你可以按说话人轮次打印转写稿。就像 AnyLanguageModel 中的图像支持一样,这超出了 Apple API 目前所提供的能力。

分离器默认使用 Sortformer v2。你也可以选择 v2.1——NVIDIA 用更多会议数据训练了它,在我们测量中它在会议场景也更准确。

效果如何?#

我们在配备 macOS 27.0.1 的 M4 Max Mac Studio 上,用该软件包自带的 hear 命令行工具进行了测量。以下是 3 到 8 分钟的 LibriSpeech 文件上的词错误率:

转写器test-cleantest-other
Parakeet v3,文件1.55%2.78%
Parakeet v3,实时4.73%4.29%
Parakeet Ultra,文件1.66%2.55%
Parakeet Ultra,实时2.65%3.28%
Parakeet Unified,文件1.43%2.19%
Parakeet Unified,实时1.45%2.42%
Apple 的转写器,文件2.12%3.97%

Parakeet 转写一小时的音频,从文件约需 20 到 40 秒,实时输入约需 1.5 到 2.5 分钟,Core ML 编译完模型后内存占用约 100 MB。Qwen3-ASR 慢得多,且需要数 GB 内存。会议场景对任何引擎都很难:在人们互相插话的 AMI 录音上,我们测试的每个引擎词错误率都高于 23%。

这些数字来自一台机器和少数几个数据集,因此请在你自己的硬件和音频上测量。hear evaluate 可作用于你自己的文件,仓库中的 natural-speech-eval.py 工具可复现这些测量。到目前为止,我们只在 macOS 27 上测试过,尚未在 iPhone 上测试。

接下来,我们希望加入更多引擎,包括 macOS 27 和 iOS 27 上的 Core AI。

AnyDecisionModel#

AnyDecisionModel 是另一回事。它没有可对应的 Apple 框架,而决策模型严格来说也不算新东西。

只有产自法国 Décision 地区的才算决策模型;否则,它只能叫气泡感十足的零样本分类。

新的是这个名字、大量的炒作,以及一批专门训练来做决策的模型。我们认为这是趋势所在,所以想要一个为它们准备好的 Swift API。

应用经常让语言模型做决策。这条消息紧急吗?这个工单该给哪个团队?这位客户有多不满?通常的做法是给语言模型一个提示,然后解析它生成的文本。这样你能得到一个答案,但看不到它给各个备选项分配的概率。

在 Swift 中,一个决策是这样的:

import AnyDecisionModel

enum Department: String, Choosable {
    case billing, technical, sales
}

let ticket = "I was charged twice for my order, and now the app logs me out when I try to ask for a refund."

let model = MLXDecisionModel(modelID: "mlx-community/Qwen3-4B-Instruct-2507-4bit")
let session = DecisionSession(model: model, state: .text(ticket))

let urgent = try await session.probability(of: "Does this convey urgency?")
let team = try await session.choice("Which team?", from: Department.self)
let anger = try await session.score(
    "How frustrated?",
    levels: ["Calm", "Frustrated", "Very angry"]
)

urgent 是一个 Double。team 是一个 Choice<Department>,包含被选中的 case、每个选项的概率,以及一个置信度值。anger 是一个 Score,包含期望的等级、每个等级的概率,以及一个置信度值。因为 Department 遵循 Choosable,team.value 是一个 Department,你可以把它用在穷尽式 switch 中。

如果你用过 Foundation Models 或 AnyLanguageModel,这种"模型加会话"的模式会很熟悉。一个 DecisionSession 持有一份状态,可以是文本或 JSON,每个问题都是针对这份状态提问的。问题之间相互独立:一个答案永远不会成为后续问题的输入。要一次问多个问题,把它们作为一批传给 decide(_:)。

对每个问题,MLX 后端会构建一个提示,为每个允许的答案分配一个单 token 标签,然后从一次前向传播中读取每个标签的概率。

试试看:Emoji Magnet#

Emoji Magnet 是一个 macOS 应用,文本框下方是一堆 343 个 emoji。输入一个短语,例如 "things you wear",匹配的 emoji 就会从堆里升起。把它细化为 "things you wear in winter",不再匹配的 emoji 就落回去。

这个应用没有内置关键词规则;一切都由模型在推理时决定。当你停止输入时,它会为每个 emoji 向模型提一个是/否问题,例如:

陈述:一件厚实的冬衣符合 "things you wear" 这一描述。该陈述是否正确?

会话的状态保存着这个短语。上面的演示使用托管的 Jev 后端,它在一次请求中回答全部 343 个问题,因此每个短语的 emoji 大约半秒就位。在没有 TYPESAFE_AI_API_KEY 的情况下,应用会用 MLX 后端在你的 Mac 上运行 Qwen3 4B。该后端使用前缀缓存(prefix caching)只评估一次状态,然后每次前向传播回答 16 个问题。在 M4 Max 上,模型加载后一组 16 个问题约需 0.45 秒。完整遍历 343 个 emoji 约需 10 秒,每组结果一到就立即显示。

Emoji Magnet 位于 AnyDecisionModel-Examples 仓库中。

决策头(decision head)与 Clef#

读取下一个 token 的概率对通用语言模型有效,但这些模型并非为做决策而训练。Cloudflare 的 Clef 模型代表了一种更新的思路:模型带有训练好的决策头(decision head),能把一份状态和一份带类型的提问 schema 转化为决策,在一次前向传播中返回所有问题所有允许选项的概率。Clef-Flash 是其中的 9B 变体。

AnyDecisionModel 在 Mac 上用 MLX 运行 Clef-Flash:

let model = MLXHeadDecisionModel(
    modelID: "mlx-community/clef-flash-4bit",
    head: .jointSchema
)

你使用相同的会话和问题,但 MLXHeadDecisionModel 从 Clef 训练好的决策头读取每个决策,而不是从下一个 token 的概率中读取。4-bit 模型需下载 6.2 GB,其模型卡建议使用至少 16 GB 内存的 Mac。Clef 可以读取图像和视频,但此后端只接受文本和 JSON 状态。

校准与检验概率#

语言模型给出的原始概率往往过度自信。本地后端接受一个 Calibration(校准),它会为每个答案施加一个温度(temperature),并为是/否答案施加一个偏置(bias)。请使用独立的数据集来拟合校准并评估结果。

语言模型也可能因为某个选项在列表中的位置而偏好它。在 MLX、Core AI 和 llama.cpp 这些下一个 token 后端上,打开 rotationDebiasing 会让选择题对每个选项轮换位置各问一次,并对概率取平均。这需要每个轮换一次提示评估。

每个本地选择题和评分题还包含一个基于其概率熵(entropy)的置信度值。它衡量的是概率有多集中,而非答案是否正确,因此在选定阈值之前,请先在你自己的数据上检查错误率。下一个 token 后端的诊断信息会报告模型把多少概率分配给了允许的答案。数值低意味着它把大部分概率分配到了别处。

后端#

AnyDecisionModel 有六个后端:

MLX 在 Apple silicon 上用 mlx-swift-lm 运行一个小语言模型,并从 Hub 下载。

MLX 决策头运行带有自身训练好的决策头的模型,例如 Clef。

Core AI 在 macOS 27 和 iOS 27 上做与 MLX 相同的事,但使用 Core AI 模型。

llama.cpp 通过 llama.swift 从本地 GGUF 模型读取决策。

Jev 通过 System One API 调用 TypeSafe 的 Jev,或任何实现了该 API 的服务器,例如 Kev。

Ollama 调用本地 Ollama 服务器或 Ollama 云端上的 System One 决策模型。需要 Ollama 0.35.0 或更高版本,云端模型则需要 0.40.5 或更高版本。

下一个 token 后端把答案标记为 "yes" 和 "no"、A 到 Z,或 0 到 9,因此一个选择题最多有 26 个选项,一个评分题最多 10 个等级。决策头没有此限制。

与 AnyLanguageModel 一样,该软件包使用 Swift 包特性(package traits)。不启用任何特性时,它没有第三方依赖。你会得到核心类型以及 Jev 和 Ollama 后端,后者也能在 Linux 上构建。为本地后端启用 MLX、CoreAI 或 Llama 特性。

开始使用#

两个软件包都要求 Swift 6.2 或更高版本,以及 macOS 26 或 iOS 26 或更高版本。Parakeet 与 Sortformer 运行时以及 MLX 后端需要 Apple silicon。AnyDecisionModel 的 MLX 构建任务使用 Xcode 的 Swift 6.4 或更高版本以及 Metal 工具链;Core AI 需要 Xcode 27 和 OS 27 运行时。

把软件包加入你的 Package.swift:

dependencies: [
    .package(url: "https://github.com/mattt/AnySpeechRecognizer", from: "0.1.0"),
    .package(
        url: "https://github.com/mattt/AnyDecisionModel",
        from: "0.6.0",
        traits: ["MLX"]
    ),
]

目前,AnyDecisionModel 的 CoreAI 特性无法与 AnySpeechRecognizer 组合使用,因为 coreai-kit 0.7.3 要求 swift-huggingface 低于 1.0,而 AnySpeechRecognizer 要求 1.x。

每个 README 都有更多示例,以及关于要求和后端的细节。AnySpeechRecognizer 还包含一个命令行工具 hear,用于转写本地文件、标注说话人并评估结果。每个 hear 命令都需要 --backend apple、--backend parakeet 或 --backend qwen;在分析之前,先用相同的后端和模型选项运行 hear prepare 以安装所需资源。

在你的应用中试用它们,并告诉我们哪些能用、哪些不能用。欢迎在两个仓库提交 issue 和 pull request。

链接#

  • AnySpeechRecognizer on GitHub
  • AnyDecisionModel on GitHub
  • AnyDecisionModel-Examples on GitHub
  • AnyLanguageModel on GitHub
  • swift-huggingface on GitHub

感谢 NVIDIA 提供 Parakeet、Sortformer 和 NeMo,感谢 Moondream 提供 Parakeet Ultra,感谢 FluidInference——其 FluidAudio 和 mobius 项目记录了 Parakeet 运行时和 Core ML 转换所遵循的约定,感谢 Cloudflare 提供 Clef,也感谢所有为 AnyLanguageModel 做出贡献的人——两个软件包借用了它的诸多模式。

本文提到的模型:10 个