news 2026/9/7 7:15:19

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
开源电话机器人搭建:Asterisk+Vosk实现自动接听与离线转写

最近“谁想要本船长的电话号码”这句话在短视频和评论区刷屏,排队要号码的人能从船头排到船尾。玩梗归玩梗,先别急着要船长的号——真要给你一个电话号码,你能让它自动接听、自动说话、把留言转成文字吗?估计大部分人答不上来。

所以这篇文章换个玩法:不为难船长,自己搭一套“自动接听 + 语音播报 + 留言录音 + 离线转写”的电话机器人原型。整套系统全部基于开源组件:Asterisk 做电话交换中心,SIP 软电话做测试端,Python AGI 脚本做对话逻辑,Vosk 做离线语音识别,espeak-ng 或任意 TTS 引擎做文字转语音。

先给结论:这套东西不吃显卡,一台 2 核 2G 的 Linux 机器就能跑,服务方式以 systemd 为主,可以通过 AMI/ARI 对外提供接口,也能扩展成批量外呼任务。文章会带你把环境准备、安装配置、分机注册、自动应答、录音转写、接口调用完整过一遍,最后附常见问题排查和合规使用边界。跑完这套流程,你手里就有一个真正能“接电话”的号码入口,虽然它叫 1000 分机号,不叫船长专线。

1. 电话机器人原型核心能力速览

先把这套原型的关键规格摆出来,方便你判断要不要继续往下看。

能力项说明
项目类型电话语音应答原型,可扩展为 IVR 菜单、语音留言、自动外呼测试
核心组件Asterisk(软交换)、PJSIP(SIP 协议栈)、Python AGI、Vosk、espeak-ng
主要功能自动接听、语音播报、分机互拨、留言录音、离线语音识别、文本合成语音
硬件要求2 核 CPU / 2GB 内存起步,不需要 GPU;识别模型越大,内存占用越高
支持平台Linux(Debian / 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.04,2 核 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 安装 Asterisk

Debian / 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] type=transport protocol=udp bind=0.0.0.0:5060 [1001] type=endpoint context=captain-context disallow=all allow=ulaw allow=alaw auth=1001-auth aors=1001-aor [1001-auth] type=auth auth_type=userpass username=1001 password=captain-secret-1001 [1001-aor] type=aor max_contacts=1 [1002] type=endpoint context=captain-context disallow=all allow=ulaw allow=alaw auth=1002-auth aors=1002-aor [1002-auth] type=auth auth_type=userpass username=1002 password=captain-secret-1002 [1002-aor] type=aor max_contacts=1

配置要点:

  • context对应拨号计划里的上下文名称,两边必须一致。
  • disallow=allallow=ulaw是稳妥的编解码组合,兼容性最好。
  • 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, flush=True) 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()

设置执行权限并重启 Asterisk:

sudo 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(errors="ignore") s = socket.create_connection(("127.0.0.1", 5038), timeout=5) 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/1002

Python 批量脚本模板如下:

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="", encoding="utf-8") as f: rows = list(csv.DictReader(f)) s = socket.create_connection((AMI_HOST, AMI_PORT), timeout=5) 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/null

7.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并重启 Asterisk
Vosk 识别结果为空录音格式与模型不匹配检查 wav 采样率统一录音格式,或换大模型
AMI 登录失败密码错误或绑定地址限制检查 manager.conf 和端口确认 admin 密码,bindaddr改为本机 IP
5060 端口被占用其他 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 脚本改成带事件监听的完整任务队列,甚至可以在会后处理里接一个大语言模型,让“船长”不仅能接电话,还能听懂留言并自动回复。这套原型本身就是一个很好的语音机器人实验基座,建议收藏备用,等需要搭电话自动化流程时直接照着这套思路落地。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 7:14:48

自研战棋地图编辑器:重画火纹初代25张地图的数据结构与工程实践

把火纹初代全部25张地图重画一遍,最花时间的不是画格子,而是想清楚地图数据到底怎么组织。我花了两周左右先做了一个自研地图编辑器,再基于它把25张地图按原始结构重画并导出。这篇文章不聊具体关卡剧情,只讲重画地图这件事里的数…

作者头像 李华
网站建设 2026/9/7 7:11:48

C#与SQL Server构建工业上位机任务调度平台:核心架构与踩坑实践

简介:面向C#与SQL技术栈的运维及开发人员,这份任务调度平台资源包提供了一套完整的系统部署与配置方案。内容涵盖数据库安装脚本、Web站点发布配置、Node服务端部署以及系统级任务设定,适合需要构建错误邮件通知与长时间运行任务检测机制的企…

作者头像 李华
网站建设 2026/9/7 7:11:02

Buzz 离线转录:免费在本地把语音变文字

Buzz 离线转录:免费在本地把语音变文字 【免费下载链接】buzz Buzz transcribes and translates audio offline on your personal computer. Powered by OpenAIs Whisper. 项目地址: https://gitcode.com/GitHub_Trending/buz/buzz 访谈录音、会议声音笔记、…

作者头像 李华
网站建设 2026/9/7 7:07:44

Vibe Coding实战指南:零基础用AI编程工具从需求到完整项目

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华