跳到正文

生产部署与落地手册(Debian 12 / Linux & WSL) ​

当前生产拓扑:aliyun 公网节点运行 FreeSWITCH(已有常驻实例)、callflow-esl 业务编排服务、FRPS、coturn、Nginx 以及两个 Web 前端 SPA 静态站点(callflow 与 callout)与文档站; WSL 本地环境(/data/ai-voice)运行两个 Bun 服务端(callflow-server、callout-server,其中 callout-server 原生内嵌 AI 策略中台)、三个 ONNX 语音服务(ASR、TTS、Emotion)与 FRPC 客户端。 FRP 端口见 deploy/aliyun/frp/README.md 与 deploy/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 后端或构建独立可执行文件;
  • Rust 稳定版工具链(1.80+,通过 rustup 安装),用于构建 ONNX 平台各 Rust 语音服务;
  • FreeSWITCH 1.11.2,并安装、启用 mod_audio_fork;流式 MP3 播放还需 mod_shout;
  • PostgreSQL、Redis,以及 OpenAI 兼容推理后端和其中已加载的模型(供 callout-server 与 llm-chat-business 调用);
  • ffmpeg,供流式 TTS 的 PCM → MP3 链路使用;
  • 用于托管前端静态文件和媒体文件的 HTTP 服务或反向代理(按需)。

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

2. 构建与分发 ​

ONNX 平台语音服务(纯 Rust 架构) ​

ONNX 平台服务已全部重构为纯 Rust 架构,依赖官方 onnxruntime 动态库或 sherpa-onnx C API。 在 Linux / Debian 12 主机上,安装 Rust 编译环境后,可直接在各服务工程目录下执行 build.sh 脚本进行编译和装配:

bash
cd onnx-platform

# 依次构建各语音服务(生成 release 二进制并自包含装配依赖动态库与配置)
bash sherpa-asr-server/build.sh
bash sherpa-tts-server/build.sh
bash emotion-analysis-server/build.sh
bash funasr-asr-server/build.sh
bash stream-vad-proxy/build.sh

装配产物统一输出至各服务的 dist/linux_x64/ 目录。分发 ASR、TTS、Emotion 与可选 VAD 时,应复制完整自包含目录(例如 cp -a onnx-platform/<服务>/dist/linux_x64/. /data/ai-voice/<服务>/),不要从 Cargo target/ 目录挑取二进制或动态库。deploy/wsl-docker/docker-compose.yml 将各交付目录完整挂载为容器工作目录;ASR、TTS、Emotion 另外挂载共享 /data/ai-voice/models,VAD 的模型随自身交付目录分发。模型默认来自 onnx-platform/models/;分发产物时必须 同时保证模型、ONNX Runtime / sherpa-onnx 运行库和配置中的相对路径能够解析。对于模型资产的分发与同步,推荐在部署机上使用 uv 运行 onnx-platform/models/model-down/download.py 从 ModelScope 远端一键拉取(或通过 upload.py 上传备份,详见 models/model-down/README.md)。Linux 构建环境 和运行库兼容性详见 ../onnx-platform/README.md。

Bun 后端与前端 ​

WSL Linux x64 的两个 Bun 后端必须使用各模块实际提供的 build:linux,不能使用默认构建(默认目标为 Windows)。产物位于各模块的 dist/linux_x64/:

bash
bun run --cwd apps/callflow-server build:linux
bun run --cwd apps/callout-server build:linux

分发时复制对应 dist/linux_x64/ 目录内容,再用 deploy/wsl-docker/configs/*.toml 覆盖部署配置。各 Bun 服务的其他构建命令和独立可执行文件配置路径以模块 README 为准。两个 Quasar/Vite 前端的 API 根地址在构建期注入,默认值均为同域 /api。从仓库根目录构建:

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

前端产物分别位于 apps/callflow-webpage/dist/spa/ 与 apps/callout-webpage/dist/spa/(其中 callout-webpage 已包含「AI 策略中心」三栏话术策略工作台)。使用默认值时,生产环境必须将 callflow 前端站点的同域 /api 转发到 callflow-server(默认 127.0.0.1:9913),callout 前端站点转发到 callout-server(默认 127.0.0.1:9920),并保留 /api 路径。多个前端应使用能够分别 配置 upstream 的站点或 origin。需要自定义代理前缀或浏览器可访问的后端地址时,在构建前 设置对应的 VITE_CALLFLOW_API_BASE_URL 或 VITE_CALLOUT_API_BASE_URL,然后重新构建前端。

仓库不会把后端、前端、ONNX 可执行文件和模型自动装配到统一目录。这里分发的是运行产物与部署资产,不是已废止的源码交付包。部署方应建立自己的版本目录, 并保留上一个可运行版本以便回滚。

3. 使用部署配置 ​

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

重点检查:

  • callflow-esl.config.toml: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 指向 callout-server 的 /api/v1/conversation,校准 strategy(样例 kb-mock)与内部鉴权 token;同时校准 callout.calloutServer / nlu / chat 三组服务的 baseUrl / token / requestTimeoutMs(统一指向 callout-server 的 9920);
  • callflow-server.toml:FreeSWITCH / callflow 数据库、callflowEsl.baseUrl、鉴权与 turn;
  • callout-server.toml:PostgreSQL、Redis、FreeSWITCH ESL、可选 [llm] 推理配置、Emotion、媒体公开地址、鉴权与 turn;
  • sherpa-asr-server.toml:统一监听地址(流式 WS 10096 与离线 HTTP 10095)、模型 / VAD 路径、并发、日志和录音目录;
  • funasr-asr-server.config.toml:统一监听地址(流式 WS 10099 与离线 HTTP 10094)、模型路径、热词配置、日志和录音目录;
  • sherpa-tts-server.toml:监听地址、publicBaseUrl、模型、缓存、日志和 ffmpeg 路径; 当前公网播放基址为 https://tts.wangijun.com;
  • emotion-analysis-server.toml:监听地址、文本 / 音频模型、并发和输出目录。

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

4. 部署 FreeSWITCH 配置 ​

deploy/freeswitch/ 提供通用容器化 FreeSWITCH 部署包 docker_fs_server.tar.gz 与详细运维文档 deploy/freeswitch/README.md(线上现场快照另见 deploy/aliyun/freeswitch/),包含 FreeSWITCH 1.11.2 二进制、mod_audio_fork、Nginx 录音服务、拨号计划、directory、SIP profiles、ESL、模块加载和 TLS 等配置。在新环境或容器化节点部署时,重点核对:

  • autoload_configs/event_socket.conf.xml 的监听地址、端口、密码和 ACL;
  • autoload_configs/modules.conf.xml 中 mod_audio_fork、mod_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.245,ext-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.11.2 部署仍需显式限制 candidate 选择,避免优先选手机的 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-secret 与 fingerprint,不启用 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 schema(各自 drizzle.config.ts 读取的环境变量不同,缺省时回落到本地开发库 postgres://postgres:postgres@localhost:5432/dev-freeswitch;其中 callout-server 的 db:push 会自动同步 callout Schema;生产环境部署必须显式注入以生产库 freeswitch 为目标的连接串):

bash
# 线上生产环境示例(目标库必须为 freeswitch):
CALLFLOW_DB_URL="postgres://<USER>:<PASSWORD>@<DB_HOST>:5432/freeswitch" bun run --cwd apps/callflow-esl db:push
CALLOUT_DB_URL="postgres://<USER>:<PASSWORD>@<DB_HOST>:5432/freeswitch" bun run --cwd apps/callout-server db:push

建议按依赖顺序启动:

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

生产环境应使用 systemd、Docker Compose 或既有运维平台托管进程,配置运行用户、工作目录、 环境变量、日志轮转、失败重启和开机自启。deploy/aliyun/ 下提供了当前生产节点在用的 callflow-esl.service、frps.service、coturn.service 等 unit,deploy/wsl-docker/ 下提供 docker-compose.yml 与 frpc.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:29913/health   # callflow-server
curl http://127.0.0.1:29920/health   # callout-server(含原生 AI 策略接口)

阿里云节点上则通过 FRP 回环端口探测,例如 curl http://127.0.0.1:12012/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 是唯一内容源。