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

最近在折腾一个挺有意思的东西:一个完全在本地运行的、能听懂你说话并执行任务的AI助手。想象一下,你对着电脑说一句“帮我总结一下上周的销售报告”,它就能自动打开文档、分析内容、生成摘要,整个过程无需联网,数据也完全留在你自己的机器上。这听起来像是科幻电影里的场景,但现在用开源工具就能自己搭建出来。

这个项目的核心,就是利用两个强大的开源模型:Whisper和Ollama。Whisper是OpenAI开源的语音识别模型,识别准确率非常高,尤其是对中文的支持相当不错。Ollama则是一个让你能在本地轻松运行各种大型语言模型(LLM)的工具,比如Llama 3、Mistral、Qwen等。把它们俩结合起来,Whisper负责“耳朵”,把你说的话转成文字;Ollama负责“大脑”,理解文字指令并规划行动;最后再通过一些脚本或工具调用,完成具体的任务,比如操作文件、查询信息、控制智能家居等。

我之所以花时间搞这个,主要是出于几个实际的考虑。第一是隐私,很多需要处理敏感信息或内部文档的场景,把数据上传到云端总让人不放心。第二是可控性,本地部署意味着你可以自定义一切,从模型选择到任务流程,完全按你的需求来。第三是成本,对于高频次的使用,本地化长期来看更经济,也没有API调用的次数限制。这个方案特别适合开发者、技术爱好者,或者对数据安全有要求的小团队,用来打造一个专属的、智能的桌面工作伴侣。

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

2.1 为什么是Whisper + Ollama?

搭建一个语音控制的AI代理,技术路径其实有不少。你可以用云服务的语音识别和对话API,但那违背了我们“本地化”和“隐私”的初衷。在本地语音识别领域,Whisper几乎是当前开源方案中的最优选。它支持多语言,在嘈杂环境下的识别鲁棒性很好,而且有不同规模的模型(tiny, base, small, medium, large)可供选择,可以根据你的硬件性能进行权衡。比如在CPU上跑,用 base small 模型就能获得不错的实时性;如果有GPU,那上 medium 甚至 large 模型,识别准确率会非常惊艳。

Ollama的出现,则彻底降低了本地运行大语言模型的门槛。以前你要自己处理模型下载、环境配置、推理加速,非常麻烦。Ollama把它变成了几条命令的事。它内置了丰富的模型库,一键拉取,并且对模型进行了优化,在消费级硬件(甚至苹果的M系列芯片)上也能跑出可用的速度。更重要的是,它提供了简洁的API,让我们可以像调用云端服务一样,用HTTP请求与本地模型交互,这极大地简化了开发流程。

这个组合的另一个优势是“松耦合”。Whisper只负责转文字,Ollama只负责理解和生成文本计划,具体的任务执行可以由Python脚本、系统命令或任何可编程的接口来完成。这种架构非常灵活,未来你想换一个更快的语音模型,或者换一个更擅长编程的LLM,只需要替换对应的模块即可,核心流程不用大改。

2.2 硬件与软件环境准备

在开始之前,我们需要评估一下自己的硬件。这是本地AI应用无法绕过的一环。

硬件建议:

  • CPU : 建议至少是近几年的i5或Ryzen 5及以上。多核有利于模型推理。
  • 内存 : 16GB是起步线 ,推荐32GB或以上。运行一个7B参数量的LLM,加上操作系统和其他应用,16GB会非常紧张,容易导致频繁交换内存,速度骤降。
  • 存储 : 需要预留至少20GB的可用空间,用于存放模型文件。
  • GPU (可选但强烈推荐) : 如果有NVIDIA显卡(显存6GB以上),体验会提升一个数量级。使用Ollama时,可以通过参数指定使用GPU,推理速度能快10倍不止。苹果M系列芯片的电脑也有很好的GPU加速支持。
  • 麦克风 : 一个清晰的麦克风是良好体验的基础。内置麦克风通常够用,但如果环境嘈杂,可以考虑外接一个。

软件基础环境:

  1. 操作系统 : Linux (Ubuntu/Debian等)、macOS或Windows (WSL2) 均可。我个人在Ubuntu和macOS上测试通过,流程最顺畅。Windows用户强烈建议使用WSL2来获得接近Linux的开发体验。
  2. Python : 需要Python 3.8或以上版本。我们将用Python来编写主控脚本。
  3. FFmpeg : Whisper处理音频文件依赖它。在Ubuntu上可以用 sudo apt install ffmpeg 安装,在macOS上用 brew install ffmpeg
  4. Ollama : 前往其官网下载并安装对应操作系统的版本。安装后,终端运行 ollama --version 确认安装成功。
  5. 代码编辑器 : VS Code、PyCharm等任选。

注意:模型下载的“墙”问题 。直接运行Ollama拉取模型,或者Python安装某些包时,可能会因为网络连接问题失败。对于Ollama,可以配置镜像源。对于Python包,可以使用国内镜像源(如清华源、阿里源)来加速安装。这是初期搭建时最常见的“坑”,务必优先解决网络连通性问题。

3. 核心模块搭建与配置详解

3.1 搭建听觉系统:Whisper的部署与优化

Whisper的安装非常简单。我们使用OpenAI官方提供的Python包。

pip install openai-whisper

安装完成后,你可以先测试一下命令行工具是否工作:

whisper audio_sample.mp3 --model base --language zh --output_dir ./transcript

这条命令会用 base 模型识别 audio_sample.mp3 中的中文语音,并将结果输出到 transcript 目录。 --model 参数是关键,它决定了识别速度和精度。下面是几个常用模型的对比:

模型大小 参数量 所需显存 (近似) 相对速度 适用场景
tiny 39M <1GB 最快 实时性要求极高,精度要求低,嵌入式设备。
base 74M ~1GB 很快 平衡之选,CPU上实时识别的常用型号。
small 244M ~2GB 精度显著提升,在有GPU或强CPU的机器上推荐。
medium 769M ~5GB 中等 高精度场景,需要GPU支持。
large 1550M >10GB 最高精度,通常用于离线转录,非实时。

对于我们的语音控制代理,追求的是“低延迟”和“足够准”。 我个人的经验是,在拥有GPU的机器上,使用 small 模型是最佳平衡点 。在纯CPU上, base 模型是保证实时性的底线。

然而,直接使用命令行工具不适合集成到我们的AI代理中。我们需要以编程方式调用Whisper。下面是一个Python函数示例,它实时监听麦克风,并在检测到静音时自动进行识别:

import whisper
import pyaudio
import numpy as np
import threading
import queue
import wave

class WhisperStreamingTranscriber:
    def __init__(self, model_size="base", language="zh", energy_threshold=500, pause_threshold=1.5):
        """
        初始化流式转录器
        :param model_size: Whisper模型大小,如 'base', 'small'
        :param language: 识别语言,'zh' 代表中文
        :param energy_threshold: 语音能量阈值,用于语音活动检测(VAD)
        :param pause_threshold: 静音持续时间(秒),超过则判定为一段话结束
        """
        print(f"正在加载Whisper {model_size}模型...")
        self.model = whisper.load_model(model_size)
        self.language = language
        self.energy_threshold = energy_threshold
        self.pause_threshold = pause_threshold
        self.audio_queue = queue.Queue()
        self.transcription = ""

        # 音频参数
        self.FORMAT = pyaudio.paInt16
        self.CHANNELS = 1
        self.RATE = 16000  # Whisper模型期望的采样率
        self.CHUNK = 1024

        self.audio = pyaudio.PyAudio()
        self.stream = None
        self.is_recording = False

    def _audio_callback(self, in_data, frame_count, time_info, status):
        """PyAudio回调函数,将音频数据放入队列"""
        audio_data = np.frombuffer(in_data, dtype=np.int16)
        self.audio_queue.put(audio_data.copy()) # 使用copy避免数据被覆盖
        return (in_data, pyaudio.paContinue)

    def start_listening(self):
        """开始监听麦克风"""
        self.stream = self.audio.open(
            format=self.FORMAT,
            channels=self.CHANNELS,
            rate=self.RATE,
            input=True,
            frames_per_buffer=self.CHUNK,
            stream_callback=self._audio_callback
        )
        self.stream.start_stream()
        self.is_recording = True
        print("麦克风已开启,请说话...")

    def stop_listening(self):
        """停止监听"""
        if self.stream:
            self.stream.stop_stream()
            self.stream.close()
        self.audio.terminate()
        self.is_recording = False
        print("麦克风已关闭。")

    def transcribe_audio_buffer(self, audio_buffer):
        """将音频缓冲区数据发送给Whisper进行识别"""
        # 将缓冲区数据转换为float32格式
        audio_np = np.concatenate(audio_buffer).astype(np.float32) / 32768.0
        # 调用Whisper识别
        result = self.model.transcribe(audio_np, language=self.language, fp16=False) # CPU上fp16=False
        return result["text"].strip()

# 使用示例
if __name__ == "__main__":
    transcriber = WhisperStreamingTranscriber(model_size="base", language="zh")
    transcriber.start_listening()
    input("按回车键停止并识别最后一段话...")
    transcriber.stop_listening()
    # 这里需要处理队列中的数据,进行VAD和识别,详见下文

这段代码搭建了一个基础的流式音频捕获框架。但其中缺少一个关键部分: 语音活动检测 。我们需要判断用户什么时候开始说话,什么时候停止。一个简单的方法是计算音频块的能量(音量),当能量连续超过阈值时认为是语音开始,当能量低于阈值持续一段时间(如 pause_threshold )后,认为一段话结束,触发识别。

实操心得:环境噪音处理 。在实际房间中,空调声、键盘声都是干扰。有两种处理方式:一是在代码中设置一个合理的 energy_threshold ,通过实验调整;二是在物理上使用指向性麦克风。我通常会在程序启动后,先让用户保持安静2秒,采集背景噪音的能量水平,然后以此为基础动态设置阈值,效果比固定值好很多。

3.2 构建思考中枢:Ollama与本地大模型

安装好Ollama后,第一步是拉取一个合适的模型。对于中文理解和通用任务, qwen2.5:7b llama3.2:3b 都是不错的起点。它们在7B或3B参数量下保持了较好的能力,对硬件要求相对友好。

# 拉取模型 (以Qwen2.5-7B为例)
ollama pull qwen2.5:7b

# 运行模型服务 (默认在11434端口)
ollama run qwen2.5:7b

ollama run 会启动一个交互式对话界面,但这并不是我们需要的。我们需要通过API来调用它。Ollama提供了与OpenAI API兼容的接口,这意味着我们可以使用 openai 这个Python库来调用本地模型,只需改一下 base_url

首先安装OpenAI库:

pip install openai

然后,我们可以这样编写一个与Ollama对话的类:

from openai import OpenAI
import json

class LocalLLMClient:
    def __init__(self, model_name="qwen2.5:7b", base_url="http://localhost:11434/v1", api_key="ollama"):
        """
        初始化本地LLM客户端
        :param model_name: Ollama中已拉取的模型名
        :param base_url: Ollama API地址
        :param api_key: 可任意填写,Ollama不验证此字段
        """
        self.client = OpenAI(
            base_url=base_url,
            api_key=api_key,
        )
        self.model_name = model_name

    def chat_completion(self, prompt, system_prompt=None, temperature=0.7, max_tokens=500):
        """
        发送对话请求
        :param prompt: 用户输入的提示词
        :param system_prompt: 系统提示词,用于设定AI的角色和行为
        :param temperature: 创造性,越高越随机,越低越确定
        :param max_tokens: 生成的最大token数
        :return: AI回复的文本
        """
        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_name,
                messages=messages,
                temperature=temperature,
                max_tokens=max_tokens,
                stream=False # 为简化示例,关闭流式输出
            )
            return response.choices[0].message.content
        except Exception as e:
            print(f"调用Ollama API出错: {e}")
            return None

    def generate_action_plan(self, user_command):
        """
        根据用户指令,生成一个可执行的动作计划(JSON格式)。
        这是本项目的核心逻辑之一。
        """
        system_prompt = """你是一个任务规划AI。用户会给你一个中文语音指令,你需要将其解析为一个结构化的JSON动作计划。
        可用的动作类型包括:
        1. "search_file": 在指定目录搜索文件。参数: "directory"(路径), "keyword"(文件名关键词)
        2. "read_summarize": 读取并总结文本文件。参数: "file_path"(文件路径)
        3. "web_search": 进行网络搜索(此功能需额外工具,此处仅作示例)。参数: "query"(搜索词)
        4. "run_script": 运行一个本地脚本。参数: "script_path"(脚本路径), "args"(参数列表)
        5. "answer_directly": 直接回答问题。参数: "answer"(回答内容)

        请只输出一个合法的JSON对象,不要有任何其他解释。
        示例:
        用户指令:“帮我找一下上个月的财务报告PDF。”
        输出:{"action": "search_file", "params": {"directory": "~/Documents", "keyword": "财务报告 上月 pdf"}}

        用户指令:“总结一下项目计划书的主要内容。”
        输出:{"action": "read_summarize", "params": {"file_path": "./docs/项目计划书.md"}}
        """

        prompt = f"用户指令:{user_command}\n请生成对应的动作计划JSON:"

        response_text = self.chat_completion(prompt, system_prompt=system_prompt, temperature=0.1) # 低temperature保证输出格式稳定
        if response_text:
            try:
                # 清理响应文本,可能包含markdown代码块标记
                cleaned_text = response_text.strip().replace('```json', '').replace('```', '')
                action_plan = json.loads(cleaned_text)
                return action_plan
            except json.JSONDecodeError as e:
                print(f"解析AI返回的JSON失败: {e}\n原始响应:{response_text}")
                return {"action": "answer_directly", "params": {"answer": f"我理解您的指令是:'{user_command}',但我暂时无法将其转化为具体操作。"}}
        return None

# 使用示例
if __name__ == "__main__":
    llm = LocalLLMClient(model_name="qwen2.5:7b")
    plan = llm.generate_action_plan("帮我打开今天写的代码文件")
    print(f"生成的计划:{plan}")

这个 generate_action_plan 函数是整个系统的“大脑”。它通过精心设计的 system_prompt ,引导LLM将模糊的自然语言指令,解析成结构化的、可编程执行的JSON命令。 这里的 system_prompt 设计是成败的关键 。你需要清晰地定义你的AI能做什么(动作类型),每个动作需要什么参数。LLM会根据这个“说明书”来工作。

注意事项:提示词工程 。让LLM稳定输出JSON格式有时是个挑战。除了在 system_prompt 中明确要求,还可以采用以下技巧:1. 使用 temperature=0.1 甚至 0 来降低随机性;2. 在 prompt 中给出更丰富的示例;3. 如果模型支持,可以使用其“JSON模式”(如果Ollama的模型有此功能)。如果解析失败,一定要有降级方案,比如让AI直接回答。

3.3 连接与执行:任务分发器的实现

现在,我们有了“耳朵”(Whisper)和“大脑”(Ollama),还需要一个“手脚”来执行大脑发出的命令。这就是任务分发器,它会根据 action_plan 中的 action 字段,调用不同的函数。

import subprocess
import os
import glob

class TaskExecutor:
    def __init__(self):
        self.action_handlers = {
            "search_file": self._handle_search_file,
            "read_summarize": self._handle_read_summarize,
            "run_script": self._handle_run_script,
            "answer_directly": self._handle_answer_directly,
            # 可以继续添加更多动作处理器
        }

    def execute(self, action_plan):
        """执行动作计划"""
        action_type = action_plan.get("action")
        params = action_plan.get("params", {})

        handler = self.action_handlers.get(action_type)
        if handler:
            return handler(params)
        else:
            return f"错误:不支持的动作类型 '{action_type}'。"

    def _handle_search_file(self, params):
        """处理文件搜索"""
        directory = os.path.expanduser(params.get("directory", ".")) # 支持 ~ 符号
        keyword = params.get("keyword", "")
        if not keyword:
            return "请提供要搜索的关键词。"

        # 简单的基于文件名的搜索
        pattern = os.path.join(directory, f"*{keyword}*")
        try:
            files = glob.glob(pattern)
            if files:
                # 只返回前5个结果,避免过长
                result_list = "\n".join(files[:5])
                return f"找到以下文件:\n{result_list}"
            else:
                return f"在目录 {directory} 中未找到包含 '{keyword}' 的文件。"
        except Exception as e:
            return f"搜索文件时出错:{e}"

    def _handle_read_summarize(self, params):
        """处理文件读取与总结"""
        file_path = os.path.expanduser(params.get("file_path", ""))
        if not file_path or not os.path.exists(file_path):
            return f"文件不存在或路径错误:{file_path}"

        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read(5000) # 限制读取长度,避免上下文过长

            # 这里可以再次调用LLM对content进行总结
            # 为简化示例,我们直接返回文件头几行
            preview = '\n'.join(content.split('\n')[:10])
            return f"文件 `{file_path}` 的前10行预览:\n```\n{preview}\n```\n(完整总结功能需调用LLM)"
        except Exception as e:
            return f"读取文件时出错:{e}"

    def _handle_run_script(self, params):
        """运行本地脚本"""
        script_path = params.get("script_path")
        args = params.get("args", [])
        if not script_path or not os.path.exists(script_path):
            return f"脚本不存在:{script_path}"

        try:
            # 注意:直接运行脚本有安全风险!务必确保脚本来源可信。
            cmd = [script_path] + args
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
            if result.returncode == 0:
                return f"脚本执行成功。输出:\n{result.stdout}"
            else:
                return f"脚本执行失败(代码{result.returncode})。错误:\n{result.stderr}"
        except subprocess.TimeoutExpired:
            return "脚本执行超时。"
        except Exception as e:
            return f"运行脚本时出错:{e}"

    def _handle_answer_directly(self, params):
        """直接回答"""
        return params.get("answer", "我已收到您的指令。")

# 使用示例
if __name__ == "__main__":
    executor = TaskExecutor()
    test_plan = {"action": "search_file", "params": {"directory": "~/Downloads", "keyword": "invoice"}}
    result = executor.execute(test_plan)
    print(result)

这个执行器是功能扩展的核心。每当你需要让AI代理具备一项新能力时,就在 action_handlers 字典里添加一个新的键值对,并实现对应的处理函数。例如,添加控制智能家居、发送邮件、查询数据库等功能,都是在这里进行扩展。

4. 系统集成与完整工作流

4.1 主控循环:将一切串联起来

我们把麦克风监听、语音识别、意图解析、任务执行这几个模块串起来,形成一个完整的闭环。

import time
from whisper_transcriber import WhisperStreamingTranscriber # 假设之前的类保存在这个文件
from local_llm import LocalLLMClient
from task_executor import TaskExecutor

class VoiceControlledAIAgent:
    def __init__(self):
        print("初始化语音控制AI代理...")
        self.transcriber = WhisperStreamingTranscriber(model_size="small", language="zh")
        self.llm_client = LocalLLMClient(model_name="qwen2.5:7b")
        self.executor = TaskExecutor()
        self.is_running = False

    def start(self):
        """启动代理主循环"""
        self.is_running = True
        self.transcriber.start_listening()
        print("代理已启动。说出你的指令(例如:'帮我找文档'),说完后保持安静即可。")
        print("说 '退出' 或 '停止' 来关闭程序。\n")

        audio_buffer = []
        last_voice_time = time.time()
        silence_duration = 0

        try:
            while self.is_running:
                # 1. 从队列获取音频块
                try:
                    audio_chunk = self.transcriber.audio_queue.get(timeout=0.1)
                    audio_buffer.append(audio_chunk)

                    # 简单VAD:计算当前块的能量
                    energy = np.sqrt(np.mean(audio_chunk**2))
                    if energy > self.transcriber.energy_threshold:
                        last_voice_time = time.time() # 检测到语音,重置静音计时
                        silence_duration = 0
                    else:
                        silence_duration = time.time() - last_voice_time

                except queue.Empty:
                    silence_duration = time.time() - last_voice_time if audio_buffer else 0

                # 2. 判断是否达到静音阈值,触发识别
                if silence_duration > self.transcriber.pause_threshold and len(audio_buffer) > 0:
                    print(f"\n检测到静音,开始识别...")
                    # 识别音频
                    text = self.transcriber.transcribe_audio_buffer(audio_buffer)
                    print(f"识别结果:{text}")

                    # 清空缓冲区,准备下一轮
                    audio_buffer = []

                    # 3. 处理指令
                    if text:
                        self._process_command(text)

        except KeyboardInterrupt:
            print("\n接收到中断信号。")
        finally:
            self.stop()

    def _process_command(self, command_text):
        """处理识别到的文本指令"""
        # 退出指令
        if any(exit_cmd in command_text.lower() for exit_cmd in ["退出", "停止", "quit", "exit"]):
            print("收到退出指令。")
            self.is_running = False
            return

        print(f"正在分析指令:{command_text}")
        # 4. 调用LLM生成动作计划
        action_plan = self.llm_client.generate_action_plan(command_text)
        if not action_plan:
            print("未能生成有效动作计划。")
            return

        print(f"生成计划:{action_plan}")
        # 5. 执行计划
        result = self.executor.execute(action_plan)
        print(f"执行结果:{result}\n")

    def stop(self):
        """停止代理"""
        self.is_running = False
        self.transcriber.stop_listening()
        print("AI代理已停止。")

if __name__ == "__main__":
    agent = VoiceControlledAIAgent()
    agent.start()

这个 VoiceControlledAIAgent 类就是整个系统的“总指挥”。它在一个循环中不断检查麦克风输入,利用简单的能量检测实现端点检测,在用户说完一句话后自动触发识别和后续流程。这是一个基础的、可工作的原型。

4.2 唤醒词与持续对话优化

上面的实现是“按讲”模式,即用户需要手动触发或等待静音。一个更自然的交互是加入 唤醒词 ,比如“小智小智”,只有听到唤醒词后,AI才开始聆听后续指令。这可以用一个更轻量级的、专门做唤醒词检测的模型(如VAD或Porcupine)来实现,或者用Whisper本身做实时流式识别,但只对包含特定关键词的片段进行后续处理。

另一个优化点是 持续对话 。目前的实现是单轮对话,AI执行完一个命令就结束了。要实现多轮,需要在 LocalLLMClient 中维护一个对话历史( messages 列表),每次将新的用户指令和AI的回复追加进去。但要注意上下文长度限制,当历史过长时,需要做摘要或丢弃最早的对话。

5. 性能调优、问题排查与安全考量

5.1 性能瓶颈分析与优化

本地AI应用的性能瓶颈通常集中在两方面: 语音识别的延迟 大模型推理的速度

针对Whisper的优化:

  1. 模型量化 :使用Whisper的 .en 版本(纯英文)或更小的模型。社区也有许多量化版本(如通过 ctranslate2 库),能在几乎不损失精度的情况下大幅提升速度。
  2. GPU加速 :如果机器有CUDA显卡,确保安装了 torch 的GPU版本,Whisper会自动利用GPU。
  3. 流式识别 :我们上面的示例是“端到端”识别,即等一句话说完再整句识别。更高级的做法是使用Whisper的流式API(需要更复杂的处理),实现“边说边转”,延迟感更低。

针对Ollama的优化:

  1. 使用GPU :运行Ollama时,可以通过环境变量或修改配置强制使用GPU: OLLAMA_GPU=1 ollama run qwen2.5:7b
  2. 选择更小的模型 :3B、7B的模型响应速度远快于13B、70B的模型。对于任务规划这类相对简单的逻辑,7B模型通常足够。
  3. 调整参数 :在调用API时,设置 num_predict (最大生成token数)为一个合理的值,避免生成过长无关内容。 temperature 调低也能让响应更快、更确定。
  4. 模型量化 :Ollama拉取的很多模型已经是4位或8位量化版本(如 qwen2.5:7b-q4_K_M ),在速度和内存占用上做了优化。

5.2 常见问题与排查实录

在搭建和运行过程中,你几乎一定会遇到下面这些问题:

问题现象 可能原因 排查步骤与解决方案
Ollama拉取模型失败 网络连接问题。 1. 检查网络。2. 配置Ollama镜像源: export OLLAMA_HOST=镜像源地址 (需寻找可用镜像)。3. 手动下载模型文件并加载。
调用Ollama API超时或无响应 Ollama服务未启动;端口被占用;模型未加载。 1. 运行 ollama serve 查看服务日志。2. 检查11434端口是否监听: netstat -an | grep 11434 。3. 确认模型已通过 ollama list 列出。
Whisper识别速度极慢 使用了过大的模型(如large);在CPU上运行。 1. 换用 tiny , base small 模型。2. 检查是否安装了GPU版本的PyTorch: python -c "import torch; print(torch.cuda.is_available())"
识别结果全是英文或乱码 未指定语言或语言错误。 transcribe 函数中明确指定 language="zh" 。对于中英混杂场景,可以不指定语言,让模型自动检测。
麦克风无法捕获音频 权限问题;PyAudio找不到设备。 1. Linux/macOS检查麦克风权限。2. 尝试更换PyAudio后端或使用 sounddevice 库。3. 列出音频设备: python -c "import pyaudio; p = pyaudio.PyAudio(); [print(i, p.get_device_info_by_index(i)['name']) for i in range(p.get_device_count())]"
LLM生成的JSON格式错误 提示词不够清晰;模型“不听话”。 1. 强化 system_prompt 中的格式要求。2. 在 prompt 中提供更精确的示例。3. 使用 temperature=0 或更低。4. 在代码中添加更健壮的JSON解析和错误处理,尝试修复常见格式错误。
程序运行一会儿就卡死或内存爆炸 内存/显存泄漏;音频队列堆积。 1. 监控内存使用。2. 确保音频数据在被处理后从队列中及时移除。3. 对于长时间运行,考虑定期清理LLM的对话历史。

5.3 安全与隐私考量

这是本地AI代理最大的优势,但也需注意以下几点:

  1. 脚本执行风险 TaskExecutor _handle_run_script 函数非常强大,但也非常危险。 绝对不要让AI拥有执行任意命令或脚本的能力,尤其是在生产环境或联网环境中 。应该将其限制在少数几个你明确知道其功能的、安全的脚本白名单内。
  2. 文件访问范围 :同样,文件搜索和读取功能也应限制在特定的、非敏感目录下,避免AI意外读取或泄露隐私文件。
  3. 模型本身 :虽然数据不离线,但你使用的开源LLM和语音模型本身是否有潜在偏见或安全问题,也需要有所了解。建议从官方或可信渠道获取模型。
  4. 网络隔离 :如果你不需要AI进行网络搜索等功能,最好在断网环境下运行,形成物理隔离,这是最彻底的隐私保护。

搭建这样一个语音控制的本地AI代理,就像在组装一个乐高机器人。Whisper和Ollama提供了最核心、最优质的部件,而如何设计它的“行为逻辑”(提示词),如何为它打造“四肢”(任务执行器),则完全取决于你的想象力和编程能力。从简单的文件管理,到复杂的工作流自动化,这个框架的潜力是巨大的。我自己的使用体会是,初期把提示词和任务执行器设计得稳健、安全比追求功能的繁多更重要。先让它在有限的、明确的领域可靠地工作,再逐步扩展它的能力边界,这个过程本身就像在训练一个数字伙伴,充满了探索的乐趣。

Logo

免费领 100 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐