1. 项目概述:当AI能听懂你的话并为你做事

最近在捣鼓一个挺有意思的东西:一个能完全用语音控制的AI智能体。想象一下,你只需要对着麦克风说句话,比如“帮我查一下明天上午十点的会议安排,然后给团队发个提醒”,它就能自动理解你的意图,执行一连串的操作。这听起来像是科幻电影里的场景,但现在,用AssemblyAI和Groq这两个工具,我们完全可以在自己的电脑上把它搭建出来。

这个项目的核心,就是让机器不仅能“听见”你说的话,更要“听懂”你的意思,并“思考”如何行动。AssemblyAI负责前端的语音识别,把你说的话精准地转成文字;Groq则扮演后端的大脑,它是一个能高速运行大语言模型的推理平台,负责理解文字指令、规划任务步骤并生成执行代码或调用API。两者的结合,相当于给AI装上了“耳朵”和“大脑”,让它能真正响应你的语音命令,成为一个实用的个人助理原型。

无论你是对语音AI感兴趣的开发者,想为自己的智能家居项目增加自然交互能力,还是希望探索AI智能体(Agent)的自动化潜力,这个项目都是一个绝佳的起点。它不涉及复杂的硬件,主要考验的是你对API调用、任务编排和提示词工程的理解。接下来,我会带你一步步拆解这个项目的核心思路、技术选型背后的考量,并分享从零搭建到优化落地的完整过程,以及我踩过的那些坑。

2. 核心架构与工具选型解析

2.1 为什么是AssemblyAI + Groq?

市面上语音识别和AI推理的选项不少,比如OpenAI的Whisper API和ChatGPT API组合似乎更“全家桶”。但我选择AssemblyAI和Groq,是基于几个非常实际的考量。

首先, AssemblyAI在语音转文字的准确性和功能丰富度上表现突出 。它不仅仅是简单转写,还提供了说话人分离(谁在说话)、情感分析、内容总结等高级功能。对于构建一个语音控制Agent来说,清晰的说话人分离能避免多人对话的指令混淆,而情感分析或许能在未来用于判断用户指令的紧急程度。更重要的是,它的API设计非常清晰,响应速度快,对于实时或准实时的语音交互场景至关重要。相比之下,虽然Whisper开源且强大,但在生产环境的API稳定性、附加功能和易用性上,AssemblyAI提供了更省心的选择。

其次, Groq的核心优势在于其惊人的推理速度 。它使用独特的LPU(语言处理单元)推理引擎,运行一些开源大模型(如Llama、Mixtral)时,速度可比传统GPU快一个数量级。对于语音交互的Agent,低延迟是用户体验的生命线。你绝不想说完指令后,等上好几秒才有反应。Groq的高速响应能力,使得“语音输入-思考-行动输出”这个闭环能够近乎实时地完成,感觉更像是在和一个真正敏捷的助手对话。虽然Groq的模型可能不是参数最大的,但在速度与效果的平衡上,它非常适合需要快速响应的交互式应用。

最后,这个组合也体现了 “专精工具链” 的思路。AssemblyAI专注做好“听”(语音识别),Groq专注做好“想”(推理与规划),我们作为开发者则专注于“做”(业务逻辑与任务编排)。这种解耦让系统更健壮,也便于未来单独升级某一环节。

2.2 系统工作流设计

整个智能体的工作流可以清晰地分为四个阶段,形成一个完整的闭环:

  1. 语音捕获与预处理 :通过电脑或设备的麦克风实时采集用户的语音流。这里需要注意音频质量,过大的背景噪音会影响识别精度。通常我们会添加一个简单的VAD(语音活动检测)模块,只在检测到人声时才将音频流发送给后端,节省资源。
  2. 语音转文本(AssemblyAI) :将预处理后的音频数据(通常是WAV或MP3格式)通过HTTP请求发送到AssemblyAI的转录API。这里的关键是选择正确的模型和参数。对于实时性要求高的场景,可以使用其流式转录API,实现边说边转;对于指令性语音,准确度优先,则使用标准的异步转录API即可。API返回的结构化JSON结果中,就包含了我们需要的文字指令。
  3. 意图理解与任务规划(Groq) :这是智能体的“大脑”。我们将AssemblyAI返回的文本指令,连同我们定义好的系统提示词(System Prompt),一起发送给Groq的聊天补全API。系统提示词至关重要,它定义了AI的角色、能力范围和输出格式。例如,我们会告诉AI:“你是一个语音控制助手,可以执行查询天气、发送邮件、创建日历事件等任务。用户会用自然语言发出指令,你需要:1. 理解用户意图;2. 将复杂指令拆解为步骤;3. 对于可自动化的步骤,生成对应的Python函数调用代码或API请求参数。”
  4. 任务执行与反馈 :Groq返回的结果通常是一段结构化的文本或代码。我们的主程序需要解析这个结果。如果结果是代码(比如调用 requests.get() 查询天气),则在一个安全的沙箱环境中执行它;如果结果是需要人工确认的参数(比如邮件的收件人和内容),则将其格式化后显示给用户,并等待语音确认。最后,将任务执行的结果(成功、失败、或需要的信息)通过文本转语音(TTS)模块播报出来,完成交互。

这个流程中,步骤3和4是核心难点,涉及到如何让大语言模型稳定地输出可执行的行动计划,这很大程度上依赖于提示词工程和输出格式的约束。

3. 环境搭建与核心依赖配置

3.1 基础环境准备

这个项目对编程语言没有强制要求,Python因其在AI和自动化领域的丰富生态而成为首选。你需要准备:

  • Python 3.8+ :确保你的Python环境已就绪。
  • API密钥
    • AssemblyAI :去其官网注册账号,在控制台可以找到你的API密钥。它通常提供一定的免费额度,足够用于开发和测试。
    • Groq :同样,去Groq Cloud官网注册,获取API密钥。关注其提供的免费模型和速率限制。
  • 必要的Python库 :我们将通过 pip 安装以下核心库。
pip install assemblyai groq python-dotenv requests pyttsx3 pyaudio

简单解释一下这些库的作用:

  • assemblyai groq :官方的Python SDK,封装了API调用,比手动发HTTP请求更方便。
  • python-dotenv :用于管理环境变量,安全地存储你的API密钥,避免硬编码在代码中。
  • requests :用于执行AI规划出的HTTP请求任务(如调用天气API)。
  • pyttsx3 :一个离线的文本转语音库,用于将AI的回复或执行结果读出来。选择它是因为无需额外API密钥,虽然音质不如一些云服务,但用于原型快速验证足够了。
  • pyaudio :用于从麦克风捕获音频流。

注意 pyaudio 在某些系统上安装可能比较麻烦。如果遇到问题,可以尝试先安装 portaudio 库(macOS: brew install portaudio ;Linux: sudo apt-get install portaudio19-dev ),然后再安装 pyaudio

3.2 项目结构与配置管理

一个好的项目结构能让代码更清晰。建议创建如下目录和文件:

voice_ai_agent/
├── .env                    # 存储敏感信息(API密钥)
├── main.py                 # 主程序入口
├── config.py               # 配置文件,读取环境变量
├── audio_handler.py        # 语音录制和播放模块
├── stt_engine.py           # 语音转文本模块(封装AssemblyAI)
├── ai_planner.py           # AI规划与推理模块(封装Groq)
├── task_executor.py        # 任务执行模块
└── skills/                 # 技能库目录
    ├── __init__.py
    ├── weather.py          # 查询天气技能
    ├── calendar.py         # 日历事件技能(示例)
    └── web_search.py       # 网络搜索技能(示例)

.env 文件中,这样配置你的密钥:

ASSEMBLYAI_API_KEY=your_assemblyai_api_key_here
GROQ_API_KEY=your_groq_api_key_here

config.py 中,安全地加载它们:

import os
from dotenv import load_dotenv

load_dotenv()

ASSEMBLYAI_API_KEY = os.getenv("ASSEMBLYAI_API_KEY")
GROQ_API_KEY = os.getenv("GROQ_API_KEY")

if not ASSEMBLYAI_API_KEY or not GROQ_API_KEY:
    raise ValueError("请检查 .env 文件中的 ASSEMBLYAI_API_KEY 和 GROQ_API_KEY 配置")

这种将配置、音频处理、AI逻辑、任务执行分离的方式,符合单一职责原则,调试和扩展都更方便。

4. 核心模块实现详解

4.1 语音捕获与转写模块实现

首先实现 audio_handler.py 。这里我们使用 pyaudio 录制音频,并保存为AssemblyAI支持的格式(如WAV)。

import pyaudio
import wave
import threading
import time

class AudioRecorder:
    def __init__(self, chunk=1024, format=pyaudio.paInt16, channels=1, rate=16000):
        self.CHUNK = chunk
        self.FORMAT = format
        self.CHANNELS = channels
        self.RATE = rate
        self.frames = []
        self.is_recording = False
        self.p = pyaudio.PyAudio()
        self.stream = None

    def start_recording(self):
        """开始录制音频"""
        self.is_recording = True
        self.frames = []
        self.stream = self.p.open(format=self.FORMAT,
                                  channels=self.CHANNELS,
                                  rate=self.RATE,
                                  input=True,
                                  frames_per_buffer=self.CHUNK)
        print("录音开始... (按Ctrl+C停止)")
        threading.Thread(target=self._record).start()

    def _record(self):
        """内部录音循环"""
        while self.is_recording:
            data = self.stream.read(self.CHUNK, exception_on_overflow=False)
            self.frames.append(data)

    def stop_and_save(self, filename="output.wav"):
        """停止录音并保存为WAV文件"""
        self.is_recording = False
        if self.stream:
            self.stream.stop_stream()
            self.stream.close()
        # 保存文件
        wf = wave.open(filename, 'wb')
        wf.setnchannels(self.CHANNELS)
        wf.setsampwidth(self.p.get_sample_size(self.FORMAT))
        wf.setframerate(self.RATE)
        wf.writeframes(b''.join(self.frames))
        wf.close()
        print(f"录音已保存至: {filename}")
        return filename

接下来,在 stt_engine.py 中实现与AssemblyAI的交互:

import assemblyai as aai
from config import ASSEMBLYAI_API_KEY

class SpeechToTextEngine:
    def __init__(self):
        aai.settings.api_key = ASSEMBLYAI_API_KEY
        self.transcriber = aai.Transcriber()

    def transcribe_file(self, audio_file_path):
        """转录本地音频文件"""
        try:
            transcript = self.transcriber.transcribe(audio_file_path)
            if transcript.status == aai.TranscriptStatus.error:
                print(f"转录错误: {transcript.error}")
                return None
            return transcript.text
        except Exception as e:
            print(f"调用AssemblyAI API时出错: {e}")
            return None

    # 如果需要实时流式转录,可以添加以下方法
    # def transcribe_stream(self, audio_stream):
    #     ...

实操心得 :在实际测试中,我发现背景噪音对识别准确率影响很大。即使是安静的办公室,键盘声也可能被误识别为词语。后来我做了两处改进:一是在录音时增加一个简单的音量阈值过滤,低于阈值的帧视为静音丢弃;二是在发送给AssemblyAI时,启用其 audio_enhancer 参数( config = aai.TranscriptionConfig(audio_enhancer=True) ),这对提升嘈杂环境下的识别率有帮助。

4.2 AI规划与推理模块实现

这是项目的“大脑”, ai_planner.py 。我们需要精心设计给Groq的提示词(Prompt),让它扮演一个任务规划师。

from groq import Groq
from config import GROQ_API_KEY
import json

class AIPlanner:
    def __init__(self, model="mixtral-8x7b-32768"): # Groq上可用的高速模型
        self.client = Groq(api_key=GROQ_API_KEY)
        self.model = model
        self.system_prompt = """你是一个高级语音控制AI助手。你的职责是理解用户的自然语言指令,并将其转化为可执行的任务计划。

        你可以调用的技能(工具)包括:
        1. get_weather(city: str) -> str: 获取指定城市的当前天气。
        2. send_email(to: str, subject: str, body: str) -> str: 发送电子邮件(需要后续确认)。
        3. create_calendar_event(title: str, start_time: str, duration_minutes: int) -> str: 创建日历事件。
        4. search_web(query: str) -> str: 在互联网上搜索信息并返回摘要。
        5. calculate(expression: str) -> str: 执行数学计算。

        用户指令可能是简单的(如“北京天气怎么样?”)或复杂的(如“查一下北京天气,如果下雨就提醒我带伞,并推迟下午的会议”)。

        你必须以以下JSON格式回应,且只输出这个JSON对象:
        {
            "intent": "对用户意图的简短总结",
            "is_complex": true/false,
            "plan": ["步骤1的描述", "步骤2的描述", ...],
            "immediate_action": {
                "skill": "要调用的技能名称,如 get_weather",
                "parameters": {"参数名": "参数值"},
                "code_snippet": "可选的、直接执行的Python代码片段(仅当技能很简单时)"
            },
            "need_human_confirm": true/false,
            "confirm_prompt": "需要用户确认时,对用户说的话"
        }

        规则:
        - 如果指令简单且可直接执行(如查询天气),`immediate_action`必须填充,`need_human_confirm`通常为false。
        - 如果指令复杂,`plan`要详细列出步骤,`immediate_action`可能是第一步,`need_human_confirm`可能为true。
        - 如果指令涉及敏感操作(如发邮件),`need_human_confirm`必须为true,并在`confirm_prompt`中清晰列出待确认信息。
        - 如果你无法理解指令,或要求执行不支持的技能,`immediate_action`为null,并在`intent`中说明。
        """

    def plan(self, user_input):
        """接收用户文本指令,返回AI规划的结果"""
        try:
            completion = self.client.chat.completions.create(
                model=self.model,
                messages=[
                    {"role": "system", "content": self.system_prompt},
                    {"role": "user", "content": user_input}
                ],
                temperature=0.1, # 低温度使输出更确定、更遵循格式
                max_tokens=1024
            )
            response_text = completion.choices[0].message.content
            # 尝试解析JSON
            return json.loads(response_text.strip())
        except json.JSONDecodeError as e:
            print(f"AI返回的响应不是有效的JSON: {response_text}")
            print(f"错误: {e}")
            return {"error": "AI响应解析失败", "raw_response": response_text}
        except Exception as e:
            print(f"调用Groq API时出错: {e}")
            return None

注意事项 :提示词工程是这里的灵魂。 temperature 参数设置为较低值(如0.1),是为了让模型输出更稳定、更可预测的JSON格式。最初我用默认值0.7,经常得到格式混乱或随意发挥的文本,导致后续解析崩溃。另外,在系统提示词中明确列出可用技能和输出格式,相当于给AI划定了行动边界,这是让AI Agent行为可控的关键。

4.3 任务执行器与技能库实现

task_executor.py 负责解析AI的规划结果,并调用具体的技能函数。

import importlib
import subprocess
import sys
from skills import weather, web_search # 导入技能模块

class TaskExecutor:
    def __init__(self):
        self.skills = {
            "get_weather": weather.get_weather,
            "search_web": web_search.search_web,
            # ... 注册其他技能函数
        }

    def execute(self, plan_result):
        """执行AI规划出的动作"""
        if not plan_result or "error" in plan_result:
            return "抱歉,我无法处理这个指令。"

        intent = plan_result.get("intent", "")
        action = plan_result.get("immediate_action")

        if not action:
            # 如果没有立即动作,可能是复杂计划或无法处理
            if plan_result.get("need_human_confirm"):
                return plan_result.get("confirm_prompt", "请确认此操作。")
            else:
                return f"我理解你想{intent}。这是一个多步骤任务,我的计划是:{';'.join(plan_result.get('plan', []))}。请告诉我是否继续?"

        skill_name = action.get("skill")
        params = action.get("parameters", {})

        if skill_name in self.skills:
            try:
                # 调用对应的技能函数
                result = self.skills[skill_name](**params)
                return f"已完成。结果:{result}"
            except Exception as e:
                return f"执行技能'{skill_name}'时出错:{e}"
        elif action.get("code_snippet"):
            # 警告:直接执行AI生成的代码有安全风险!仅用于演示或受控环境。
            try:
                # 一种相对安全的方式:在子进程中用受限环境执行
                # 这里简化处理,实际生产环境需要沙箱
                print(f"警告:正在执行AI生成的代码片段:{action['code_snippet']}")
                # 此处应替换为安全的沙箱执行逻辑
                return "此操作需要执行代码,出于安全考虑,请在确认后手动执行。"
            except Exception as e:
                return f"代码执行出错:{e}"
        else:
            return f"未知的技能:{skill_name}"

# 示例技能:weather.py
import requests
def get_weather(city):
    """调用公开天气API示例(此处使用假想API)"""
    # 实际使用时,请替换为真实的天气API,如OpenWeatherMap
    api_url = f"https://api.example.com/weather?city={city}&units=metric"
    try:
        response = requests.get(api_url, timeout=5)
        data = response.json()
        # 解析数据...
        temp = data.get('main', {}).get('temp', '未知')
        desc = data.get('weather', [{}])[0].get('description', '未知')
        return f"{city}当前天气:{desc},温度{temp}摄氏度。"
    except requests.RequestException:
        return f"无法获取{city}的天气信息。"

安全警告 :让AI生成并执行代码是极其危险的操作!上面的示例中,我虽然留了 code_snippet 的解析,但在生产环境中, 绝对不要 轻易在主机环境中执行未经严格审查的AI生成代码。正确做法是:要么完全禁止代码执行,只允许调用预定义的安全API;要么使用 Docker 容器、沙箱环境进行完全隔离的执行,并对代码内容进行严格的安全扫描和限制(如禁用 import os, sys, subprocess 等)。

4.4 主循环与TTS反馈集成

最后,在 main.py 中将所有模块串联起来,形成交互循环。

import time
from audio_handler import AudioRecorder
from stt_engine import SpeechToTextEngine
from ai_planner import AIPlanner
from task_executor import TaskExecutor
import pyttsx3

class VoiceAIAgent:
    def __init__(self):
        self.recorder = AudioRecorder()
        self.stt_engine = SpeechToTextEngine()
        self.planner = AIPlanner()
        self.executor = TaskExecutor()
        self.tts_engine = pyttsx3.init()
        self.tts_engine.setProperty('rate', 150) # 设置语速

    def speak(self, text):
        """文本转语音"""
        print(f"AI: {text}")
        self.tts_engine.say(text)
        self.tts_engine.runAndWait()

    def run(self):
        """主运行循环"""
        self.speak("语音助手已启动,请说出您的指令。")
        while True:
            input("按回车键开始录音... (或输入 'q' 退出)")
            user_input = input().strip().lower()
            if user_input == 'q':
                break

            # 1. 录音
            self.recorder.start_recording()
            input("正在录音... 按回车键停止。")
            audio_file = self.recorder.stop_and_save()

            # 2. 语音转文字
            self.speak("正在处理您的语音...")
            text_command = self.stt_engine.transcribe_file(audio_file)
            if not text_command:
                self.speak("抱歉,我没有听清,请再说一遍。")
                continue
            print(f"您说: {text_command}")

            # 3. AI规划
            self.speak("正在思考...")
            plan = self.planner.plan(text_command)

            # 4. 执行与反馈
            result = self.executor.execute(plan)
            self.speak(result)

if __name__ == "__main__":
    agent = VoiceAIAgent()
    try:
        agent.run()
    except KeyboardInterrupt:
        print("\n程序退出。")

这个主循环实现了基本的“录音-识别-思考-执行-反馈”流程。你可以通过按回车来控制录音的起止,适合演示和调试。在实际产品中,需要替换为更智能的VAD来自动检测语音起止。

5. 进阶优化与实战技巧

5.1 提升语音交互的流畅度

基础的流程跑通后,你会发现几个影响体验的问题:录音需要手动控制、TTS播报时无法接收新指令、复杂任务处理生硬。以下是优化方向:

  • 实现全自动的语音端点检测(VAD) :使用像 webrtcvad 这样的库,可以实时判断音频流中何时有人声开始和结束,实现“即说即停”,无需手动按键。这能极大提升交互的自然感。
  • 引入对话状态管理 :当前的Agent是“单轮对话”,执行完就忘记上下文。你需要维护一个对话历史列表,在每次调用Groq API时,将之前的对话记录也作为上下文传入。这样就能处理像“今天天气怎么样?”(回答后)“那明天呢?”这样的连续追问。
  • 优化TTS与STT的并发 :使用多线程或异步编程。当TTS在播报结果时,可以在另一个线程中持续监听是否有用户打断(例如说“停”),这需要更精细的音频流管理。

5.2 设计更强大的技能系统与安全边界

简单的函数调用式技能很快会遇到瓶颈。你需要一个更健壮的技能系统:

  • 技能描述与自动发现 :为每个技能编写一个标准的描述文件(如JSON Schema),说明其功能、输入参数、输出格式。AI Planner在初始化时,可以动态读取这些描述来构建系统提示词。这样,新增技能只需添加一个文件,无需修改核心提示词。
  • 工具调用(Function Calling)标准化 :与其让AI输出自定义的JSON,不如适配OpenAI标准的工具调用格式。虽然Groq的API可能不完全原生支持,但你可以通过提示词引导AI输出相同结构的JSON,然后在执行端做映射。这能让技能调用更规范。
  • 严格的安全沙箱 :对于任何涉及外部操作(如文件读写、网络请求、系统命令)的技能,必须在严格的沙箱中执行。可以使用 docker-py 在隔离的Docker容器中运行代码,或者使用 restrictedpython 等工具限制可用的Python内置函数和模块。

5.3 处理复杂指令与任务分解

当用户说“帮我查天气,如果下雨就取消下午的公园行程并提醒我带伞”时,AI需要将其分解为:1. 查询天气;2. 判断是否下雨;3a. 如果下雨,执行取消行程(调用日历API)和设置提醒(调用通知API);3b. 如果不下雨,可能什么都不做或给出其他建议。

这要求你的系统提示词需要引导AI进行条件判断和步骤规划。一个更高级的架构是引入“工作流引擎”的概念。AI Planner只负责生成一个由基础技能节点组成的工作流DAG(有向无环图),然后由一个独立的Workflow Executor来按顺序或并行地执行这些节点,并处理节点之间的数据传递(例如,将“查询天气”节点的输出“rainy”传递给“判断”节点)。

6. 常见问题与调试实录

在开发过程中,我遇到了不少典型问题,这里分享排查思路和解决方案。

6.1 语音识别准确率低

  • 现象 :简单的指令经常转错词,比如“打开灯”识别成“打开等”。
  • 排查
    1. 检查音频质量 :用Audacity等工具录制一段测试音频,查看波形。如果音量太小或背景噪音太大,都会影响识别。确保录音时麦克风距离适中,环境相对安静。
    2. 检查采样参数 :AssemblyAI推荐16kHz采样率、单声道(Mono)。确保你的 pyaudio 录音参数与之匹配(见上文 AudioRecorder 类的初始化)。
    3. 使用AssemblyAI增强功能 :在转录配置中开启 audio_enhancer=True 。对于有口音或特定领域的术语,可以尝试上传自定义词汇表(Custom Vocabulary)来提高特定词汇的识别率。
  • 解决 :我最终采用了“预加重滤波”来提升高频分量,并增加了手动增益控制。对于关键指令,可以设计一个简单的本地关键词匹配作为后备,当AssemblyAI的置信度低于某个阈值时,触发本地匹配逻辑。

6.2 AI Planner返回的JSON格式不稳定

  • 现象 :Groq偶尔会返回非JSON文本,或者JSON键名错误,导致 json.loads() 崩溃。
  • 排查
    1. 打印原始响应 :在 ai_planner.py plan 方法中,捕获并打印 response_text ,观察AI到底输出了什么。很多时候是AI在JSON前后加了解释性文字。
    2. 检查temperature参数 :过高的 temperature (如0.8)会导致输出随机性大。尝试将其降至0.1或0.2。
    3. 强化提示词约束 :在系统提示词中反复强调“只输出JSON对象”,并使用“ json ... ”这样的Markdown代码块格式来引导,有时效果更好。你甚至可以在提示词末尾加上“你的输出必须是:”,然后直接写一个完美的JSON示例。
  • 解决 :我采用了“防御性解析”策略。在 json.loads() 外面包裹 try-except ,如果解析失败,则使用正则表达式尝试从响应文本中提取第一个 {...} 之间的内容。这虽然不完美,但能挽救大部分格式不规范的响应。
import re
def safe_parse_json(response_text):
    try:
        return json.loads(response_text)
    except json.JSONDecodeError:
        # 尝试用正则提取可能的JSON对象
        match = re.search(r'\{.*\}', response_text, re.DOTALL)
        if match:
            try:
                return json.loads(match.group())
            except:
                pass
        return {"error": "无法解析AI响应", "raw": response_text[:200]} # 截断部分原始文本供调试

6.3 任务执行中的网络与超时问题

  • 现象 :调用天气API或发送邮件时,程序长时间挂起或无响应。
  • 排查
    1. 为所有外部请求设置超时 :在使用 requests 或任何网络库时, 务必 设置 timeout 参数(如 timeout=(3.05, 27) )。第一个是连接超时,第二个是读取超时。这能防止一个挂起的请求阻塞整个程序。
    2. 实现重试机制 :对于非关键任务,可以添加简单的重试逻辑(如最多重试3次,每次间隔递增)。
    3. 异步执行 :对于可能耗时的任务,使用 asyncio 或线程池来异步执行,避免阻塞主线程和语音交互循环。
  • 解决 :我在 task_executor.py 的每个技能函数内部,以及调用Groq和AssemblyAI的API处,都加上了带超时的 try-except 块,并记录日志。对于发送邮件等操作,改为将其加入一个后台任务队列,立即返回“请求已接收,正在处理”的语音反馈,提升用户体验。

6.4 资源消耗与成本控制

  • 现象 :长时间运行后内存增长,或者API调用费用超出预期。
  • 排查
    1. 监控API调用 :记录每次向AssemblyAI和Groq发送的请求长度和token数。Groq的计费通常与输入输出token数相关。过长的对话历史会显著增加token消耗。
    2. 音频文件管理 :程序生成的WAV音频文件会累积。需要定期清理,或在处理完成后立即删除。
    3. 对话历史裁剪 :不要无限制地增长对话历史。可以只保留最近3-5轮对话,或者当历史token数超过一定阈值时,尝试用AI总结之前的对话内容,用总结替代详细历史。
  • 解决 :我实现了一个简单的对话上下文管理器,它维护一个token计数器。当添加新的用户/助理消息导致总token数超过阈值(如3000)时,会自动移除最早的一对对话,直到低于阈值。同时,设置了一个定时任务,每小时清理一次临时音频文件目录。

构建一个语音控制的AI Agent是一个融合了音频处理、大语言模型应用和自动化流程的综合性项目。从“听得见”到“听得懂”,再到“做得到”,每一步都有不少细节需要打磨。AssemblyAI和Groq的组合提供了一个高性能的起点,但真正的挑战在于如何设计一个稳定、安全、可扩展的任务规划和执行框架。这个项目最大的收获不是最终的代码,而是在调试过程中对提示词工程、错误处理和系统架构的深刻理解。你可以从我这个基础版本出发,加入更先进的语音唤醒、多模态理解(结合摄像头)、或者更复杂的自动化工作流,打造出真正属于你的智能助手。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐