1. 项目概述当传统电话系统遇上AI大脑最近在折腾一个挺有意思的玩意儿把Asterisk这个老牌的开源电话交换系统PBX和ChatGPT的API给接上了。简单说就是让电话那头的人能直接跟一个AI语音助手聊天。这可不是简单的语音转文字再转语音而是一个完整的、基于Asterisk AGIAsterisk Gateway Interface接口的概念验证项目。想象一下你拨打一个特定的分机号接起来的是一个能理解上下文、可以进行多轮对话的AI无论是用来做个智能客服的前端测试还是搞个有趣的语音互动应用都挺有搞头。这个项目的核心价值在于它打通了成熟的电信基础设施和前沿的AI语言模型。Asterisk负责处理所有底层的电话信令、音频编解码和通道管理而ChatGPT则充当了电话另一端的“大脑”。对于通信开发者、系统集成商或者任何想探索AI在实时语音交互中应用的朋友来说这提供了一个非常直观且可操作的起点。你不用从零开始搭建语音处理管道Asterisk已经帮你把最复杂的部分搞定了。接下来我就把自己从环境搭建、代码解读到调试排坑的全过程以及背后的设计思路详细拆解一遍。2. 核心原理与架构设计拆解2.1 为什么是Asterisk AGI要理解这个项目首先得明白Asterisk AGI是干什么的。Asterisk本身是一个功能强大的软交换平台它内置了非常灵活的拨号计划Dialplan可以控制通话的流向比如播放语音菜单、接收DTMF按键、录音、转接等等。但是当我们需要实现更复杂的、需要外部程序逻辑控制的交互时比如语音识别、自然语言处理就需要AGI出马了。AGI本质上是一个通信协议。当Asterisk执行到AGI()这个拨号计划应用时它会启动一个外部程序比如我们的Python脚本并通过标准输入stdin和标准输出stdout与这个程序进行通信。Asterisk会告诉脚本“当前通道ID是什么”、“主叫号码是多少”、“被叫号码是多少”。然后脚本可以发送命令给Asterisk例如“播放某个语音文件”、“等待并接收DTMF输入”、“录音”、“获取语音识别结果”等。脚本就像一个遥控器指挥着Asterisk这只“机械臂”完成各种动作。选择AGI而不是其他更现代的Asterisk接口如ARI是因为AGI模型简单直接特别适合这种“一问一答”式的同步交互场景。我们的脚本在通话期间会一直运行循环执行“接收输入 - 调用AI - 生成回复 - 输出语音”这个过程直到挂机。这种控制粒度对于实现与AI的对话再合适不过了。2.2 系统交互流程全景图整个系统的运行流程可以概括为以下几个核心步骤理解了这个流程再看代码就清晰了来电接入用户拨打配置好的分机号码如示例中的31532401205Asterisk根据extensions.conf的配置接听电话。AGI脚本启动Asterisk执行AGI()指令启动我们的Python脚本openai_agi.py。此时Asterisk会通过stdin向脚本发送一系列初始化变量如agi_network_script,agi_request,agi_channel等。播放欢迎语脚本首先通过AGI命令STREAM FILE播放一个预先准备好的欢迎语音文件chatgpt-welcome.wav告诉用户正在接入AI助手。语音输入循环录音脚本发送RECORD FILE命令指示Asterisk开始录音并等待用户说话。这里可以设置静音检测silence参数当用户停止说话一段时间后自动停止录音。语音转文本录音生成一个音频文件如.wav格式。脚本需要调用一个语音转文本服务。原项目可能默认使用了某种STT服务但在实际部署中这一步是关键需要集成像Google Cloud Speech-to-Text、Azure Speech Services或开源的Whisper等。文本处理与调用ChatGPT将识别出的文本连同之前对话的历史记录用于保持上下文一起发送给ChatGPT的API通常是gpt-3.5-turbo或gpt-4模型。这里需要构造符合OpenAI API格式的请求包含role为user的新消息和role为assistant的历史消息。文本转语音与播放文本转语音收到ChatGPT返回的文本回复后需要将其转换为语音。这同样需要集成TTS服务如Google Text-to-Speech、Azure TTS或Amazon Polly。服务会返回一个音频文件。播放语音脚本再次使用STREAM FILE命令播放TTS服务生成的音频文件。循环与挂机完成一次“听-思-说”的循环后脚本跳回第4步开始下一次录音等待用户继续说话。直到用户挂机Asterisk会终止AGI脚本进程通话结束。注意原项目代码可能是一个极简的POC上述流程中的语音转文本和文本转语音这两个核心环节可能需要你根据选用的云服务或本地工具进行补充开发。这是本项目从“概念验证”迈向“可用系统”最关键的一步。3. 环境准备与依赖部署详解3.1 Asterisk系统基础环境评估在开始之前你需要一个已经安装并基本运行正常的Asterisk环境。这可以是物理服务器或虚拟机安装CentOS、Debian、Ubuntu等Linux发行版然后从源码或包管理器安装Asterisk。云服务器在AWS EC2、Google Cloud Compute Engine或DigitalOcean Droplet上部署。开发环境甚至可以在Windows的WSL2或macOS上编译安装Asterisk用于测试。确保你的Asterisk版本不要太老建议16 LTS或18并且安装了常用的音频格式支持如asterisk-core-sounds-en-wav。你可以通过asterisk -rx “core show version”查看版本。3.2 项目代码获取与Python环境隔离按照原项目的步骤我们将代码克隆到一个合适的目录。选择/usr/local/src/是很好的习惯因为这里通常用于存放本地安装的软件源代码。sudo mkdir -p /usr/local/src/ cd /usr/local/src/ sudo git clone https://github.com/speakupnl/chatgpt-agi.git cd chatgpt-agi接下来是Python虚拟环境。强烈建议使用虚拟环境这可以避免污染系统Python环境也便于管理项目特定的依赖包。# 检查python3和pip3是否可用 python3 --version pip3 --version # 创建虚拟环境venv目录会在当前chatgpt-agi文件夹下生成 python3 -m venv venv # 激活虚拟环境 source venv/bin/activate # 激活后命令行提示符前通常会显示 (venv) # 安装依赖。原项目的requirements.txt可能只包含openai等基础包。 # 如果你需要补充STT/TTS库比如openai-whispergTTS等需要先加进去。 pip3 install -r requirements.txt # 安装完成后可以先退出虚拟环境 deactivate实操心得在服务器上我习惯将虚拟环境的激活命令和项目启动命令写在一个简单的shell脚本里比如run_agi.sh内容如下#!/bin/bash cd /usr/local/src/chatgpt-agi source venv/bin/activate # 这里可以添加环境变量例如你的OpenAI API Key export OPENAI_API_KEY“your_key_here“ exec /usr/local/src/chatgpt-agi/venv/bin/python3 /usr/local/src/chatgpt-agi/openai_agi.py然后给脚本执行权限chmod x run_agi.sh。这样在Asterisk的AGI命令中直接调用这个脚本即可它能确保环境正确加载。3.3 核心配置文件与密钥管理原项目提到了要替换API Key。我们需要仔细查看chatgpt_agi.py或openai_agi.py根据实际文件名的开头部分。vim openai_agi.py你可能会看到类似这样的代码import openai openai.api_key “your-openai-api-key-here“安全警告绝对不要将真实的API密钥硬编码在代码中并上传到任何版本控制系统。最佳实践是使用环境变量。修改代码从环境变量读取import openai import os openai.api_key os.getenv(“OPENAI_API_KEY“) if not openai.api_key: # 可以记录错误日志并退出 sys.stderr.write(“ERROR: OPENAI_API_KEY environment variable not set.\n“) sys.exit(1)那么如何为AGI脚本设置环境变量呢有几种方法在调用AGI的拨号计划中设置不推荐因为拨号计划是明文same n,AGI(OPENAI_API_KEYyour_key_here /usr/local/src/chatgpt-agi/venv/bin/python3 /usr/local/src/chatgpt-agi/openai_agi.py)通过系统服务文件设置如果你将AGI脚本包装成一个系统服务可以在service文件中定义Environment。使用外部配置文件将密钥放在一个只有脚本和特定用户能读的配置文件里如config.ini或config.yaml。使用我们上面提到的启动脚本在run_agi.sh中通过export设置这是相对简单安全的方式。对于欢迎语音文件复制到Asterisk的声音文件目录是正确的。但要注意音频格式。Asterisk默认支持.wav.gsm等。确保你的chatgpt-welcome.wav是Asterisk兼容的格式通常为8kHz或16kHz采样率16位PCM mono。你可以用sox或audacity工具进行转换。# 检查Asterisk声音目录可能不止一个 ls /var/lib/asterisk/sounds/ ls /usr/share/asterisk/sounds/ # 复制文件假设使用/usr/share/asterisk/sounds/ sudo cp chatgpt-welcome.wav /usr/share/asterisk/sounds/ # 确保Asterisk的运行用户通常是asterisk有读取权限 sudo chown asterisk:asterisk /usr/share/asterisk/sounds/chatgpt-welcome.wav4. Asterisk拨号计划配置精讲4.1 拨号计划上下文与分机配置Asterisk的拨号计划是其核心逻辑所在文件通常是/etc/asterisk/extensions.conf。我们需要在合适的上下文Context中定义一个分机Extension来触发我们的AGI脚本。原示例给出了一个非常简单的配置exten 31532401205,1,Noop(ChatGPT) same n,answer() same n,AGI(/usr/local/src/chatgpt-agi/venv/bin/python3 /usr/local/src/chatgpt-agi/openai_agi.py)我们来逐行拆解exten 31532401205,1,Noop(ChatGPT)定义分机号31532401205优先级为1执行Noop应用一个空操作仅用于在日志中打印注释“ChatGPT”。same n,answer()same表示同一个分机n表示下一个优先级这里是2。answer()应用用于接听来电。重要对于PSTN或SIP来电通常需要先answer()才能进行后续的媒体操作如播放声音。same n,AGI(...)优先级3执行AGI()应用启动我们的外部脚本。这里提供了Python解释器和脚本的绝对路径。配置要点与扩展分机号31532401205看起来像一个完整的电话号码。在真实场景中你可能是在一个SIP中继上接收呼叫然后通过DID直接呼入号码映射到内部的分机或上下文。更常见的配置可能是定义一个短号比如6666用于内部测试。exten 6666,1,Noop(ChatGPT Test Line) same n,Answer() same n,AGI(/usr/local/src/chatgpt-agi/run_agi.sh) ; 使用我们封装的启动脚本上下文确保这个分机定义在正确的上下文中。例如你可能有一个[incoming-calls]的上下文来处理所有外来呼叫或者[internal]上下文处理内部分机互拨。你需要根据你的网络拓扑来调整。超时与挂机处理当前的配置一旦AGI脚本启动控制权就完全交给了脚本直到脚本结束用户挂机。脚本内部需要处理好超时和挂机检测。Asterisk在通道挂断时会向AGI脚本的stdin发送一个以HANGUP开头的行脚本收到后应该主动退出。4.2 拨号计划重载与测试配置修改后必须重载拨号计划使其生效。# 连接到Asterisk CLI asterisk -rvvv # 在CLI中执行重载命令 dialplan reload # 或者直接用命令行 asterisk -rx “dialplan reload“测试配置是否正确有一个非常实用的命令dialplan show可以在CLI中查看特定上下文的拨号计划asterisk -rx “dialplan show internal“ # 查看internal上下文的拨号计划你应该能看到你刚添加的分机配置。5. AGI脚本核心代码剖析与增强原项目的脚本可能是一个骨架。为了让它真正工作起来我们需要填充语音识别和语音合成的部分。下面我将构建一个更完整的openai_agi.py示例并解释关键部分。5.1 AGI通信基础框架首先脚本需要处理与Asterisk的AGI协议通信。我们可以使用Python的sys.stdin和sys.stdout。#!/usr/bin/env python3 import sys import os import openai from datetime import datetime import subprocess import tempfile import json # 初始化AGI通信 def read_agi_env(): env {} while True: line sys.stdin.readline().strip() if line ‘’: break key, value line.split(‘:‘, 1) env[key] value.strip() return env def send_agi_command(command): sys.stdout.write(command ‘\n‘) sys.stdout.flush() response sys.stdin.readline().strip() return response # 读取Asterisk传递的环境变量 agi_env read_agi_env() # agi_env 现在包含如 ‘agi_request‘: ‘agi://localhost‘, ‘agi_channel‘: ‘SIP/101-00000001‘, ‘agi_callerid‘: ‘1234567890‘ 等信息 # 设置OpenAI API Key (从环境变量获取) openai.api_key os.getenv(“OPENAI_API_KEY“) if not openai.api_key: send_agi_command(‘VERBOSE “ERROR: OPENAI_API_KEY not set” 1‘) sys.exit(1) # 初始化对话历史 conversation_history [ {“role“: “system“, “content“: “You are a helpful and friendly AI assistant on a phone call. Keep your responses concise and suitable for spoken conversation.“} ]5.2 实现语音转文本模块这里以使用OpenAI Whisper API为例需要额外付费但识别质量高与ChatGPT同属一家集成方便。你需要安装openai库并确保API Key有Whisper的权限。def speech_to_text(audio_file_path): “““调用Whisper API将音频文件转换为文本“““ try: with open(audio_file_path, “rb“) as audio_file: transcript openai.Audio.transcribe( model“whisper-1“, fileaudio_file, language“zh“ # 指定语言例如中文。可选不指定会自动检测。 ) return transcript[“text“].strip() except Exception as e: send_agi_command(f‘VERBOSE “STT Error: {e}” 1‘) return None替代方案如果你希望使用免费或本地方案可以考虑Vosk离线开源语音识别工具包支持多种语言准确度不错。需要下载模型文件。Google Cloud Speech-to-Text通过其Python客户端库调用有免费额度。本地部署Whisper使用openai-whisper库非API但需要较强的CPU/GPU资源。5.3 实现与ChatGPT的交互这部分是核心逻辑负责管理对话历史并获取AI回复。def get_chatgpt_response(user_input, history): “““调用ChatGPT API传入整个对话历史获取回复“““ # 将用户输入追加到历史 history.append({“role“: “user“, “content“: user_input}) try: response openai.ChatCompletion.create( model“gpt-3.5-turbo“, # 或 “gpt-4“根据需求和成本选择 messageshistory, temperature0.7, max_tokens500 # 限制回复长度适合语音输出 ) assistant_reply response.choices[0].message[“content“] # 将AI回复追加到历史 history.append({“role“: “assistant“, “content“: assistant_reply}) return assistant_reply, history except Exception as e: send_agi_command(f‘VERBOSE “ChatGPT API Error: {e}” 1‘) return None, history5.4 实现文本转语音模块这里以使用Google Text-to-Speech为例它是免费的且音质可以接受。需要安装gtts库。# 在虚拟环境中安装 pip3 install gttsfrom gtts import gTTS import os def text_to_speech(text, language‘zh-cn‘): “““使用gTTS将文本转换为语音保存为临时文件返回文件路径“““ try: # 创建临时文件 with tempfile.NamedTemporaryFile(suffix“.mp3“, deleteFalse) as tmp_audio: temp_audio_path tmp_audio.name # 生成语音 tts gTTS(texttext, langlanguage, slowFalse) tts.save(temp_audio_path) # gTTS生成的是mp3Asterisk可能更偏好wav或gsm。需要转换。 # 这里我们假设Asterisk支持mp3需要编译时启用mp3支持。 # 更稳妥的做法是转换为wav。 wav_path temp_audio_path.replace(‘.mp3‘, ‘.wav‘) # 使用sox或ffmpeg转换需要系统已安装这些工具 # subprocess.run([‘ffmpeg‘, ‘-i‘, temp_audio_path, ‘-ar‘, ‘8000‘, ‘-ac‘, ‘1‘, wav_path], checkTrue) # os.unlink(temp_audio_path) # return wav_path # 为简单起见这里直接返回mp3路径如果Asterisk支持 return temp_audio_path except Exception as e: send_agi_command(f‘VERBOSE “TTS Error: {e}” 1‘) return None重要提示音频格式兼容性是语音项目中最常见的坑之一。Asterisk对音频格式有特定要求。最通用的格式是8kHz或16kHz采样率、单声道、16位PCM的WAV文件.wav。gTTS默认输出MP3所以必须进行转码。你需要确保服务器上安装了ffmpeg或sox并在代码中调用它们进行转换。5.5 主循环逻辑整合现在我们将所有模块整合到主循环中。def main(): # 1. 播放欢迎语 send_agi_command(‘STREAM FILE “chatgpt-welcome“ “”‘) # 第二个参数是中断按键这里为空 # 进入主对话循环 while True: # 2. 录音 # 生成一个唯一的临时文件名 with tempfile.NamedTemporaryFile(suffix“.wav“, deleteFalse) as tmp: record_file tmp.name # AGI RECORD FILE命令参数文件名 格式 中断按键 超时(ms) 静音时长(ms) # 格式‘wav‘代表slin16位线性PCM这是Asterisk原生支持的格式。 record_result send_agi_command(f‘RECORD FILE {record_file} wav # 10000 2000‘) # record_result 格式如 ‘200 result0 endpos6400‘ # 如果用户按了#键我们设置的interrupt digitresult可能不为0或者我们可以检查endpos。 # 检查录音是否成功例如result0表示成功-1表示挂机或错误 if ‘result0‘ not in record_result: # 可能是用户挂机或超时 send_agi_command(‘VERBOSE “Recording ended or hangup detected.” 1‘) # 清理临时文件 if os.path.exists(record_file): os.unlink(record_file) break # 3. 语音转文本 user_text speech_to_text(record_file) os.unlink(record_file) # 删除临时录音文件 if not user_text: send_agi_command(‘STREAM FILE “invalid“ “”‘) # 可以播放一个“抱歉没听清”的提示音 continue send_agi_command(f‘VERBOSE “User said: {user_text}” 3‘) # 在Asterisk CLI中输出日志级别3 # 4. 调用ChatGPT ai_response, conversation_history get_chatgpt_response(user_text, conversation_history) if not ai_response: send_agi_command(‘STREAM FILE “error“ “”‘) continue send_agi_command(f‘VERBOSE “AI Response: {ai_response}” 3‘) # 5. 文本转语音并播放 tts_audio_path text_to_speech(ai_response, ‘zh-cn‘) if tts_audio_path and os.path.exists(tts_audio_path): # 提取文件名不含路径给STREAM FILE命令 audio_filename os.path.basename(tts_audio_path) # 去掉后缀因为STREAM FILE不需要后缀 audio_basename os.path.splitext(audio_filename)[0] send_agi_command(f‘STREAM FILE {audio_basename} “”‘) os.unlink(tts_audio_path) # 删除临时TTS文件 else: send_agi_command(‘STREAM FILE “error“ “”‘) # 循环结束脚本退出 send_agi_command(‘VERBOSE “AGI Script Finished.” 1‘) if __name__ “__main__“: main()6. 高级配置、优化与故障排查6.1 性能优化与稳定性考量音频处理优化录音参数RECORD FILE命令中的10000超时10秒和2000静音2秒是示例值。根据实际场景调整。静音检测太短容易截断用户说话太长则响应迟钝。音频格式统一在整个管道中尽量使用同一种Asterisk原生支持的格式如slin16-bit linear PCM。在录音时指定wav格式在TTS后也转换为相同的格式可以避免不必要的编解码开销和兼容性问题。临时文件管理确保所有生成的临时音频文件在使用后都被正确删除防止磁盘空间被占满。API调用优化超时与重试网络请求必须设置合理的超时时间并对可重试的错误如网络抖动实现重试机制。上下文长度管理对话历史会不断增长无限制地发送给ChatGPT会导致API调用成本增加和响应变慢。可以设定一个最大token数或轮次数丢弃最早的对话历史。异步处理对于STT和TTS这种可能耗时的I/O操作可以考虑使用异步库如aiohttp,asyncio来避免阻塞主循环提升并发处理能力如果有多路通话。但AGI协议本身是同步的需要谨慎设计。错误处理与降级STT失败如果语音识别失败可以播放提示音“抱歉我没有听清请再说一遍”然后重新录音而不是直接挂断。ChatGPT API失败可以准备一些默认回复如“我现在有点忙请稍后再试”或者切换到更简单的规则引擎。TTS失败可以直接播放一个“系统错误”的提示音或者尝试用简单的Festival等本地TTS引擎作为备用。6.2 安全与权限配置脚本执行权限确保AGI脚本文件有执行权限并且Asterisk的运行用户通常是asterisk有权限读取和执行它。sudo chmod x /usr/local/src/chatgpt-agi/openai_agi.py sudo chown -R asterisk:asterisk /usr/local/src/chatgpt-agi/网络访问如果脚本需要访问外部APIOpenAI, Google Cloud等确保服务器防火墙或安全组允许出站HTTPS流量。API密钥安全如前所述使用环境变量或安全配置文件并限制其读取权限。输入验证虽然来自电话的输入相对可控但理论上用户可以说任何内容。传递给ChatGPT的提示词System Prompt应明确其身份和边界避免产生不当内容。可以在System Prompt中加入“你是一个电话助手请提供友好、专业、安全的回答拒绝回答任何涉及敏感、违法或有害信息的问题。”6.3 系统化故障排查指南当你的AGI脚本不工作时按照以下步骤排查可以节省大量时间。第一步检查Asterisk CLI日志这是最直接有效的方法。以高详细级别启动CLIasterisk -rvvv然后拨打测试分机。在CLI中你会看到详细的执行流程是否进入了正确的分机和上下文Answer()是否成功AGI()命令是否执行有没有报错如“Failed to execute”AGI脚本启动后是否有VERBOSE日志输出我们在代码中插入了VERBOSE命令。第二步检查AGI脚本自身的日志我们的脚本将错误信息通过VERBOSE写回了Asterisk。同时也可以让脚本将更详细的日志写入一个文件。import logging logging.basicConfig(filename‘/var/log/asterisk/chatgpt_agi.log‘, levellogging.DEBUG)记得确保Asterisk用户有权限写入该日志文件。第三步检查系统日志如果Asterisk本身崩溃或脚本启动失败查看系统日志# 对于systemd系统 journalctl -fu asterisk # 或直接查看Asterisk日志文件 tail -f /var/log/asterisk/full第四步独立测试AGI脚本在Shell中手动模拟Asterisk调用AGI脚本可以快速定位脚本本身的语法错误或导入问题。cd /usr/local/src/chatgpt-agi sudo -u asterisk ./venv/bin/python3 openai_agi.py注意使用sudo -u asterisk来以Asterisk用户的身份运行避免权限问题。第五步检查音频文件与格式确认欢迎语音文件chatgpt-welcome.wav存在于正确的目录且权限正确。使用soxi命令检查音频文件格式sudo apt-get install sox # 安装sox soxi /usr/share/asterisk/sounds/chatgpt-welcome.wav查看输出确保是Asterisk支持的格式如Channels: 1,Sample Rate: 8000。第六步网络与API连通性在服务器上使用curl测试是否能访问外部API端点。curl https://api.openai.com/v1/models -H “Authorization: Bearer $OPENAI_API_KEY“如果使用gTTS测试是否能正常访问Google的服务。下面是一个常见问题速查表你可以对照症状快速定位可能的原因症状可能原因排查步骤拨打分机无反应直接忙音拨号计划配置错误分机未定义或不在当前上下文。1.asterisk -rx “dialplan show”检查配置。2. 在CLI中拨打时观察日志。电话接通后立即挂断Answer()应用执行失败SIP通道媒体问题AGI脚本立即崩溃。1. CLI查看Answer()是否报错。2. 检查SIP的canreinvite、directmedia等设置。3. 独立运行AGI脚本看是否报错。能听到欢迎语但之后没反应AGI脚本主循环卡住或崩溃录音命令失败。1. CLI查看RECORD FILE命令的返回结果。2. 检查脚本日志文件。3. 检查临时目录权限/tmp。能录音但AI不回复STT或ChatGPT API调用失败网络问题API密钥无效。1. 查看脚本中的VERBOSE日志确认User said:后面是否有内容。2. 在服务器上手动运行脚本的speech_to_text函数测试。3. 检查API密钥和网络连通性。AI回复是文字但听不到声音TTS失败生成的音频格式Asterisk不支持STREAM FILE命令路径错误。1. 检查text_to_speech函数是否成功生成文件。2. 用soxi检查生成的音频文件格式。3. CLI查看STREAM FILE命令是否报错“File not found”。对话一轮后挂断脚本主循环逻辑错误可能在一次循环后退出检测到挂机信号。1. 检查while True循环的条件和内部逻辑。2. 检查RECORD FILE的返回结果判断是否错误地将正常结束当作挂机。声音卡顿或延迟很大网络延迟高API调用慢服务器性能不足音频编解码转换耗时。1. 测量各API调用的响应时间。2. 检查服务器CPU/内存使用率。3. 尝试使用更低采样率的音频如8kHz。7. 扩展思路与应用场景这个POC项目只是一个起点。在此基础上你可以进行大量扩展打造更强大、更实用的系统多模态交互结合DTMF电话按键。例如用户可以说“转人工客服”或者按“0”键转人工。在AGI脚本中可以使用WAIT FOR DIGIT命令来接收按键实现混合交互。集成企业知识库在调用ChatGPT前先根据用户问题查询本地知识库或数据库将检索到的信息作为上下文提供给ChatGPT使其回答更精准。这就是RAG在语音交互中的应用。通话分析与记录将每一通电话的录音、识别文本、AI回复都保存到数据库用于后续的质量分析、模型优化或合规审计。与CRM/工单系统集成当AI无法解决用户问题需要转人工时可以将本通电话的对话历史、用户信息自动推送给客服坐席的弹屏实现无缝衔接。动态语音选择根据对话内容的情感或紧急程度切换不同的TTS语音或语速。支持多种语言通过语音识别结果自动判断语言然后调用对应语言的ChatGPT模型和TTS引擎实现多语言智能客服。这个项目完美地展示了如何将看似古老的电信技术与最前沿的AI能力相结合。它不需要昂贵的专用硬件或复杂的SDK利用开源软件和云服务就能搭建出一个可用的原型。无论是用于教育演示、内部工具开发还是作为更复杂产品的技术验证这套方案都提供了坚实的基础和广阔的想象空间。