ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

开源电话机器人搭建:Asterisk+Vosk实现自动接听与离线转写

开源电话机器人搭建:Asterisk+Vosk实现自动接听与离线转写 最近“谁想要本船长的电话号码”这句话在短视频和评论区刷屏排队要号码的人能从船头排到船尾。玩梗归玩梗先别急着要船长的号——真要给你一个电话号码你能让它自动接听、自动说话、把留言转成文字吗估计大部分人答不上来。所以这篇文章换个玩法不为难船长自己搭一套“自动接听 语音播报 留言录音 离线转写”的电话机器人原型。整套系统全部基于开源组件Asterisk 做电话交换中心SIP 软电话做测试端Python AGI 脚本做对话逻辑Vosk 做离线语音识别espeak-ng 或任意 TTS 引擎做文字转语音。先给结论这套东西不吃显卡一台 2 核 2G 的 Linux 机器就能跑服务方式以 systemd 为主可以通过 AMI/ARI 对外提供接口也能扩展成批量外呼任务。文章会带你把环境准备、安装配置、分机注册、自动应答、录音转写、接口调用完整过一遍最后附常见问题排查和合规使用边界。跑完这套流程你手里就有一个真正能“接电话”的号码入口虽然它叫 1000 分机号不叫船长专线。1. 电话机器人原型核心能力速览先把这套原型的关键规格摆出来方便你判断要不要继续往下看。能力项说明项目类型电话语音应答原型可扩展为 IVR 菜单、语音留言、自动外呼测试核心组件Asterisk软交换、PJSIPSIP 协议栈、Python AGI、Vosk、espeak-ng主要功能自动接听、语音播报、分机互拨、留言录音、离线语音识别、文本合成语音硬件要求2 核 CPU / 2GB 内存起步不需要 GPU识别模型越大内存占用越高支持平台LinuxDebian / Ubuntu 为例软电话客户端跨平台启动方式systemd 服务或前台命令Asterisk 自带 CLI 管理台是否支持 API支持可通过 AGI 内联、AMI 管理器、ARI 提供外部接口是否支持批量任务可扩展批量外呼用脚本读取名单依次呼叫网络要求局域网内可用接运营商中继需实名和相应资质适合场景内网通信测试、客服导航原型、通话记录转写分析、语音机器人教学这里先说明下面所有命令、配置、脚本都是可以修改的模板包名、路径、密码、模型版本需要按你本机实际环境替换。Asterisk 版本不同PJSIP 配置字段可能有细微差异以asterisk -V输出和官方文档为准。2. 适用场景与使用边界这个原型能做的事可以用一条链说明白电话进来 → 系统接听 → 播放欢迎语 → 用户说话或按键 → 录音保存 → Vosk 离线转写 → 文本交给后续程序处理。具体能落到这些场景内网电话实验环境学习 SIP 注册、呼叫路由和软交换原理。搭建一个简单的自动应答导航比如“按 1 转人工按 2 听介绍”。做一个“语音留言箱”把通话留言存成 wav再用离线语音识别转成文字。对接本地脚本实现“打个电话触发一个任务”的自动化演示。作为语音机器人课程或实验的基座后续可以接大模型做对话。需要划清楚的边界这套系统只建议跑在自有设备的测试环境或已经取得合法使用权的线路上。在中国大陆环境接运营商电话线路涉及入网、实名、码号资质等问题个人随意接入外线存在合规风险测试阶段要避免接入公网中继。严禁用于骚扰电话、诈骗、伪造主叫号码、批量营销等行为。如果录音包含他人声音或个人信息必须提前告知并取得同意并做好数据加密和访问控制。批量外呼前必须确认被叫方授权任何自动化呼叫工具都不能成为骚扰工具。3. 环境准备与前置条件开始之前把下面这些条件先对一遍。3.1 硬件与系统一台 Linux 机器或虚拟机推荐 Debian 12 或 Ubuntu 22.042 核 CPU、2GB 内存起步。不需要独立显卡Vosk 小模型在 CPU 上就能完成实时识别。磁盘至少留 10GB主要给系统、模型文件和录音文件。一台测试手机或电脑安装 SIP 软电话用来注册分机并打电话测试。3.2 网络与端口这套系统默认监听以下端口端口协议用途5060UDPSIP 注册和信令10000 - 20000UDPRTP 语音流5038TCPAMI 管理接口仅建议监听本机如果没有特殊要求防火墙只对这些端口做局域网放行测试阶段也可以直接把服务绑在内网 IP 上避免暴露到公网。3.3 依赖组件清单Asterisk核心电话交换机。Python 3 pip运行 AGI 脚本和识别脚本。Vosk 中文模型离线语音识别。espeak-ng离线 TTS语音质量一般但部署简单。软电话客户端Zoiper、MicroSIP、Linphone 都可以。4. 安装部署与启动方式4.1 安装 AsteriskDebian / Ubuntu 系直接用 apt 安装是最快的路径sudo apt update sudo apt install -y asterisk asterisk-core-sounds-en asterisk -V如果系统源里的 Asterisk 版本偏老也可以从源码编译安装。生产环境建议编译指定版本这里为了快速跑通先用系统包。安装完成后确认版本号能输出版本说明核心程序已经就位。4.2 配置 PJSIP 分机Asterisk 安装好之后先配置两个测试分机和一个自动应答入口。编辑/etc/asterisk/pjsip.conf; pjsip.conf 基础模板 [transport-udp] typetransport protocoludp bind0.0.0.0:5060 [1001] typeendpoint contextcaptain-context disallowall allowulaw allowalaw auth1001-auth aors1001-aor [1001-auth] typeauth auth_typeuserpass username1001 passwordcaptain-secret-1001 [1001-aor] typeaor max_contacts1 [1002] typeendpoint contextcaptain-context disallowall allowulaw allowalaw auth1002-auth aors1002-aor [1002-auth] typeauth auth_typeuserpass username1002 passwordcaptain-secret-1002 [1002-aor] typeaor max_contacts1配置要点context对应拨号计划里的上下文名称两边必须一致。disallowall加allowulaw是稳妥的编解码组合兼容性最好。password只是演示密码测试完要改不要让默认口令留在生产环境。如果系统里存在旧版sip.conf注意不要混用新旧两套栈Asterisk 默认优先读取pjsip.conf。4.3 配置拨号计划编辑/etc/asterisk/extensions.conf追加以下内容[captain-context] ; 自动应答入口播放欢迎语然后进入 AGI 脚本 exten 1000,1,Answer() same n,Playback(welcome) same n,AGI(captain_bot.py) same n,Hangup() ; 分机互拨 exten 1001,1,Dial(PJSIP/1001,30) same n,Hangup() exten 1002,1,Dial(PJSIP/1002,30) same n,Hangup()这里把分机互拨和自动应答放在同一个上下文里。1000 是自动应答测试号1001 和 1002 是两个分机号。Playback(welcome)会播放/var/lib/asterisk/sounds/welcome.wav这个文件我们需要先把这个 wav 准备好。4.4 准备语音提示文件用 espeak-ng 生成一段中文欢迎语sudo apt install -y espeak-ng mkdir -p /tmp/captain-tts espeak-ng -v cmn 你好欢迎致电本船长的电话机器人。请在提示音后留言我们会把留言转成文字。 -w /tmp/captain-tts/welcome.wav sudo cp /tmp/captain-tts/welcome.wav /var/lib/asterisk/sounds/welcome.wav-v cmn指定中文普通话发音。espeak-ng 音质比较机械但作为原型验证完全够用。如果觉得音质差可以换成 Piper 或 Edge-TTS生成文件后同样放到 sounds 目录即可。4.5 部署 Python AGI 脚本Asterisk 的 AGI 脚本默认放在/var/lib/asterisk/agi-bin/。新建/var/lib/asterisk/agi-bin/captain_bot.py#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import sys def agi_command(cmd): # AGI 协议命令写到 stdout响应从 stdin 读 print(cmd, flushTrue) resp sys.stdin.readline().strip() return resp def main(): caller os.environ.get(agi_callerid, unknown) # 提示用户开始留言 agi_command(STREAM FILE beep \\) # 录音 10 秒保存为 /tmp/message.wav agi_command(RECORD FILE /tmp/message wav 123456 10 0) agi_command(VERBOSE \captain bot done, caller%s\ 1 % caller) if __name__ __main__: main()设置执行权限并重启 Asterisksudo chmod x /var/lib/asterisk/agi-bin/captain_bot.py sudo systemctl restart asterisk这个脚本只做了最基础的一件事播放提示音然后录 10 秒留言。到这一步系统已经具备“自动接电话并录音”的能力。5. 功能测试与效果验证部署完成后按下面顺序从基础到进阶依次验证。5.1 分机注册测试在软电话里添加两个账号分别填入 Asterisk 服务器 IP、分机号 1001 / 1002 和对应密码。注册成功后Asterisk 侧能看到在线状态sudo asterisk -rx pjsip show endpoints预期输出里能看到 1001 和 1002 的 AOR/Contact 处于在线状态。如果看不到先检查软电话填的账号密码是否和pjsip.conf一致再检查 5060 端口是否放行。5.2 分机互拨测试用 1001 拨打 1002预期 1002 响铃接听后双方能正常通话并听到声音。判断标准响铃时间不超过配置里的 30 秒。通话建立后没有单通或剧烈杂音。挂机后 Asterisk 里没有残留呼叫通道。如果出现单通优先查 RTP 端口 10000-20000 是否放行以及双方编解码是否一致。5.3 自动应答与留言录音测试用任意分机拨 1000预期流程电话立即被接起。播放欢迎语“你好欢迎致电本船长的电话机器人……”。播放 beep 提示音。对着话筒说话持续 10 秒后自动挂机。挂机后检查录音文件ls -lh /tmp/message.wav文件存在且大小不是 0说明录音链路正常。如果提示音播放失败检查 sounds 目录下 welcome.wav 权限如果录音文件没生成重点看 AGI 脚本有没有执行权限以及/tmp/message.wav路径是否被系统清理策略影响。5.4 离线语音转写测试录音文件拿到了下一步用 Vosk 把它转成文字。先下载中文小模型官方模型页面地址会根据版本更新下面 URL 只是示例cd /opt wget https://alphacephei.com/vosk/models/vosk-model-small-cn-0.22.zip unzip vosk-model-small-cn-0.22.zip -d /opt/vosk-models安装 Python 依赖pip install vosk写一个独立的转写脚本/opt/captain-transcribe.py#!/usr/bin/env python3 # -*- coding: utf-8 -*- import json import wave from vosk import Model, KaldiRecognizer model Model(/opt/vosk-models/vosk-model-small-cn-0.22) wf wave.open(/tmp/message.wav, rb) rec KaldiRecognizer(model, wf.getframerate()) text_parts [] while True: data wf.readframes(4000) if len(data) 0: break if rec.AcceptWaveform(data): result json.loads(rec.Result()) text_parts.append(result.get(text, )) final json.loads(rec.FinalResult()) text_parts.append(final.get(text, )) print(识别结果, .join(text_parts))运行python3 /opt/captain-transcribe.py如果识别结果为空或错得离谱优先检查录音采样率和模型是否匹配或者换用更大的中文模型。小模型追求速度识别率靠环境安静程度和发音清晰度保证。6. 接口 API 与批量任务扩展原型跑通后下一步就是考虑怎么把它接进自己的程序里。Asterisk 提供了三种常用方式按距离从近到远分别是 AGI、AMI、ARI。6.1 AGI内联脚本接口AGI 已经在上面用过了。它的特点是跟随呼叫流程执行适合做“接听后做什么”的逻辑。缺点是一个通话对应一个脚本进程复杂逻辑写起来比较繁琐。6.2 AMI外部管理接口AMI 走 TCP 5038 端口可以让外部程序发命令比如发起呼叫、查询通道状态、监听事件。先开启 AMI编辑/etc/asterisk/manager.conf[general] enabled yes port 5038 bindaddr 127.0.0.1 [admin] secret strong-password-here read all write all注意bindaddr 127.0.0.1只允许本机连接不要暴露到公网。重启 Asterisk 后用 Python 脚本通过 socket 发命令import socket import time def ami_action(sock, lines): cmd \r\n.join(lines) \r\n\r\n sock.sendall(cmd.encode()) time.sleep(1) return sock.recv(65536).decode(errorsignore) s socket.create_connection((127.0.0.1, 5038), timeout5) print(ami_action(s, [ Action: Login, Username: admin, Secret: strong-password-here, ])) print(ami_action(s, [ Action: Originate, Channel: PJSIP/1001, Context: captain-context, Exten: 1000, Priority: 1, Timeout: 30000, ])) s.close()这段代码的作用是登录 AMI然后触发 1001 分机拨打 1000 自动应答入口。执行后1001 分机会振铃接起后进入之前配置的语音流程。6.3 批量外呼任务批量外呼的原理就是用脚本循环调用 AMI 的 Originate。准备一个 CSV 名单name,channel 张三,PJSIP/1001 李四,PJSIP/1002Python 批量脚本模板如下import csv import socket import time AMI_HOST 127.0.0.1 AMI_PORT 5038 AMI_USER admin AMI_PASS strong-password-here def send_ami(sock, lines): cmd \r\n.join(lines) \r\n\r\n sock.sendall(cmd.encode()) time.sleep(0.3) def originate(sock, channel): send_ami(sock, [ Action: Originate, Channel: %s % channel, Context: captain-context, Exten: 1000, Priority: 1, Timeout: 30000, ]) with open(call_list.csv, newline, encodingutf-8) as f: rows list(csv.DictReader(f)) s socket.create_connection((AMI_HOST, AMI_PORT), timeout5) send_ami(s, [Action: Login, Username: %s % AMI_USER, Secret: %s % AMI_PASS]) for row in rows: originate(s, row[channel]) print(已发起呼叫, row[name]) time.sleep(5) # 避免并发过高每个呼叫间隔 5 秒 s.close()批量任务最重要的三个工程点是并发控制、失败重试、完整日志。上面模板里的time.sleep(5)就是最简单的限流方式生产级系统还要记录每个号码的呼叫结果失败号码放进重试队列。再次强调批量外呼只允许应用于已授权的测试线路严禁用于营销骚扰或其他违法违规用途。7. 资源占用与性能观察这套系统不需要 GPU但不代表可以完全忽略资源。运行过程中重点关注四个指标。7.1 CPU空闲状态下Asterisk 本身几乎不占 CPU。呼叫建立时CPU 会出现短暂峰值。espeak-ng 合成语音和 Vosk 转写是 CPU 消耗大户。小模型转写基本能跟上实时语音但会占用一个核心的大部分算力。观察命令top -p $(pgrep -f asterisk | tr \n , | sed s/,$//) 2/dev/null7.2 内存Vosk 小模型加载后占用内存不算夸张但如果你换成大模型内存占用会明显上升。测试时用free -h判断是否够用。如果内存紧张优先保证不要同时跑多个转写进程。7.3 通话通道观察当前活跃通道sudo asterisk -rx core show channels sudo asterisk -rx pjsip show channels并发越高CPU 和内存压力越大。测试时从 1 路开始逐步增加到 5 路、10 路记录每一档的 CPU 和内存变化就能摸清这台机器的容量上限。7.4 磁盘每通留言都会生成 wav 文件长时间跑测试一定要设置录音保留周期避免磁盘被写满du -sh /tmp/message.wav如果用/tmp存录音重启后文件会丢正式使用建议把输出目录改到/var/spool/captain-recordings并写一个定时清理脚本。8. 常见问题与排查方法问题现象可能原因排查方式解决方案软电话注册不上端口不通或密码错误检查 UDP 5060查看asterisk -rx pjsip show endpoints放行端口核对分机号和密码能注册但互拨不通context 配置不一致查看extensions.conf和 endpoint 的 context统一改成captain-context通话建立但听不到声音RTP 端口未放行或编解码不一致检查 UDP 10000-20000查看日志放行 RTP 端口统一用 ulaw/alaw有杂音或单通网络抖动或 NAT 未配置查看通话时的丢包统计优先用有线网络配置 STUN 或 NAT 参数欢迎语播放失败wav 文件不存在或权限错误查Playback日志确认 welcome.wav 在 sounds 目录且可读录音文件没有生成AGI 脚本无执行权限查看 AGI 日志chmod x captain_bot.py并重启 AsteriskVosk 识别结果为空录音格式与模型不匹配检查 wav 采样率统一录音格式或换大模型AMI 登录失败密码错误或绑定地址限制检查 manager.conf 和端口确认 admin 密码bindaddr改为本机 IP5060 端口被占用其他 VoIP 服务冲突ss -ulpngrep 5060批量呼叫全部失败AMI 会话未保持或超时查看脚本报错和 AMI 事件日志增加事件监听确认 Originate 成功返回排查的第一原则是看日志。Asterisk 日志默认在/var/log/asterisk/messages启动失败、拨号失败、AGI 报错都会写在这里。遇到问题先tail -n 200 /var/log/asterisk/messages绝大多数线索都在里面。9. 最佳实践与使用建议这套原型跑通只是第一步真正要稳定用起来下面这些工程习惯值得从一开始就建立。9.1 先小参数验证再扩大规模第一次测试只用两个分机、一段 10 秒录音、小模型转写。全链路通了再逐步加并发、换大模型、接外部接口。一上来就堆功能出问题反而难定位。9.2 配置和脚本分目录管理建议建立统一目录/opt/captain/ ├── config/ # 备份的 Asterisk 配置 ├── scripts/ # AGI 和 AMI 脚本 ├── recordings/ # 录音输出 ├── transcript/ # 转写文本输出 └── logs/ # 自定义运行日志不要直接改完配置就忘了备份。一套能跑通的最小配置值得单独保存出问题可以快速回滚。9.3 录音与转写流程解耦AGI 脚本只负责录音转写由独立进程处理。这样即使识别模型加载慢也不会阻塞通话流程。生产环境可以用消息队列或定时任务扫描录音目录发现新文件就触发转写。9.4 接口服务严格限制访问AMI 只监听 127.0.0.1密码用强随机字符串不要复用默认口令。外部程序需要调用时通过本机 API 服务转发而不是直接开放 5038 端口。9.5 合规红线不能碰再强调一遍这条线必须守住。只在自己有权的测试设备上运行。接运营商线路必须确认入网资质和实名要求。录音涉及他人声音和个人信息必须告知、取得同意、加密存储。外呼和批量任务严禁用于骚扰、诈骗、营销轰炸。不要伪造主叫号码不要尝试绕过运营商监管。10. 总结与下一步回到开头那个梗“谁想要本船长的电话号码”这个问题到现在也没办法回答但你已经知道该怎么自己造一个“电话号码入口”了。这套基于 Asterisk Python Vosk 的电话机器人原型最值得尝试的点在于不吃显卡、配置轻量、全链路都能在局域网里跑通而且从自动接听、录音、转写再到 AMI 接口调用每一步都有明确的验证方法。最先要验证的功能永远是分机注册和互拨。这个通了说明电话交换的基础链路没问题后面加自动应答、录音、转写才有意义。最容易踩的坑也提前说UDP 5060 和 RTP 端口没放行、AGI 脚本没有执行权限、Vosk 模型语言和录音格式不匹配。这三个问题占据了大多数新手排错时间。往后扩展的方向也很明确把 Vosk 换成更大的模型提升识别率把 espeak-ng 换成高音质 TTS把 AMI 脚本改成带事件监听的完整任务队列甚至可以在会后处理里接一个大语言模型让“船长”不仅能接电话还能听懂留言并自动回复。这套原型本身就是一个很好的语音机器人实验基座建议收藏备用等需要搭电话自动化流程时直接照着这套思路落地。
返回列表