开发测试手册(Windows 11)
本手册面向在 Windows 11 上进行日常开发与测试的场景。约定:FreeSWITCH 1.11.2 运行在 Win11 本机的 WSL2 (Ubuntu) Docker 容器中(部署于 WSL 宿主机 /data/docker_fs_server),本仓库的全部应用与语音服务(ONNX Rust 语音服务、Go 全双工音频网关、Bun 后端服务、Quasar 前端)都在本机 Win11 上启动运行,以便极速联调。生产上线请改用 production-deployment.md。
阅读顺序:环境准备 → 依赖服务 → WSL FreeSWITCH 架构与挂载直达 → 配置联调地址 → 构建本地语音与网关服务 → 启动 → 冒烟测试 → 自动化测试与质量门禁(typecheck / lint / bun test / check:rust / docs:build) → 收尾关闭。
1. 拓扑与 WSL 容器运行环境
text
+─────────────────────────────── Windows 11 开发机 ─────────────────────────────────+
│ │
│ [本机开发服务] │
│ - ONNX 语音服务: Sherpa ASR 10096(WS)/10095(HTTP) / TTS 9080 / Emotion 9090 │
│ (按需: FunASR 10099/10094) │
│ - 全双工语音网关: audio-fork-server 10091 (Go + air 热重载,默认随 bun run dev 启动)│
│ - 业务中台: callflow-esl 9911(Outbound Socket)/9912(HTTP API) │
│ - 基础服务: PostgreSQL 5432 (dev-freeswitch 库), Redis 6379, OpenAI 兼容推理后端 │
│ - 管理后台与前端: callflow-server 9913, callout-server 9920, webpage 19913/19920 │
│ - 文件系统互通: Windows 资源管理器直接访问 \\wsl$\Ubuntu-24.04\data\docker_fs_server\ │
│ │
│ +─────────────────────────────── WSL2 (Ubuntu) ───────────────────────────────+ │
│ | | |
│ | 宿主机挂载根目录: /data/docker_fs_server/ | |
│ | ├── freeswitch-install/ | |
│ | │ ├── etc/freeswitch/ <== 【改配置/看配置直接操作此目录】 | |
│ | │ ├── var/log/freeswitch/freeswitch.log <== 【看日志直接 tail -f 此文件】| |
│ | │ └── var/recordings/ <== 【录音落盘目录】 | |
│ | └── docker-compose.yml | |
│ | | |
│ | +──────────────────────── Docker 容器体系 (Host 网络) ──────────────────+ | |
│ | | [fs-app] FreeSWITCH 1.11.2 (mod_audio_fork, mod_shout, Inbound ESL 8021) | |
│ | | - 挂载: ./freeswitch-install ---> /usr/local/freeswitch | | |
│ | | [fs-recordings-nginx] Nginx 录音静态托管 (8088 端口) | |
│ | | - 挂载: ./freeswitch-install/var/recordings ---> Nginx html | | |
│ | +───────────────────────────────────────────────────────────────────────+ | |
│ +─────────────────────────────────────────────────────────────────────────────+ |
+───────────────────────────────────────────────────────────────────────────────────+1.1 本机 WSL2 深度环境配置(.wslconfig 与 /etc/wsl.conf)
开发机推荐采用 WSL2(Ubuntu 24.04 LTS) 配合镜像网络(Mirrored networking)。该模式下 WSL2 与 Windows 宿主机直接共享物理网卡与局域网 IP,消除了传统 NAT 端口转发与虚拟网卡隔离的壁垒,使外部 SIP 软电话、本地语音推理服务与容器内 FreeSWITCH 能够极速双向互通。
1. Windows 端全局配置:%USERPROFILE%\.wslconfig
在 Windows 用户目录(如 C:\Users\<用户名>\.wslconfig)中创建或编辑该文件,写入本机实测的最佳实践配置:
ini
[wsl2]
# 启用镜像网络模式:WSL2 直接镜像 Windows 宿主机的网络接口与物理局域网 IP
networkingMode=Mirrored
# 分配固定内存上限,保障 FreeSWITCH 与 Docker 服务集群稳定运行
memory=16GB
# 虚拟机闲置不自动休眠(-1 为永不超时),防止后台通话服务与数据库连接中断
vmIdleTimeout=-1
[experimental]
# 允许回环地址互通:WSL2 内部可通过 127.0.0.1 直接访问 Windows 宿主机服务
hostAddressLoopback=true
# 关闭自动代理接管,避免开发机代理工具干扰本地微服务内部通信
autoProxy=false
# 启用 DNS 隧道,直接借用宿主机网络栈解析域名,解决 WSL 内解析超时
dnsTunneling=true
# 渐进式内存回收:WSL 进程释放内存后,后台自动缓慢归还 Windows 宿主机
autoMemoryReclaim=gradual关键参数收益说明:
networkingMode=Mirrored:让 WSL2 中的 FreeSWITCH 容器直接监听宿主机的局域网 IP(例如192.168.2.246),外部局域网内的硬件话机或 MicroSIP 软电话无需经过任何端口映射即可直连注册。hostAddressLoopback=true:让容器与 WSL 内部程序可通过127.0.0.1访问 Windows 端启动的 TS 服务(如callflow-esl、callout-server)、Go 全双工网关或 ONNX 语音服务。vmIdleTimeout=-1:默认情况下 WSL2 在无前台活动时可能进入挂起状态,导致呼叫信令断流;设为-1确保 FreeSWITCH 容器常驻后台。
2. WSL 发行版配置:/etc/wsl.conf
在 WSL2 内部(以 Ubuntu-24.04 为例),通过 sudo nano /etc/wsl.conf 配置:
ini
[boot]
# 启用 systemd 初始化系统,使 Docker daemon 及系统守护服务自启并稳定运行
systemd=true3. 配置生效与验证
修改上述配置后,必须在 Windows PowerShell 中完整重启 WSL 实例以使其生效:
powershell
# 1. 彻底停止 WSL 实例
wsl --shutdown
# 2. 重新进入 WSL 发行版并验证
wsl -d Ubuntu-24.04
# 3. 验证网络模式与 IP(在 WSL 终端中执行)
ip a验证标准:在 WSL 中执行
ip a时,若能看到与 Windowsipconfig完全一致的真实物理局域网 IP(如192.168.2.246),且在 WSL 中执行systemctl is-system-running返回running或degraded,即表明镜像网络与 systemd 环境已完全就绪。
1.2 双向网络可达约定
FreeSWITCH 运行在 WSL2 的 Docker 容器中(采用 network_mode: "host",共享 WSL 网络栈),与 Windows 开发机之间必须双向可达:
- Windows 本机 → FreeSWITCH 容器:
callflow-esl/callout-server通过 Inbound ESLfreeswitch.host:8021控制呼叫;录音静态访问通过http://<IP>:8088/recordings/拉取。 - FreeSWITCH 容器 → Windows 本机:FreeSWITCH 呼叫接通或旁路推流时需反向回连开发机:
- Outbound ESL Socket:
callbackHost:callbackPort(默认9911) - 单向 ASR 音频流:
audioFork.wsUrl(默认ws://127.0.0.1:10096/audio) - 全双工音频网关:
duplex.wsUrl(默认ws://127.0.0.1:10091/audio) - TTS 播放:
callflow-esl默认通过audio_fork://驱动直接拉取sherpa-tts-server的原生 PCM16 流(或通过publicBaseUrl下载静态 WAV)
- Outbound ESL Socket:
网络模式与 IP 填写建议:
- 本机 Mirrored 模式(
127.0.0.1开箱即用):当%USERPROFILE%\.wslconfig已启用networkingMode=Mirrored与hostAddressLoopback=true时,Windows 宿主机与 WSL2 容器直接共享127.0.0.1。仓库内apps/callflow-esl/config.toml、onnx-platform/sherpa-tts-server/config.toml等单机闭环配置已默认填为127.0.0.1,无需随网卡切换改动即可直接联通,还能规避物理网卡抖动。 - 局域网 IP 填写场景:
apps/callout-server/config.toml的cells[0].freeswitch.*与cells[0].media.freeswitchRecordingBaseUrl默认示例为开发机局域网 IP(如192.168.2.246)。当需要接入外部局域网硬件话机/软电话(如另一台设备上的 MicroSIP)、局域网其他终端试听录音,或对接非本机 Mirrored 的远端 FreeSWITCH 时,请将相关host、callbackHost、wsUrl、publicBaseUrl统一改为开发机的真实局域网 IP(在 PowerShell 执行ipconfig查看)。 - 防火墙:使用局域网 IP 跨网段/跨机通信时,Windows 防火墙需放通
9911(Outbound ESL)、10096(Sherpa ASR)、10091(全双工网关audio_fork_server)、9080(TTS);若启用 FunASR 还需放通10099。 - 外部设备与音视频排障:若涉及外部移动端设备联调或音视频异常断开/黑屏排障,推荐遵循宿主机回环或标准公网 ICE/TURN 路径,详见专项指南
wsl-webrtc-lan-solutions.md。
连通性自测:在 WSL 终端中执行
curl http://127.0.0.1:10096/health、curl http://127.0.0.1:10091/health与curl http://127.0.0.1:9080/health(若配置为局域网 IP 则替换为<开发机IP>),均返回健康响应即说明 FreeSWITCH 回连开发机的链路畅通。
1.3 容器目录挂载与免进容器直达操作规范
FreeSWITCH 容器在启动时将 WSL 宿主机的 /data/docker_fs_server/freeswitch-install 双向穿透挂载至容器内的 /usr/local/freeswitch。日常开发排查与修改配置无需进入容器内部,直接在宿主机进行操作:
| 关注对象 | WSL 宿主机物理路径 | Windows 端访问路径 (UNC) | 规范操作方式 |
|---|---|---|---|
| 配置文件 | /data/docker_fs_server/freeswitch-install/etc/freeswitch/ | \\wsl$\Ubuntu-24.04\data\docker_fs_server\freeswitch-install\etc\freeswitch\ | 在宿主机或 VS Code 直接编辑 XML;保存后在 WSL 执行 docker exec -it fs-app fs_cli -x "reloadxml" 秒级热生效 |
| 运行日志 | /data/docker_fs_server/freeswitch-install/var/log/freeswitch/freeswitch.log | \\wsl$\Ubuntu-24.04\data\docker_fs_server\freeswitch-install\var\log\freeswitch\freeswitch.log | 在 WSL 终端直接执行 tail -f .../freeswitch.log 实时追踪,不受容器输出截断限制 |
| 通话录音 | /data/docker_fs_server/freeswitch-install/var/recordings/ | \\wsl$\Ubuntu-24.04\data\docker_fs_server\freeswitch-install\var\recordings\ | 宿主机直接查看 .wav 文件,或在浏览器打开 http://localhost:8088/recordings/ 在线试听 |
| 扩展模块 | /data/docker_fs_server/freeswitch-install/lib/freeswitch/mod/ | - | 编译出的 mod_audio_fork.so 等动态库直接放入该目录即可动态加载 |
1.4 FreeSWITCH 容器运维与调试指令速查
在 WSL 终端中执行,无需进入容器内部:
bash
# 1. 容器生命周期管理 (在 /data/docker_fs_server 目录下)
cd /data/docker_fs_server
docker compose ps # 查看容器运行状态与健康检查
docker compose restart # 重启 FreeSWITCH 与 Nginx 容器
docker compose up -d # 启动
docker compose stop # 停止
# 2. 配置秒级热重载 (无需重启容器)
docker exec -it fs-app fs_cli -x "reloadxml" # 重新加载全部 XML 配置
docker exec -it fs-app fs_cli -x "sofia profile internal rescan" # 重新扫描 internal SIP 配置与网关
# 3. 核心状态与模块诊断
docker exec -it fs-app fs_cli -x "status" # 查看运行时长、并发呼叫数与系统状态
docker exec -it fs-app fs_cli -x "module_exists mod_audio_fork" # 检查流式 ASR 旁路模块是否就绪 (应返回 true)
docker exec -it fs-app fs_cli -x "sofia status" # 查看 SIP Profile 监听状态与 IP 绑定
docker exec -it fs-app fs_cli -x "show channels" # 查看当前活跃呼叫通道与 UUID
# 4. 进入交互式控制台
docker exec -it fs-app fs_cli # 实时查看 FreeSWITCH 控制台交互日志2. 环境准备(一次性)
2.1 必备工具
- Bun 1.x —— 所有 TS/前端服务的运行时与工作区包管理器。
bun --version验证。 - Rust 1.80+(通过 rustup 安装稳定版工具链)—— 构建 ONNX 平台各 Rust 语音服务。
rustc --version验证。 - Go 1.23+ 与 air ——
bun run dev默认启动speech-gateway/audio-fork-server(10091全双工音频网关)热重载所需。通过go version与air -v验证(未安装air可执行go install github.com/air-verse/air@latest)。 - PowerShell 7 (
pwsh) —— 仓库脚本默认 shell(使用$null/$env:VAR/ 反引号续行)。 - ffmpeg 在
PATH中 —— 仅当 TTS 切换为传统shout://MP3 流式播放(或在 TTS 配置里指定mp3.encoderPath)以及离线 ASR(sherpa-asr-server、funasr-asr-server)转码非 WAV 音频时需要;默认的audio_fork://原生裸 PCM16 流式播放与标准 WAV 合成/识别不依赖它。
2.2 依赖服务(本机安装)
| 服务 | 默认地址 | 用途 | Win11 推荐获取方式 |
|---|---|---|---|
| PostgreSQL | localhost:5432 | callflow / callout 数据库 schema | WSL2 Docker Compose(postgres:15),或本机安装包 / Docker Desktop |
| Redis | 127.0.0.1:6379 | 会话协调、外呼分布式锁与状态 | WSL2 Docker Compose(redis:7.2.8),或 Docker Desktop / Memurai |
| OpenAI 兼容推理后端 | 按实际部署 | callout-server 与 callflow-esl 的大模型推理 | GPUStack / vLLM / Ollama / DeepSeek API 等 |
1. 一键编排启动 PostgreSQL 与 Redis(推荐)
在 WSL2 任意工作目录(或 Windows Docker Desktop)中新建 docker-compose.yaml,账号、密码、端口与默认开发库名均已对齐仓库各服务的 config.toml:
yaml
services:
pgsql:
image: postgres:15
container_name: pgsql
restart: unless-stopped
ports:
- "5432:5432"
environment:
POSTGRES_DB: dev-freeswitch
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
PGTZ: Asia/Shanghai
TZ: Asia/Shanghai
volumes:
- pgsql-data:/var/lib/postgresql/data
redis:
image: redis:7.2.8
container_name: redis
restart: unless-stopped
ports:
- "6379:6379"
environment:
TZ: Asia/Shanghai
command: redis-server --requirepass redis
volumes:
- redis-data:/data
volumes:
pgsql-data:
redis-data:启动容器并确保开发库与两个业务 schema(callflow / callout)就绪:
bash
# 1. 后台启动 PostgreSQL 与 Redis 容器
docker compose up -d
# 2. (可选兜底) 若复用已有 Postgres 容器,幂等创建 dev-freeswitch 开发库及业务 schema
docker exec -it pgsql psql -U postgres -tc "SELECT 1 FROM pg_database WHERE datname = 'dev-freeswitch'" | grep -q 1 || docker exec -it pgsql psql -U postgres -c 'CREATE DATABASE "dev-freeswitch";'
docker exec -it pgsql psql -U postgres -d dev-freeswitch -c "CREATE SCHEMA IF NOT EXISTS callflow; CREATE SCHEMA IF NOT EXISTS callout;"- PostgreSQL 分库隔离约定:线上生产环境统一使用
freeswitch库,本地开发使用dev-freeswitch库(默认连接串postgres://postgres:postgres@localhost:5432/dev-freeswitch)以实现物理分库隔离,避免本地开发与调试误操作或污染线上/集群共享数据。callflow-esl、callflow-server与callout-server共享同一实例上的callflow/callout两个 schema。 - Redis 命名空间约定:确保连接 URL 与
apps/callout-server/config.toml、apps/callflow-esl/config.toml的redis.url一致(默认redis://default:redis@127.0.0.1:6379/0,密码redis);callout-server默认使用keyPrefix = "callout-dev"与生产环境隔离,也可用CALLOUT_REDIS_URL覆盖。
2. 配置 OpenAI 兼容推理后端
如 GPUStack / vLLM / Ollama 或云端兼容 API,提供标准 /v1/chat/completions:
callout-server的推理地址有两种来源:config.toml显式写[llm]段(openaiUrl/ 各领域模型),或留空由数据库callout.ai_llm_configs中的启用记录提供(在callout-webpage的「AI 策略中心」->「后端模型」页维护,也可用GET /api/ai/llm-config与POST /api/ai/llm-config/update读写)。仓库内示例config.toml未写llm段,即默认走数据库配置。callflow-esl的原生大模型直连业务(如llm-chat-business、kb-llm-chat-business)读取apps/callflow-esl/config.toml中的顶层[llm]配置段(endpoint、apiKey、model)。
2.3 安装依赖与建表
powershell
bun install # 安装全部 workspace 依赖
bun run --cwd apps/callflow-server db:push # 同步 callflow schema 到本地 dev-freeswitch
bun run --cwd apps/callout-server db:push # 同步 callout schema 到本地 dev-freeswitchdb:push 直接把 Drizzle schema 推到数据库(各应用 drizzle.config.ts 缺省 fallback 均已对齐本地 dev-freeswitch),不保留迁移目录。若需推向指定目标库,可通过环境变量(CALLFLOW_DB_URL、CALLOUT_DB_URL)覆盖。首次建表后:
- 启动
callflow-esl会把代码侧注册表(src/business/index.ts的allBusinesses)与callflow.call_businesses双向对账:注册表里的业务按businessCodeupsert 并置为启用(软删过的会被恢复),表里存在但代码里已删除的业务被强制软删(isEnabled=0+deletedAt=now()),结果记在启动日志事件call_businesses_synced里。 - 启动
callout-server会通过ensureSeedData自动幂等初始化引导数据:自动创建默认管理员账号(admin)、完整 RBAC 角色/菜单/按钮权限树以及默认外呼线路,并自动加载活动大模型配置与归一化历史坐席状态,无需手动执行额外的种子脚本。
2.4 语音模型资产一键拉取 (ModelScope + uv)
onnx-platform/models/ 目录下集中管理 FunASR(流式/离线)、Sherpa-ONNX(Zipformer/SenseVoice)、Silero VAD、Kokoro/Matcha TTS 以及多模态情绪分析模型,总资产体积约 4.37 GB。模型资产不存放在 Git 代码仓中,新开发机或新环境建议通过内置的 model-down 工具从 ModelScope (魔搭社区) 一键高速同步:
powershell
# 1. 确保系统已安装 uv(未安装则在终端执行:powershell -c "irm https://astral.sh/uv/install.ps1 | iex")
# 2. 进入模型同步工具目录并初始化 Python 依赖
cd onnx-platform/models/model-down
uv sync
# 3. 一键拉取完整语音模型库(自动下载还原至 onnx-platform/models/)
uv run download.py --model-id wlf213/ai-voice-platform-models
# 可选:仅按需下载特定模型族(如仅需 FunASR 或 Sherpa 系列)
uv run download.py --model-id wlf213/ai-voice-platform-models --allow-patterns "funasr-*"模型资产落地后,按各服务 config.toml 配置核对模型相对路径即可直接启动服务。
3. 配置本机联调地址
根据当前仓库各服务的 config.toml 默认配置,在 WSL2 Mirrored 镜像网络(networkingMode=Mirrored + hostAddressLoopback=true)下,本机单机闭环链路可直接保持 127.0.0.1;涉及局域网分机/录音访问或跨机联调的字段按实际网络环境校准:
apps/callflow-esl/config.tomlfreeswitch.host(端口8021Inbound ESL)与freeswitch.callbackHost(端口9911Outbound Socket)→ 仓库默认均为127.0.0.1(WSL2 Mirrored 模式下推荐保持127.0.0.1以避免物理网卡切换或抖动;若跨机联调或未启用回环互通,请改为开发机局域网 IP)audioFork.wsUrl→ 默认ws://127.0.0.1:10096/audio(供容器内mod_audio_fork推送单向流式 ASR 音频)duplex.wsUrl/duplex.healthUrl→ 默认ws://127.0.0.1:10091/audio与http://127.0.0.1:10091/health(供ctx.duplex对接本机audio_fork_server全双工音频网关;请勿误配为单向 ASR 端口10096)tts.profiles.wav.endpoint(http://127.0.0.1:9080/tts)与tts.profiles.stream.endpoint(http://127.0.0.1:9080/tts-stream,默认playbackPrefix = "audio_fork://"走原生 PCM16 流)db.url/redis.url→ 本机 PostgreSQL(默认postgres://postgres:postgres@localhost:5432/dev-freeswitch)/ Redis(redis://default:redis@127.0.0.1:6379/0)[llm]→ 原生大模型对话插件(llm-chat-business、kb-llm-chat-business)直连的endpoint、apiKey与model
apps/callout-server/config.toml(仓库示例已启用[[cells]],改顶层同名字段不生效——顶层值只用于填补 cell 里省略的字段)cells[0].freeswitch.host(Inbound ESL8021)与cells[0].freeswitch.callbackHost/callbackPort(默认9911)→ 仓库示例为局域网 IP(如192.168.2.246),请改为当前开发机的真实局域网 IP(若仅在本机 Mirrored 环境下闭环调试,亦可改为127.0.0.1)cells[0].callflowEsl.baseUrl(默认http://127.0.0.1:9912本机即可)与token("esl-0x21")cells[0].media.freeswitchRecordingBaseUrl→ WSL 上的 Nginx 录音静态服务地址(如http://<开发机局域网IP>:8088/recordings,本机浏览器访问亦可填http://127.0.0.1:8088/recordings)ringbackDetection.wsUrl(默认ws://127.0.0.1:10096/audio)与emotionAnalysis.endpoint(默认http://127.0.0.1:9090/analyze)db.url/redis.url→ 本机 PostgreSQL(默认dev-freeswitch)/ Redis(默认keyPrefix = "callout-dev")- 可选
[llm]配置节(可配置openaiUrl与各领域模型;留空则直接走数据库callout.ai_llm_configs)
onnx-platform/sherpa-tts-server/config.toml与speech-gateway/audio-fork-server/config.tomlsherpa-tts-server的server.publicBaseUrl默认为http://127.0.0.1:9080(Mirrored 模式下本机 FreeSWITCH 容器可直接拉取音频;若由远端机器的 FreeSWITCH 访问 TTS,必须改为开发机局域网 IP)audio-fork-server的asr.wsUrl(ws://127.0.0.1:10096/audio)与tts.baseUrl(http://127.0.0.1:9080)均在本机内部通信,保持127.0.0.1即可
架构避坑指南:不要把改后的机器专属 IP / 明文密码提交回仓库(见
contributing-docs.md)。当外部局域网设备(如另一台电脑/手机上的 SIP 软电话)参与呼叫,或未开启 WSL2hostAddressLoopback=true时,FreeSWITCH 侧回连开发机的地址(callbackHost、audioFork.wsUrl、duplex.wsUrl、publicBaseUrl)请务必填写开发机真实局域网 IP 并放通对应防火墙端口。
4. 构建本地语音与网关服务(Win11)
bun run dev:asr/tts/emotion/funasr 通过各模块的 run-server.ts 从 dist/win_x64/ 拉起已构建产物,产物不存在会直接退出,所以首次必须先构建。推荐在仓库根目录使用统一的 build:native 脚本:
powershell
# 推荐:在仓库根目录一键构建各原生服务(编译 Release 二进制并装配依赖动态库至 dist/win_x64/)
bun run build:native asr # Sherpa 统一 ASR 服务 (10096 WS 流式 / 10095 HTTP 离线)
bun run build:native tts # Sherpa TTS 语音合成服务 (9080)
bun run build:native emotion # 多模态情绪分析服务 (9090)
# 按需构建可选服务:
bun run build:native funasr # FunASR 统一 ASR 服务 (单进程双端口 10099 WS / 10094 HTTP)
bun run build:native audio-fork # Go 全双工网关独立二进制(日常 bun run dev 由 air 自动编译热重载,无需手动预构建)也可进入各服务子目录直接执行对应的 build.ps1(与 bun run build:native 完全等价):
powershell
cd onnx-platform\sherpa-asr-server; pwsh .\build.ps1
cd ..\sherpa-tts-server; pwsh .\build.ps1
cd ..\emotion-analysis-server; pwsh .\build.ps1
cd ..\funasr-asr-server; pwsh .\build.ps1
cd ..\..\speech-gateway\audio-fork-server; pwsh .\build.ps15. 启动服务
一次性启动(推荐日常开发)
powershell
bun run dev默认进程范围(共 9 个服务:asr、tts、emotion、audio-fork、esl、flow-api、flow-web、out-api、out-web)和标签以服务目录为准。ONNX 语音服务必须已构建,且本机 PATH 中已具备 go 与 air。 Ctrl+C 一次性退出全部子进程。
单服务启动(排障 / 只调某一块)
powershell
bun run dev:asr # 也有 dev:tts / dev:emotion / dev:audio-fork:air
bun run dev:callflow-esl
bun run dev:callflow-server
bun run dev:callout-server
bun run dev:callflow-webpage
bun run dev:callout-webpage如需启动 FunASR 系列服务(单进程同时监听 10099 流式与 10094 离线):
powershell
bun run dev:funasr云端兼容网关不在
bun run dev默认组内,按需bun run dev:aliyun-speech(10097/9081)、bun run dev:azure-speech(10098/9082)。
6. 启动后冒烟测试
按依赖顺序验证,避免上游未就绪造成下游误判:
powershell
# 1. 本地 ONNX 语音服务与全双工网关健康检查
Invoke-WebRequest http://127.0.0.1:10096/health # Sherpa 流式 ASR
Invoke-WebRequest http://127.0.0.1:10095/health # Sherpa SenseVoice 离线 ASR
Invoke-WebRequest http://127.0.0.1:9080/health # Sherpa TTS
Invoke-WebRequest http://127.0.0.1:9090/health # Emotion Analysis
Invoke-WebRequest http://127.0.0.1:10091/health # audio_fork_server Go 全双工音频网关
Invoke-WebRequest http://127.0.0.1:10099/health # FunASR 2-pass 流式实时 ASR (仅当启动 dev:funasr)
Invoke-WebRequest http://127.0.0.1:10094/health # FunASR Paraformer 离线 ASR (仅当启动 dev:funasr)
# 2. callout-server(含原生 NLU / Chat / Script 引擎,默认 9920)
Invoke-WebRequest http://127.0.0.1:9920/health
Invoke-WebRequest http://127.0.0.1:9920/api/ai/nlu/capabilities -Headers @{ 'X-Callout-Internal-Token' = 'callout-token' }
Invoke-WebRequest http://127.0.0.1:9920/api/ai/chat/capabilities -Headers @{ 'X-Callout-Internal-Token' = 'callout-token' }
Invoke-WebRequest http://127.0.0.1:9920/api/ai/models -Headers @{ 'X-Callout-Internal-Token' = 'callout-token' }
# 3. TTS 合成(确认能返回 wavUrl,且 WSL FreeSWITCH 容器能访问该 URL)
Invoke-WebRequest -Method Post http://127.0.0.1:9080/tts -ContentType 'application/json' -Body '{"text":"测试"}'
# 4. 管理台 / 外呼后端
Invoke-WebRequest http://127.0.0.1:9912/health # callflow-esl HTTP API
Invoke-WebRequest http://127.0.0.1:9913/health # callflow-server
Invoke-WebRequest http://127.0.0.1:9920/api/media/config
# 5. 前端可达
Invoke-WebRequest http://127.0.0.1:19913
Invoke-WebRequest http://127.0.0.1:19920端到端一通电话(联通性最终验证): 从 WSL FreeSWITCH 侧发起一次呼叫路由到 callflow-esl(例如:软电话注册分机 1000 后拨打已配置的业务被叫号;或在 WSL 宿主机执行 docker exec -it fs-app fs_cli -x "originate user/1000 &socket(127.0.0.1:9911 async full)";亦或通过 callflow-esl POST http://127.0.0.1:9912/outbound-calls 发起外呼)。 观察 callflow-esl 控制台出现握手、业务命中、ASR transcription、TTS 播放。若链路任何一环出现异常,直接在 WSL 宿主机执行 tail -f /data/docker_fs_server/freeswitch-install/var/log/freeswitch/freeswitch.log 定位 FreeSWITCH 报错,并对照第 1 节网络约定与 troubleshooting.md 排查。
7. 自动化测试与质量门禁(改代码后的最低验证)
在提交代码或完成任务前,相关改动必须通过对应的全局质量门禁检查:
powershell
bun run typecheck # 全局 TS/Vue 静态类型检查(一次性检查 5 个核心 TS/Vue 应用 + 阿里云/Azure 两个语音网关)
bun run lint # 全局代码格式化与 Lint 门禁(一次性执行 7 个工作区子项目的 oxfmt + oxlint --fix)
bun run check:rust # 全局 Rust 质量门禁(覆盖全部 4 个 Rust 服务的 cargo fmt --check + clippy -D warnings + cargo test)
bun run docs:build # VitePress 文档站死链与构建校验(修改任何 Markdown 文档后必跑)触及运行时 / 业务逻辑且有 *.test.ts 覆盖时,追加单元测试或单模块检查:
powershell
bun --cwd apps/callflow-esl test # 跑该 app 全部 *.test.ts
bun --cwd apps/callout-server test
bun run --cwd apps/callflow-esl typecheck # 单 app 的 tsc --noEmit
bun run --cwd apps/callflow-webpage lint # 单前端项目的 oxfmt + oxlint
bun test apps/callflow-esl/src/xxx.test.ts # 单文件测试
bun test -t "关键字" # 按用例名过滤触及单个 ONNX Rust 语音服务时,也可按服务名单独执行静态门禁与单测:
powershell
bun run check:rust tts # 仅检查 sherpa-tts-server(可选: funasr / sherpa-asr / tts / emotion)改动 Drizzle schema 后需 bun run --cwd apps/<app> db:generate 与 db:push 再重启对应服务。 新增 callflow-esl 业务的完整步骤见 apps/callflow-esl/README.md 与 apps/callflow-esl/docs/business-development.md。
8. 收尾:关闭本机实例(仓库硬性约定)
测试结束必须关闭本机手动启动的服务,避免端口/资源残留:
powershell
# bun run dev 单终端: Ctrl+C 即可
# 拆分启动的补充清理:
Get-Process | Where-Object { $_.ProcessName -in @('sherpa_asr_server','funasr_asr_server','sherpa_tts_server','emotion_analysis_server','audio_fork_server','air','node','bun') } | Format-Table Id,ProcessName
netstat -ano | Select-String '10099|10096|10095|10094|10091|9080|9090|9911|9912|9913|9920|19913|19920'依赖目录彻底清理(删除各子项目 node_modules/,执行后需重新 bun install):
powershell
bun ./scripts/clean-modules.ts9. 常见问题速查
| 现象 | 首查项 |
|---|---|
dev:asr/tts/emotion/funasr 秒退 | dist/win_x64/ 无产物,先执行 bun run build:native <service> 或对应目录 build.ps1 |
dev:audio-fork:air 启动报错退出 | 本机未安装 Go 1.23+ 或 air 不在 PATH 中(执行 go install github.com/air-verse/air@latest),或 10091 端口被残留进程占用 |
| FreeSWITCH 呼入不进 callflow-esl | 检查 .wslconfig 是否已开启 networkingMode=Mirrored 与 hostAddressLoopback=true;若跨机或未开启回环互通,callbackHost 须改为开发机局域网 IP 并放通 Windows 防火墙 9911 |
无 ASR 结果 / 全双工 ctx.duplex 建连失败 | FreeSWITCH 容器访问不到 audioFork.wsUrl(10096)或 duplex.wsUrl(10091);在 WSL 执行 curl http://127.0.0.1:10096/health 与 curl http://127.0.0.1:10091/health 验证连通性 |
| TTS 不播放 | 检查 callflow-esl 的 tts.profiles.stream.playbackPrefix(推荐 audio_fork://)及 sherpa-tts-server 是否在线;若跨机 FreeSWITCH 拉取 WAV,publicBaseUrl 须改为开发机局域网可达 IP |
| FreeSWITCH 容器异常重启 / PID 冲突 | 宿主机上次异常残留 .pid 锁;在 WSL 执行 rm -f /data/docker_fs_server/freeswitch-install/var/run/freeswitch/*.pid 后 docker compose restart |
| FreeSWITCH 日志报 Permission denied | WSL 宿主机挂载目录属主不匹配;在 WSL 执行 sudo chown -R 1000:1000 /data/docker_fs_server/freeswitch-install/var 修复权限 |
| 查看呼叫明细与故障现场不知道查哪 | 直接在 WSL 执行 tail -f /data/docker_fs_server/freeswitch-install/var/log/freeswitch/freeswitch.log 或在 Windows 资源管理器打开对应 UNC 路径 |
| NLU / 大模型对话报错 | callout-server 未启动,或其推理后端([llm] 配置 / 数据库 callout.ai_llm_configs 启用的配置)不可达、模型名不存在 |
| 建表/连库失败 | PostgreSQL 容器未启动、未建 dev-freeswitch 库,或连接串与 config.toml 不一致 |
| 外呼调度不动 | Redis 容器未启动或密码不符 |
更细的模块级排障见 troubleshooting.md 与各模块 README。