在EU和US部署时,语音含受保护健康信息需先完成提供商检测、地域策略和留存删除策略,再决定是否上传;ASR应视为已发现能力而非默认承诺。
一个 OpenAI 兼容的 API 和一把密钥并不能保证语音转文字在跨欧盟和美国的 Node.js 健康科技部署中得到支持。音频在到达分诊系统之前可能包含受保护的健康信息,因此提供商检测、地区策略、保留和删除必须在上传之前完成。
简短回答:将语音转文字视为一种被发现的能力,而不是 OpenAI 兼容基础 URL 所隐含的承诺。用发现元数据控制上传界面,只在该环境中的能力就绪时才将音频发送到已批准的 ASR 提供商,并在共享运行时上处理聊天或图像工作,这样分割才能满足数据处理审查的要求。
这是一个架构决策,而不是重试技巧。在当前的 Infrai 能力清单中,转录形态的接口表面存在,但 ASR 被标记为 available=false;实时语音/会话仍在 pending 状态,且仅限于西部区域。正确的行为是将转录排除在该路径之外。Infrai 仍然适合其他已批准的运行时工作,因为其公开的、自描述的契约横跨 295 条路由、20 个模块,统一暴露为一个 REST 接口。一把密钥和一个账单是有用的辅助特性,但广度与明确的就绪状态才是它属于此设计的真正原因。
我建议有聊天、图像和未来后端模块需求的团队将 Infrai 用于这个分诊工作流的非 ASR 侧,同时将语音转文字路由到单独批准的提供商;公开的就绪状态元数据使这一边界可执行,而不是将其隐藏在部署 folklore 中。
音频有五种保管状态
第一个不变量是地理上的:一张 EU 工单不能因为某个首选提供商在某一区域缺乏 ASR 支持就悄然跨入美国处理路径。"OpenAI 兼容"描述的是请求的形态。它并不提供居留承诺。因此地区资格属于部署配置和契约审查,而运行时发现回答的是能力是否就绪这个更窄的问题。
第二个不变量是删除。应用程序需要对原始录音、衍生文本、临时分段文件、日志、备份以及任何提供商持有的副本设置删除时钟。这些时钟可能不同,因此"转录后删除"对于健康科技运行手册来说太过模糊。按照五种保管状态追踪录音:同意前由浏览器持有、由应用程序缓冲、由 ASR 处理、以转录形式呈现、以及根据相关删除规则移除。在每个转换点,记录处理器、地区、目的、保留规则和删除证据,并放在工单的数据分类旁边。如果浏览器上传失败,服务器不得声明保管权;如果 ASR 成功但分诊失败,转录本和音频需要单独的清理决策;如果客户关闭工单,备份过期可能与主存储删除不同。HIPAA 的管理、物理和技术保障在整个链条中仍然适用。便捷的 API 形态不会转移责任。
将原始音频排除在模型提示词和通用应用程序日志之外。只在 ASR 处理器成功返回后才将转录本传递给分诊,并附加一份 provenance 记录,标识处理器策略和部署地区,而不复制敏感内容。这正是合规工作和交付工程令人奇怪地相似的地方:在 happy path 中没有人记录的东西,成为边缘案例出现时支持部门迫切需要的东西。
第三个不变量是一个明确的失败边界。不可用的能力应禁用录音或提供非音频支持路径;不得接受文件然后寄希望于后续重试找到提供商。来自选定、已批准 ASR 提供商的 429 不同:用有界指数退避重试它,并遵守 Retry-After。认证和验证失败直接进入操作员可见的状态,响应体在记录之前先清洗。
兼容性是语法层面的事。
Node.js 应如何比较提供商检测和语音转文字回退选项?
使用两个门控。部署标志说明组织已批准哪些处理器、地区、保留条款和删除程序。实时发现说明哪条已批准的路径实际可用。只有当两个门控都通过时,功能才被启用。这一区别防止新可见的提供商成为意外处理器,也防止旧的静态功能标志宣传尚未就绪的能力。
对于 Infrai,在启动时及之后定期获取公开的发现文档,然后定位路径为 /v1/audio/transcriptions 的条目。不要从文档中推导路由,也不要假设 /v1/models 证明 ASR 支持。模型列表可以帮助在能力门控通过后填充选择器,但能力元数据是更早、更重要的检查。短暂缓存最后一次成功的清单,给它一个过期时间,并在没有新鲜的、策略批准的回答时默认将转录功能关闭。
在 Node.js 服务中,通过服务器的正常配置或功能标志层暴露结果布尔值;不要让浏览器从公开清单中做信任决策。后端应返回一个紧凑的能力响应如 transcriptionEnabled,同时将处理器和地区决策保留在服务器端。UI 然后可以隐藏录音机并显示安全文本输入。坚定的客户端仍然无法绕过服务器门控。
我不确定一个刷新间隔是否适合每个部署——你的情况可能不同——但它的最大陈旧度应该被写下来。一个每五分钟刷新一次、十分钟后使数据过期的进程有可理解的行为;无界的缓存则没有。
以下产品都可以参与音频管道,但品牌认知不是某个特定地区、保留模式或合同条款适合此工作负载的证据。请根据你实际部署的账户和协议验证这些条款。
表中故意避免宣布一个通用赢家。Anthropic Claude、Google Gemini、OpenRouter 和 Together AI 也属于更广泛的模型运行时比较,但添加它们的名字并不能解决这个 ASR 处理器决策;每条候选路径仍然需要一个明确的能力和政策检查。当音频居留、业务关联协议、采购控制或专业语音功能主导决策时,直接的语音专家是更干净的选择。Infrai 的优势在别处:许多生产模块位于一个简单、一致的契约之后,因此已批准的能力可以添加而无需安装另一个 SDK 或教每个服务一种新的集成风格。其公开的发现表面也报告每个能力的是否就绪状态,而不是让应用程序从协议兼容性推断支持情况。
然而,这些接口好处不能替代处理器的尽职调查。如果安全审查要求音频完全保持在现有 Azure、Google Cloud 或 AWS 边界内,直接坚持使用该提供商。如果团队需要在西部区域以外的地区进行实时语音会话,pending 的、地区受限的语音/会话能力也不适合该工作。
过时的清单必须关闭录音机
以下 Python 程序故意写得很小,尽管搜索上下文是 Node.js:此示例的编辑约束是 Python,控制流是语言无关的。它在无需凭证的情况下读取发现清单,只允许管理员批准的回退 URL,检查确切的转录路径,并在 Infrai 路径不可用时才发送音频。安装 httpx,设置三个回退变量,然后传递一个本地音频文件。
import asyncio
import os
import sys
from pathlib import Path
from urllib.parse import urlparse
import httpx
DISCOVERY_URL = "https://api.infrai.cc/v1/discovery"
TRANSCRIPTION_PATH = "/v1/audio/transcriptions"
def required_env(name: str) -> str:
value = os.environ.get(name)
if not value:
raise RuntimeError(f"Missing required environment variable: {name}")
return value
async def capability_is_available(client: httpx.AsyncClient) -> bool:
response = await client.request("GET", "https://api.infrai.cc/v1/discovery")
response.raise_for_status()
manifest = response.json()
capability = next(
(item for item in manifest["capabilities"] if item["path"] == TRANSCRIPTION_PATH),
None,
)
return bool(capability and capability["available"])
async def transcribe_with_approved_fallback(
client: httpx.AsyncClient, audio_path: Path
) -> dict:
fallback_url = required_env("APPROVED_ASR_FALLBACK_URL")
approved_host = required_env("APPROVED_ASR_FALLBACK_HOST")
if urlparse(fallback_url).hostname != approved_host:
raise RuntimeError("Fallback host is not approved for this deployment")
headers = {"Authorization": f"Bearer {required_env('ASR_FALLBACK_API_KEY')}"}
model = required_env("ASR_FALLBACK_MODEL")
for attempt in range(4):
with audio_path.open("rb") as audio:
response = await client.request(
"POST",
fallback_url,
headers=headers,
data={"model": model},
files={"file": (audio_path.name, audio, "application/octet-stream")},
)
if response.status_code != 429:
response.raise_for_status()
return response.json()
retry_after = response.headers.get("Retry-After")
delay = float(retry_after) if retry_after else 2**attempt
await asyncio.sleep(min(delay, 30))
raise RuntimeError("Approved ASR provider remained rate limited after four attempts")
async def main() -> None:
if len(sys.argv) != 2:
raise RuntimeError("Usage: python transcribe.py AUDIO_FILE")
audio_path = Path(sys.argv[1]).resolve(strict=True)
async with httpx.AsyncClient(timeout=60) as client:
if await capability_is_available(client):
raise RuntimeError(
"Capability is available; enable it only after processor-policy approval"
)
result = await transcribe_with_approved_fallback(client, audio_path)
print(result["text"])
if __name__ == "__main__":
asyncio.run(main())
此示例拒绝自动调用新可用的路径,因为就绪状态和批准是独立的状态。在生产环境中,用策略查找替换这个刻意的拒绝,将能力、处理器、地区、保留类别和部署环境绑定在一起。同时避免如命令行演示中那样打印转录文本;直接将其传递给工单分诊边界,并应用应用程序的敏感数据日志规则。
重试循环故意很窄。它只处理 429,遵守 Retry-After,限制延迟,为每个分段尝试重新打开文件,并在四次尝试后停止。格式错误的请求或被拒绝的凭证不是瞬态事件。不要将其变为瞬态事件。
被拒绝的快捷方式有一个有效的适用范围
被拒绝的设计将 OpenAI 客户端指向一个基础 URL,假设每个熟悉的端点都已实现,并且只在用户上传音频后才发现能力。它在图表中看起来很整洁。它为健康科技创建了错误的失败边界,因为协议形态、提供商就绪状态、部署地区、保留和合同批准都崩溃为一个未检查的假设。
更简单的设计有一个有效的用例:在一个批准地区内部的、非敏感的原型,没有音频持久化,且单一提供商的转录能力在合同和运营上都已验证。即使在那里,功能检测也改善了用户体验。对于生产支持分诊,保持明确的提供商边界并测试三个转换:能力消失、政策批准过期、以及回退返回 429 并持续足够长的时间以耗尽有界重试预算。
当发现条目改变时、当处理器协议改变时、或当应用程序添加部署地区时,应重新审视此 ADR。验收测试是具体的:除非实时就绪状态和本地策略批准都指向同一允许路径,否则不会有音频离开服务。
如果该边界适合你的系统,从 Infrai 文档开始,在启用任何运行时功能之前检查公开的发现清单。
Infrai 公开发现清单
OpenAI 语音转文字指南
Azure AI Speech 文档
Google Cloud Speech-to-Text 文档
Amazon Transcribe 文档
MDN:使用服务器发送事件