Skip to main content

同声传译 v3

使用在 VUILabs 国内生产环境创建、已开通同传权限的 API Key。发起同传前,请确认账户有可用的同传赠送额度。在 WebSocket 握手和 HTTPS 请求中设置 X-API-Key 请求头。请使用支持自定义握手请求头的 WebSocket 客户端,例如 Python、Go 或 Node.js 客户端。浏览器原生 WebSocket 无法直接设置此请求头。

用途方法接入地址
实时同传WebSocket(HTTP GET 升级)wss://api.vuilabs.cn/v1/interpret/stream
查询音色HTTPS GEThttps://api.vuilabs.cn/v1/interpret/voices
查询单次用量HTTPS GEThttps://api.vuilabs.cn/v1/interpret/usage

接入顺序为:按需查询音色 → 建立同传连接 → 发送音频并读取结果 → 按 request_id 查询用量。默认实时克隆模式可以直接建立连接。

两个查询接口使用平台统一 JSON:error_code=0、succeed=true 时读取 data;业务失败也是 HTTP 200,succeed=false。网络故障与 WebSocket 升级错误按实际 HTTP 状态处理。

快速跑通​

准备 Python 3.9 或以上版本。下载 interpret_client.py,在脚本所在目录打开终端。脚本可以独立运行。安装依赖:

python3 -m pip install 'websockets>=15' certifi

先查询模型音色。脚本提示时输入你的 VUILabs 国内生产 API Key,输入时终端不会回显。也可以预先设置 VUILABS_API_KEY,供下文命令复用(脚本也接受 INTERPRET_API_KEY)。凭据只放入请求头,不要放在 URL 或命令行参数中。

CI、重定向输入和其他非交互环境必须通过受保护的环境变量提供 VUILABS_API_KEY(或 INTERPRET_API_KEY)。未设置时脚本立即提示环境变量名并以退出码 2 结束,不等待输入。交互输入遇到 EOF 或被中断时也会友好提示并以 2 结束,不输出异常堆栈。不要把真实 Key 写入脚本、共享的配置或运行日志。

python3 interpret_client.py voices --source model

准备一段清晰的中文录音。已有其他格式时,可使用 FFmpeg 转为所需的 WAV:

ffmpeg -i original.m4a -ac 1 -ar 16000 -c:a pcm_s16le input.wav

用默认的实时克隆模式进行中译英:

python3 interpret_client.py stream input.wav --source-lang zh --target-lang en --output translated.wav

终端会依次显示 session_opened(保存其中的 request_id)、started、分段译文、用量和 completed。成功后可用播放器打开 translated.wav。脚本不会覆盖已有输出文件,再次调用时可换一个文件名。

使用预置音色时,先从音色查询结果确认该音色 ID,再运行下面的命令。此处以 michael 为例,--voice 会选择 preset 模式。

python3 interpret_client.py stream input.wav --target-lang en --voice michael --output michael-en.wav

使用 --text-only 时只输出译文,不生成 WAV。需要启用语音活动检测时加 --enable-vad;不加时为关闭。

脚本退出码 0 表示调用成功,2 表示命令参数或凭据输入有误,其他调用失败返回非零。收到 tts_error 或 speech_done.code="tts_failed" 后,即使后续出现 completed,脚本仍以非零结束,不打印音频保存成功提示,并删除本次未成功完成的输出 WAV。已经打印的译文、用量和 request_id 保留,可据此查询实际调用结果。

脚本全局参数 --base-url 可显式选择已获授权的 HTTPS 接入地址,必须放在 voices / stream 子命令前;省略时为 https://api.vuilabs.cn。地址只能包含协议、主机和可选端口,不能包含路径、查询参数或凭据。切换环境时同时使用该环境的 Key;HTTPS 查询与 WSS 同传使用相同主机。

建立同传​

连接 /v1/interpret/stream 后,首先发送一条文本 JSON:

{"action":"start","source_lang":"zh","target_lang":"en","enable_tts":true,"tts_backend":"lunasi-tts","voice_mode":"clone"}

先收到 session_opened,再收到模型确认的 started。收到 started 后才能发送音频。保存事件里的 request_id;兼容字段 session_id 与其相同,重新连接会产生新的调用,不自动续传。

start 字段类型、默认值及用途
action必填字符串,固定为 start。
source_lang字符串,默认 zh;源语言。
target_lang字符串,默认 en;目标语言。
enable_tts布尔,默认 true;设为 false 时只返回译文,不合成语音。
enable_vad可选布尔,默认 false;不传或传 false 时关闭语音活动检测,传 true 时启用。
tts_backend可选字符串,使用 lunasi-tts。省略时使用同一服务。
voice_mode字符串,默认 clone;可选 clone、preset、prompt。
voicepreset 时必填,使用模型音色目录的 ID。
voice_promptprompt 的直接参考输入,与 platform_voice_id 二选一。
platform_voice_idprompt 时可选,使用平台系统音色或当前用户有权访问的个人音色 ID;与直接参考二选一。

lunasi-tts 支持实时克隆、预置音色和参考素材三种模式。服务根据目标语言和所选音色模式自动选择合成路线。

支持的语言​

source_lang 是输入音频的语言,默认 zh。target_lang 是译文的语言,默认 en。

语言代码语言源语言 source_lang目标语言 target_lang
zh简体中文✓✓
en英语✓✓
ja日语✓✓
ko韩语✓✓
pt葡萄牙语✓✓
de德语✓✓
fr法语✓✓
ru俄语✓✓
es西班牙语✓✓
it意大利语✓✓
ms马来语✓✓
yue粤语✓✓
id印度尼西亚语✓✓
th泰语✓✓
ar阿拉伯语—✓
vi越南语—✓
ur乌尔都语—✓
tr土耳其语—✓
sw斯瓦希里语—✓

✓ 表示支持,— 表示不支持。比如,中文翻译成越南语可以填 source_lang=zh、target_lang=vi。

选择声音​

模式填写方式
clone使用本次输入声音,不传 voice、voice_prompt 或 platform_voice_id。
preset使用模型音色,传 voice,不传另外两种参考字段。
prompt直接传 voice_prompt,或传 platform_voice_id,恰好选择一项;不传 voice。

preset、prompt 要求 enable_tts=true。不合法的参数组合会返回错误。平台音色 ID 与模型预置音色 ID 属于不同来源,即使同名也不能互换。

直接参考的结构为:

{"audio":"<普通WAV的base64>","text":"<与参考音频匹配的转写>","language":"zh"}

audio 是 base64,不能填写 OSS URL 或文件路径。参考音频要求普通 RIFF WAV、单声道、8–48 kHz、1–30 秒、非静音且有效,支持 PCM 8/16/24/32 位或 float32/float64(浮点采样必须为有限值),解码后整个 WAV 文件不超过 4 MiB;text 为 1–4000 字符,不能包含 NUL。language 可选,支持 zh/en/ja/ko/pt/de/fr/ru/es/it/ms/yue/id/th,省略时默认 en。请按参考录音的实际语言填写。例如参考录音为中文、译文目标为英语时,设置 voice_prompt.language="zh" 和 target_lang="en"。不支持的参考语言会在启动前返回 invalid_reference_language。

参考 WAV 的编码和文件头都需要符合要求。当前客户端与接口接受普通 PCM 文件头(格式标记 0x0001)及普通 IEEE float 文件头(0x0003),暂不接受 WAVE_FORMAT_EXTENSIBLE 扩展文件头(0xFFFE)。上文的 24/32 位 PCM 支持也要求普通 PCM 文件头;单声道和采样率合规不代表扩展文件头可用。FFmpeg 生成 24/32 位 WAV 时可能默认使用扩展文件头。

遇到扩展文件头,或不确定录音格式时,可转换为以下普通 PCM16 WAV,再作为参考录音上传:

ffmpeg -i original.wav -ac 1 -ar 16000 -c:a pcm_s16le reference.wav

转换后的参考录音仍须满足 1–30 秒、非静音、整个文件不超过 4 MiB,并使用与它匹配的转写。请使用不同的输入和输出文件名。脚本遇到扩展文件头会在连接前直接报告 WAVE_FORMAT_EXTENSIBLE / 0xFFFE,并给出上述转换命令。直接构造 API 请求时,服务端也会以 invalid_prompt_audio 拒绝扩展文件头,错误消息明确标识 WAVE_FORMAT_EXTENSIBLE / 0xFFFE;按上述命令转换后再发起新的调用。

用脚本直接提供参考录音​

使用 --reference-audio 读取本地参考 WAV,脚本会编码为 voice_prompt.audio,自动选择 prompt 模式。实时输入 input.wav 仍须为 PCM16、16 kHz、单声道;参考文件 reference.wav 使用上文参考音频格式,两者用途和格式要求不同。

下面以中文参考录音为例。先准备一段内容为“你好,欢迎使用实时同传。”的参考录音 reference.wav;如果使用其他录音,必须把 --reference-text 改为该录音的实际完整转写,--reference-language 改为实际语言。目标译文为英语不表示参考录音语言也是英语。

python3 interpret_client.py stream input.wav --source-lang zh --target-lang en \
--reference-audio reference.wav --reference-text '你好,欢迎使用实时同传。' \
--reference-language zh --output reference-en.wav

也可以把与参考录音匹配的完整转写保存为 UTF-8 文件 reference.txt,替换为文件选项:

python3 interpret_client.py stream input.wav --source-lang zh --target-lang en \
--reference-audio reference.wav --reference-text-file reference.txt \
--reference-language zh --output reference-file-en.wav

--reference-text 与 --reference-text-file 必须二选一,并与 --reference-audio 一起使用;转写去除首尾空白后须为 1–4000 个字符且不含 NUL。--reference-language 只能随参考音频使用,可选值与上文 voice_prompt.language 相同;省略时不发送该字段,API 使用默认值 en,请仅在实际参考录音为英语时省略。

--reference-audio、--voice 和 --platform-voice-id 互斥,且都不能与 --text-only 一起使用。脚本在连接前检查参考音频的格式、大小、时长、采样完整性和非静音,以及转写的长度与编码;格式检查不能判断转写是否与声音内容匹配,调用方必须确保两者一致。

平台素材只在启动时读取一次,客户发送实时音频时不需要重复发送参考音频。

发送音频与读取结果​

输入是二进制 PCM16、小端、单声道、16 kHz,只发送采样字节(去掉 WAV 文件头),每帧非空、字节数为偶数且不超过 32000;建议每帧 100–200 ms。按实时速率发送,允许最多约两秒的初始突发。

  • translation:读取 text、index、文本计量和分词版本。每个唯一 translation 事件的 text_tokens 是本段译文的用量;需要展示累计量时,按 index 去重后累加各段 text_tokens,不要重复累计相同 index。最终文本用量以 usage.output_text_tokens 或用量查询的 output_text_tokens 为权威结果。text_tokenizer_version 用于标识分词版本。旧分段标注 segmentId/segmentText/startMs/endMs 若提供可用于展示,客户端应允许省略。
  • speech / speech_chunk:先收到 JSON 音频元信息,再收到紧随其后的二进制帧,两者必须配对。音频为 PCM float32 小端、单声道,以本帧 audio.sample_rate 的 16000 或 24000 Hz 解码;二进制字节数为 audio.samples × 4。
  • translation_complete、speech_done:对应输出阶段的信息。tts_error 或 speech_done.code="tts_failed" 表示部分合成失败;已经交付的有效译文仍保留,不能将局部语音失败判为完整语音成功。
  • error:读取 code、message 及 request_id,停止继续送音频;不要把错误事件当作翻译结果。

音频输入结束时发送:

{"action":"finish"}

finish 结束输入。继续读取尾部译文、语音、usage,正常完成最后收到 completed,随后服务端以 WebSocket 关闭码 1000 结束连接;不要发送 finish 后立即关闭连接。异常断开可能收不到最终 usage,仍可通过保存的 request_id 查询。最久会话为 15 分钟;长静默不因暂时没有输出事件就判定完成。

随附脚本每 20 秒发送 WebSocket Ping 保活,不设置短时 Pong 响应期限;收尾阶段 Pong 延迟本身不会使脚本提前退出,Ping/Pong 也不是译文事件。脚本不会因为连续 60 秒没有业务事件就退出,会继续等待长静默、分段处理和尾部结果。发送 start 后的总等待上限为 15 分钟加 30 秒收尾时间;收到完成、错误或实际连接故障时提前结束。真实读写错误或超过总等待上限时,脚本返回非零并在已知时附上 request_id;查询旧调用,不要自动重放整段音频。

并发限制​

同一平台用户默认最多同时占用 2 路同传名额,按鉴权后的用户 UID 合并计数;该用户使用不同 API Key、不同项目空间或连接到不同网关副本,仍共用这 2 路名额。平台全局 20 路上限同时保留,任一上限达到都会拒绝新增调用。

同一用户已经占用 2 路时,第 3 路在 started 前收到 concurrency_limited,按错误事件中的 request_id 查询为 start_failed,没有音频/文本有效用量。请等待已有调用结束后再发起,不要通过更换 Key 或项目空间重试绕过。正常或异常收尾都会释放本次名额;网关进程故障退出时,遗留名额可能要等租约过期后回收。

音色查询​

将 VUILABS_API_KEY 设置为你的 VUILabs 国内生产 API Key,再执行:

curl --silent --show-error \
-H "X-API-Key: ${VUILABS_API_KEY}" \
'https://api.vuilabs.cn/v1/interpret/voices?source=model'

GET /v1/interpret/voices 默认 source=model,返回模型预置音色,条目的 id 填入 preset 的 voice。

source=system 返回平台系统素材;source=personal 返回当前用户可用的个人素材,其 id 填入 prompt 的 platform_voice_id。个人目录每页最多 20 条,使用上一次 next_cursor 作为下一页 cursor;其他来源不接受游标。个人目录按支持的素材模型及启用状态过滤。

data.voices 返回 ID、名称、语言、描述、来源及模式;模型音色还可包含 duration_seconds(参考时长)和 source_audio_type(音频来源类型,例如 demo_used_as_reference 表示使用示例音频作参考)。来源类型不是下载地址,也不保证音频可下载。tts_backends 返回公开服务名称 lunasi-tts,automatic_minor_targets 返回自动选用合成路线的目标语言列表。列表不包含个人音频下载地址或参考转写。模型目录暂不可用时明确报错,不以空列表表示读取失败;这不会要求 clone 或直接 prompt 先查询音色。

用量、额度与中断​

同传调用使用专用赠送 token。赠送额度为 0 时不能启动新的同传,即使钱包有余额也不能使用;音色和用量查询仍可使用。

计量基于有效交付:输入为成功送入模型的音频,输出为成功写到客户连接的译文与语音。输入音频每秒 7 token,输出音频每秒 12.5 token,分别按会话累计采样向上取整,再加译文 token。不会按每个小帧分别取整。

例如,输入音频 10 秒、输出语音 8 秒、译文 25 token,合计为 70 + 100 + 25 = 195 token。如果只输出译文,在输入和文本用量相同的情况下,合计为 95 token。赠送额度实际扣减量看 free_units_used。

调用过程中定期扣减赠送额度,结束时结算尚未扣减的用量。赠送最多扣到 0;已交付但超出剩余赠送的部分由平台承担,记录为 platform_covered_quantity,不会成为客户欠款,也不会扣钱包。得知额度耗尽后停止新增消费。中断保留已交付有效量,不按预期时长扣一半,也不把已交付量全部清零。

将下面的 REQUEST_ID 换成 session_opened 返回的 request_id:

curl --silent --show-error --get \
-H "X-API-Key: ${VUILABS_API_KEY}" \
--data-urlencode 'request_id=REQUEST_ID' \
'https://api.vuilabs.cn/v1/interpret/usage'

查询用量时,data 包含:

  • request_id、原调用的 api_key_id 和 trace_id、开始及结束时间,时间为秒级 Unix 时间戳。
  • status:running/start_failed/completed/interrupted,以及 error_code/error_message/fail_stage。
  • 启动参数校验失败会记录 start_failed,按错误事件中的 request_id 查询。启动参数 enable_tts/tts_backend/voice_mode/source_lang/target_lang 可能为 null,表示未取得完整可信的启动快照;不能把 null 当成 false、已关闭或已采用默认值。失败记录描述本次请求尝试,不表示模型已经启动、音频已经交付或参数已经生效。
  • 输入、输出采样量及实际采样率;input_audio_tokens/output_audio_tokens/output_text_tokens、分词和计量版本。没有输出音频时输出采样率为 0。
  • text_tokenizer_version 是平台分词器版本标识,格式为 vuilabs-tok-…。同一次调用中,流式译文、流式用量和用量查询返回的版本标识一致。
  • billable_tokens:已确认的实际有效 token 总量;free_units_used:抵扣的赠送量;platform_covered_quantity:平台承担量。这些字段不能相互替代。
  • billing_status=processing/settled 与 usage_complete:结算或恢复尚未完成时,当前数值是已确认水位,不能当最终完整结果或认为缺失量为 0。
  • remaining_quota 是查询时账户的实时剩余,不是历史结束余额;price_snapshot/estimated_cost 为 null,表示尚未定价。charged_amount_micro=0 表示没有扣钱包。

每个 request_id 对应一次调用。可使用当前账户、同一空间内有权的其他有效平台 Key 查询;不能凭 request_id 越权读取其他账户或空间。

启动参数未知与显式关闭​

对于 start_failed,如果 start JSON 语法或字段类型有误,无法完整解析,就不采信部分已读到的字段,以下五项启动参数全部返回 null;历史记录缺少完整快照时也一样。结合已有 error_code 和 fail_stage 判断失败原因,不要从布尔默认零值推断实际配置。以下均为用量查询 data 的相关片段,其余字段省略。

未知参数:

{"status":"start_failed","enable_tts":null,"tts_backend":null,"voice_mode":null,"source_lang":null,"target_lang":null}

例如 enable_tts 应为布尔却传成字符串时,即使请求里还有其他可读字段,也不能把错误解析得到的 false 当作客户明确关闭语音。

取得完整可信的启动快照后,显式 enable_tts=false 仍返回 false;省略时为默认 true,省略 voice_mode 时为默认 clone。下面是明确选择仅译文的参数片段:

{"enable_tts":false,"tts_backend":"lunasi-tts","voice_mode":"clone","source_lang":"zh","target_lang":"en"}

非法后端名只在已知的参数校验失败记录中保留请求值。例如完整可解析的请求传入 tts_backend="bogus",查询到以下片段:

{"status":"start_failed","error_code":"unsupported_tts_backend","fail_stage":"validate","enable_tts":true,"tts_backend":"bogus","voice_mode":"clone","source_lang":"zh","target_lang":"en"}

这里的 bogus 是被拒绝的请求值,不是平台支持的后端,也不表示实际路由到了该后端。正常调用及已接受的兼容后端值仍统一显示公开名称 lunasi-tts。

额度不足用 quota_exhausted 表达;参数不合法时修正参数再新建连接,模型或计量故障时先查询旧调用结果,再按业务需要发起新调用。新连接不是旧调用重试,也不会恢复已丢失的音频。

错误处理​

握手与升级后的错误分开处理:握手成功为 HTTP 101;未成功时保留实际 HTTP 状态和响应内容。鉴权失败通常为 HTTP 401,无同传权限为 403,服务不可用为 503,握手并发繁忙为 429。未进入 WebSocket 的错误没有流式 JSON 事件。

升级成功后,错误事件形如:

{"type":"error","request_id":"...","code":"invalid_audio","message":"..."}

收到 error 后停止发送音频。错误事件成功交付后,服务端也会在本次收尾结束时以关闭码 1000 关闭连接;调用仍按错误事件判为失败。连接异常或服务端错误可能导致尾部事件未能送达,不能依赖总能收到 error、最终 usage 或 completed。保留已获得的 request_id,连接结束后查询用量;查询得到 billing_status=processing 或 usage_complete=false 时稍后重查,只有结算完成且用量完整时才作为最终账单。关闭码 1000 本身不能代替 completed 与用量状态。

下表是流式字符串 code,不是 HTTPS 查询响应的数字 error_code:

code触发条件与处理
invalid_message首条不是合法 start JSON、未等待 started 就发音频 / finish,或开始后发送其他动作;按发送顺序修正。
unsupported_language源语言或目标语言不在支持列表;修正语言。
unsupported_tts_backend / unsupported_voice_mode合成服务或声音模式不受支持;使用 lunasi-tts 和本指南的三种模式。
invalid_voice模型音色 ID 无效或 preset 字段组合错误;重新查询模型音色并修正。
invalid_voice_prompt参考文本、platform_voice_id 长度或声音字段组合无效;按声音模式表修正。
invalid_reference_language参考音频语言不受支持;按录音实际语言填写支持的语言。
invalid_prompt_audio参考音频 base64、WAV 格式、大小、时长或内容不合规;扩展文件头的消息标识 WAVE_FORMAT_EXTENSIBLE / 0xFFFE。按参考音频要求重新准备或转为普通 PCM16。
voice_mode_not_supported仅译文模式使用了 preset / prompt;启用 TTS 或改用 clone。
voice_unavailable平台音色不存在或无访问权限;查询当前账户可用音色并更换 ID。
invalid_audio实时音频帧为空、字节数为奇数或超过 32000;按输入格式拆帧。
input_too_fast超过允许的实时发送速率;按采样时长控制发送节奏。
quota_exhausted同传赠送额度耗尽;确认补充额度后再启动,查询旧调用结算。
permission_denied无相应访问权限;确认 Key、账户及空间权限。
concurrency_limited单用户 2 路或全局 20 路名额已满;等待已有调用收尾后重试。同一用户的不同 Key / 项目空间共用名额,超限在 started 前拒绝,记录 start_failed、有效用量为 0。
upstream_unavailable / upstream_rejected模型连接、传输或处理失败;先查询旧调用,再按业务需要稍后重试。
upstream_protocol_error模型输出不符合协议或计量要求;保留 request_id 联系支持,先查询旧调用。
tts_failed部分语音合成失败;保留已交付译文,按实际收到的音频处理,查询用量。
metering_unavailable计量结算等待超时;停止本次输入并查询旧调用,等待结算完成。
timeout / cancelled会话超时或服务端取消;查询旧调用状态,重新连接会创建新调用。
client_disconnected客户端连接断开或结果交付失败;可能只在用量记录中可见,查询旧调用。
service_unavailable服务依赖或并发租约等暂不可用;查询旧调用,稍后重试或联系支持。

HTTPS 查询失败时仍使用统一 JSON,并检查 succeed=false 和数字 error_code。常见值:10000 服务异常(稍后重试或联系支持)、10001 参数错误(检查 source / cursor / request_id)、10002 资源不存在(检查调用 ID 及归属)、10003 未授权(检查 Key)、10004 无访问权限(确认同传能力及账户 / 空间权限)。不要把流式字符串 code 与数字错误码混用。

参数错误应修正后再新建连接,不能原样重试。上游故障、网络断开和计量异常也不能推断“未计费”;已交付有效量保留,未交付结果不按预计量计费。重连创建新调用,不会续传或补发旧音频;避免自动重放整段音频造成重复业务和新用量。

常见接入问题​

现象检查方法
握手没有返回 HTTP 101检查 WSS 地址和握手的 X-API-Key 请求头,并保留实际 HTTP 状态。确认 Key 已开通同传权限。
收到 error 或没有 started查看事件的 code 和 message。检查语言、音色字段组合及额度。收到 started 前不要发送音频。
invalid_reference_language参考录音语言不受支持。按录音实际语言修正 voice_prompt.language 后再发起调用。
voice_unavailable平台音色不存在或无访问权限。重新查询可用的系统或个人音色,再更换 platform_voice_id。
已启动但没有语音输出检查是否开启 enable_tts,以及是否收齐音频元信息和紧随其后的二进制帧。脚本的 --text-only 不生成语音。
finish 后连接断开,缺少最终结果用已保存的 request_id 查询调用状态和用量。排查时提供该 ID、发生时间和错误码。
长静默或暂时没有译文脚本每 20 秒发送 Ping 保活,Pong 延迟本身不会使等待提前结束;不要以 60 秒无译文判为完成。实际连接错误或达到会话总等待上限后查询旧调用。
CI 或管道调用提示缺少 API Key在受保护的环境变量设置 VUILABS_API_KEY 或 INTERPRET_API_KEY;无 Key 时退出码为 2,不会等待交互输入。
脚本以非零结束并报告异常类型保留终端已输出的事件及 request_id。错误包含异常类型、可读原因,获得调用 ID 后会附该 ID;用量可能仍需查询确认。
出现 tts_error / tts_failed 后还有 completed仍视为合成失败;脚本返回非零并删除本次输出 WAV。已打印译文与用量仍可使用,查询该 request_id 核对结果。

macOS / python.org 的证书错误​

如果 Python 报 CERTIFICATE_VERIFY_FAILED 或 unable to get local issuer certificate,先确认运行脚本与安装依赖使用的是同一个 python3。本脚本已用 ssl.create_default_context(cafile=certifi.where()) 为 HTTPS 和 WSS 显式加载 certifi 的 CA,并保持证书及主机名校验。

使用 python.org 安装包的 macOS 用户还应打开对应版本目录 /Applications/Python 3.x/,双击 Install Certificates.command 完成根证书安装(把 3.x 对应到实际安装版本)。操作见 Python 官方 macOS 安装说明。若安装依赖时就遇到证书错误,先完成该安装步骤,再执行上文依赖安装命令。

如果仍失败,检查系统时间、网络代理及所在网络的证书策略,并向网络管理员或平台支持提供脱敏错误。不要使用 CERT_NONE、check_hostname=False、--insecure 或 curl -k 关闭校验。