基于Whisper与Streamlit构建语音控制AI代理:从原理到实践 1. 项目概述当语音指令遇见AI代理最近在捣鼓一个挺有意思的小项目用语音直接控制一个AI智能体。想象一下你只需要对着麦克风说句话比如“帮我查一下明天的天气”或者“总结一下我昨天的工作日志”一个后台的AI代理就能理解你的意图并自动执行相应的任务。这听起来像是科幻电影里的场景但其实用现有的开源工具比如OpenAI的Whisper语音识别模型和Streamlit这个轻量级的Web框架我们完全可以在一个下午就搭建出一个可用的原型。这个项目的核心就是把语音这个最自然的交互方式和AI代理的自动化能力结合起来创造一个更直观、更“懒人友好”的人机交互界面。我之所以想做这个是因为发现很多AI工具虽然强大但交互方式还是停留在打字输入上。对于某些场景比如双手被占用开车、做饭或者就是单纯想“动口不动手”的时候语音控制就显得特别方便。Whisper提供了高精度的语音转文本能力而Streamlit能让我们快速构建一个带有录音功能的Web界面。剩下的就是设计一个能理解文本指令并调度相应工具的“AI代理”大脑。这个项目非常适合想要入门语音交互和智能体开发的开发者它涉及了前端交互、语音处理、自然语言理解和任务调度等多个环节是一个综合性很强的练手项目。无论你是想做个私人语音助手还是为某个特定工作流比如语音控制数据查询、语音生成报告添加入口这个技术栈都能提供一个坚实的起点。2. 核心架构与工具选型解析2.1 为什么是Whisper Streamlit在开始敲代码之前我们先聊聊为什么选这两个核心组件。这背后是一系列务实的工程权衡。首先是Whisper。在开源语音识别领域Whisper几乎是一个“默认选择”。它由OpenAI开源支持多语言在嘈杂环境下的识别鲁棒性非常好特别是对于带有口音或背景音的语音。相比于一些云端语音识别API虽然它们可能更准、功能更多Whisper最大的优势是可以本地离线运行。这意味着隐私性你的语音数据完全不需要离开本地机器对于处理敏感信息如个人日程、工作内容的应用至关重要。零成本与无延迟没有API调用次数限制也没有网络往返延迟识别速度取决于你的本地算力。可定制性虽然直接使用预训练模型但本地部署为后续可能的微调针对特定领域词汇优化打开了大门。我们通常会选择whisper这个Python库并使用其“base”或“small”模型来平衡精度与速度。对于实时性要求高的语音控制“small”模型通常是甜点。其次是Streamlit。我们的目标是快速构建一个可交互的演示界面而不是花大量时间在前端开发上。Streamlit的核心哲学是“将脚本变成可分享的Web应用”它用简单的Python代码就能生成包含按钮、滑块、图表、音频组件的页面。对于这个项目Streamlit的杀手级特性是内置媒体支持st.audio组件可以轻松播放和录制音频通过浏览器APIst.file_uploader能接收音频文件上传。会话状态管理通过st.session_state我们可以方便地在多次用户交互间保持状态比如存储识别后的文本、AI代理的回复历史。极快的开发迭代保存代码页面自动刷新所见即所得。这个组合Whisper处理语音Streamlit构建界面形成了一个清晰的前后端分离架构前端Streamlit负责音频采集和展示后端Whisper AI代理逻辑负责核心处理。整个应用可以打包成一个单一的Python脚本部署和分享都非常简单。2.2 AI代理的设计思路从文本到行动语音被转成文本后真正的魔法在于“AI代理”。这里我们说的不是某个单一的模型而是一个系统它能够理解用户的自然语言指令并将其分解、转化为一系列可执行的动作。一个最简单的代理可以是基于规则或关键词的。例如如果文本包含“天气”和“北京”就调用一个天气查询函数。但这种方式僵硬、扩展性差。更现代的做法是使用一个大语言模型作为代理的“大脑”。主流设计模式我们通常采用“LLM 函数调用Function Calling”的架构。规划与理解LLM比如通过OpenAI API调用的GPT-4/GPT-3.5或本地部署的Llama 3、Qwen等分析用户输入的文本理解其意图。工具选择LLM从一个预定义的“工具列表”中选择一个或多个合适的工具来完成任务。每个工具都是一个Python函数有明确的名称、描述和参数格式。例如工具可以是get_weather(city: str)描述是“获取指定城市的当前天气”。执行与汇总系统执行被选中的工具函数可能是调用一个API、查询数据库、运行一段计算然后将执行结果返回给LLM。回复生成LLM根据工具执行的结果组织成一段自然、友好的语言回复给用户。在这个项目中我们可以先实现几个简单的工具来演示比如时间查询返回当前系统时间。简单计算器解析并计算“123乘以456等于多少”这类问题。网络搜索需谨慎集成一个搜索API获取实时信息。文件操作列出指定目录下的文件需注意安全边界。注意AI代理的安全性至关重要。必须严格限制工具的可访问范围。例如一个面向公众的演示应用其工具绝对不能有执行任意系统命令、访问敏感文件或进行未授权网络请求的能力。所有工具函数都应在一个严格的沙盒环境中设计。3. 环境搭建与核心依赖安装3.1 创建虚拟环境与安装清单为了避免污染全局Python环境以及解决依赖冲突第一步永远是创建独立的虚拟环境。我强烈推荐使用conda或venv。# 使用 venv (Python 3.3 内置) python -m venv voice_agent_env source voice_agent_env/bin/activate # Linux/macOS # voice_agent_env\Scripts\activate # Windows # 使用 conda conda create -n voice_agent_env python3.9 conda activate voice_agent_env激活虚拟环境后我们安装核心依赖。创建一个requirements.txt文件是个好习惯。# requirements.txt streamlit1.28.0 openai-whisper20231117 openai1.0.0 # 用于调用ChatGPT API作为代理大脑 python-dotenv1.0.0 # 管理环境变量如API密钥 pydub0.25.1 # 音频格式处理 sounddevice0.4.6 # 可选用于高级音频录制 soundfile0.12.1 # 可选配合sounddevice使用然后通过pip安装pip install -r requirements.txt关键依赖说明streamlit我们的Web框架。openai-whisper官方Whisper库。安装时会自动下载模型首次运行时会下载可以指定模型大小如small。openaiOpenAI官方Python SDKV1版本。注意如果你打算使用其他LLM如通过litellm调用Anthropic、Cohere或本地模型则需要安装相应的库。pydubWhisper对音频格式有要求通常为16kHz单声道WAVpydub可以轻松完成格式转换特别是当处理从浏览器录制或上传的MP3等格式时。3.2 解决可能遇到的“坑”在安装过程中你可能会遇到两个常见问题Whisper的依赖FFmpegWhisper底层依赖FFmpeg来处理各种音频文件。如果系统没有安装FFmpeg运行时会报错。Linux (Ubuntu/Debian)sudo apt update sudo apt install ffmpegmacOS (使用Homebrew)brew install ffmpegWindows去FFmpeg官网下载编译好的可执行文件将其所在目录如C:\ffmpeg\bin添加到系统的PATH环境变量中。PyTorch的安装Whisper依赖PyTorch。虽然pip install openai-whisper通常会尝试安装PyTorch但有时版本或CUDA版本可能不匹配。最稳妥的方式是先去 PyTorch官网 根据你的系统操作系统、Python版本、CUDA版本获取正确的安装命令。例如对于仅使用CPU的Linux系统pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu先安装好PyTorch再安装Whisper可以避免很多冲突。4. 分步实现从语音采集到智能响应4.1 构建Streamlit语音交互前端我们的Streamlit应用界面需要提供至少两种语音输入方式实时录制和文件上传。我们先搭建一个简单的UI。创建一个名为app.py的文件import streamlit as st import whisper from pydub import AudioSegment import io import tempfile import os # 设置页面标题和布局 st.set_page_config(page_title语音控制AI代理, layoutwide) st.title( 语音控制AI代理 Demo) # 初始化session state用于存储状态 if transcribed_text not in st.session_state: st.session_state.transcribed_text if agent_response not in st.session_state: st.session_state.agent_response # 侧边栏用于配置和显示信息 with st.sidebar: st.header(设置) # 选择Whisper模型大小 model_size st.selectbox( 选择Whisper模型, [tiny, base, small, medium, large], index2 # 默认选择small ) st.caption(模型越大精度越高但速度越慢。small是精度与速度的较好平衡。) # 主界面分为两列 col1, col2 st.columns(2) with col1: st.header(1. 语音输入) input_method st.radio(选择输入方式, (实时录制, 上传音频文件)) audio_bytes None filename None if input_method 实时录制: # 使用Streamlit的audio_input组件Streamlit 1.28 audio_bytes st.audio_input(点击开始录音, keyrecorder) if audio_bytes: st.audio(audio_bytes, formataudio/wav) # 将字节数据保存为临时文件供Whisper处理 with tempfile.NamedTemporaryFile(deleteFalse, suffix.wav) as tmpfile: tmpfile.write(audio_bytes) filename tmpfile.name else: # 上传音频文件 uploaded_file st.file_uploader(上传一个音频文件 (MP3, WAV, M4A等), type[mp3, wav, m4a, ogg]) if uploaded_file is not None: # 显示上传的音频 st.audio(uploaded_file) # 保存为临时文件 with tempfile.NamedTemporaryFile(deleteFalse, suffixf.{uploaded_file.name.split(.)[-1]}) as tmpfile: tmpfile.write(uploaded_file.getvalue()) filename tmpfile.name # 识别按钮 if filename and st.button( 开始识别语音, typeprimary): with st.spinner(Whisper正在努力识别中...): # 加载Whisper模型这里为了演示每次点击都加载实际应用应缓存模型 model whisper.load_model(model_size) # 执行语音识别 result model.transcribe(filename, fp16False) # fp16False在CPU上更稳定 transcribed_text result[text].strip() st.session_state.transcribed_text transcribed_text st.success(识别完成) # 识别完成后删除临时文件 os.unlink(filename) # 显示识别结果 if st.session_state.transcribed_text: st.subheader(识别出的文本) st.write(st.session_state.transcribed_text) with col2: st.header(2. AI代理响应) # 这里将放置AI代理的逻辑和响应显示区域 st.write(代理功能将在下一步实现)这段代码构建了一个基本的双栏界面。左侧负责语音输入和识别右侧预留给了AI代理。我们使用了st.audio_input进行实时录音需要浏览器支持并通过st.file_uploader支持文件上传。识别后的文本存储在st.session_state中这样在页面交互时不会丢失。实操心得st.audio_input返回的是未经压缩的PCM WAV字节流非常适合直接送给Whisper处理。对于上传的文件我们统一保存为临时文件因为Whisper的transcribe方法主要接受文件路径作为输入。务必记得在处理完成后用os.unlink删除临时文件避免磁盘空间被慢慢占满。4.2 集成Whisper并优化识别效果上面代码中已经集成了Whisper的基本调用。但为了更好的效果和性能我们需要深入一些细节。模型加载优化每次点击按钮都加载模型是极其低效的。我们应该利用Streamlit的缓存机制只加载一次模型。st.cache_resource # Streamlit 1.18.0 推荐使用cache_resource缓存不可变对象 def load_whisper_model(model_sizesmall): 加载并缓存Whisper模型 st.info(f正在加载Whisper-{model_size}模型首次加载可能需要几分钟...) model whisper.load_model(model_size) st.success(模型加载完成) return model # 在需要的地方调用 model load_whisper_model(model_size) result model.transcribe(filename, fp16False, languagezh) # 指定语言可提高识别精度识别参数调优fp16False如果在CPU上运行设置为False可以避免一些兼容性问题且精度更高但稍慢。languagezh如果你确定输入语音是中文明确指定语言可以显著提升识别准确率和速度。tasktranscribe默认就是转写。如果音频中有多种语言可以不指定language让模型自动检测。initial_prompt提供一个提示文本可以引导模型更好地识别专业术语或上下文。例如如果你的应用是医疗领域的可以设置initial_prompt以下是关于患者病情的医患对话。处理长音频如果上传的音频很长直接转录可能内存不足或速度很慢。可以考虑使用pydub进行切片。def transcribe_long_audio(file_path, model, chunk_length_ms30000): 将长音频切片后分批转录 audio AudioSegment.from_file(file_path) chunks [audio[i:i chunk_length_ms] for i in range(0, len(audio), chunk_length_ms)] full_text for i, chunk in enumerate(chunks): with tempfile.NamedTemporaryFile(deleteFalse, suffix.wav) as chunk_file: chunk.export(chunk_file.name, formatwav) result model.transcribe(chunk_file.name, fp16False, languagezh) full_text result[text] os.unlink(chunk_file.name) return full_text.strip()4.3 实现AI代理核心逻辑现在我们来填充右侧的AI代理部分。我们将使用OpenAI的Chat Completions API和函数调用功能。首先确保你有一个OpenAI API密钥并将其保存在环境变量.env文件中。# 在app.py开头添加 from openai import OpenAI import json from dotenv import load_dotenv import os load_dotenv() # 加载.env文件中的环境变量 client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) # 定义工具函数AI代理可以调用的能力 def get_current_time(): 获取当前系统时间 from datetime import datetime now datetime.now() return now.strftime(%Y-%m-%d %H:%M:%S) def calculate_expression(expression: str): 计算一个简单的数学表达式。注意使用eval有安全风险此处仅作演示生产环境需替换为安全解析器。 try: # 警告实际生产中应对expression做严格过滤或使用ast.literal_eval等安全方法 result eval(expression, {__builtins__: None}, {}) return str(result) except Exception as e: return f计算错误{e} def search_web(query: str): 模拟网络搜索实际应接入Serper API、Google Search API等 # 此处为模拟返回 return f这是关于{query}的模拟搜索结果相关资讯1相关资讯2。 # 将工具定义格式化为OpenAI函数调用所需的格式 tools [ { type: function, function: { name: get_current_time, description: 获取当前的日期和时间。, parameters: { type: object, properties: {}, required: [] } } }, { type: function, function: { name: calculate_expression, description: 计算一个基础的数学表达式例如(3 5) * 2。支持加减乘除和括号。, parameters: { type: object, properties: { expression: { type: string, description: 需要计算的数学表达式字符串。 } }, required: [expression] } } }, { type: function, function: { name: search_web, description: 在互联网上搜索最新的信息。, parameters: { type: object, properties: { query: { type: string, description: 搜索查询关键词。 } }, required: [query] } } } ] # 工具名称到实际函数的映射 available_functions { get_current_time: get_current_time, calculate_expression: calculate_expression, search_web: search_web, }接下来在app.py的右侧列col2中添加调用代理的逻辑with col2: st.header(2. AI代理响应) # 显示识别出的文本并作为代理的输入 if st.session_state.transcribed_text: user_input st.text_area(代理输入可编辑, valuest.session_state.transcribed_text, height100) if st.button( 询问AI代理, keyask_agent): if not user_input.strip(): st.warning(请输入一些内容。) else: with st.spinner(AI代理思考中...): # 第一步调用LLM让其决定是否使用工具以及使用哪个 response client.chat.completions.create( modelgpt-3.5-turbo, # 或 gpt-4 messages[ {role: system, content: 你是一个有帮助的AI助手可以根据用户需求调用工具来解决问题。如果用户指令清晰请调用合适的工具。}, {role: user, content: user_input} ], toolstools, tool_choiceauto, # 让模型自动决定是否调用函数 ) response_message response.choices[0].message tool_calls response_message.tool_calls # 初始化最终回复 final_response # 第二步如果模型决定调用工具则执行对应的函数 if tool_calls: for tool_call in tool_calls: function_name tool_call.function.name function_to_call available_functions.get(function_name) if function_to_call: # 解析模型传回的参数 function_args json.loads(tool_call.function.arguments) # 执行函数 function_response function_to_call(**function_args) # 将函数执行结果追加到对话历史中让模型生成面向用户的回复 # 注意这里需要将工具执行结果以特定的格式追加回消息列表 # 但为了简化流程我们可以直接在本轮将结果交给模型进行总结 final_response f[调用 {function_name} 结果]: {function_response}\n else: final_response f[错误]: 未知工具 {function_name}\n # 第三步将工具执行结果反馈给模型让它生成一段友好的总结性回复 second_response client.chat.completions.create( modelgpt-3.5-turbo, messages[ {role: system, content: 你是一个有帮助的AI助手。}, {role: user, content: user_input}, {role: assistant, content: str(response_message)}, # 包含工具调用信息 {role: tool, tool_call_id: tool_calls[0].id, name: function_name, content: function_response} ], ) agent_reply second_response.choices[0].message.content else: # 模型没有调用工具直接使用其回复 agent_reply response_message.content st.session_state.agent_response agent_reply # 显示代理的回复 if st.session_state.agent_response: st.subheader(代理回复) st.write(st.session_state.agent_response) # 可以添加一个语音合成按钮用TTS将回复读出来可选功能 # if st.button( 播放回复): # # 调用TTS引擎如edge-tts或gTTS # pass这段代码实现了完整的AI代理工作流将用户输入语音转写的文本发送给GPT模型。GPT模型根据我们定义的tools列表判断是否需要调用工具。如果需要它会返回一个tool_calls对象其中包含了要调用的函数名和参数。我们在本地根据函数名找到对应的Python函数并用解析出的参数执行它。将函数执行的结果按照OpenAI要求的格式role: tool再次发送给GPT模型。GPT模型结合工具执行结果生成最终面向用户的自然语言回复。核心技巧在实际开发中为了处理多个工具调用和更复杂的对话历史你需要维护一个消息列表messages并不断将用户输入、助手回复、工具调用和工具结果追加进去。上面的代码为了清晰做了一定简化。生产级应用需要考虑对话状态的管理、错误处理如工具调用失败和流式输出让回复一个字一个字显示出来体验更好。5. 功能增强与部署上线5.1 添加文本转语音TTS反馈一个完整的语音交互闭环不仅包括“听”还应该包括“说”。我们可以让AI代理的回复以语音形式播放出来。这里推荐使用edge-tts它是一个免费、高质量的跨平台TTS库利用微软Edge浏览器的在线语音合成服务。首先安装pip install edge-tts然后在app.py中添加TTS功能import edge_tts import asyncio import base64 async def generate_speech_async(text, voicezh-CN-XiaoxiaoNeural): 异步生成语音文件并返回Base64编码的音频数据 communicate edge_tts.Communicate(text, voice) with tempfile.NamedTemporaryFile(deleteFalse, suffix.mp3) as tmp_file: tmp_path tmp_file.name await communicate.save(tmp_path) with open(tmp_path, rb) as f: audio_bytes f.read() audio_b64 base64.b64encode(audio_bytes).decode() os.unlink(tmp_path) # 清理临时文件 return audio_b64 def generate_speech(text): 同步包装函数供Streamlit调用 loop asyncio.new_event_loop() asyncio.set_event_loop(loop) audio_b64 loop.run_until_complete(generate_speech_async(text)) loop.close() return audio_b64在显示代理回复的部分添加一个播放按钮if st.session_state.agent_response: st.subheader(代理回复) st.write(st.session_state.agent_response) if st.button( 播放语音回复): with st.spinner(生成语音中...): audio_b64 generate_speech(st.session_state.agent_response) # 使用HTML audio标签播放Base64音频 audio_html faudio controls autoplaysource srcdata:audio/mp3;base64,{audio_b64} typeaudio/mp3/audio st.markdown(audio_html, unsafe_allow_htmlTrue)注意edge-tts是异步库在Streamlit的同步环境中调用需要处理事件循环。另外在线TTS服务有速率限制对于高频使用需要考虑缓存或使用其他本地TTS方案如pyttsx3但音质较差。5.2 使用本地LLM替代OpenAI API依赖OpenAI API意味着需要网络、付费且有隐私顾虑。对于想要完全本地运行的项目我们可以用本地部署的大模型如Llama 3、Qwen、ChatGLM来替代GPT。这里以使用Ollama一个强大的本地LLM运行和管理的工具为例。安装Ollama前往 Ollama官网 下载并安装。拉取模型在终端运行ollama pull llama3:8b或其他你喜欢的模型如qwen:7b。修改代码不再使用openai库而是通过Ollama的API来调用。Ollama提供了与OpenAI API兼容的端点。首先安装requests库如果还没有pip install requests然后修改AI代理的调用部分import requests import json def query_local_llm_with_tools(messages, tools): 调用本地Ollama服务模拟函数调用Ollama本身可能不支持原生function calling需要一些技巧 # 方法一通过Prompt Engineering让模型输出结构化JSON来选择工具较复杂 # 方法二简化使用支持function calling的模型或使用litellm等桥接库 # 此处演示一个简化版直接让模型回答不进行复杂的工具调度。 ollama_url http://localhost:11434/api/chat payload { model: llama3:8b, # 改成你拉取的模型名 messages: messages, stream: False } try: response requests.post(ollama_url, jsonpayload, timeout60) response.raise_for_status() return response.json()[message][content] except Exception as e: return f调用本地模型失败{e} # 在Streamlit按钮点击事件中替换OpenAI的调用 # response_text query_local_llm_with_tools(messages, tools)重要提示让本地LLM稳定地进行函数调用Tool Calling比使用GPT API要复杂得多。通常需要使用专门微调了工具调用能力的模型。或者使用像LangChain、LlamaIndex或Transformers Agents这样的框架它们对工具调用有更好的抽象和支持。或者采用更简单的“提示词正则表达式解析”的方式让模型输出ACTION: 函数名\nINPUT: 参数这样的格式然后自己解析并执行。对于快速原型如果工具不多可以暂时绕过复杂的工具调用让本地LLM直接生成答案或者只为它集成一两个核心工具如计算器。5.3 使用Streamlit Community Cloud部署让应用在本地运行只是第一步分享给他人使用才更有价值。Streamlit提供了极其简单的免费部署平台Streamlit Community Cloud。准备部署文件确保你的代码在一个GitHub仓库中。在项目根目录创建requirements.txt包含所有依赖。创建一个.streamlit/config.toml文件来配置一些部署选项可选但推荐[server] maxUploadSize 200 # 允许上传的文件最大大小(MB)访问部署平台访问 share.streamlit.io 用GitHub账号登录。新建应用点击“New app”选择你的仓库、分支和主文件路径例如app.py。高级设置在“Advanced settings”中可以设置Python版本和环境变量如你的OPENAI_API_KEY。千万不要把API密钥直接写在代码里在这里通过环境变量注入。部署点击“Deploy”。Streamlit会自动安装依赖并启动你的应用。你会获得一个唯一的URL如https://yourapp-name.streamlit.app/可以分享给任何人。部署注意事项资源限制免费版有内存和CPU限制。Whisper的small模型加载后内存占用约1GB在免费版1GB内存上运行可能会比较吃力可以考虑使用tiny或base模型。长时间运行Streamlit Cloud应用在一段时间无活动后会休眠下次访问会有冷启动延迟需要重新加载模型。替代方案如果对性能要求高可以考虑部署在更强大的云服务器如AWS EC2、Google Cloud Run或使用其他支持GPU的PaaS平台。6. 常见问题与优化技巧6.1 语音识别精度不佳怎么办语音识别效果受多种因素影响以下是一些排查和优化方向问题现象可能原因解决方案中文识别为英文或乱码模型未检测到正确语言在transcribe()中明确指定languagezh。专业术语人名、产品名识别错误模型训练数据中缺乏该领域词汇1. 使用initial_prompt参数提供上下文提示。2. 考虑使用Whisper的“微调”功能用少量领域数据训练适配层高级技巧。背景噪音大时识别率低环境嘈杂1. 使用whisper的noise_reduce选项如果可用或前置一个降噪库如noisereduce。2. 提示用户在安静环境下使用。识别结果有大量“呃”、“嗯”等语气词这是Whisper的特点它会忠实转录所有声音在后处理阶段用简单的正则表达式或规则过滤掉这些无意义的语气词。长句子中间被错误切断音频本身不连贯或模型VAD语音活动检测过于敏感调整transcribe的word_timestamps和prepend_punctuations/append_punctuations参数或尝试不同的模型medium或large通常长上下文理解更好。一个实用的后处理函数示例def clean_transcription(text): 对Whisper识别结果进行简单的后处理清洗 import re # 移除常见的无意义语气词可根据需要扩充 filler_words [呃, 嗯, 啊, 那个, 这个] pattern r\b( |.join(filler_words) r)\b text re.sub(pattern, , text) # 合并多余的空白字符 text re.sub(r\s, , text).strip() # 确保句末有标点如果模型漏了 if text and text[-1] not in [., 。, !, , ?, ]: text 。 return text6.2 代理响应慢或出错如何排查AI代理链路较长出问题的环节也多。网络延迟如果使用云端APIOpenAI网络是主要瓶颈。考虑为Streamlit应用设置超时st.spinner配合timeout参数。在UI上给用户明确的等待提示。如果响应时间经常超过30秒Streamlit Cloud可能会中断连接需要考虑优化或使用更快的模型/本地模型。函数调用失败参数解析错误检查LLM返回的function.arguments是否是合法的JSON。有时模型会生成格式错误的JSON需要添加try...except进行容错并可能要求模型重试。函数执行异常在工具函数内部做好异常捕获返回清晰的错误信息给LLM让它能向用户解释。权限问题例如工具函数需要访问网络或文件系统但部署环境没有相应权限。提示词工程代理的表现很大程度上取决于给它的系统提示词systemmessage。如果代理经常不理解指令或调用错误的工具需要优化提示词更清晰地描述每个工具的功能、适用场景和参数格式。在系统提示中给出几个具体的例子Few-shot Learning。明确告诉模型“如果没有合适的工具请直接根据你的知识回答”。一个更健壮的系统提示词示例你是一个智能语音助手可以调用工具来帮助用户。你有以下工具可用 - get_current_time: 当用户询问当前时间、日期、今天星期几时使用。无需参数。 - calculate_expression: 当用户需要计算一个数学表达式时使用。参数expression是一个字符串如“(105)*2”。 - search_web: 当用户询问需要最新、实时信息的问题时使用如新闻、股价、天气。参数query是搜索关键词。 请遵循以下步骤 1. 理解用户请求。 2. 决定是否需要调用工具。如果需要选择最合适的一个。 3. 如果调用工具请严格按照工具定义的JSON格式输出。 4. 如果不需要工具或没有合适工具请直接用自己的知识友好地回答。 示例 用户“北京现在几点” 助手思考用户问时间应调用get_current_time工具。 模型应输出对应的tool_calls结构6.3 如何扩展更多工具项目的魅力在于其可扩展性。添加新工具只需两步定义工具函数在Python中实现一个具体功能的函数确保它有清晰的文档字符串描述会被用于提示词。注册工具将工具的函数定义和描述添加到tools列表和available_functions字典中。示例添加一个记事本工具def add_note(content: str): 添加一条笔记到记事本文件中。 with open(notes.txt, a, encodingutf-8) as f: f.write(f{datetime.now().strftime(%Y-%m-%d %H:%M:%S)}: {content}\n) return 笔记已成功添加。 def read_notes(): 读取所有历史笔记。 try: with open(notes.txt, r, encodingutf-8) as f: notes f.read() return notes if notes else 记事本还是空的。 except FileNotFoundError: return 记事本还是空的。 # 将新工具添加到tools列表和available_functions字典中 tools.append({ type: function, function: { name: add_note, description: 添加一条文本笔记。, parameters: { type: object, properties: { content: {type: string, description: 笔记内容} }, required: [content] } } }) tools.append({ type: function, function: { name: read_notes, description: 读取所有历史笔记。, parameters: { type: object, properties: {}, required: [] } } }) available_functions.update({ add_note: add_note, read_notes: read_notes })现在用户就可以通过语音说“记下明天下午三点开会”AI代理就会调用add_note工具了。通过这种方式你可以将任何你能用Python脚本实现的功能发送邮件、控制智能家居、查询数据库都封装成工具赋予你的语音代理强大的能力。这个项目就像一个乐高底座Whisper和Streamlit提供了语音和交互的基础模块而AI代理的“工具集”则是你可以无限扩展的乐高积木。从今天开始动手搭建属于你自己的“贾维斯”吧。