一台 Windows 电脑用蓝牙连上自己的手机,安卓或苹果都可以。连上以后,电脑能让这部手机拨出去,也能接听打进来的电话;通话中把对方的声音识别出来,再让 AI 回答,回答的声音送回给对方。
手机里不装软件,也不用数据线。原因不是绕过了手机,而是蓝牙耳机、车载免提本来就能远程使用手机的电话。这个方案把电脑做成一套由程序控制的免提设备。号码、信号和通话仍然在手机上。电脑负责发指令、处理声音。
为什么是免提协议
手机和电脑配对之后,蓝牙里其实有好几套互不替代的用途。
放音乐是一套。它把音频从一个设备送到另一个设备,通常是单向的,没有“拨号”“接听”“现在是不是在通话”这些动作。所以电脑能把歌放到蓝牙音箱,不能由此推出电脑能接电话。
传通讯录是另一套。这个方案没有用它,也不读取来电号码和联系人。
低功耗蓝牙是为了省电传少量数据,不是这条双向通话语音的路径。包装上写蓝牙 5.x,只说明无线电版本,不能说明它会不会打电话。
接电话用的是免提协议。规范里的名字是 HFP,Hands-Free Profile。它规定的就是:一部拥有电话功能的设备,怎样把拨号、接听、挂断和通话声音提供给一个没有电话卡的配件。蓝牙耳机接电话、上车后用车机打电话,走的都是它。
这里手机叫音频网关,缩写 AG。它才接入移动网络。电脑叫免提设备,缩写 HF。HF 不能自己把电话打到对方那里,它只能请求 AG 去做,同时接收和送回这一通的声音。
程序拨出一个号码,实际是手机在拨。程序播出一段 AI 语音,实际是把声音送进手机的通话上行,对方才听得到。识别、对话、语音合成都是后来接在这段声音上的,不属于蓝牙协议。

一个协议里为什么有两条连接
如果动作和声音塞进同一条连接,拨号这种短命令会和连续不断的语音抢传输。免提协议因此把它们分开。两条都通,客服才算接通。
第一条是控制连接。
电脑先要发现手机有没有提供免提服务。这个发现过程叫 SDP。找到之后,控制数据走 RFCOMM。它相当于蓝牙上的一条串口:一次传一行文本命令,不传语音波形。
这些文本命令沿用调制解调器时代的 AT 命令。拨号写成 ATD 加号码,接听是 ATA,挂断是 AT+CHUP。手机不会只在电脑询问时才说话,它也会主动报告状态,例如正在振铃、已经接通、已经挂断,这就是 +CIEV 一类通知。
手机回复 OK,表示它接受了这条命令,不表示对方已经拿起电话。双方把支持的功能和状态指示器协商完,叫服务级连接。它只说明控制通道准备好了,语音可能还没开始。
第二条是语音连接。
电话接通后,手机另开一条专门传实时声音的同步连接,规范里叫 SCO;改进后的宽带形式叫 eSCO。旧的窄带语音和后来的宽带语音,编码方式不同,所以到达电脑时可能表现为 8 kHz 或 16 kHz 的音频。这个方案固定使用 16 kHz、单声道、每个样本 16 位。程序看到的是已经解码后的音频数据,不是无线电上的原始编码。因此不能从程序打开了 16 kHz,反推出空中一定使用某一种编码。
这就是排障时不能只看“蓝牙已连接”的原因。配对只说明两台设备互相认识。控制连接成功只说明能发命令。语音连接成功,才说明双方的声音正在持续到达。

程序为什么不自己发送这些命令
既然命令就是几行文本,业务程序直接连上 RFCOMM 看起来更简单。正常运行不这样做。
免提协议里,一部手机的控制连接由一个免提端负责。Windows 连上手机后,系统的电话服务已经是这个免提端:它负责拨号、接听和读取通话状态。程序再打开另一条 RFCOMM,就是两个免提端同时指挥一部手机,两边看到的通话会不一致。
所以程序请求系统去连接这部手机的免提线路,然后使用系统给出的拨号、状态和音频设备。程序自己发送 AT 命令的客户端可以留下来查协议,但查的时候必须先让系统放开这部手机。那个客户端也没有实现语音连接,即使拨号成功,也不能录音,更不能让 AI 说话。
系统允许电脑使用电话,也要单独确认。没有这项权限时,免提线路还没有建立,不存在后续拨号。
从一通电话到 AI 客服
先按蓝牙地址选定这部手机。旁边可能还有耳机或另一部手机,不能拿错连接。系统连上后,还要看到这条线路现在可以拨号。手机里若还有一通没挂,新的客服不能开始。
拨出时,系统请手机拨号。来电时,手机把振铃报上来。接听前再确认一次:仍是当前这一声铃,这条线路上没有另一通,也没有被人在手机上接走。条件变了就取消,不反复抢接。
无论拨出还是接听,系统都会给这通分配一个编号。程序记住它。后面的录音、识别、回复和挂断都只操作这个编号,避免挂掉或回答到下一通。
声音不走电脑默认喇叭。对方的声音从这路免提录音读入,AI 的声音写回这路免提播放。写进播放设备,声音才会沿语音连接回到手机,再进到对方耳中。
读到声音以后有三种处理,可以按通话选择:
- 只把整通保存成录音。
- 按对方一次说话的停顿切成小段,送去识别,同时保留整通录音。
- 识别结果交给对话模型,模型的文字再合成语音,送回这一通。
读取音频的回调里不能同时做识别、访问网络或写文件。回调被堵住,这通语音就会断。这些工作放到旁边的线程,合成好的语音再排队送回播放设备。合成服务给出的采样率可能是 48 kHz,要先变成这通正在使用的采样率。转换只是让格式一致,不会让电话语音变得更清晰。
通话中途发现没有声音,也不要改成 8 kHz 再重新打开。语音连接的格式变了,这通就要重新建立,正在进行的对话会断。
对方打断 AI 时,要等这一段回复真正播放完再算一轮结束。模型已经生成完,不代表声音已经送到对方耳朵里。过早接收下一轮,两段话会叠在一起。
挂断请求发给系统,只表示请求已经送出。要看到这个编号对应的通话结束、系统重新空闲、这条线路又能拨号、蓝牙仍然连着,才能确认手机挂断了。连接留着,下一通不用重新配对。为了挂断而断开整条蓝牙,下一通的语音经常建立不起来。
识别或对话临时失败时,保留已经录下的音频,用原来的方式结束这通电话,不把蓝牙拆掉。

方案边界
这个方案使用的是一部已经能当蓝牙免提使用的手机。它不让电脑自己接入移动网络,也不从通话里取来电号码和通讯录。
换电脑或更新 Windows 后,要重新确认三件事:系统还能不能建立这部手机的免提线路,接听和挂断是否仍然可用,双向语音是否持续到达。只有控制、没有持续语音时,AI 没有可以对话的声音。