1. 项目概述:一个复活节周末诞生的本地AI操作系统
如果你和我一样,对市面上的AI助手感到厌倦——它们大多只是带了个麦克风的聊天机器人,除了闲聊和设定闹钟,干不了什么正经事——那你可能会对这个项目感兴趣。我叫它 JARVIS OS,一个完全运行在你本地硬件上的AI操作系统。它不依赖任何云端API,你的数据、你的记忆、你的命令,全部都在你自己的电脑里。这个想法源于我对所谓“AI革命”的长期怀疑:太多新瓶装旧酒,核心的数据流转、存储、查询问题其实没变。但ReAct模式(推理-行动循环)的出现改变了我的看法,一个能真正使用工具、执行多步骤任务的AI,才是质的不同。于是,在一个复活节周末,我撸起袖子,用我的游戏PC(RTX 3090)、WSL2和Ollama,把它从想法变成了现实。
现在,它已经稳定运行了一周多,处理着从控制家庭影院、管理智能灯光,到编写代码、接听电话等20多项技能。最让我自豪的不是它有多“智能”,而是它有多“实在”——一个你可以完全掌控、审计甚至亲手修改其“大脑”的系统。这篇文章,我会毫无保留地拆解整个系统的架构、实现细节、踩过的坑,以及那些让AI从“玩具”变成“伙伴”的关键设计。无论你是想复现一个类似的系统,还是仅仅想了解如何将大模型深度集成到你的工作流中,这里都有你需要的干货。
2. 核心架构与设计哲学
2.1 为什么是“操作系统”,而不是“助手”?
市面上几乎所有的AI助手,无论是云端的还是本地的,其设计范式都是“问答机”。你提问,它生成回答,顶多再调用一两个预定义好的API。这本质上和二十年前的命令行脚本没有区别,只是自然语言界面更友好些。JARVIS OS的设计目标截然不同:它要成为一个协调中心,一个能理解复杂意图、自主分解任务、调用一系列工具并管理其自身状态和记忆的执行环境。
这其中的关键跃迁,在于从“单次调用”到“持续循环”的转变。一个真正的操作系统,其核心是一个调度器(Scheduler)和资源管理器。JARVIS OS的“内核”就是这个基于ReAct模式的循环。它接收一个意图(比如“为我营造一个浪漫的氛围”),然后进入“思考-行动-观察”的循环:思考需要哪些工具(调暗灯光、播放音乐、打开壁炉视频),依次调用它们,观察结果,并根据结果决定下一步行动,直到任务完成或达到迭代上限。这个循环让它具备了处理开放式、多步骤任务的能力,这是它与所有“聊天机器人式”助手的根本区别。
2.2 三层架构:清晰的责任边界
为了实现上述目标,整个系统被清晰地划分为三层,每一层职责单一,通过明确的接口通信。
第一层:路由器(Router)这是系统的第一道关卡,也是性能与可靠性的基石。它的核心职责是进行意图的快速分类和路由,决定后续的处理路径。并非所有指令都需要动用庞大的语言模型。很多日常高频操作是确定性的,比如“播放电台Nova”、“打开浏览器”。让一个300亿参数的模型去理解并执行这些命令,不仅是杀鸡用牛刀,更会引入不必要的延迟(2-3秒)和潜在的误解风险。
路由器的实现基于关键词匹配和简单的规则引擎。它会扫描输入文本,匹配预定义的技能关键词(如 “play”, “switch to”, “volume up”)。如果匹配成功,则直接调用对应的技能函数,完全绕过AI模型。只有在指令模糊、需要推理或涉及多工具协调时,才会被路由到ReAct循环。这种设计哲学我称之为“AI最小化”:只在真正需要智能的地方使用AI。这极大地提升了系统的响应速度和确定性,用户体验上就是“快且准”。
第二层:ReAct循环服务器(ReAct Loop Server)这是系统的“大脑”或“调度中心”,运行在独立的Python服务(react_server.py)中,监听特定端口(如7900)。它封装了ReAct模式的核心逻辑:
- 思考(Think):根据当前查询和上下文,决定下一步需要调用哪个工具(或技能)。
- 行动(Act):执行选定的工具,并获取其输出(可能是成功/失败的状态,也可能是具体的数据,如查询结果、设备状态)。
- 观察(Observe):将工具执行的结果作为新的观察,合并到上下文中。
- 循环:重复步骤1-3,直到模型认为已经得出最终答案,或达到最大迭代次数(我设置为5次,防止死循环)。
为了让交互更自然,我在这里加入了一个重要的UX优化:即时确认。当系统接收到一个明显需要多步处理的复杂命令时(例如,“帮我查一下上个月的Git提交记录,并总结成邮件草稿”),在ReAct循环开始之前,会立即、确定性地回复一句“好的,正在处理”。这是硬编码的,不经过任何模型推理。这解决了AI助手常见的“沉默期”问题——用户说完后不知道它是否听见、是否在干活。一个即时的状态反馈,能极大提升交互的安心感。
第三层:技能层(Skills)这是系统的“手脚”,是具体能力的实现。每个技能都是一个独立的Python模块,存放在skills/目录下。这种插件化架构是系统可扩展性的核心。
- 注册机制:每个技能在加载时,会向系统注册两类信息:1) 它提供的工具函数列表;2) 它关心的关键词或意图模式。
- 工具函数:这是技能暴露给ReAct循环的接口。一个工具函数接收参数,执行操作(如调用设备API、查询数据库、运行Shell命令),并返回结构化的结果。
- 自包含:技能模块应尽可能自包含,管理自己的依赖、配置和状态。新增一个技能,理论上只需要将文件放入目录并重启服务即可生效。
目前实现的20多个技能覆盖了多个领域:
- 家庭自动化:Denon功放、NVIDIA Shield电视盒、LG电视、飞利浦Hue灯光、Plex媒体服务器的控制。
- 开发工具:Git操作(提交、拉取、查看日志)、Shell命令执行、简单的网络扫描、网页搜索。
- 通讯:通过Twilio接打电话和收发短信,通过SMTP/IMAP发送邮件。
- 记忆与知识:与MemPalace向量数据库和Obsidian笔记库交互。
- 多媒体:通过ComfyUI调用FLUX进行AI生图,通过Orpheus进行TTS,通过Whisper进行STT。
- 系统工具:KeePass密码查询、Windows应用切换、剪贴板管理。
一个强大的特性是技能链(Skill Chaining)。一个技能可以调用其他技能的工具。例如,“夜间模式”这个技能本身不直接操作任何设备,它的逻辑是:1) 调用hue_skill调暗灯光;2) 调用denon_skill切换音频输出到耳机并降低音量;3) 调用shield_skill暂停视频播放。用户一个指令,背后是多个技能的协同作业。这实现了从“执行预定流程”到“根据意图动态组装流程”的跨越。
3. 硬件、模型与本地化部署实战
3.1 硬件配置与系统环境选择
我的核心硬件是一台Windows 11游戏PC,搭载了NVIDIA RTX 3090显卡(24GB显存)、64GB内存和高速NVMe SSD。选择Windows 11 + WSL2 (Ubuntu)的方案,是基于实用性的权衡:
- GPU直通:WSL2现在支持GPU的CUDA直通(需要安装WSL2专用的NVIDIA驱动)。这意味着Ubuntu子系统可以几乎无损地访问宿主Windows的RTX 3090的全部性能,用于Ollama的模型推理。避免了双系统启动的麻烦,也绕过了完整虚拟机的性能开销。
- 职责分离:让Windows负责它擅长的部分——音频路由(通过Voicemeeter等工具可以精细管理输入输出)、家庭自动化网络调用(许多智能家居设备的Windows客户端更稳定)、以及作为日常办公娱乐的主系统。让Linux (WSL2) 专注于运行所有AI相关的Python服务、Ollama和模型推理。这种混合架构在实践中非常稳定。
如果你的目标是纯AI开发,原生Linux可能是更干净的选择。但对于一个需要兼顾日常使用和AI服务的“个人操作系统”,WSL2提供了绝佳的平衡点。
关于多GPU:单个RTX 3090的24GB显存对于同时运行多个大模型是捉襟见肘的。我的计划是“征用”家里旧电脑的RTX 3090和RTX 2080,通过PCIe延长线组建多GPU系统。这部分硬件操作和我之前搭建加密货币矿机的经验是相通的。软件层面的挑战在于,需要改造Ollama和技能系统,使其能够感知不同GPU,并将模型推理任务路由到指定的GPU实例上,实现真正的模型并行加载,彻底告别显存换入换出带来的延迟。
3.2 模型选型与显存管理策略
模型的选择是性能、效果和资源约束的三角平衡。我的原则是:按需加载,分而治之。下表是我的模型配置:
| 模型 | 显存占用 (约) | 架构 | 角色与加载策略 |
|---|---|---|---|
| qwen3:8b | ~5GB | Dense (稠密) | 常驻。用于快速查询、闲聊、简单推理。响应快,负担小。 |
| qwen3:30b-a3b | ~18GB | MoE (混合专家,约30B激活3B) | 按需加载。用于复杂分析、工具使用、策略思考。知识量大,推理能力强。 |
| qwen3-coder:30b | ~18GB | MoE (混合专家,约30B激活3B) | 按需加载。专用于代码生成、代码理解等开发任务。 |
| Orpheus 3B TTS | ~3GB | Dense | 常驻。文本转语音,需要快速响应,故常驻。 |
| FLUX (via ComfyUI) | ~12GB | Diffusion (扩散模型) | 按需加载。图像生成。与大型语言模型无法共存。 |
| Whisper STT | ~1GB | Encoder-only | 常驻。语音识别,需要实时处理音频流。 |
关键解读:
- MoE架构的魔力:
qwen3:30b-a3b这类模型是“混合专家”架构。它拥有约300亿参数的总知识量,但每次前向传播(推理)时,只激活其中约30亿参数。这就像有一个庞大的专家委员会,每次只请几位相关的专家出来回答问题。因此,它既能提供接近300B模型的丰富知识,又只需消耗约180B模型的显存和计算资源,实现了效果和效率的兼得。 - 显存博弈:所有模型如果同时加载,总显存需求约57GB,远超3090的24GB。因此,动态加载/卸载是必须的。Whisper、Qwen3-8B和Orpheus这三个轻量且高频使用的模型常驻显存。当需要运行30B模型或FLUX生图时,系统会卸载当前不用的重型模型,加载所需的模型。这个过程(尤其是生图时)可能需要15-20秒。为了不让用户感到“卡死”,在触发重载任务时,JARVIS会立即给出一个“正在加载模型,请稍候”的语音反馈。
- 路由与模型的匹配:路由器不仅决定是否走AI路径,还决定走哪条AI路径。简单查询路由到8B模型,复杂分析和工具使用路由到30B-a3b模型,代码任务路由到30B-coder模型。这确保了“好钢用在刀刃上”。
3.3 音频链:让AI的声音融入环境
TTS(文本转语音)的输出质量直接影响AI的“存在感”。我选择Orpheus 3B这个开源模型,是因为它在音质和自然度上达到了接近商业产品的水平。但硬件和路由同样重要。
我的音频输出不是简单的桌面音箱。JARVIS的语音通过电脑声卡输出,接入一台Denon AVR-X4100W功放,并专门指定从中央声道(Center Channel)输出。在5.1环绕声系统中,中央声道是专门负责对白人声的。电影中人物的对话之所以感觉定位精准、清晰且富有沉浸感,正是因为这个设计。将AI的语音也路由到中央声道,会产生奇妙的效应:声音仿佛从房间正前方传来,沉稳而清晰,与背景音乐(通过其他声道播放)分离得非常好,极大地减少了听觉疲劳,提升了真实感。
此外,Denon功放本身也通过HTTP API被JARVIS控制。这意味着可以创建诸如“耳机模式”这样的复合技能:一键将音频输出从音箱切换到耳机,并自动调整到合适的音量。这种深度集成,让AI不再是电脑里的一个软件,而是真正融入家庭音频环境的一个“声源”。
4. 核心实现:从代码到交互的细节
4.1 ReAct循环服务器的实现剖析
react_server.py是这个系统的引擎。其核心是一个循环,但实现上有很多细节决定体验。
# 伪代码,展示核心逻辑 class ReActAgent: def __init__(self, llm_client, tools): self.llm = llm_client self.tools = tools # 所有注册的工具字典 self.max_iterations = 5 def run(self, query, context): # 0. 即时确认(如果判断为复杂任务) if self._is_complex_task(query): self.tts.say("好的,正在处理。") # 1. 初始化 full_context = f"历史: {context}\n当前目标: {query}" for i in range(self.max_iterations): # 2. 思考 (Think) prompt = self._build_think_prompt(full_context) llm_response = self.llm.generate(prompt) # 解析LLM响应,提取“思考”和要执行的“动作” thought, action_name, action_input = self._parse_llm_response(llm_response) # 如果LLM认为可以给出最终答案了 if action_name == "Final Answer": final_response = action_input break # 3. 行动 (Act) if action_name not in self.tools: # 处理工具不存在的情况 observation = f"错误:工具 '{action_name}' 不存在。" else: tool_func = self.tools[action_name] try: # 执行工具,获取结果 observation = tool_func(**action_input) except Exception as e: observation = f"工具执行出错: {str(e)}" # 4. 观察 (Observe) & 更新上下文 full_context += f"\n\n思考: {thought}\n行动: {action_name}({action_input})\n观察: {observation}" # 5. 返回最终结果 return final_response关键实现点:
- 提示工程(Prompt Engineering):构建给LLM的提示(Prompt)至关重要。它需要清晰地定义格式(如用
Thought:,Action:,Action Input:,Observation:等标签),列出所有可用工具及其描述和参数格式,并给出几个少样本(Few-shot)示例。好的提示能让模型稳定地输出可解析的结构化文本。 - 工具描述:每个工具在注册时,必须提供清晰、机器可读的描述,包括功能、输入参数格式和返回值的说明。这部分描述会直接拼接到提示词中,指导LLM如何选择和使用工具。
- 错误处理与鲁棒性:循环中必须妥善处理LLM输出格式错误、工具调用失败、网络超时等各种异常。良好的错误处理能让系统在部分失败时依然能给出有意义的响应,而不是直接崩溃。
- 上下文管理:每次“观察”的结果都要追加到上下文中,作为下一轮“思考”的依据。需要小心管理上下文长度,避免无限增长导致模型性能下降或显存溢出。对于长对话,可能需要一个摘要(Summarization)机制来压缩历史。
4.2 技能(Skill)开发指南
技能是系统的扩展单元。创建一个新技能,本质上就是编写一个Python类,并实现几个约定的方法。
# 示例:一个简单的“系统信息”技能 import psutil from skills.base_skill import BaseSkill class SystemInfoSkill(BaseSkill): """提供系统资源信息(CPU、内存、磁盘)的技能。""" def __init__(self): super().__init__() self.skill_name = "system_info" # 注册本技能提供的工具 self.register_tool("get_cpu_usage", self.get_cpu_usage) self.register_tool("get_memory_info", self.get_memory_info) # 注册本技能响应的关键词 self.register_keyword("cpu") self.register_keyword("memory") self.register_keyword("disk") self.register_keyword("系统状态") def get_cpu_usage(self): """获取当前CPU使用率。""" usage = psutil.cpu_percent(interval=1) return {"status": "success", "data": f"当前CPU使用率为 {usage}%"} def get_memory_info(self): """获取内存使用情况。""" mem = psutil.virtual_memory() total_gb = mem.total / (1024**3) used_gb = mem.used / (1024**3) percent = mem.percent return { "status": "success", "data": f"内存总量 {total_gb:.1f} GB,已使用 {used_gb:.1f} GB ({percent}%)" } # 技能可以有自己的后台线程或状态 def on_load(self): print(f"技能 {self.skill_name} 已加载。") def on_unload(self): print(f"技能 {self.skill_name} 已卸载。")技能开发要点:
- 继承基类:通常有一个
BaseSkill类,提供工具注册、关键词注册、配置加载等通用功能。 - 工具函数:工具函数应尽量保持幂等性(多次调用结果相同)和无副作用(除非必要)。输入参数应做类型检查和验证。返回值应是一个结构化的字典,至少包含
status(成功/失败)和data(具体数据或错误信息)。 - 错误处理:技能内部应捕获尽可能多的异常,并返回友好的错误信息,而不是抛出异常导致整个ReAct循环中断。
- 配置化:技能的配置(如API密钥、设备IP地址)应通过配置文件或环境变量管理,避免硬编码。
- 异步支持:如果工具调用涉及网络I/O(如控制智能家居),应考虑使用异步函数(
async/await)以避免阻塞主循环。
4.3 记忆系统:可审计的“大脑”
这是JARVIS OS最让我满意的设计之一。我拒绝使用一个黑盒的、无法查看和修改的向量数据库作为唯一记忆。系统的记忆分为两层,且都是可读、可写、可审计的。
第一层:Obsidian知识库(结构化记忆)这是一个存储在本地磁盘上的Markdown文件库。所有我主动记录的项目笔记、技术决策、个人偏好、联系信息等都存放在这里。JARVIS可以通过技能读取和搜索这些文件。它的优势在于:
- 完全透明:我可以用任何文本编辑器打开、修改、删除任何“记忆”。
- 结构自由:我可以按照自己的思维习惯组织笔记,建立双向链接。
- 归属感强:这是“我”的知识,AI只是在辅助访问它。如果AI基于错误记忆做出了错误推断,我可以直接找到对应的Markdown文件进行修正。
第二层:MemPalace向量数据库(情景记忆)这是一个专门记录JARVIS与我的每一次交互的向量数据库。每次对话、执行的每个命令、产生的结果,都会被自动记录,并转换成向量存储起来。它的作用是提供语义搜索能力。当我说“上周我们讨论的那个关于用户登录优化的想法是什么?”,MemPalace可以通过语义相似度,找到相关的过往对话记录。
两者的协同:
- 长期 vs 短期:Obsidian存储的是长期、结构化的知识。MemPalace存储的是短期、情景化的交互记录。
- 精确 vs 模糊:当需要精确信息(如某个项目的API密钥)时,查询Obsidian。当需要回忆模糊的过往对话时,查询MemPalace。
- 主动 vs 被动:Obsidian的内容是我主动编写的。MemPalace的内容是系统被动记录的。
这种设计彻底改变了人机关系。我不再是向一个神秘的黑盒提问,而是在与一个我可以随时翻开其笔记本进行查阅和批改的伙伴协作。这种“可审计性”带来了前所未有的信任感和控制感。
5. 实战中遇到的“硬骨头”与解决方案
5.1 语音交互的魔鬼细节:环境噪音与自我回声
问题一:Whisper的“幻听”在真实的家庭环境中,背景噪音无处不在:音乐、电视声、家人谈话、厨房电器声。Whisper模型在转录时,会试图将所有音频都解释为语音,导致它将背景音乐甚至白噪音“幻听”成毫无意义的句子,这会错误地触发JARVIS。简单的静音检测(VAD)阈值调整效果有限,因为音乐和人声的能量级别可能相似。
我的解决方案(组合拳):
- 能量门限与频谱分析:在音频送入Whisper之前,先进行预处理。设置一个比普通VAD更高的能量阈值。同时,进行简单的频谱分析,持续性的、谐波丰富的信号(如音乐)与人声的频谱特征有区别,可以部分过滤。
- 上下文过滤(治标不治本):对Whisper的转录结果进行后处理,如果文本完全无法理解或包含大量无意义字符,则直接丢弃该次输入。但这会误伤一些口音重或发音不清的指令。
- 硬件指向性麦克风(终极方案):我最终增加了一个USB指向性麦克风,并将其放置在靠近我常坐位置的地方。物理上减少环境噪音的拾取,是最有效的方法。软件优化可以改善,但无法根除物理问题。
问题二:自我回声(Self-Transcribing)这是一个有趣的问题:当JARVIS通过音箱说话时,麦克风也会收录它自己的声音,Whisper会再次将其转录为文本,可能导致循环响应或误触发。
我尝试过的复杂方案(并放弃了):最初我想通过声学回声消除(AEC)或语音活动检测(VAD)来区分用户语音和TTS输出。这需要复杂的音频信号处理,且效果不稳定。
最终采用的“简单粗暴”方案:既然TTS的文本内容是JARVIS自己生成的,那么当Whisper转录出一段文本时,我只需要将这段文本与JARVIS最近几秒内说过的文本进行字符串匹配。如果匹配度很高,就认为这是自我回声,直接丢弃。这个方法实现简单,效果却出奇地好。它不依赖任何音频层面的分析,纯粹在文本层面解决问题,完美规避了音频处理的复杂性。这再次印证了一个道理:在工程中,最简单的、直击问题核心的方案,往往是最有效的。
5.2 多GPU与模型热加载的工程挑战
随着技能增多,单一GPU的显存瓶颈日益突出。尤其是当我想同时保持一个大型语言模型(如30B)常驻以供快速响应,又不想在生图(FLUX)时等待漫长的模型切换,多GPU就成了必选项。
硬件层面:使用PCIe延长线将第二块、第三块显卡连接到主板,并确保电源供电充足。这部分和装一台多卡矿机或深度学习工作站没有区别。
软件层面的挑战与方案:
- 多Ollama实例:无法让一个Ollama进程同时管理多块GPU上的不同模型。我的方案是为每块GPU运行一个独立的Ollama服务实例,每个实例绑定到特定的GPU ID(通过
CUDA_VISIBLE_DEVICES环境变量控制)。 - 模型路由:需要在ReAct服务器或一个专门的“模型调度器”中维护一个模型-GPU的映射表。例如:
{‘qwen3:8b’: ‘gpu:0’, ‘qwen3:30b-a3b’: ‘gpu:1’, ‘flux’: ‘gpu:2’}。 - 技能适配:调用AI模型的技能(如对话技能、生图技能)需要被修改。它们不再直接调用一个固定的Ollama API地址,而是向“模型调度器”发起请求,由调度器根据请求的模型类型,将请求转发到对应GPU上的Ollama实例。
- 热加载优化:即使有了多GPU,一些大型模型(如不同的30B模型)可能仍需在同一个GPU上切换。为了减少延迟,可以实现一个模型缓存池。对于近期使用过的模型,不是完全从显存中卸载,而是将其权重换出到GPU的共享内存或甚至高速NVMe SSD上(如果Ollama支持),下次加载时速度会快很多。
这部分是正在进行的工作,核心思想是将“计算资源”抽象化,让上层的技能无需关心模型具体跑在哪张卡上,由调度系统统一管理,实现资源利用的最大化。
5.3 远程访问与安全:MCP服务器与Unifi Teleport
我希望即使不在家,也能通过我的笔记本电脑访问JARVIS的“大脑”(主要是Obsidian知识库)。但我不愿意将任何服务直接暴露在公网上。
解决方案:MCP服务器 + Unifi Teleport隧道
- MCP服务器:我在JARVIS主机上运行了一个轻量级的MCP(Model Context Protocol)服务器。这个服务器的唯一功能,就是提供对本地Obsidian仓库和代码仓库的安全访问接口。
- Unifi Teleport:这是Ubiquiti UniFi路由器提供的一个“按需VPN”功能。它不需要复杂的端口转发、动态DNS或传统的VPN配置。我只需在UniFi控制器中启用Teleport,然后在笔记本电脑上安装Teleport客户端并扫码认证。当我在外需要连接时,启动客户端,它就会在我和家庭网络之间建立一个安全的点对点WireGuard隧道。
- 工作流:笔记本电脑(在外) -> 启动Unifi Teleport连接 -> 连接成功后,笔记本电脑仿佛就在家庭局域网内 -> 我可以在笔记本电脑上使用Claude等支持MCP客户端的AI工具,直接连接到家庭网络中JARVIS主机上的MCP服务器 -> Claude因此获得了和家里JARVIS完全相同的上下文(Obsidian笔记)。
这个方案的优雅之处在于:
- 安全:没有永久开放的端口。连接是按需建立,且使用现代加密协议。
- 便捷:对于已有UniFi设备的用户,设置只需几分钟。
- 无缝:实现了上下文的无缝同步,让我在任何地方都能延续同样的AI辅助工作流。
6. 未来演进与反思
6.1 自我进化的技能系统:从记录到创造
目前的技能需要我手动编写。但系统已经具备了自我进化的基础条件:
- 记忆:MemPalace记录了所有的交互历史。
- 知识:Obsidian存储了结构化的流程和项目文档。
- 执行能力:ReAct循环可以调用现有工具。
设想的闭环:
- 模式发现:定期分析MemPalace中的交互记录,通过聚类或简单统计,发现我频繁执行的、模式固定的任务序列(例如,每次开始写代码前,都会执行“打开IDE、打开终端、拉取最新代码、打开相关文档”这一系列操作)。
- 技能提案:让LLM(如Qwen-Coder)分析这个模式,并基于现有的基础工具(
open_application,git_pull,open_obsidian_note),自动生成一个新的Python技能模块代码。这个新技能将这一系列操作封装成一个单一命令,比如start_coding_session [project_name]。 - 安全审核与部署:生成的代码不会直接生效。系统会将其保存为一个待审核的草稿,并通知我。我可以审查代码,确认无误后,将其放入
skills/目录。重启服务后,这个新技能就诞生了。 - 迭代优化:新技能被使用后,其执行日志又会被记录到MemPalace,形成新的数据,可能在未来触发对该技能的进一步优化或重构。
这不再是调整模型的权重,而是让系统的行为逻辑基于使用经验自动扩展和优化。它从“我教它做事”变成了“它看我做事,然后学会帮我做得更好”。
6.2 经验教训:什么不该让AI做
构建这个系统的最大收获之一,是明确了AI的边界。初期,我怀揣着“AI优先”的热情,试图让LLM处理所有事情,包括那些最简单的、确定性的命令。结果就是响应慢,且偶尔会出一些匪夷所思的错(比如让AI“播放音乐”,它可能会开始长篇大论地介绍音乐史)。
“Play Radio Nova” 这个命令是最好的老师。它就是一个简单的字符串匹配和函数调用。通过路由器将其直接路由到play_radio技能,延迟在毫秒级,且100%准确。如果走ReAct循环,延迟增加数秒,且存在误解析的微小风险(比如把“Nova”解析成其他东西)。
因此,我总结出的原则是:凡是有明确、单一映射关系的操作,都应该用确定性逻辑(规则、查找表)来处理;只有在需要理解模糊意图、处理未知情况、进行复杂规划或创造时,才动用LLM。一个强大的AI系统,其智能恰恰体现在它知道何时不该使用“重智能”,而是使用“轻逻辑”。这种架构上的“混合智能”(Hybrid Intelligence)设计,才是系统既强大又可靠的关键。
6.3 给后来者的建议
如果你想尝试构建类似的系统,我的建议是:
- 从核心循环开始:先实现一个最小可用的ReAct循环,只连接1-2个最简单的工具(比如查询天气、计算器)。把交互流程跑通。
- 重视路由与架构:在早期就引入路由器的概念,明确区分“确定性命令”和“需推理命令”。这会让你的系统架构清晰,后期不易变成一团乱麻。
- 语音放在最后:文本交互(命令行或聊天框)是调试和验证逻辑最快的方式。先把所有AI和工具调用的逻辑在文本模式下打磨稳定,再引入语音识别和合成这个巨大的变量。
- 拥抱可观测性:一定要为系统的每一步(语音识别结果、路由器决策、ReAct循环的每一步思考与行动、工具调用结果)留下清晰的日志。这是你调试复杂交互、理解AI“脑回路”的唯一途径。
- 硬件量力而行:你不一定需要RTX 3090。现在有很多优秀的量化模型(如Qwen2.5-7B, Llama-3.1-8B)在消费级显卡(RTX 4060 Ti 16GB)上就能流畅运行。关键是先让想法跑起来,再逐步升级硬件。
这个项目对我而言,远不止是一个周末黑客马拉松的产物。它是我对下一代人机交互方式的一次深度实践和思考。它不完美,但它在我的日常生活中真实地运行着,并持续地演化。它让我看到,真正的个人AI,不是那个在云端对你一无所知的聊天机器人,而是一个扎根于你的数据、你的环境、你的习惯,并且完全受你掌控的数字化延伸。