当前位置: 首页 > news >正文

Spratt Skills:基于LLM规划与代码执行的OpenClaw家庭自动化架构实践

1. 项目概述:Spratt Skills,一个为OpenClaw打造的家庭自动化基础设施套件

如果你正在使用OpenClaw,并且已经厌倦了让LLM(大语言模型)去处理那些它天生就不擅长的事情——比如定时发送消息、轮询航班状态、或者可靠地写入数据库——那么Spratt Skills就是为你准备的。这不是另一个教你如何写提示词(SKILL.md)的教程,而是一套完整的、经过实战检验的“基础设施”插件。它的核心哲学非常明确:让LLM做它最擅长的事(理解和规划),然后用确定性的代码去执行它最不擅长的事(可靠地交付)

这个项目源于一个真实家庭的日常需求,运行在一台Mac Mini M4上,处理着从旅行规划、购物清单到信用卡优惠提醒的一切。它解决了我们在早期使用纯LLM代理时遇到的所有痛点:消息发错群聊、航班监控任务神秘消失、购物记录无法关联。Spratt Skills通过引入数据库、守护进程和清晰的工具边界,将OpenClaw从一个聪明的聊天机器人,转变为一个真正可靠的家庭操作系统核心。

想象一下这样的场景:你告诉助手“我们下周五去纽约”,它不仅能理解,还会自动在数据库里创建行程,为航班添加监控,并预约出发前的提醒消息。当你的特斯拉设置导航去超市时,相关的购物清单会自动弹出。每周六,系统会检查你信用卡里即将过期的月度消费券并提醒你。所有这些,背后都没有LLM在实时决策,只有稳定运行的代码在默默工作。

2. 核心架构与设计哲学

2.1 “LLM规划,代码执行”的分层模型

Spratt Skills的整个架构都建立在一个简单的二分法上:什么是LLM该做的,什么是代码该做的。

LLM的职责(规划层):

  • 自然语言理解:从用户的模糊指令(“记得提醒我下周取药”)或非结构化数据(邮件正文)中提取结构化信息。
  • 意图识别与路由:判断用户是想发消息、查行程、还是记下一个地点,并调用正确的工具(Tool Routing技能的核心)。
  • 内容生成:撰写友好、个性化的消息正文,或者生成复杂的多步骤计划(如旅行规划)。
  • 决策制定:基于上下文(如购物历史)决定“现在需要买牛奶吗?”。

代码的职责(执行层):

  • 状态管理与持久化:所有数据(行程、订单、消息队列)都写入SQLite数据库。代码负责创建、读取、更新、删除(CRUD)记录。
  • 调度与轮询:守护进程(Daemon)定时检查数据库中的任务(如发送消息、查询航班状态),并在准确的时间触发操作。
  • 外部API调用:与FlightAware、Google Places、Microsoft Graph等第三方服务进行稳定、认证的通信。
  • 资源管理与错误处理:处理网络超时、API限流、浏览器崩溃恢复等一切需要确定性和可靠性的问题。

这种分离带来了巨大的稳定性提升。早期我们尝试用OpenClaw的Cron功能让LLM自己轮询航班状态,结果它在几次查询后,因为无法解析“未找到航班”的状态,竟然自己删除了整个监控任务,导致一次重要的接机提醒完全遗漏。而现在的flight-monitor守护进程,只是一个简单的Python脚本,每3分钟查询一次API,逻辑清晰,永不“理解错误”。

2.2 统一的数据层:所有数据库归于一处

一个早期让我们头疼不已的问题是“数据库去哪儿了”?不同的技能脚本将SQLite文件创建在不同的目录:./outbox/messages.db,../trips/trips.db, 散落各处。当LLM代理运行时,如果它找不到数据库文件,sqlite3.connect()会“友好地”在它认为的路径上创建一个全新的空文件。这导致了灾难性的“数据分裂”:脚本A写入了一个路径,脚本B从另一个空文件读取,用户看到的数据莫名其妙消失了。

踩坑实录:数据库路径的幽灵我们曾三次在两周内遇到这个问题。一次是在调整项目结构后,trip-db.py因为相对路径计算错误,在用户家目录下创建了一个空的trips.sqlite。所有新添加的行程都进了这个“黑洞”,而LLM查询时却从正确的路径读取旧数据,导致用户以为行程添加成功,实则全部丢失。排查时,需要对比多个潜在路径下数据库文件的修改时间,非常痛苦。

解决方案:硬编码的单一数据目录。现在,所有Spratt Skills组件都遵循一个铁律:所有数据库文件必须位于一个统一的、硬编码的目录下。在我们的配置中,就是~/.config/spratt/db/

  • ~/.config/spratt/db/outbox.sqlite- 消息队列
  • ~/.config/spratt/db/trips.sqlite- 行程管理
  • ~/.config/spratt/db/orders.sqlite- 订单追踪
  • ~/.config/spratt/db/cards.sqlite- 信用卡管理
  • ...等等

每个脚本在连接数据库前,都会强制检查文件是否存在。如果不存在,不是静默创建,而是立即报错退出。这彻底杜绝了因路径错误导致的数据分裂。

# 以 outbox.py 中的代码为例 def require_db_file(path, name): """如果SQLite数据库文件不在预期路径,则大声报错退出。""" if not os.path.exists(path): sys.stderr.write( f"\n致命错误: 未找到 {name} 数据库文件于:\n {path}\n\n" f"拒绝自动创建(防止因路径错误导致静默数据丢失)。\n\n" f"请运行初始化命令: `python outbox.py init`\n" ) sys.exit(1) # 在数据库类初始化时调用 class OutboxDB: def __init__(self, db_path='~/.config/spratt/db/outbox.sqlite', allow_create=False): self.db_path = os.path.expanduser(db_path) if not allow_create: require_db_file(self.db_path, "Outbox") # ... 后续连接逻辑

2.3 基于事件的响应式通信,而非轮询LLM

传统LLM智能体架构中,实现定时或事件触发功能,通常需要设置一个Cron任务,定期唤醒LLM,让它检查状态并决定做什么。这在Spratt Skills中被视为一种“反模式”。

反例:LLM轮询航班状态我们最初的设计是:一个Cron任务,每30分钟运行一次,调用LLM。LLM会读取trips.sqlite,找出所有“进行中”的航班,然后调用一个“查询航班状态”的工具。这个工具再调用FlightAware API。LLM拿到结果后,判断是否有“延误”、“降落”等事件,最后调用message工具发送通知。

  • 问题1(成本):每次轮询都需要调用LLM和工具,即使航班状态无变化。
  • 问题2(可靠性):LLM对API返回的复杂JSON状态码(如“未找到”、“已取消”)解析可能出错。
  • 问题3(状态管理):LLM需要维护“上次查询状态”和“本次状态”的对比,逻辑复杂且容易在多次调用间丢失上下文。

Spratt的解决方案:专用守护进程flight-monitor是一个独立的Python守护进程。它直接读取trips.sqlite中的航班数据,自己管理一个简单的内存状态(如“已通知登机口变更”)。它直接调用FlightAware API,根据规则(时间差、状态变更)判断是否需要通知,然后直接向outbox.sqlite插入一条消息记录。整个循环中没有LLM参与

  • 优势1(高效):纯代码逻辑,执行速度快,成本为零。
  • 优势2(可靠):状态判断是确定性的if-else逻辑,不会误解。
  • 优势3(自适应):可以轻松实现智能轮询(航班起飞前3分钟查询一次,起飞后30分钟查询一次)。

这个模式被广泛应用:outbox的发送守护进程、destination-aware的Tesla导航监听服务,都是独立于LLM的、事件驱动或定时轮询的守护进程。

3. 核心组件深度解析与实操要点

3.1 Outbox(发件箱):可靠的消息调度中枢

它解决了什么问题?直接让LLM调用message工具发送定时消息是不可靠的。OpenClaw的Cron任务在触发时,会重新实例化一个LLM会话。这个新的LLM会话可能对原始指令有不同的解读。我们遇到过:一个“晚餐提醒”本应发到家庭群聊,结果Cron触发时LLM将其理解为“向每个家庭成员单独发送”,导致晚餐通知变成了刷屏的私信。Outbox模式将“决定发什么”(LLM)和“在正确的时间发送”(代码)彻底分离。

工作原理:

  1. 写入阶段:当LLM需要发送一条定时消息(例如,“明早8点提醒我带伞”),它不再直接调用message,而是调用outbox.py schedule工具。这个工具将消息内容、接收者、计划发送时间写入outbox.sqlitemessages表,状态为pending
  2. 轮询阶段:一个名为sender.py的Python守护进程在后台持续运行(通过macOS的launchd或Linux的systemd)。它每60秒查询一次数据库,找出所有状态为pendingsend_at时间小于当前时间的消息。
  3. 发送阶段:对于每条待发送消息,sender.py调用本地命令行工具imsg(BlueBubbles项目的一部分)通过iMessage发送。发送成功后,将消息状态更新为sent;失败则更新为failed并记录错误信息。

实操要点与配置:

  • 数据库初始化:首先需要创建数据库和表结构。项目提供了SQL文件,但更推荐使用脚本内嵌的初始化命令,确保版本一致。
cd outbox python outbox.py init # 这会创建数据库文件并写入模式
  • 守护进程部署sender.py需要以守护进程形式运行。对于macOS,项目提供了com.spratt.outbox.sender.plist文件。你需要将其复制到~/Library/LaunchAgents/,并修改其中的路径,然后加载它。
cp outbox/com.spratt.outbox.sender.plist ~/Library/LaunchAgents/ # 编辑plist文件,将 `~/code/spratt-skills` 改为你的实际路径 launchctl load ~/Library/LaunchAgents/com.spratt.outbox.sender.plist launchctl start com.spratt.outbox.sender
  • iMessage集成:这依赖于 BlueBubbles 和其附带的imsg命令行工具。确保BlueBubbles服务在你的Mac上运行,并且imsg在系统路径中。在sender.py中检查IMSG_BIN变量的路径是否正确。
  • 错误处理与重试sender.py内置了简单的重试逻辑。对于发送失败的消息,可以手动排查原因(如iMessage服务未启动),然后通过outbox.py retry <message_id>命令重试。

关键设计:取消操作的安全性早期版本曾因为一个错误的SQL语句DELETE FROM messages WHERE ...漏掉了WHERE条件,导致整个消息历史被清空。现在的设计是:

  • 取消消息使用UPDATE messages SET status='cancelled' WHERE id IN (?,?,?)
  • 永远不使用没有精确WHERE条件的DELETE操作。
  • 已取消的消息仍然保留在数据库中,用于审计。如果需要清理,必须通过一个显式的、独立的管理命令进行。

3.2 Trip Manager(行程管理器):以数据库为中心的旅行自动化

它解决了什么问题?家庭旅行信息散落在邮件、短信、聊天记录和各种App中。航班时间、酒店确认号、餐厅预订、接送提醒……没有统一视图,极易遗忘。Trip Manager为LLM提供了一个结构化的“单一事实来源”(Single Source of Truth)数据库。

核心工作流:

  1. 创建行程:用户说“计划一个去西雅图的周末旅行”。LLM调用trip-db.py add-trip,创建一条行程记录。
  2. 自动协调:数据库触发器(或后续脚本)检测到新行程,自动通过Outbox向“家庭管理员”发送消息:“这是单人旅行还是家庭旅行?”。根据回复(“solo”或“group”),触发不同的自动化脚本。
  3. 添加详情:LLM解析用户的航班确认邮件,调用trip-db.py add-flight插入航班信息。同理添加酒店add-hotel、餐厅add-reservation等。
  4. 自动生成提醒:一个独立的脚本trip-outbox-gen.py(通常由Cron触发)扫描trips.sqlite,为即将发生的项目(如航班值机、酒店入住)生成提醒消息,并插入Outbox。关键点:这个生成过程是确定性的模板化操作,不经过LLM。
  5. 航班监控联动flight-monitor守护进程会读取trips.sqlite中的航班数据,自动开始监控。

“查找群聊”的巧思家庭旅行通常有一个对应的iMessage群聊。手动获取群聊的GUID(全局唯一标识符)非常麻烦。Trip Manager的find-group-chat功能解决了这个问题:

  • 当行程被标记为“group”时,该功能被触发。
  • 它使用imsgCLI扫描最近一段时间(如7天)的iMessage聊天记录。
  • 通过分析聊天标题(可能包含目的地关键词,如“西雅图”)和参与者(家庭成员),用启发式算法匹配最可能的群聊。
  • 自动将匹配到的群聊GUID关联到该行程。之后所有针对该行程的群发提醒都会发送到这个聊天。

实操心得:数据变更与消息同步最初,当我们修改一个航班时间时,只是简单更新了数据库记录,并清空了关联的outbox_msg_id(外键),指望下次trip-outbox-gen.py运行时生成新消息。但这导致旧消息仍然留在Outbox中处于pending状态,最终会和新的提醒一起发送,造成重复。解决方案:在trip-db.py的任何更新或删除操作中,如果会影响已生成的提醒,必须首先显式地取消(cancel)Outbox中对应的旧消息,然后再清空或更新关联ID。这确保了数据变更与消息队列状态的原子性。

3.3 Flight Monitor(航班监控器):智能、自适应的状态追踪

从爬虫到官方API的演进最初我们使用一个名为FlightRadarAPI的第三方库,它本质上是FlightRadar24网站的爬虫。我们发现它对于某些有效航班会返回“未找到”或过时数据,稳定性很差。我们果断切换到了FlightAware的AeroAPI。虽然这是付费服务(约每月5美元),但它提供稳定、权威的航班状态数据,包括实时位置、延误、登机口变更、甚至备降信息,对于家庭自动化来说可靠性远超成本。

守护进程设计详解flight_monitor.py作为守护进程,其逻辑比看起来更精细:

  1. 数据源:它不维护自己的状态表,而是直接读取trips.sqlite中的flights表。这避免了数据同步问题。
  2. 自适应轮询
    • 活跃窗口:在航班计划起飞前2小时到计划降落后1小时的时间段内,每3分钟查询一次API。这是事件高发期(值机、登机、起飞、降落)。
    • 空闲窗口:其他时间,每30分钟查询一次。只是为了检测航班是否有重大变动(如提前一天取消)。
  3. 事件检测与去重:守护进程在内存中为每个监控的航班维护一个“上次通知状态”。只有当API返回的新状态与旧状态相比发生了需要通知的变更(如从“计划中”变为“延误”,或登机口变更),才会生成通知消息。防止因API数据微小波动导致的重复提醒。
  4. 通知生成:检测到事件后,守护进程直接构造消息内容(例如,“UA123航班已降落,比计划提前15分钟”),并调用outbox.py的Python API插入到消息队列,而不是通过LLM。

配置关键点:

  • API密钥:需要在FlightAware网站注册开发者账号,获取AeroAPI密钥。将其设置在环境变量FLIGHTAWARE_API_KEY中。
  • 守护进程化:与Outbox类似,使用launchdplist文件。特别注意在plist中配置KeepAliveRunAtLoad,确保进程异常退出后能自动重启,并且开机自启。
  • 日志:务必配置日志重定向到文件(如StandardOutPathStandardErrorPath),便于监控和调试。

3.4 智能购物与清单体系:从邮件到购物车的闭环

这一系列组件(Email-to-Orders, Instacart Orders, Smart Reorder, Instacart Skill)共同构成了一个从订单捕获、分析到自动补货的完整闭环。

3.4.1 Email-to-Orders:订单信息的统一入口大多数在线购物(亚马逊、DoorDash、Instacart)都会发送确认邮件。这个组件通过一个定时Cron任务,使用LLM(通常是轻量级的Haiku模型)扫描并解析这些邮件。

  • 解析内容:提取商家、订单号、日期、金额、商品列表(如果邮件中有)、物流单号。
  • 结构化存储:将所有信息写入orders.sqlite。这里的设计重点是扩展性。表结构不仅包含订单头信息,还有独立的order_items表存储商品明细,以及tracking_events表存储物流状态更新。
  • 物流追踪:当收到包含物流单号的邮件(如“您的包裹已发货”)时,系统会更新对应订单的物流状态。后续如果收到“已送达”的邮件,可以自动触发Outbox通知。

3.4.2 Instacart Orders:补齐商品信息的最后一块拼图我们发现,Instacart的确认邮件里没有商品明细,只有一个链接。Instacart Orders技能就是为了解决这个问题。

  • 工作原理:一个每日运行的Cron任务,让OpenClaw启动浏览器,自动登录Instacart网站,导航到“历史订单”页面。
  • 数据抓取:使用Playwright等浏览器自动化工具,抓取订单详情页上的完整商品列表、单价、数量。
  • 数据回填:调用order-ingest.py update-items,将抓取到的商品明细回填到orders.sqlite中对应的订单记录里。至此,我们拥有了完整的、带商品明细的购物历史数据库。

3.4.3 Smart Reorder:基于历史的智能预测这是数据产生价值的核心。purchase-cadence.py脚本分析orders.sqlite中的数据。

  • 商品归类(Item Classification):这是最大的挑战。收据上的商品名可能是“QFC Vitamin D Whole Milk Half Gallon”、“QFC Vitamin D Milk”、“Organic Valley Whole Milk”。我们需要知道哪些是同一商品的不同表述。这里再次引入LLM(Haiku),运行一个夜间任务,将新的、未分类的商品名归类到“规范商品名”下,并记录在item_aliases表中。
  • 购买周期计算:对于每个“规范商品”,计算历史购买间隔的中位数。例如,牛奶可能每7天购买一次,纸巾每30天一次。
  • 补货提醒:结合最后购买日期和计算出的周期,判断哪些商品“已过期”(超过周期)或“即将过期”(达到周期的80%)。这些信息会被后续的Instacart Skill使用。

3.4.4 Instacart Skill:自动化购物车构建这是面向用户的最终界面。当用户说“我们需要买点 groceries”时:

  1. LLM调用Instacart Skill
  2. 该技能首先调用Smart Reorder获取需要补货的商品列表。
  3. 然后,它控制浏览器打开Instacart,使用URL搜索技巧(因为发现Playwright直接填充搜索框不稳定),将商品逐一加入购物车。
  4. 它还可以根据食谱(meal-planner技能提供)添加额外的食材。
  5. 重要:技能只构建购物车,不自动结账。最后一步需要用户自己确认商品、选择送货时间并支付。这确保了安全性和用户控制权。

实操陷阱:浏览器自动化稳定性浏览器自动化(Playwright)在与复杂网页交互时非常脆弱。元素选择器可能因页面更新而失效,登录状态可能过期,页面可能加载缓慢。

  • 快照优先(Snapshot-First):在关键操作(如点击“结账”按钮)前,先让浏览器等待页面完全加载,并保存一个屏幕截图。如果操作失败,截图有助于调试。
  • URL搜索:直接导航到如https://www.instacart.com/store/items/search?q=milk这样的搜索URL,比在搜索框输入文本更可靠。
  • 崩溃恢复:在Cron任务中,设置超时和重试机制。如果浏览器会话崩溃,脚本应能捕获异常,清理残留进程,然后重新启动任务。

3.5 Destination-Aware Reminders(目的地感知提醒):上下文在正确的时间浮现

这是我最喜欢的组件之一,它体现了“环境计算”的理念:信息在你需要的时候自动出现。

传统提醒的局限:你在早上7点设置了一个提醒“下午5点去幼儿园接孩子时带上体检表”。到了下午5点,你可能正在开会,提醒弹出,你瞟了一眼,心想“好的,记得了”。然后你离开办公室,开车,路上接了电话……到了幼儿园门口,你空着手下了车。提醒出现的时间(5点整)和需要行动的上下文(到达幼儿园)是脱节的。

Spratt的解决方案

  1. 事件监听destination-daemon.py守护进程通过WebSocket连接到家庭助理(Home Assistant)。它订阅特斯拉汽车的navigation_destination传感器。
  2. 目的地解析:当你设置导航时,特斯拉会将目的地(一个地址或POI名称)发送给Home Assistant。守护进程捕获到这个事件,调用goplacesCLI(一个封装了Google Places API的工具)将地址解析为结构化的地点信息,包括类型(如grocery_store,school,doctor)。
  3. 上下文搜集:根据地点类型,去不同的地方搜集相关信息:
    • grocery_store-> 查询Smart Reorder中需要补货的商品,或meal-planner生成的购物清单。
    • school(daycare) -> 查询Apple Reminders中与“幼儿园”、“孩子”相关的待办事项。
    • doctor-> 查询日历中即将到来的医疗预约笔记。
  4. 智能过滤:这是关键一步!不能把所有的待办事项都塞给用户。系统使用LLM(Haiku)进行轻量级、分类别的过滤。例如,对于超市类别,提示词是:“从以下待办事项中,只挑选出需要购买的商品或与杂货购物直接相关的项目(如‘买牛奶’,‘记得用优惠券’)。忽略所有其他类型的提醒(如‘去邮局’,‘打电话给妈妈’)。” 这样,当你导航去超市时,你只会看到购物清单,而不是一堆无关的提醒。
  5. 消息发送:将过滤后的、简洁的上下文列表通过Outbox发送到你的手机。例如:“🛒 正在前往Whole Foods — 需要购买:牛奶、鸡蛋、面包”。

技术细节:WebSocket连接可靠性家庭助理的WebSocket连接可能因为网络波动而中断。destination-daemon.py实现了“三重活性检测”:

  1. Ping/Pong:定期发送WebSocket Ping帧,期待Pong回复。
  2. 心跳事件:定期向Home Assistant发送一个无害的get_services调用。
  3. 看门狗定时器:如果超过一定时间(如70秒)没有收到任何来自Home Assistant的消息(包括事件和Pong),则主动断开并重连。 这种设计确保了服务能7x24小时稳定运行,应对临时的网络问题。

3.6 Card Wallet(卡包):信用卡优惠与消费优化的智能管家

管理多张信用卡的优惠(如“每月$20餐饮抵扣”、“季度加油5%返现”)和最佳消费策略(“吃饭用哪张卡最划算?”)是一件繁琐且反人性的事。Card Wallet将其自动化。

核心功能拆解:

  1. 优惠追踪(Card Perks):在cards.sqlitebenefits表中记录每张卡的所有优惠信息:描述、类型(月度/季度/年度)、额度、有效期、已使用金额。一个每周六运行的Cron任务(card-wallet-check.py)会检查所有即将在未来10天内过期的优惠,并通过Outbox发送分级提醒:
    • 紧急(3天内过期):详细列出优惠、剩余额度、建议用法。
    • 本周(7天内过期):简要提醒。
    • 远期(30天内):合并成一行摘要,避免信息过载。
  2. 消费优化(Card Optimizer):在reward_rates表中记录每张卡在不同消费类别(餐饮、旅游、超市、加油等)的返现/积分倍数。当用户问“出去吃饭用哪张卡?”时,LLM查询此表,结合每张卡的季度特定优惠(如Chase Freedom本季度餐饮5%),计算并推荐返现最高的卡。它还会考虑现实因素,比如“这张卡是American Express,有些小餐馆可能不收”。
  3. 信息自动更新:信用卡的优惠和返现规则时常变动。一个每月运行的Cron任务(card-wallet-refresh.py)会调用LLM(Haiku),让它基于卡的最新公开信息(通过网页搜索)进行总结和对比,如果发现变动,则更新数据库并通知用户。

数据库设计经验:为了支持多持卡人家庭,数据库设计需要精细化:

  • cards表:卡的基本信息(银行、卡名、持卡人)。
  • benefits表:每张卡的优惠,与cards表关联。
  • benefit_usage表:记录每次优惠的使用情况(日期、金额、商户),用于计算剩余额度和分析使用习惯。
  • reward_rates表:每张卡在不同类别的返现率。
  • quarterly_categories表:记录那些有季度轮换优惠的卡(如Chase Freedom, Discover It)的本季度类别。 这种结构使得查询“用户A有哪些即将过期的餐饮优惠?”或“在超市消费,全家人的卡中哪张回报最高?”变得非常高效。

3.7 Tool Routing(工具路由):为LLM绘制清晰的地图

当你的OpenClaw拥有了十几个技能和工具后,LLM很容易“迷路”。它应该用message发即时消息,还是用outbox发定时消息?用户问“我们的计划是什么?”,它应该查行程数据库、日历,还是提醒事项?Tool Routing技能就是LLM的“决策手册”。

它不是一个工具,而是一份“说明书”(SKILL.md),里面定义了清晰的规则:

  • 消息发送
    • 如果消息需要立即发送-> 使用message工具。
    • 如果消息需要在未来的特定时间发送-> 使用outbox schedule工具。
    • 绝对禁止使用Cron来发送消息。
  • “我们的计划是什么?”查询:这是一个“多源聚合查询”。LLM需要依次检查:1)trips.sqlite(未来行程), 2) Outlook日历(通过outlook-calendar.sh), 3) Apple Reminders(通过remindctl),然后将结果合并成一份统一的摘要。
  • 任务流(TaskFlow)指引:对于复杂的多步骤交互(如规划一次旅行),Tool Routing定义了标准的模式:先创建行程框架,然后逐步添加航班、酒店,每一步都确认,最后生成总结。这保证了复杂任务执行的一致性。

为什么这如此重要?在没有明确路由规则时,我们观察到LLM会出现“工具选择摇摆”。例如,对于“明早8点提醒我”,它有时会用outbox,有时会错误地创建一个Cron任务。Tool Routing通过提供明确的“如果-那么”规则,极大地提高了LLM行为的可靠性和可预测性。它本质上是对LLM进行“流程编排”的提示工程。

4. 部署、集成与运维实战

4.1 系统准备与依赖安装

Spratt Skills主要面向macOS环境(因其深度集成iMessage和Apple Reminders),但核心组件经过设计,可以适配Linux。

核心依赖:

  1. Python 3.9+:大多数脚本的基础。
  2. SQLite 3:轻量级数据库,macOS通常已预装。
  3. OpenClaw:作为智能中枢,需要先安装并运行起来。
  4. BlueBubbles + imsg:用于iMessage发送。在Mac上安装BlueBubbles服务端,并确保其命令行工具imsg在PATH中。
  5. Home Assistant:用于destination-aware技能,需要安装Tesla集成。
  6. 各种API密钥
    • FlightAware AeroAPI
    • Google Places API (forgoplaces)
    • Microsoft Graph API (foroutlook-graph)
    • Anthropic Claude API (for Haiku模型,用于分类和摘要)

环境配置:项目使用一个统一的env.sh文件管理所有敏感信息。部署第一步就是复制模板并填写。

cd spratt-skills cp shared/env/env.example.sh shared/env/env.sh # 使用文本编辑器打开 env.sh,填入你的所有API密钥 # export FLIGHTAWARE_API_KEY='your_key_here' # export ANTHROPIC_API_KEY='your_key_here' # ...

重要:确保所有需要环境变量的守护进程(如通过launchd运行的)都能读取到这个文件。通常需要在plist文件的EnvironmentVariables部分显式设置,或者在启动脚本中source这个文件。

4.2 数据库初始化与技能注册

部署遵循一个清晰的顺序,因为组件间有依赖关系。

  1. 创建统一数据目录
    mkdir -p ~/.config/spratt/db
  2. 部署核心基础设施(无依赖)
    • Outbox:初始化数据库,部署发送守护进程。这是所有通知的基础,应最先设置。
    • Tool Routing:将SKILL.md复制到OpenClaw的skills/目录。这为LLM提供了全局指引。
  3. 部署数据生产者组件
    • Trip Manager:初始化数据库。现在LLM就可以开始记录行程了。
    • Email-to-Orders:初始化数据库,并设置邮件扫描Cron任务。开始积累购物历史。
    • Places:初始化数据库。用于保存地点信息。
  4. 部署数据处理与自动化组件
    • Flight Monitor:配置API密钥,部署守护进程。它依赖trips.sqlite
    • Instacart Orders:设置每日Cron任务。它依赖orders.sqlite和浏览器登录状态。
    • Smart Reorder:运行一次性的历史数据回溯分析,生成初始的购买周期数据。
  5. 部署交互式技能
    • Instacart Skill:将SKILL.md复制到技能目录。确保OpenClaw的浏览器配置文件已登录Instacart。
    • Meal Planner:配置食谱数据库路径。
    • Card Wallet:初始化数据库,输入初始的信用卡和优惠信息,部署每周/每月Cron任务。
    • Destination-Aware:配置Home Assistant连接和API密钥,部署守护进程。这是最复杂的之一,需仔细测试WebSocket连接。
    • Apple Reminders:编译Swift二进制文件,并确保remindctl安装正确。

4.3 与OpenClaw的集成:SKILL.md与Cron

Spratt Skills与OpenClaw通过两种方式集成:

1. 技能定义(SKILL.md)每个面向用户交互的组件(如Instacart Skill,Places,Tool Routing)都有一个SKILL.md文件。这个文件定义了:

  • 技能名称和描述:LLM如何识别和调用这个技能。
  • 可用工具(Tools):列出了该技能暴露给LLM的所有命令行工具或API端点,包括参数说明和示例。
  • 工作流程(Workflows):复杂的多步骤任务应该如何执行。 你需要将这些SKILL.md文件复制到OpenClaw的skills/目录下,OpenClaw会在启动时加载它们,使LLM知晓这些功能。

2. 定时任务(Cron)OpenClaw支持配置Cron任务,定期执行某个技能或工具。Spratt Skills中的许多后台作业都通过Cron触发:

  • 邮件扫描email-to-orders的扫描任务,每天运行3次。
  • Instacart订单抓取instacart-orders的抓取任务,每天运行1次。
  • 智能补货分析smart-reorder的分类任务,每天运行1次。
  • 信用卡优惠检查card-wallet-check,每周运行1次。 你需要在OpenClaw的Cron配置文件中添加这些任务条目。

4.4 监控、日志与故障排查

一个家庭自动化系统需要保持安静稳定地运行,但出问题时,必须有迹可循。

日志策略:

  • 守护进程:所有守护进程(sender.py,flight_monitor.py,destination-daemon.py)都应配置将日志输出到文件。在macOS的launchdplist中,使用StandardOutPathStandardErrorPath指定日志文件路径。定期检查这些日志文件的大小和错误信息。
  • Cron任务:OpenClaw的Cron执行结果通常会在其主日志中体现。确保OpenClaw的日志级别设置得当,能够记录Cron任务的开始、结束和错误。
  • 数据库审计:关键表(如outbox.messages)设计了statuserror字段。定期查询SELECT * FROM messages WHERE status='failed';可以快速发现发送失败的消息。

健康检查:

  • 进程存活:使用ps aux | grep sender.pylaunchctl list | grep spratt检查守护进程是否在运行。
  • 数据库连接:编写一个简单的健康检查脚本,尝试连接所有数据库文件,并执行一个简单的SELECT 1;查询。
  • API连通性:定期(如每周)运行一个测试脚本,调用FlightAware、Google Places等API,验证密钥是否有效。

常见故障排查:

  1. 消息未发送
    • 检查sender.py守护进程是否运行:ps aux | grep sender
    • 检查Outbox数据库中消息状态:sqlite3 ~/.config/spratt/db/outbox.sqlite "SELECT id, status, error FROM messages ORDER BY id DESC LIMIT 5;"
    • 检查iMessage连接:手动运行imsg --text "Test" --contacts "你的手机号"看是否能发送。
  2. 航班监控无通知
    • 检查flight_monitor.py守护进程。
    • 检查trips.sqlite中是否有状态为active的航班。
    • 检查FlightAware API密钥是否过期,查看守护进程日志是否有API错误。
  3. LLM找不到技能工具
    • 确认SKILL.md文件已正确复制到OpenClaw的skills目录。
    • 重启OpenClaw服务以重新加载技能。
    • 检查OpenClaw日志,看是否有技能加载错误。

5. 设计模式总结与演进思考

经过一年多的生产环境运行,Spratt Skills沉淀出一些超越具体代码的通用设计模式,这些模式对于构建任何基于LLM的可靠自动化系统都具有参考价值。

模式一:LLM作为规划器,代码作为执行器这是贯穿始终的最高原则。让LLM处理它擅长的模糊性、创造性和语言任务,让确定性代码处理它不擅长的调度、持久化和精确操作。任何违背此原则的设计(如让LLM轮询API)最终都会在可靠性上付出代价。

模式二:统一、硬编码的数据存储路径数据存储的混乱是系统腐化的开端。一个明确的、所有组件都遵守的数据存放约定,是避免“数据幽灵”和“分裂脑”问题的基石。

模式三:守护进程化长时间运行任务对于需要持续监控或定时触发的任务(消息发送、航班查询、目的地监听),将其实现为独立的守护进程,而不是依赖LLM Cron。守护进程更轻量、更可控、更容易实现复杂的逻辑(如自适应轮询、连接状态管理)。

模式四:显式的工具路由与边界定义当系统工具增多时,必须为LLM提供一张清晰的“地图”。Tool Routing技能就是一种显式的边界定义,它减少了LLM的决策模糊性,提高了整个系统的行为一致性。

模式五:审计追踪与幂等操作所有关键操作(如消息发送、状态更新)都应在数据库中有记录。更新操作尽量设计为幂等的(多次执行效果相同)。删除操作非常谨慎,优先使用“软删除”(标记为取消或无效)而非物理删除。

未来的演进方向:

  1. 更细粒度的权限与隐私:目前所有家庭数据共享一个数据库。未来可能需要引入简单的用户概念和数据隔离,特别是对于提醒、消息等个人化内容。
  2. 跨平台消息支持:目前严重依赖iMessage。可以抽象一个“消息发送器”接口,轻松接入Telegram、Slack、微信等平台。
  3. 机器学习增强预测:目前的购买周期分析基于简单中位数。可以引入更复杂的模型,考虑季节性、促销等因素,实现更精准的预测补货。
  4. 健康与健身数据集成:与Apple Health、Strava等数据源连接,实现如“根据本周运动量推荐晚餐食谱”的个性化场景。

Spratt Skills不是一个完美的、开箱即用的产品,而是一个高度定制化的、模块化的工具箱。它的价值在于展示了一种架构范式:如何将前沿的LLM能力与经典的、稳健的软件工程实践相结合,构建出真正能为日常生活提供持续、可靠价值的智能系统。部署过程可能需要一些调试,但一旦各个齿轮咬合运转起来,那种“系统在默默为你打点一切”的感觉,无疑是数字生活的一种美好形态。

http://www.jsqmd.com/news/805454/

相关文章:

  • 2026年至今,四川地区可靠的成都实木门批发优选推荐 - 2026年企业推荐榜
  • Articuler.Ai 技术深度解析:海量人脉匹配、数字足迹解析与高转化冷触达引擎
  • Python 爬虫高级实战:爬虫接口限流自适应调节
  • Verilog移位运算避坑指南:为什么你的`reg1 << (a+b+3‘d4)`结果总不对?
  • 基于MCP协议与FFmpeg构建AI视频处理服务器:原理、部署与实战
  • Poppler Windows终极指南:3步搞定Windows平台PDF处理难题
  • 8720个AI岗位真相:LLM和Agent吃掉58%的岗位
  • 淘金币自动化脚本:3分钟完成淘宝全任务,每天节省20分钟
  • LayerDivider终极指南:5分钟掌握智能插画分层技术
  • 四川弱电劳务分包技术规范与合规服务商实操推荐 - 优质品牌商家
  • SRWE终极指南:5分钟学会游戏窗口分辨率自定义技巧
  • ARMv8存储释放指令原理与应用详解
  • Clawforce:开源AI智能体团队基础设施,实现持久化与安全协作
  • 贾子之路理论体系与六步实施路径详解
  • 2026届学术党必备的六大降重复率平台推荐榜单
  • Krita AI智能选区工具:3分钟掌握专业级图像分离技术
  • Notero终极指南:打通Zotero与Notion的学术工作流桥梁
  • 终极指南:如何让淘宝淘金币任务全自动完成,每天节省20分钟
  • 如何解锁数字化制造的数据瓶颈:stltostp的轻量级STL转STEP解决方案
  • 告别显示器:树莓派4B无头模式(Headless)安装系统与VNC远程桌面配置详解
  • 【AI面试临阵磨枪-53】AI 应用成本优化:模型选型、Token 控制、缓存、异步、轻量降级
  • 2026年q2四川弱电工程服务商实力排行一览:停车场道闸安装/小区道闸安装/工地道闸安装/弱电劳务分包/优选指南 - 优质品牌商家
  • 基于Ollama与Stable Diffusion的Discord AI机器人本地部署指南
  • 2026年中式化妆培训可靠机构:技术与实力双维度解析 - 优质品牌商家
  • ncmdumpGUI完整使用手册:简单快速解锁网易云音乐NCM格式转换
  • D26: 向下负责——保护团队免受 AI 焦虑影响
  • 2026年国内玻璃钢格栅花纹盖板厂家TOP5客观盘点 - 优质品牌商家
  • Python 爬虫数据处理:特殊格式文档爬虫解析处理
  • AI Agent 的难点,不在搭 Demo,而在让人敢交任务
  • Mac鼠标滚轮终极优化指南:用Mos实现触控板般的丝滑滚动体验