一、自建 Relay:先解决连接链路

Paseo 的手机端需要通过 Relay 与桌面上的 Paseo daemon 建立连接。原作者的出发点是:默认 Relay 在海外,国内网络环境下可能不稳定,因此自行部署 zenghongtu/paseo-relay,再把桌面端和手机端都指向自己的域名。

1. 部署 Relay

官方仓库给出的最小 Docker 启动方式是:

docker run -p 8411:8411 ghcr.io/zenghongtu/paseo-relay:latest

Relay 默认监听 8411。部署后先在服务器本机或受控网络中检查:

curl http://127.0.0.1:8411/health

/health 应返回服务状态和版本信息。生产环境不要直接向公网暴露明文 HTTP;应使用 Caddy、nginx、Traefik 或 Cloudflare 在上游终止 TLS,并确认域名证书可自动续期。

2. 配置 Paseo

编辑桌面端的 ~/.paseo/config.json。以下使用占位域名,不能原样照抄到生产环境:

{
  "daemon": {
    "relay": {
      "enabled": true,
      "endpoint": "relay.example.com:443",
      "publicEndpoint": "relay.example.com:443",
      "useTls": true
    }
  },
  "app": {
    "baseUrl": "https://relay.example.com"
  }
}

保存后重启 Paseo daemon:

paseo daemon stop
paseo daemon start

重新生成或扫描连接二维码,再从手机网络实际测试会话连接,不能只以 /health 成功作为最终结论。

3. Relay 的安全边界

据官方 Relay 说明,转发内容使用端到端加密,Relay 只转发加密字节,不应看到会话正文;但它仍可能观察到 IP、连接时间、流量大小和会话标识等元数据。Relay 本身没有单独的账户认证层,安全性依赖客户端的 ECDH/NaCl box 加密与 TLS 传输。因此需要同时做到:

  • 公网入口只开放 HTTPS/WSS,禁止明文回源暴露;
  • 保持 Relay 镜像、反向代理和系统补丁更新;
  • 日志不记录查询参数、令牌或会话内容;
  • 对异常连接数、带宽和 /health 状态做监控。

官方参考:paseo-relay

二、语音输入:本地模型与兼容接口

Paseo 的语音能力可以本地运行,也可以调用 OpenAI 兼容服务。官方文档说明,本地 ONNX 模式默认使用 CPU;模型缺失时才会下载到 $PASEO_HOME/models/local-speech。

目前官方列出的本地 STT 模型包括英语版 parakeet-tdt-0.6b-v2-int8 和支持 25 种欧洲语言的 v3,均不包含中文。因此,中文语音输入需要换用外部 STT,或直接使用操作系统、输入法自带的语音听写。

1. 接入 OpenAI 兼容 STT

原作者先以本地 FunASR 服务为例,把听写和语音模式的 STT provider 指向 openai,同时为 STT 单独设置地址和密钥:

{
  "features": {
    "dictation": {
      "stt": { "provider": "openai" }
    },
    "voiceMode": {
      "stt": { "provider": "openai", "model": "paraformer" }
    }
  },
  "providers": {
    "openai": {
      "stt": {
        "apiKey": "YOUR_STT_API_KEY",
        "baseUrl": "http://127.0.0.1:8000/v1"
      }
    }
  }
}

这里的密钥必须使用专用、最小权限凭据,不能把真实值写进公开帖子、仓库或客户端日志。若服务只在本机使用,应绑定回环地址;若跨机访问,则需要 TLS、访问控制和防火墙限制。

2. Qwen3-ASR-Flash 为什么需要适配层

Paseo 的 STT 客户端按 OpenAI Audio API 发送 multipart/form-data 到 /v1/audio/transcriptions。阿里云百炼的 Qwen3-ASR-Flash 虽提供 OpenAI 兼容访问,但使用的是 Chat Completions 形态:请求进入 /v1/chat/completions,音频作为 input_audio(Base64 Data URI 或公开 URL)提交,结果从聊天响应中读取。

两个接口的路径、请求体和响应结构并不相同,不能只改 baseUrl。原作者使用 Cloudflare Worker 做薄适配层,核心流程是:

  1. 接收 Paseo 发往 /v1/audio/transcriptions 的 multipart 音频文件;
  2. 把音频转换为 Base64 Data URI;
  3. 构造 qwen3-asr-flash 的 input_audio 消息并请求百炼 Chat Completions 接口;
  4. 从 choices[0].message.content 提取转写文本;
  5. 向 Paseo 返回 { "text": "转写结果" }。

这类适配器至少应补充请求大小限制、超时、上游错误映射和严格的 CORS 来源白名单。不要记录 Authorization、原始音频或完整上游响应;生产环境也不应把长期密钥硬编码在脚本中。官方接口参考:Qwen-ASR API。

三、验证清单

完成配置后,建议逐项验证:

  1. GET /health 返回预期状态和版本,TLS 证书链与自动续期正常;
  2. Paseo 重启后能通过自建 Relay 生成连接信息,手机在 Wi-Fi 与移动网络下均可连接;
  3. 用几秒钟的短音频测试听写,确认中文结果进入 Paseo,而不是只看到上游 HTTP 200;
  4. 分别测试缺少文件、错误密钥、上游超时和超大音频,确保适配层返回可理解的错误;
  5. 检查 Relay、反向代理和适配层日志,确认没有令牌、音频正文或转写内容;
  6. 若不需要 Paseo 内置语音,直接关闭相关功能,避免误触和不必要的模型下载。

原帖没有给出统一的性能基准、并发容量、费用测算或长期稳定性数据。自建 Relay 能改善链路可控性,但不能保证所有网络环境下都稳定;云端 STT 也会引入新的隐私、费用、延迟和供应商依赖边界。

官方语音文档:Paseo Voice

原作者:wbc
原帖:https://linux.do/t/topic/2829068