外观
emotion-analysis-server
本地 HTTP 服务,对外呼通话做情绪分析。文本走 BERT 多语种情感模型,音频走 wav2vec2 情绪识别, 两者结果统一归一为极性(positive / neutral / negative)+ 0–100 分后融合输出。
直接调用 ONNX Runtime(C++ API)跑模型,不依赖 sherpa-onnx。
默认监听 127.0.0.1:9090(config.json 的 server.host / server.port), 由 apps/callout-server 的情绪分析 worker 调用;跨机部署时把 server.host 改为可被访问的地址。
端点
GET /health— 返回模型就绪状态(textReady/audioReady)与模型文件探测结果。POST /analyze— 分析单条通话。GET /emotion-audio/<fileName>.wav— 试听本次音频情绪分析实际送入模型前保存的 16k 单声道 WAV。
默认读取运行目录下的 config.json。logging.dir 可配置日志目录,默认 "logs"; 相对路径按运行目录解析,日志文件按天滚动。
音频分析默认按配置中的客户声道执行。示例配置:
json
{
"audio": {
"channel": "customer",
"channelRoles": {
"customer": "right",
"agent": "left"
},
"trimSilence": true,
"silenceThreshold": 0.01,
"processedDir": "public/emotion-audio"
}
}audio.channel支持mix、left、right、channel0、channel1、customer、agent。left等价于channel0,right等价于channel1;customer/agent通过audio.channelRoles映射到实际声道。- 会议 WAV 有几个声道取决于 FreeSWITCH 录音方式,不保证“一个参与者一个声道”。上线前请用真实录音听音确认 客户 / 坐席分别在哪个声道,再调整
audio.channelRoles。 - 如果录音只有单声道但配置为
right/channel1,音频模态会失败并在reason中说明声道不存在,不会静默回退。 audio.processedDir用于保存声道选择/混音、重采样、静音裁剪、30s 截断后的最终分析音频(归一化前)。 分析响应里的audio.url返回短文件名,浏览器播放完整 URL 由调用方系统按自身媒体配置拼接。
请求示例:
json
{
"attemptId": 1,
"text": "客户说暂时不需要",
"recordingUrl": "http://127.0.0.1/recordings/a.wav",
"transcriptUrl": "http://127.0.0.1/transcripts/a.json"
}text、recordingUrl 至少提供一个;两者都有时融合输出(文本权重 0.6、音频 0.4)。
响应示例:
json
{
"ok": true,
"label": "positive", // 融合后的极性
"score": 81, // 融合极性分 0-100(50 为中性)
"confidence": 0.81,
"reason": "text=positive(97, 4.9star); audio=neutral(56, calm 0.76); fused=positive(81)",
"text": {
"available": true,
"label": "positive",
"score": 97,
"raw": "5 stars",
"stars": 4.88,
"analyzedText": "客户说暂时不需要"
},
"audio": {
"available": true,
"label": "neutral",
"score": 56,
"raw": "calm",
"confidence": 0.76,
"fileName": "attempt-1-1782748800000-0.wav",
"url": "attempt-1-1782748800000-0.wav",
"processed": true,
"processing": { "channel": "customer(right)", "targetSampleRate": 16000, "trimmed": true, "truncated": false }
},
"fused": { "label": "positive", "score": 81 },
"risks": [],
"model": { "text": "emotion-text/model.onnx", "audio": "emotion-audio/model.onnx" },
"elapsedMs": 755.6
}- 顶层 /
fused的label、score是归一后的极性,供业务直接使用。 text/audio子对象保留各模型的原始输出:文本是星级(raw如"4 stars",stars为期望星级 1–5), 音频是情绪类别(raw如"happy",confidence为该类置信度)。text.analyzedText是本次实际送入文本模型的文本;audio.fileName/audio.url是本次保存的复核音频短文件名。- 某一模态不可用时
available:false,并在reason中说明原因;两者都不可用时ok:false、label:"unavailable"。
推理细节
文本(emotion-text,bert-base-multilingual-uncased-sentiment):内置 BERT WordPiece 分词器 (清理 → CJK 按字切分 → 空白切分 → 小写 → 标点切分 → 贪心 WordPiece),输出 5 类星级 logits, softmax 后取期望星级 E,score = round((E-1)/4*100),按分数映射极性(<40 负、>60 正、其余中性)。
注:分词仅做 ASCII 小写,未做全 Unicode 大小写/去音标——对中文、英文无影响,带音标的拉丁文本可能略有偏差。
音频(emotion-audio,wav2vec2-emotion-recognition):拉取 recordingUrl → 解码 WAV → 按配置选择声道或混音 → 重采样到 16k → 静音过滤(默认开启)→ 最多截取 30s → 归一化 → 7 类情绪 logits → argmax + 置信度 → 情绪映射到极性基准分并按置信度向中性收缩。
- 仅支持
http://(无 TLS;https://会被拒绝并在reason说明)。 - WAV 支持 PCM 8/16/24/32-bit 与 IEEE float32;压缩格式(MP3/Opus 等)不支持。
- 静音过滤按 20ms 短帧 RMS 判断,并保留语音段前后约 200ms;如需保持旧行为,可设
audio.trimSilence为false。 - 重采样为线性插值;静音过滤后的录音超过 30s 截断。
模型目录结构
text
onnx-platform/models/emotion-text/
model.onnx labels.json vocab.txt
onnx-platform/models/emotion-audio/
model.onnx labels.json preprocess.json依赖与构建
- ONNX Runtime 发行版置于
onnx-platform/onnxruntime/<平台>/(含include/与lib/)。CMake 变量ONNXRUNTIME_ROOT可覆盖默认路径。 - 构建(Windows):
pwsh build.ps1,产物安装到target/win_x64/(含onnxruntime.dll)。 - 构建(Linux x64):
bash emotion-analysis-server/build.sh,或使用 Ubuntu 22.04 / Debian 12 / Debian 13 Docker 镜像;镜像命令和运行库兼容性见onnx-platform/README.md。 产物安装到target/linux_x64/,并包含libonnxruntime.so的完整版本化链接链。 - 运行时需能定位
onnx-platform/models/(按工作目录向上查找models/),例如从对应平台的target/win_x64/或target/linux_x64/启动。