基于Whisper与Ollama构建本地语音控制AI代理实战指南
语音识别与自然语言处理是人工智能领域的基础技术,它们使机器能够理解和响应人类语言。其核心原理是通过深度学习模型将音频信号转化为文本,再通过大语言模型解析语义并生成合理反馈。在工程实践中,本地化部署成为保障数据隐私和降低使用成本的关键需求。结合开源语音识别模型Whisper与本地大语言模型管理框架Ollama,开发者可以构建完全离线运行的智能代理系统。这类系统能够将口语化指令转化为精确的文本命令,并
1. 项目概述:让AI听懂你的话,并为你干活
最近在折腾一个挺有意思的东西:一个完全在本地运行的、能听懂你说话并执行任务的AI助手。想象一下,你对着电脑说一句“帮我总结一下上周的会议纪要”,它就能自动打开文档、分析内容、生成摘要,整个过程无需联网,数据也完全留在你自己的机器上。这听起来像是科幻电影里的场景,但现在,利用像Whisper和Ollama这样的开源工具,我们完全可以在自己的电脑上搭建出来。
这个项目的核心,就是“ 语音控制 ”和“ 本地AI代理 ”的结合。语音控制负责将我们模糊的、口语化的指令转化为精确的文本指令;而本地AI代理则负责理解这个指令,并调用合适的工具或执行一系列操作来完成它。我选择Whisper作为语音识别的引擎,因为它开源、免费,且在多种语言和口音上表现相当出色。而Ollama则是一个极其方便的本地大语言模型(LLM)运行和管理框架,它让你可以像拉取Docker镜像一样,轻松地在本地部署和运行Llama 2、Mistral、CodeLlama等各类模型,无需复杂的配置。
这个组合的优势非常明显: 隐私性 (所有数据不出本地)、 可定制性 (你可以选择最适合你任务的模型)、 成本可控 (没有API调用费用)以及 离线可用 。无论是想自动化一些日常的文档处理、代码生成,还是构建一个智能的桌面助手,这个技术栈都提供了一个强大而灵活的起点。接下来,我就把自己从零搭建这个系统的完整过程、踩过的坑以及一些优化心得分享出来。
2. 核心组件选型与架构设计
2.1 为什么是Whisper + Ollama?
在开始动手之前,明确技术选型的理由很重要。市面上语音识别和LLM的方案很多,我最终锁定这个组合,是基于以下几个核心考量:
Whisper的优势在于“省心且强大” 。它由OpenAI开源,虽然在云端有更强大的版本,但其开源模型(特别是 base 、 small 、 medium 这几个尺寸)在本地CPU或GPU上运行已经能达到商用级别的准确率。它支持多语言,对背景噪音和不同口音的鲁棒性很好。最重要的是,它有一个简单的Python接口,几行代码就能完成高质量的语音转文本(STT),极大地降低了入门门槛。相比于训练自己的模型或使用某些需要复杂预处理的老旧方案,Whisper几乎是“开箱即用”的最佳选择。
Ollama的优势在于“极致简化” 。在它出现之前,在本地运行一个像Llama 2这样的模型,你需要处理模型下载、转换格式、配置加载器(如llama.cpp)、设置API服务等一系列繁琐步骤。Ollama把这一切都打包了。你只需要一句命令 ollama run llama2 ,它就会自动下载、优化并启动一个模型服务,同时提供一个兼容OpenAI API格式的本地端点( http://localhost:11434 )。这意味着,你可以直接使用为ChatGPT设计的SDK(如OpenAI Python库)来调用你自己的本地模型,生态兼容性极好。它管理模型版本、优化推理性能(自动利用GPU),让开发者能完全聚焦在应用逻辑上。
架构设计思路 :整个系统的流程很清晰。首先,通过麦克风采集音频,交给Whisper处理成文本。然后,将这份文本作为“用户指令”,发送给由Ollama托管的本地大模型。这里的关键在于,我们不仅仅是将大模型当作一个聊天机器人,而是要将它升级为一个“ 代理(Agent) ”。这意味着,我们需要让模型具备“思考-行动-观察”的能力。具体来说,模型在收到指令后,应该能自主决定是否需要调用某个工具(比如搜索文件、执行Python代码、调用系统命令),然后根据工具返回的结果,再决定下一步动作,直到任务完成或无法继续。这个循环,就是智能代理的核心。
2.2 环境准备与基础依赖安装
我的开发环境是macOS,但步骤在Linux和WSL上基本通用。Windows原生支持可能需要对音频库做一些调整。
第一步:安装Ollama 这是最简单的部分。直接访问Ollama官网,下载对应系统的安装包,或者使用命令行安装。安装完成后,在终端运行 ollama --version 确认安装成功。然后,拉取一个合适的模型。对于代理任务,模型需要较强的推理和指令遵循能力。我推荐从 llama2 (7B或13B参数)或 mistral (7B)开始,它们对资源要求相对友好。
# 拉取并运行llama2模型(首次运行会自动下载)
ollama run llama2
运行后,它会进入一个交互式聊天界面,这证明模型服务已经在本地的11434端口启动了。你可以按 Ctrl+D 退出聊天,但服务会在后台继续运行。
第二步:配置Python环境与Whisper 我强烈建议使用 conda 或 venv 创建独立的Python环境,避免包冲突。
# 创建并激活环境
conda create -n voice-agent python=3.10
conda activate voice-agent
# 安装核心依赖
pip install openai-whisper # 核心语音识别库
pip install sounddevice pyaudio # 音频采集
pip install numpy scipy # 音频处理相关
pip install openai # 用于调用Ollama的API(兼容格式)
这里有个 关键点 : pyaudio 在某些系统上可能需要额外安装系统依赖。在macOS上通常没问题,在Ubuntu上可能需要 sudo apt-get install portaudio19-dev python3-pyaudio 。如果安装失败,可以尝试先安装 portaudio 。
第三步:验证Whisper 下载Whisper模型是自动的,但第一次运行时会下载对应尺寸的模型文件(如 base 、 small )。我们可以写一个简单的脚本来测试:
import whisper
model = whisper.load_model("base") # 首次运行会下载模型
result = model.transcribe("test_audio.wav") # 准备一个测试音频文件
print(result["text"])
如果能看到识别出的文字,说明Whisper工作正常。模型尺寸选择上, base 速度最快, small 是精度和速度的较好平衡, medium 和 large 精度更高但更耗资源。对于实时语音控制, small 通常是个不错的起点。
3. 核心模块实现:从语音到智能行动
3.1 实时语音采集与识别模块
构建语音控制的第一步,是让程序能持续“聆听”并识别我们的声音。这里的目标是实现一个 语音活动检测(VAD) 驱动的录音机制,即只有检测到人声时才录音,说完自动停止并识别,而不是录制固定长度的片段。
实现思路 :我们使用 sounddevice 库来捕获原始音频流,同时计算音频能量的移动平均值。当能量超过阈值(表示开始说话)时,开始将音频数据存入缓冲区;当能量低于阈值并持续一段时间(表示说话结束)时,停止录音,并将缓冲区数据交给Whisper处理。
import sounddevice as sd
import numpy as np
import whisper
import queue
import threading
from scipy.io.wavfile import write
import tempfile
import os
class VoiceRecorder:
def __init__(self, model_size="small", energy_threshold=500, silence_duration=1.0):
self.model = whisper.load_model(model_size)
self.energy_threshold = energy_threshold
self.silence_duration = silence_duration # 静音多久判定为结束
self.sample_rate = 16000 # Whisper推荐采样率
self.audio_queue = queue.Queue()
self.is_recording = False
self.audio_buffer = []
def _audio_callback(self, indata, frames, time, status):
"""声音回调函数,每次采集到音频块时调用"""
if status:
print(f"音频流错误: {status}")
# 计算当前音频块的能量(均方根)
energy = np.sqrt(np.mean(indata**2)) * 1000
# 简单的VAD逻辑
if energy > self.energy_threshold and not self.is_recording:
print("检测到语音,开始录音...")
self.is_recording = True
self.audio_buffer = [indata.copy()] # 开始新的录音
elif energy <= self.energy_threshold and self.is_recording:
# 静音中,但还在录音期内,继续缓存
self.audio_buffer.append(indata.copy())
elif self.is_recording:
# 能量高,持续录音
self.audio_buffer.append(indata.copy())
# 检查是否静音超时
if self.is_recording:
# 这里简化处理:在实际中,需要维护一个静音计时器
# 为了示例清晰,我们在另一个线程中处理结束逻辑
pass
def listen_and_transcribe(self):
"""开始监听,并在检测到语句结束后返回识别文本"""
print("请开始说话...")
with sd.InputStream(callback=self._audio_callback,
channels=1,
samplerate=self.sample_rate,
blocksize=int(self.sample_rate * 0.1)): # 100ms的块
# 这里需要更复杂的逻辑来管理录音开始/结束。
# 一个更健壮的方法是使用专门的VAD库,如webrtcvad
# 为了简化,我们改为录制固定5秒作为示例
sd.sleep(5000) # 监听5秒
# 将缓冲区的音频数据拼接并保存为临时文件
if self.audio_buffer:
audio_data = np.concatenate(self.audio_buffer, axis=0)
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmpfile:
write(tmpfile.name, self.sample_rate, (audio_data * 32767).astype(np.int16)) # 转换为16位PCM
result = self.model.transcribe(tmpfile.name, fp16=False) # CPU上运行禁用fp16
os.unlink(tmpfile.name) # 删除临时文件
text = result["text"].strip()
print(f"识别结果: {text}")
return text
return ""
注意 :上面的VAD逻辑是极简版,仅用于演示原理。在实际应用中,直接使用
webrtcvad库会可靠得多。它由WebRTC项目提供,能更准确地区分人声和静音。你需要安装pip install webrtcvad,并实现一个基于该库的录音状态机。
3.2 集成Ollama:构建本地AI对话能力
有了文本指令,下一步就是让AI理解并回应。由于Ollama提供了兼容OpenAI API的接口,我们可以直接使用 openai 这个官方库来调用,这省去了大量自定义HTTP请求的麻烦。
首先,确保Ollama服务正在运行(运行过 ollama run 即可)。然后,我们需要配置OpenAI客户端,将其指向本地端点。
from openai import OpenAI
class LocalAIClient:
def __init__(self, base_url="http://localhost:11434/v1", model="llama2"):
# 关键:将api_key设为任意非空字符串,因为Ollama不验证
self.client = OpenAI(base_url=base_url, api_key="ollama")
self.model = model
def chat_completion(self, prompt, system_prompt=None, temperature=0.7):
"""发送对话请求"""
messages = []
if system_prompt:
messages.append({"role": "system", "content": system_prompt})
messages.append({"role": "user", "content": prompt})
try:
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
temperature=temperature, # 控制创造性,任务执行建议较低值如0.1-0.3
stream=False # 为简化,先不使用流式输出
)
return response.choices[0].message.content
except Exception as e:
print(f"调用AI模型失败: {e}")
return None
# 测试
if __name__ == "__main__":
ai_client = LocalAIClient()
test_response = ai_client.chat_completion("你好,请介绍一下你自己。")
print(f"AI回复: {test_response}")
现在,我们已经能将语音识别出的文本,发送给本地大模型并获得回复。但这还只是一个“聊天机器人”,而不是“代理”。代理的核心是 工具调用(Tool Calling)或函数调用(Function Calling) 。
3.3 实现AI代理:让模型学会使用工具
这是本项目最核心也最有趣的部分。我们需要赋予模型使用外部工具的能力。例如,当用户说“现在几点了?”,模型应该调用一个 get_current_time() 函数;当用户说“创建一个名为‘demo.txt’的文件”,模型应该调用 create_file(filename, content) 函数。
实现策略 :目前,像GPT-4这样的高级模型原生支持函数调用。但对于本地运行的Llama 2等模型,我们需要采用一种“提示工程(Prompt Engineering)”的方法来模拟这一过程。基本思路是:
- 在系统提示(System Prompt)中,明确定义模型可以使用的工具(函数),包括函数名、描述和参数格式。
- 要求模型在需要时,严格按照特定格式(如JSON)输出它的“思考过程”和“行动决定”。
- 我们的程序解析模型的输出,如果发现工具调用指令,就执行对应的Python函数,并将结果作为新的上下文反馈给模型。
- 模型根据反馈,决定下一步是继续调用工具,还是给出最终答案。
我们首先定义几个简单的工具:
import json
import datetime
import subprocess
import os
class ToolBox:
"""工具箱,包含AI代理可以调用的所有函数"""
@staticmethod
def get_current_time():
"""获取当前日期和时间"""
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@staticmethod
def create_file(filename, content=""):
"""创建一个文件并写入内容"""
try:
with open(filename, 'w', encoding='utf-8') as f:
f.write(content)
return f"文件 '{filename}' 创建成功。"
except Exception as e:
return f"创建文件失败: {e}"
@staticmethod
def list_files(directory="."):
"""列出指定目录下的文件"""
try:
files = os.listdir(directory)
return f"目录 '{directory}' 下的文件: {', '.join(files)}"
except Exception as e:
return f"列出文件失败: {e}"
@staticmethod
def execute_command(command):
"""执行一个系统shell命令(危险!需谨慎使用)"""
# 注意:在实际应用中,必须严格限制可执行的命令,否则有严重安全风险
allowed_commands = ["ls", "pwd", "date", "echo"] # 白名单示例
if command.split()[0] not in allowed_commands:
return f"错误:命令 '{command}' 不在允许列表中。"
try:
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=5)
if result.returncode == 0:
return result.stdout
else:
return f"命令执行错误: {result.stderr}"
except Exception as e:
return f"执行命令异常: {e}"
接下来,我们构建一个能协调模型和工具的**代理(Agent)**类。这个类的 run 方法是核心,它管理着与模型的多次交互循环。
class VoiceControlledAgent:
def __init__(self, ai_client, toolbox):
self.ai_client = ai_client
self.toolbox = toolbox
# 定义工具的描述,用于构建系统提示
self.tools = [
{
"name": "get_current_time",
"description": "获取当前的日期和时间。",
"parameters": {}
},
{
"name": "create_file",
"description": "创建一个新文件并写入内容。需要参数:filename (字符串,文件名), content (字符串,文件内容,可选)。",
"parameters": {"filename": "str", "content": "str"}
},
{
"name": "list_files",
"description": "列出指定目录中的文件。需要参数:directory (字符串,目录路径,默认为当前目录)。",
"parameters": {"directory": "str"}
},
{
"name": "execute_command",
"description": "执行一个安全的系统命令(目前仅允许:ls, pwd, date, echo)。需要参数:command (字符串,要执行的命令)。",
"parameters": {"command": "str"}
}
]
# 构建强大的系统提示,指导模型如何扮演一个代理
self.system_prompt = f"""你是一个运行在用户本地电脑上的AI助手。你可以通过调用工具来帮助用户完成任务。
你可以使用的工具如下:
{json.dumps(self.tools, indent=2, ensure_ascii=False)}
**你必须严格遵守以下输出格式规则:**
1. 如果你需要调用工具,你必须且只能输出一个严格的JSON对象,格式如下:
```json
{{
"thought": "你的思考过程,分析用户请求并决定调用哪个工具。",
"action": {{
"name": "工具名",
"args": {{"参数1": "值1", "参数2": "值2"}}
}}
}}
- 如果你不需要调用工具,或者工具调用后得到了最终答案,请直接以自然语言回复用户。
重要: 除了上述JSON格式,不要输出任何其他额外文本、解释或Markdown标记。你的输出要么是一个纯净的JSON对象,要么是直接给用户的回复。 现在,开始处理用户的请求吧。""" def _parse_model_response(self, response): """尝试解析模型输出,看是否是工具调用指令""" response = response.strip() # 尝试查找JSON部分(模型有时会在JSON外包裹 json 标记) import re json_match = re.search(r' json\s*(.*?)\s* ', response, re.DOTALL) if json_match: response = json_match.group(1) else: # 或者响应本身就是JSON对象(以{开头,以}结尾) if response.startswith('{') and response.endswith('}'): pass # 就是它 else: return None, response # 不是工具调用,是直接回复 try: data = json.loads(response) if "action" in data and "name" in data["action"]: return data["action"], data.get("thought", "") except json.JSONDecodeError: pass return None, response
def _execute_tool(self, action):
"""执行工具调用"""
tool_name = action["name"]
args = action.get("args", {})
# 映射工具名到工具箱的方法
if hasattr(self.toolbox, tool_name):
func = getattr(self.toolbox, tool_name)
try:
result = func(**args)
return result
except TypeError as e:
return f"工具调用参数错误: {e}"
except Exception as e:
return f"工具执行异常: {e}"
else:
return f"错误:未知的工具 '{tool_name}'。"
def run(self, user_input, max_turns=5):
"""运行代理循环,处理用户输入"""
conversation_history = [{"role": "user", "content": user_input}]
print(f"用户指令: {user_input}")
for turn in range(max_turns):
# 构建当前对话上下文(系统提示 + 历史对话)
messages = [{"role": "system", "content": self.system_prompt}] + conversation_history
prompt_text = "\n".join([f"{msg['role']}: {msg['content']}" for msg in messages[-6:]]) # 限制历史长度
response = self.ai_client.chat_completion(prompt_text, system_prompt=None) # 提示已包含在消息中
if not response:
return "抱歉,AI模型没有响应。"
print(f"AI原始输出: {response}")
action, thought = self._parse_model_response(response)
if action:
print(f"代理思考: {thought}")
print(f"执行工具: {action['name']}, 参数: {action['args']}")
tool_result = self._execute_tool(action)
print(f"工具结果: {tool_result}")
# 将工具结果作为新的上下文加入对话
conversation_history.append({"role": "assistant", "content": response})
conversation_history.append({"role": "user", "content": f"工具执行结果: {tool_result}"})
# 如果工具结果已经明确回答了用户问题,可以提前结束(这里简化处理,继续循环)
else:
# 模型给出了最终回复
print(f"代理最终回复: {response}")
return response
return "代理已达到最大交互轮次,可能未能完全解决问题。"
这个`VoiceControlledAgent`类就是整个系统的大脑。它接收用户的文本指令,通过精心设计的系统提示引导本地大模型进行“思考”并输出结构化的决策。我们的程序解析这个决策,执行对应的工具,并将结果反馈给模型,直到模型认为任务完成并给出最终的自然语言回复。
## 4. 系统集成与优化实战
### 4.1 将语音识别与AI代理串联起来
现在,我们把语音识别模块和AI代理模块组合起来,形成一个完整的工作流。我们创建一个主程序,它循环等待语音输入,识别后交给代理处理,并通过语音合成(TTS)或文字输出结果。
由于本地TTS(如`pyttsx3`或`edge-tts`)需要额外配置,我们先实现一个文字输出的版本。
```python
def main():
print("初始化语音控制本地AI代理...")
# 1. 初始化语音识别器(使用更小的base模型以加快响应)
recorder = VoiceRecorder(model_size="base")
# 2. 初始化本地AI客户端和代理
ai_client = LocalAIClient(model="llama2") # 确保已运行 `ollama pull llama2`
toolbox = ToolBox()
agent = VoiceControlledAgent(ai_client, toolbox)
print("系统准备就绪。请说话...")
try:
while True:
# 3. 监听并识别语音
# 注意:这里需要替换为更健壮的、带VAD的录音函数。
# 我们暂时用输入模拟
# text = recorder.listen_and_transcribe()
text = input("请输入指令(或输入'退出'结束): ")
if text.lower() in ['退出', 'exit', 'quit']:
break
if not text:
continue
# 4. 交给AI代理处理
final_response = agent.run(text)
print(f"\n=== 最终结果 ===\n{final_response}\n================\n")
except KeyboardInterrupt:
print("\n程序被用户中断。")
finally:
print("程序结束。")
if __name__ == "__main__":
main()
在实际部署时,你需要将 input 语句替换为真正的 recorder.listen_and_transcribe() 函数调用,并完善其中的VAD逻辑。
4.2 性能调优与稳定性提升
在本地运行,尤其是资源有限的机器上,性能是关键。以下是我在实践中总结的几个优化点:
1. Whisper模型选择与量化 :
- 速度优先 :对于实时交互,使用
tiny或base模型。tiny模型识别速度极快,但准确率有所下降,适合简单命令。 - 精度优先 :如果对准确性要求高,使用
small或medium。可以尝试使用量化版本(如通过whisper.cpp项目转换的GGML模型),它们能在CPU上以更少的内存和更快的速度运行。 - 使用GPU :如果你有NVIDIA GPU,确保安装了
cuDNN和PyTorch的CUDA版本,Whisper会自动利用GPU加速,速度提升显著。
2. Ollama模型与参数调优 :
- 模型选择 :
llama2:7b是通用性不错的选择。mistral:7b在推理和指令遵循上可能表现更好。如果内存充足(16G+),可以尝试llama2:13b或codellama(针对代码任务)。 - 启动参数 :运行Ollama时可以通过环境变量调整性能。例如,
OLLAMA_NUM_PARALLEL=2可以设置并行处理数。在Ollama的模型文件(Modelfile)中,可以指定num_ctx(上下文长度)和num_gpu(GPU层数)等。 - 温度(Temperature) :在代理任务中,将温度设置为较低的值(如0.1-0.3),可以减少模型的随机性,让它的输出更稳定、更倾向于遵循指令格式。
3. 代理提示工程优化 :
- 系统提示(System Prompt)是代理的“宪法”,其质量直接决定模型的表现。需要反复测试和打磨。
- 清晰定义格式 :如上文所示,必须用极其明确的语言规定输出格式,并使用JSON Schema或示例来约束。
- 提供示例 :在系统提示中加入一两个完整的“用户指令-模型思考-工具调用-结果-最终回复”的示例,能极大地提升模型遵循格式的能力。这种方法被称为“少样本提示(Few-shot Prompting)”。
- 限制工具范围 :只暴露必要的、安全的工具给模型。像
execute_command这样的危险工具,必须设置严格的白名单。
4. 错误处理与超时控制 :
- 网络请求(虽然本地,但也是HTTP)和模型推理都可能超时。务必为
openai客户端的请求设置timeout参数。 - 在工具执行,特别是执行外部命令时,必须设置超时(如使用
subprocess.run的timeout参数),防止恶意或错误命令导致程序挂起。 - 对Whisper的转录过程进行异常捕获,音频文件损坏或格式不支持会导致错误。
4.3 常见问题与排查技巧实录
在搭建和运行过程中,你几乎一定会遇到下面这些问题。这里是我的排查记录:
问题1:Ollama服务启动失败或连接被拒绝。
- 现象 :运行
ollama run或Python脚本连接localhost:11434时报错。 - 排查 :
- 首先运行
ollama serve查看服务日志。常见问题是端口被占用。可以用lsof -i :11434(macOS/Linux)或netstat -ano | findstr :11434(Windows)检查。 - 确保Ollama版本是最新的。使用
ollama --version检查,并去官网下载最新版。 - 如果使用Docker运行Ollama,确保端口映射正确(
-p 11434:11434)。
- 首先运行
- 解决 :终止占用端口的进程,或为Ollama配置其他端口(通过环境变量
OLLAMA_HOST)。
问题2:Whisper识别速度慢,或者占用内存/CPU极高。
- 现象 :转录一句几秒钟的话需要十几秒,或者风扇狂转。
- 排查 :
- 确认模型尺寸。首次运行会下载模型,
large模型约3GB,加载和推理都慢。使用whisper.load_model("small")指定小模型。 - 检查是否在使用CPU。运行Python脚本时,观察任务管理器。如果希望用GPU,需安装
torch的CUDA版本(pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118)。 - 音频文件过长。Whisper对长音频是分段处理的,实时应用应使用短音频片段。
- 确认模型尺寸。首次运行会下载模型,
- 解决 :换用更小的模型(如
base或tiny),确保使用GPU,并优化录音长度。
问题3:AI代理不按格式输出,无法解析JSON。
- 现象 :模型回复了一堆自然语言,而不是我们规定的JSON格式。
- 排查 :
- 检查系统提示词。格式描述是否足够清晰、无歧义?是否强调了“必须且只能输出JSON”?
- 模型能力问题。较小的模型(如7B)的指令遵循和格式输出能力较弱。
- 温度(Temperature)设置过高。尝试将其降到0.1。
- 解决 :
- 强化提示 :在提示词中使用“你必须”、“严格遵循”、“只能输出”等强约束词语。提供一个完美的JSON输出示例。
- 后处理 :在解析代码中增加鲁棒性。像上面代码所示,尝试用正则表达式提取被
json包裹的内容,或者尝试解析任何看起来像JSON的文本块。 - 更换模型 :尝试
mistral或llama2:13b,它们通常有更好的指令遵循能力。
问题4:工具调用参数错误或执行失败。
- 现象 :模型输出了JSON,但执行工具时提示参数缺失或类型错误。
- 排查 :
- 检查工具描述中的参数定义是否和实际函数签名一致。
- 打印出模型输出的
args,看其值是否符合预期(例如,字符串是否带了引号)。
- 解决 :
- 在系统提示中,为每个参数提供明确的类型和示例。例如:
"filename (字符串,例如: 'report.txt')"。 - 在
_execute_tool函数中增加更详细的参数验证和类型转换逻辑(例如,将字符串数字转为整数)。
- 在系统提示中,为每个参数提供明确的类型和示例。例如:
问题5:实时录音结束时点判断不准。
- 现象 :要么还没说完就断了,要么说完后等了很久才识别。
- 排查 :简单的能量检测VAD在嘈杂环境或人说话停顿时很容易误判。
- 解决 :
- 使用专业的
webrtcvad库。它采用基于神经网络的检测算法,更准确。 - 调整静音持续时间阈值(
silence_duration)。室内安静环境可以设短一点(如0.5秒),嘈杂环境设长一点(如1.5秒)。 - 增加“语音开始前静音”检测,避免录入背景噪音的开头。
- 使用专业的
这个项目从概念到可运行的原型,涉及了语音处理、本地大模型部署、提示工程和代理系统设计等多个有趣的技术点。最大的成就感来自于看到一句口语化的指令,被一步步分解、执行,最终完成一个实际任务。虽然目前的代理还比较初级,但整个框架已经搭建起来。你可以在此基础上,为它添加更多强大的工具,比如发送邮件、查询数据库、控制智能家居,甚至集成视觉模型来处理图片。本地AI代理的世界,大门才刚刚打开。
更多推荐



所有评论(0)