外观
部署参考(Debian 12 主机)
本手册说明如何把仓库中的服务和配置落到 Debian 12 主机。当前仓库不提供容器化部署、 Docker Compose 编排、一体化部署包、部署产物装配或启停脚本。deploy/ 仅包含服务配置 样例和 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、Ollama,以及配置中选用的 Ollama 模型;
ffmpeg,供流式 TTS 的 PCM → MP3 链路使用;- 用于托管前端静态文件和媒体文件的 HTTP 服务或反向代理(按需)。
仅启用部分组件时,可以省略不在调用链上的依赖。所有监听端口、数据库、媒体目录和模型路径 都应在上线前显式确认,不要依赖配置样例中的机器特定值。
2. 构建与分发
ONNX C++ 服务
仓库提供 onnx-platform/Dockerfile.debian12 作为 Debian 12 x64 编译工具链镜像。 该镜像仅用于生成与 Debian 12 系统运行库兼容的原生产物,不是生产部署镜像,也不改变仓库 不提供容器化部署和 Compose 编排的边界。
在仓库根目录构建并进入工具链容器:
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 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 地址在构建时固化,必须是最终用户浏览器可访问的地址。从仓库根目录设置目标环境地址并构建:
bash
export VITE_CALLFLOW_API_BASE_URL=http://<callflow-api-host>:<port>
export VITE_CALLOUT_API_BASE_URL=http://<callout-api-host>:<port>
bun run --cwd apps/callflow-webpage build
bun run --cwd apps/callout-webpage build
unset VITE_CALLFLOW_API_BASE_URL VITE_CALLOUT_API_BASE_URL前端产物分别位于 apps/callflow-webpage/dist/spa/ 和 apps/callout-webpage/dist/spa/。未设置变量时,API 基址分别默认为 http://127.0.0.1:9913 和 http://127.0.0.1:9920。API 地址变化后必须重新构建 对应前端,仅修改静态文件托管服务的运行时环境变量不会改变现有产物。
仓库不会把后端、前端、ONNX 可执行文件和模型自动装配到统一目录。部署方应建立自己的版本目录, 并保留上一个可运行版本以便回滚。
3. 使用部署配置
将 deploy/config/*.config.json 复制到相应服务实际读取的位置,再按目标环境校准。不要让多个 服务直接共享一份可写配置,也不要在仓库样例中保存生产密码或 token。
重点检查:
callflow-esl.config.json:FreeSWITCH inbound ESL、callbackHost、ASR WebSocket、TTS endpoints、 PostgreSQL、Redis、录音和日志目录;必填businessConfig["ollama-chat-business"]的endpoint / model / requestTimeoutMs必须与现场 Ollama 一致,endpoint 是完整/api/chat地址;启用kb-audio-chat-business时还要把businessConfig["kb-audio-chat-business"].endpoint指向 llm-chat-server 的/api/v1/conversation,校准strategy(样例kb-mock)与内部鉴权 token;同时校准callout.calloutServer / nlu / chat三组服务的baseUrl / token / requestTimeoutMs;callflow-server.config.json:FreeSWITCH / callflow 数据库、callflowEsl.baseUrl、鉴权与turn;callout-server.config.json:PostgreSQL、Redis、FreeSWITCH ESL、NLU、Emotion、媒体公开地址、鉴权与turn;llm-nlu-server.config.json:Ollama URL、模型、超时和鉴权 token;llm-chat-server.config.json:Ollama URL、模型、超时和鉴权 token;sherpa-asr-online-server.config.json:监听地址、模型 / VAD 路径、并发、日志和录音目录;sherpa-tts-server.config.json:监听地址、publicBaseUrl、模型、缓存、日志和 ffmpeg 路径;emotion-analysis-server.config.json:监听地址、文本 / 音频模型、并发和输出目录。
配置样例使用部署端口(例如 ASR 18096、TTS 18080、Emotion 18090、NLU 18930、Chat 18931), 与本地开发默认端口不完全相同。所有调用方必须使用同一套最终端口和可达地址。
4. 部署 FreeSWITCH 配置
deploy/freeswitch/ 是 FreeSWITCH 配置参考树,包含拨号计划、directory、SIP profiles、ESL、 模块加载、会议和 TLS 等配置。部署时按现场 FreeSWITCH 的 conf_dir 合并,重点核对:
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 绑定 192.168.2.198,ext-sip-ip / ext-rtp-ip 广告 123.57.205.60。FreeSWITCH RTP 范围为 30000–30199/UDP;不要把公网 IP 配到本机绑定字段。
SIP WebSocket 由本机 FRP 客户端回送时,FreeSWITCH 看到的信令来源也是 192.168.2.198。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-secret与fingerprint,不启用 TLS/TURNS。
配置参考见 deploy/coturn/turnserver.conf.example。真实 shared secret 只能放在 coturn 私有 配置,以及 CALLFLOW_SERVER_TURN_SHARED_SECRET / CALLOUT_TURN_SHARED_SECRET 环境变量 或两个后端的服务器私有配置中。仓库默认发布脚本不部署 coturn、FreeSWITCH 或 FRP 配置。 FRP 仅承载 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:
bash
DATABASE_URL=<callflow-database-url> bun run --cwd apps/callflow-esl db:push
DATABASE_URL=<callout-database-url> bun run --cwd apps/callout-server db:push建议按依赖顺序启动:
- PostgreSQL、Redis、Ollama;
- ASR、TTS、Emotion;
- llm-nlu-server;
- llm-chat-server;
- callflow-esl、callflow-server、callout-server;
- FreeSWITCH;
- 前端静态站点和媒体文件服务。
生产环境应使用 systemd、进程管理器或既有运维平台托管进程,配置运行用户、工作目录、环境变量、 日志轮转、失败重启和开机自启。仓库没有提供可直接安装的 service unit。
6. 健康检查
以下端口按 deploy/config/ 当前样例列出;若已修改配置,以最终值为准:
bash
curl http://127.0.0.1:18096/health # ASR
curl http://127.0.0.1:18080/health # TTS
curl http://127.0.0.1:18090/health # Emotion
curl http://127.0.0.1:18930/health # NLU
curl http://127.0.0.1:18913/health # callflow-server
curl http://127.0.0.1:18920/health # callout-server健康检查通过后,再依次验证 TTS 返回 URL 能被 FreeSWITCH 访问、ASR WebSocket 能返回 partial / final、 FreeSWITCH 能连接 callflow-esl outbound ESL,以及一次真实呼叫的识别、播报、录音和结果回写链路。
7. 升级与回滚
- 升级前备份数据库、实际生效配置、FreeSWITCH
conf_dir和当前可执行文件; - 在非生产环境完成 typecheck、构建和链路验证,再按依赖顺序滚动替换;
- FreeSWITCH 或 callflow-esl 变更会影响在途通话,应安排维护窗口;
- 配置、模型和二进制作为一组版本化,回滚时一并恢复,避免接口或路径不匹配;
- 前端 API 地址变化后必须重新构建前端,仅修改服务端环境变量不会改变已生成的静态 JS。
故障定位见 troubleshooting.md。