跳到正文

部署参考(Debian 12 主机)

当前生产拓扑:aliyun 公网节点运行 FreeSWITCH(已有常驻实例)、callflow-esl 业务编排服务、FRPS、coturn、Nginx 以及三个 Web 前端 SPA 静态站点与文档站; WSL 本地环境(/data/ai-voice)运行三个 Bun 服务端(callflow-servercallout-servercallai-server)、三个 ONNX 语音服务(ASR、TTS、Emotion)与 FRPC 客户端。 FRP 端口见 deploy/aliyun/frp/README.mddeploy/wsl-docker/README.md,公网 HTTP/WSS 配置见 deploy/aliyun/nginx/ai-voice-public.conf

跨项目调用链、通用单机 / 分层 / 每服务独立 / 多 cell 方案及当前双主机端口映射统一见 部署网络拓扑与配置手册。本文聚焦 Debian 12 与 WSL 构建、分发、 服务落地与生效操作,避免重复维护拓扑和字段定义。

本手册说明如何把仓库中的服务和配置落到生产与本地主机。当前阿里云上的 FreeSWITCH 软交换中心已存在且运行正常,无需再次部署。构建、文件分发、进程托管、依赖安装、升级与回滚按本手册指导进行。

本地开发测试请参考 development-testing.md,跨平台启动和健康检查 参考 setup-deployment.md,配置目录边界参考 ../deploy/README.md

1. 准备运行环境

目标主机按实际启用的组件准备:

  • Bun 1.x,用于运行 Bun/TypeScript 后端或构建独立可执行文件;
  • CMake、Ninja、C++ 编译器和各 ONNX 服务所需运行库;
  • FreeSWITCH 1.10.x,并安装、启用 mod_audio_fork;流式 MP3 播放还需 mod_shout
  • PostgreSQL、Redis,以及 OpenAI 兼容推理后端和其中已加载的模型(供 callai-serverllm-chat-business 调用);
  • ffmpeg,供流式 TTS 的 PCM → MP3 链路使用;
  • 用于托管前端静态文件和媒体文件的 HTTP 服务或反向代理(按需)。

仅启用部分组件时,可以省略不在调用链上的依赖。所有监听端口、数据库、媒体目录和模型路径 都应在上线前显式确认,不要依赖配置样例中的机器特定值。

2. 构建与分发

ONNX C++ 服务

仓库提供 onnx-platform/Dockerfile.debian12 作为 Debian 12 x64 编译工具链镜像。 该镜像仅用于生成与 Debian 12 系统运行库兼容的原生产物,不是生产部署镜像。运行期的容器化 编排是另一套资产,见 deploy/wsl-docker/Dockerfile + docker-compose.yml,业务二进制与 模型均外部挂载)。

在仓库根目录构建并进入工具链容器:

bash
docker build \
  -f onnx-platform/Dockerfile.debian12 \
  -t ai-voice-platform-debian12 \
  onnx-platform

docker run --rm -it \
  -v "$PWD:/workspace/ai-voice-platform" \
  -w /workspace/ai-voice-platform/onnx-platform \
  ai-voice-platform-debian12 \
  bash

容器内先清理其他发行版可能遗留的 CMake 缓存,再分别构建三个服务:

bash
rm -rf build/asr-linux_x64 build/tts-linux_x64 build/emotion-linux_x64
bash sherpa-tts-server/build.sh
bash sherpa-asr-online-server/build.sh
bash sherpa-asr-offline-server/build.sh
bash fanasr-asr-online-server/build.sh
bash fanasr-asr-offline-server/build.sh
bash emotion-analysis-server/build.sh

产物位于各服务的 target/linux_x64/。模型默认来自 onnx-platform/models/;分发产物时必须 同时保证模型、ONNX Runtime / sherpa-onnx 运行库和配置中的相对路径能够解析。Linux 构建环境 和运行库兼容性详见 ../onnx-platform/README.md

Bun 后端与前端

各 Bun 服务的构建命令和独立可执行文件配置路径以模块 README 为准。三个 Quasar/Vite 前端的 API 根地址在构建期注入,默认值均为同域 /api。从仓库根目录构建:

bash
bun run --cwd apps/callflow-webpage build
bun run --cwd apps/callout-webpage build
bun run --cwd apps/callai-webpage build

前端产物分别位于 apps/callflow-webpage/dist/spa/apps/callout-webpage/dist/spa/apps/callai-webpage/dist/spa/。使用默认值时,生产环境必须将 callflow 前端站点的同域 /api 转发到 callflow-server(默认 127.0.0.1:9913),callout 前端站点转发到 callout-server(默认 127.0.0.1:9920),callai 前端站点转发到 callai-server(默认 127.0.0.1:9930),并保留 /api 路径。多个前端应使用能够分别 配置 upstream 的站点或 origin。需要自定义代理前缀或浏览器可访问的后端地址时,在构建前 设置对应的 VITE_CALLFLOW_API_BASE_URLVITE_CALLOUT_API_BASE_URLVITE_CALLAI_API_BASE_URL,然后重新构建前端。

仓库不会把后端、前端、ONNX 可执行文件和模型自动装配到统一目录。部署方应建立自己的版本目录, 并保留上一个可运行版本以便回滚。

3. 使用部署配置

deploy/aliyun/*/deploy/wsl-docker/configs/*.json 复制到相应服务实际读取的位置,再按目标环境校准。不要让多个 服务直接共享一份可写配置,也不要在仓库样例中保存生产密码或 token。

重点检查:

  • callflow-esl.config.json:FreeSWITCH inbound ESL、callbackHost、ASR WebSocket、TTS endpoints、 PostgreSQL、Redis、录音和日志目录;必填 businessConfig["llm-chat-business"]endpoint / apiKey / model / requestTimeoutMs, endpoint 是 OpenAI 兼容后端的完整 /v1/chat/completions 地址;启用 kb-audio-chat-business 时还要把 businessConfig["kb-audio-chat-business"].endpoint 指向 callai-server 的 /api/v1/conversation,校准 strategy(样例 kb-mock)与内部鉴权 token;同时校准 callout.calloutServer / nlu / chat 三组服务的 baseUrl / token / requestTimeoutMs(后两组都指向 callai-server 的 9930);
  • callflow-server.json:FreeSWITCH / callflow 数据库、callflowEsl.baseUrl、鉴权与 turn
  • callout-server.json:PostgreSQL、Redis、FreeSWITCH ESL、callai、Emotion、媒体公开地址、鉴权与 turn
  • callai-server.json:PostgreSQL callai 连接、server.authTokencallout.baseUrl 与用量记录; 推理后端可写死在 llm.openaiUrl 与各领域模型,也可留空由数据库 callai.ai_llm_configs 中启用的记录提供;
  • sherpa-asr-online-server.json:监听地址、模型 / VAD 路径、并发、日志和录音目录;
  • sherpa-asr-offline-server.config.json:监听地址(10095)、SenseVoice 模型路径、并发队列、ffmpeg 与日志目录;
  • fanasr-asr-online-server.config.json:监听地址(10099)、2-pass 双流模型路径、热词配置、日志和录音目录;
  • fanasr-asr-offline-server.config.json:监听地址(10094)、Paraformer 离线模型路径、热词配置、ffmpeg 与日志目录;
  • sherpa-tts-server.json:监听地址、publicBaseUrl、模型、缓存、日志和 ffmpeg 路径; 当前公网播放基址为 https://tts.wangijun.com
  • emotion-analysis-server.json:监听地址、文本 / 音频模型、并发和输出目录。

阿里云主机通过回环 FRPS 使用 ASR 12010、TTS 12008、Emotion 12009、统一 AI 决策中台 12013, 与本地开发默认端口不完全相同。所有调用方必须使用同一套最终端口和可达地址。

4. 部署 FreeSWITCH 配置

deploy/freeswitch/ 是 FreeSWITCH 配置参考树(当前线上现场快照另见 deploy/aliyun/freeswitch/),包含拨号计划、directory、SIP profiles、ESL、 模块加载、会议和 TLS 等配置。部署时按现场 FreeSWITCH 的 conf_dir 合并,重点核对:

  • autoload_configs/event_socket.conf.xml 的监听地址、端口、密码和 ACL;
  • autoload_configs/modules.conf.xmlmod_audio_forkmod_shout 等所需模块;
  • dialplan/ 中 outbound socket 地址是否指向 callflow-esl 的 ESL 监听端口;
  • sip_profiles/directory/、网关、分机、域名、外网地址和 RTP 范围;
  • tls/ 证书是否属于目标环境,生产环境不要直接复用样例证书。

不要直接覆盖已有生产配置。先备份现场 conf_dir,合并后执行 FreeSWITCH 配置校验和 reload; 涉及模块、SIP profile 或核心监听参数时应安排维护窗口重启。ESL 端口不得直接暴露到公网。

FreeSWITCH 的监听地址与 SDP 广告地址必须分离:当前线上 sip-ip / rtp-ip 绑定 172.21.38.245ext-sip-ip / ext-rtp-ip 广告 123.57.205.60。FreeSWITCH RTP 范围为 30000–30199/UDP;不要把公网 IP 配到本机绑定字段。

SIP WebSocket 由同机 Nginx 终止公网 TLS 后,通过 https://172.21.38.245:7443 重新加密 转发给 FreeSWITCH。不要降级代理到 5066/WS,否则浏览器 SIP Via: WSS 与 Sofia 看到的 实际传输不一致,REGISTER 可能在握手成功后超时。WebRTC profile 必须使用 local-network-acl=none,否则生成的 SDP 仍会包含私网媒体地址。同时通过 apply-candidate-acl=softphone-turn-relay 只允许 coturn 公网地址;FreeSWITCH 1.10 默认会优先选手机的 srflx 而不是 relay,在运营商 NAT 下会 表现为 ICE 已创建但 DTLS 一直停在 HANDSHAKE、双方无声。

4.1 公网 coturn

浏览器软电话跨运营商网络时使用 coturn REST 短期凭据。coturn 直接运行在公网主机 aliyun,不经过高负载 FRP:

  • 3478/UDP:STUN 与 TURN/UDP;
  • 3478/TCP:TURN/TCP 回退;
  • 31000–31040/UDP:relay allocation,与 FreeSWITCH RTP 范围分离;
  • external-ip=123.57.205.60/172.21.38.245
  • realm=softphone.ai-voice-platform,启用 use-auth-secretfingerprint,不启用 TLS/TURNS。

配置参考见 deploy/aliyun/coturn/turnserver.conf.example(同目录另有 coturn.service 与 README)。真实 shared secret 只能放在 coturn 私有 配置,以及 CALLFLOW_SERVER_TURN_SHARED_SECRET / CALLOUT_TURN_SHARED_SECRET 环境变量 或两个后端的服务器私有配置中。FRP 只承载 12000-12013 的数据和应用上游, 不承载 SIP WebSocket、FreeSWITCH RTP 或 TURN。

仓库内两个后端及部署样例均默认 turn.enabled=false,适用于浏览器与 FreeSWITCH 之间 host candidate 可达的本地直连。生产环境启用 coturn 时,必须在私有配置中同时改为 enabled=true 并注入真实 shared secret;只开启开关而缺少必要字段时,短期凭据接口会返回 503 ICE_CONFIG_UNAVAILABLE

5. 初始化与启动顺序

首次部署或 schema 变化时,在注入目标数据库 URL 后同步 callflow / callout / callai 三个 schema(各自 drizzle.config.ts 读取的环境变量不同,缺省时回落到 postgres://postgres:postgres@localhost:5432/freeswitch):

bash
CALLFLOW_DB_URL=<callflow-database-url> bun run --cwd apps/callflow-esl db:push
CALLOUT_DB_URL=<callout-database-url> bun run --cwd apps/callout-server db:push
CALLAI_DB_URL=<callai-database-url> bun run --cwd apps/callai-server db:push

建议按依赖顺序启动:

  1. PostgreSQL、Redis、OpenAI 兼容推理后端;
  2. ASR、TTS、Emotion;
  3. callai-server、callai-webpage;
  4. callflow-esl、callflow-server、callout-server;
  5. FreeSWITCH;
  6. 前端静态站点和媒体文件服务。

生产环境应使用 systemd、Docker Compose 或既有运维平台托管进程,配置运行用户、工作目录、 环境变量、日志轮转、失败重启和开机自启。deploy/aliyun/ 下提供了当前生产节点在用的 callflow-esl.servicefrps.servicecoturn.service 等 unit,deploy/wsl-docker/ 下提供 docker-compose.ymlfrpc.service;它们绑定了本套拓扑的路径与账号,套用到新环境前必须逐项校准。

6. 健康检查

以下端口按 deploy/wsl-docker/ 当前编排列出(WSL 集群统一使用 2xxxx 段,避免与 Windows 本地开发端口冲突);若已修改配置,以最终值为准:

bash
curl http://127.0.0.1:20096/health   # ASR
curl http://127.0.0.1:29080/health   # TTS
curl http://127.0.0.1:29090/health   # Emotion
curl http://127.0.0.1:29930/health   # callai-server
curl http://127.0.0.1:29913/health   # callflow-server
curl http://127.0.0.1:29920/health   # callout-server

阿里云节点上则通过 FRP 回环端口探测,例如 curl http://127.0.0.1:12013/health

健康检查通过后,再依次验证 TTS 返回 URL 能被 FreeSWITCH 访问、ASR WebSocket 能返回 partial / final、 FreeSWITCH 能连接 callflow-esl outbound ESL,以及一次真实呼叫的识别、播报、录音和结果回写链路。

7. 升级与回滚

  • 升级前备份数据库、实际生效配置、FreeSWITCH conf_dir 和当前可执行文件;
  • 在非生产环境完成 typecheck、构建和链路验证,再按依赖顺序滚动替换;
  • FreeSWITCH 或 callflow-esl 变更会影响在途通话,应安排维护窗口;
  • 配置、模型和二进制作为一组版本化,回滚时一并恢复,避免接口或路径不匹配;
  • 前端 API 根地址变化后必须通过对应 VITE_*_API_BASE_URL 重新构建前端;使用默认 /api 时只需校准静态站点的反向代理 upstream。

故障定位见 troubleshooting.md

文档与代码在同一仓库维护,现有 Markdown 是唯一内容源。