关于通过LLM及一点Agent搭建我的智能生活这件事
关于通过LLM及一点Agent搭建我的智能生活这件事
序:一切从那一声"真爽"开始
最近买了个小度,顺手把我那台空调连了上去。
于是这个夏天,每天下班进门的第一件事,就是冲着空气喊一嗓子:
"小度小度,打开我的空调。"
"好的,已为您打开空调。"
呼——冷风糊脸的那一瞬间,真爽。
科技改变生活,诚不欺我。
"真爽未果"
不过科技改变的不够彻底,爽的有些憋屈在某些角度上:我想要的其实不是"开空调"。
我想要的是计划:
- 十一点半我差不多睡着了,空调自动调到 27 度,别半夜冻醒我
- 凌晨四点关掉,省点电费
- 明天休息,早上八点用卧室灯把我晃醒,别用闹铃(回忆起读书的时候被起床铃支配的恐惧
这些需求小度 APP 能不能搞?
答:能
定时、场景、自动化,入口藏得不深也不浅,但是能编排的动作就那么几种,条件一多就歇菜。
至于"我睡着之后"这种模糊概念?不存在的,你自己换算成具体时间点去。
说白了,传统智能家居的"智能",是研发者们根据需求预先编排好的智能。
你能做到的,等于产品想到的。产品没想到的,就只能苦等升级咯。
但是等待别人这种事,生理性地不舒服。
灵光
巧了,我这段时间正好在写一个 Agent应用,MCP 开发以及LLM的思考编排略有理解,不过体验得很深
天天盯着 LLM 的 ReAct 思考过程看:看它怎么拆一句话、怎么在工具列表里挑、怎么填参数、填错了怎么自己掰回来。
然后某天晚上,空调的定时又双叒没设置对的时候,脑子里灵光一闪:
这活不该我来干,也不该小度的产品经理来干,该 LLM 来干。
与其面对一个死应用,不如使其像Iphone的siri一样:HI Siri帮我定一个6点的闹钟
说干就干!
设想很简单:
手机上装一个应用,一个网页或者一个安卓/IOS应用,然后抛弃小度 APP,我只负责对着这个应用说人话:
"十一点半把空调调到 27 度,凌晨四点关掉。"
"明天早上七点叫我,先把卧室灯打开,别开太亮。"
"如果这几天降温了,睡前那档调高一度。"
应用里的 Agent 听懂这句话,把它编排成一个可执行的计划,存下来,到点执行。
控制链路也是现成的:
我 → 我的 Agent → 小度的第三方 API → 空调
而"对接第三方 API"这个事,我在物联网公司干了好几年,云云对接就是老本行(这段历史在《物联网语音云云接入》里写过),啧啧最难的高强就这么轻易跨过了。
当晚,配合着Claude编写了一本PRD文档
我管这东西叫:伪智能家居 APP。
"伪"在哪?它一个设备都没有,一个厂商协议都不碰,站在小度的肩膀上,只把"编排"这一层抢过来,交给 LLM。小度继续当手,脑子换成我自己的。
系统的本质
把这件事拆开看,其实就是一次职责的重新分配:

传统智能家居 APP 里,"智能"是编译期就定死的:产品经理把用户可能的需求穷举成一个个功能,你在里面挑。
伪智能家居 APP 里,"智能"是运行期的:功能上限 = LLM 的理解力 × 开放 API 的能力。
LLM 负责把"睡前别冻醒我"翻译成机器能执行的计划,API 负责让计划真的砸到空调上。中间所有原来需要产品经理预判的环节,全部消失。
整个系统要解决的,说穿了就三个问题:
- 怎么听懂人话 — 意图识别 + 参数抽取
- 怎么把人话变成计划 — 计划编排
- 怎么让他有时间观念
架构

四个部分:
手机端。人话接收端
Agent 后端。Spring AI的架子,传统Agent开发的模式
MCP 工具层。把小度的开放接口封装成 MCP 工具,对外就三个能力:
discoverDevices— 拉设备列表controlDevice— 下发指令(开关、温度、模式)queryDeviceStatus— 查设备状态
封装成 MCP 而不是直接写死在代码里,是有私心的:这样这套工具不光我这个 APP 能用,任何支持 MCP 的客户端——包括后续有任何应用都可以通过调用MCP接口直接指挥我家空调。
计划内核。存计划、算下次触发时间、到点回调。这里全是老朋友了:时间片的 zset、时间轮,都是我在《自动化场景业务理解》和《处理夏令时转化业务》里推演过的东西。当年给公司推演的方案,如今砸在自家空调上,还挺感慨。
意图分诊:不是每句话都值得花钱
第一道关卡是意图识别,我把用户输入分成三类:
public IntentResult dispatch(String text) {
// 三分类:闲聊 / 即时控制 / 计划编排
// 别看就三类,这里偷懒后面全是坑
Intent intent = intentClassifier.classify(text);
switch (intent) {
case CHAT: // "你好" "今天天气咋样"
return chatAgent.reply(text);
case INSTANT: // "开空调" "调到26度"
return instantControl(text); // 直接走工具,不落库
case PLAN: // "十一点半关空调" "明早七点叫我"
return planOrchestrate(text); // 编排成计划,落库
default:
return askBack(text); // 听不懂就反问,别硬猜
}
}
画成图就一条分岔路:

有个细节值得单独说:简单指令绝不让 LLM 走完整 ReAct。
"打开空调"这种话,意图明确、参数为零,让大模型思考一轮纯属烧钱。所以意图识别用的是小模型,命中 INSTANT 之后走的是规则短路,只有 PLAN 这种真正需要"理解"的,才请大模型出场。
一天几毛钱也是钱,积少成多就是一台空调的钱(并不是)。
计划编排
真正有意思的是 PLAN 这条路。
用户说的是自然语言,机器要的是结构化数据。中间这个翻译,就是 LLM 的主场:
public Plan planOrchestrate(String text) {
// Step 1: 把当前时间喂进去
// LLM 不知道"现在几点",也不知道"明天"是哪天
String systemPrompt = SYSTEM_PROMPT
+ "\n当前时间: " + LocalDateTime.now()
+ "\n可用设备: " + deviceClient.discoverDevices();
// Step 2: 让它填表,不是让它作文
// entity() 底层是 JSON Schema 约束 + 结构化反序列化,填歪直接抛异常
Plan plan = chatClient.prompt()
.system(systemPrompt)
.user(text)
.call()
.entity(Plan.class);
// Step 3: 参数归一 + 校验,LLM 的输出在入库前一律当"不可信输入"处理
for (Action action : plan.getActions()) {
action.setTemperature(
paramNormalizer.normalize(action.getTemperature())); // "二十六度"→26
paramChecker.checkRange(action.getTemperature(), 16, 30); // 999?拦下
paramChecker.checkDevice(action.getDeviceId()); // 设备在不在这屋里
}
// Step 4: 落库,算下次触发时间
planRepository.save(plan);
triggerScheduler.schedule(plan.nextTriggerTime(), plan.getId());
return plan;
}
Plan 这个实体长这样,编排的目标就是把一句人话灌进这个结构里:
@Data
public class Plan {
private String planName; // "睡觉空调"
private List<Trigger> triggers; // 触发器:什么时候动
private List<Action> actions; // 动作序列:动什么、怎么动
}
@Data
public class Trigger {
private TriggerType type; // CRON / FIXED_TIME / DEVICE_EVENT
private String expr; // "0 30 23 * * *",每天23:30
}
@Data
public class Action {
private String device; // "卧室空调"
private String command; // "setTemp" / "powerOff"
private Integer value; // 27
private Duration delay; // 相对上一条的延迟,比如 4.5 小时
}
"凌晨四点关掉"这种相对描述,编排期就换算成绝对时间点或 cron,执行期不做任何理解——理解在写入时发生一次就够了,执行的时候它最好是个没有感情的机器。
这是我写防飘移那篇以来一以贯之的偏见:LLM 负责模糊到精确的翻译,翻译完,请它离场。
到点之后的链路就很无聊了:

踩的坑
坑一:LLM 不知道"现在"是几点
第一次测试,我说"明早七点叫我",它给我安排到了昨天早上七点。
原因很蠢:LLM 的世界里没有时钟。"明早"是相对概念,相对的是当前时间——而这个信息默认不在它的上下文里。
解法:每次请求把当前时间硬塞进 system prompt,并且要求所有时间先解析成绝对时间再输出。
坑二:温度的十种说法
"二十六度"、"26.5"、"调低点"、"最凉快那种"。
表单 Schema 约束了字段类型,约束不了值的野性。"最凉快"这种,归一层直接映射成 16 度并回复确认。参数归一这套闸门,防飘移那篇的第 5 道防线原样搬来,一个字没改——好用的设计是会在别的地方再救你一次的。
坑三:下发成功 ≠ 空调开了
API 返回 200,空调没动。
设备离线、指令丢了、红外没对准,任何一环都可能导致"我说了"但"没成"。如果不做状态回读,Agent 就会一本正经地通知你"已为您关闭空调"——它没撒谎,它只是不知道自己没做到。
所以执行完必须 queryDeviceStatus 回读一次,对不上就重试,重试还不行就老老实实通知失败。执行闭环这东西,聊天机器人可以没有,设备控制必须有。
坑四:token 半夜过期
凌晨三点触发的计划,挂在了 OAuth token 过期上。
老熟人了,桉树的 Task Processor 里写过 token 提前一小时刷新,这次原样抄回来。历史经验这种东西,就是让你在同一类坑里只摔一次。
坑五:安卓的后台,说杀就杀
手机端本来还想做地理围栏触发(到家前半小时开空调),后来发现安卓的后台进程活得跟案板上的鱼一样,指不定哪个瞬间就没了。
现在的做法是保底逻辑:本地闹铃 + 计划列表全在服务端,手机端挂了不影响执行,只是通知晚点到。
初步测试
跑通的那天晚上,我对着手机说了一句:
"十一点半空调调 27 度,凌晨四点关掉,明早七点半用卧室灯叫我。"
三秒钟,三条计划躺在列表里,参数一个没错。
那一瞬间的感觉很奇妙——我什么都没设置,我只是说了一句话,然后世界就照办了。
而且这句话不管我怎么表述,多么奇葩,多么杂糅,甚至夹杂着English,目前LLM模型的能力都足以将其思考成型
在小度 APP 里点自动化的时候,我可从来没有这种感觉。
总结
这玩意现在离"产品"十万八千里:断网就哑火;LLM 偶尔还是会抽风,把 27 度听成 17 度;家里所有动静都要过一遍大模型,隐私敏感的人怕是要睡不着。
不过对于智能化必须要划死底线:门锁、摄像头这类设备,永远不接进这个系统。幻觉答错一句话,赔的是用户体验;幻觉开错一次门,赔的就不是体验了。
家居控制只是个例子。
以前想把生活里某个环节"智能化",要么等厂商产品经理开恩,要么自己撸代码硬写——门槛是技术。
现在门槛变成了:你会不会把需求说清楚。
LLM 负责理解,代码负责执行,开放平台负责连接。三者拼起来,一个后端开发加一个周末,就能给自己长出一个智能家居 APP——这在三年前是妄想。
而且这件事会越来越便宜。模型再往前跑两代,编排成本趋近于零,到时候限制你的只剩下想象力和你家设备的开放接口。
智能家居的下一步,不是设备更聪明,而是编排设备的那颗脑子,归你自己。
小度小度,辛苦了,下班吧。
以后的活,我让 Agent 派给你。
