把整栋房子做成 MCP 工具:翻完 SmartHaus 源码,我看到的不是智能家居,是 .NET 工程师的执念

最近在 GitHub 上淘到一个有意思的仓库,叫 SmartHaus,我顺手 fork 了一份(github.com/geffzhang/SmartHaus)。吸引我的是它的定位描述:Agentic-on-the-Edge smart home implementation w/ Microsoft Agent Framework & Model Context Protocol。翻译成人话就是:把一整套房子跑在边缘设备上,让 AI 代理通过 MCP 协议来管。这个方向和咱们 .NET 社区最近在推的 Agent 生态正好对上,值得展开聊聊。

正好前阵子在博客园读到 Y00 那篇《KNX 照明系统原理与基于 KNX.net 的开发示例》,讲的是他接了个小项目,把 KNX 照明接进 .NET 后台,结果被"组地址""ETS""广播加过滤"这套楼控逻辑折腾了好几天。那篇文章的结尾有句话我印象很深:KNX 跟消费级无线方案不是一个思路,它追求的是确定性、长寿命和跨厂商互通,代价是布线成本和调试门槛。

这两个东西撞在一起,我周末就把 SmartHaus 的源码 clone 下来翻了一遍。三十二个项目,五百四十多个 C# 文件,三万六千行代码。翻完之后的感受有点复杂:这可能是我见过的最"不 demo"的 AI 智能家居项目------别人用 Python 裹一层 Home Assistant 的 API 就敢发视频,这位作者把 KNX 总线的领导选举、报文去重、百叶窗行程跟踪全写了一遍,然后把大模型扔到了体系外面。顺手说一句,仓库是全英文的,这篇就当替大家趟一遍路。

先花两分钟搞懂 KNX,不然后面看不懂

如果你跟之前的我一样,对 KNX 的认知停留在"一种很贵的智能家居总线",建议先去读 Y00 那篇文章,讲得很透。这里只捞三个最关键的干货:

KNX 的开关面板和灯之间没有直接的电线连接。面板接在 KNX 总线上(一根双绞线,同时传信号和 29V 直流供电),灯接在执行器控制的 220V 强电回路上,两者靠"组地址"在逻辑上绑在一起。

控制方式是广播加过滤。按一下面板,面板往总线上发一条带组地址的报文,总线上所有设备都收得到,但只有监听这个组地址的执行器会动作。所以 KNX 系统里中控挂了,灯照样能开------功能逻辑不靠中央控制器。

设备和组地址的绑定关系,是用官方配置软件 ETS 一次性烧进去的。这个 ETS 导出的工程文件,后面会反复出现,记住它。

SmartHaus 到底是个什么东西

原作者是 f2calv,本名 Alex Vincent,一个常年维护 CasCap 系列 .NET 开源库的老哥。仓库今年四月才建,MIT 协议,基于 .NET 10,README 第一句话就老老实实写着:这是个早期概念验证,接口随时会变,不提供支持。

但它接的设备清单一点都不"概念验证":Buderus 热泵供暖、Fronius 光伏逆变器、KNX 楼控总线、DoorBird 门口机、UniFi Protect 摄像头、Miele 家电、Shelly 智能插座、WiZ 灯泡、Sicce 水族水泵,外加边缘主机自己的 CPU、GPU 和 GPIO。看出来了,这是一栋欧洲独栋房子的全家桶,作者是真的住在自己写的代码里面。

整个系统的主线其实就一条:设备事件进来,AI 命令出去。我照着源码重画了一张架构图,先看图再读文字会顺很多:

往细里说:每个设备集成是一个 feature service,轮询设备或者收 webhook,把事件扔给一组可插拔的"事件汇"(event sink)------Redis、Azure Tables、Blob、SignalR 各取所需。Redis streams 再把领域事件喂给通信管道,通信管道对接两个外部服务:Signal 网关 signalizr 和多租户代理运行时 agentizr。用户在 Signal 群里发一句话,代理运行时里的大模型决定调哪个工具,工具通过 MCP 协议回调 SmartHaus 暴露的接口,最终落到设备上。

注意一个边界划分,我觉得这是全项目最清醒的设计决策:SmartHaus 自己不构造大模型代理。README 里写得很直白,代理定义、会话、选哪家模型、密钥管理,全是外部 Agent Runtime 的事。SmartHaus 只负责把房子变成一组好用的工具。大模型今年是这个,明年是那个,总线和热泵不会换。

源码里最值得看的四个地方

一、KNX 集成:直接上官方 SDK,而且认了真

Y00 那篇博客用的是 KNX.net,一个社区库,他自己也说"API 不算复杂,够用"。SmartHaus 的 KNX 库直接引了 Knx.Falcon.Sdk------KNX 协会官方的 Falcon SDK,商业项目要授权费的那种------外加 Tiveria.Home.Knx 做补充。Tunneling 和 Routing 两种 KNXnet/IP 接入方式都实现了。

真正让我停下来细读的是这几段:

多副本部署时,用 Redis RedLock 做领导选举,只有抢到 leader 的实例才持有总线连接,备用副本连外发命令的队列都不消费。总线断线有专门的重连通道,按 KNX 的 area/line 拓扑逐条恢复。进来的报文要过按服务族划分的去重过滤器,因为 Falcon 的事件是同步回调,作者还在代码里留了个 TODO,坦言背压策略没想好之前宁可不加上界------"静默丢弃是不安全的"。这种注释一看就是真被坑过。

最有意思的是组地址的解析。KNX 本身不管你怎么命名组地址,SmartHaus 定了一套连字符分段规范:楼层用德语缩写 KG/EG/OG/DG(地下室、一楼、二楼、顶层),类目分照明 LI、百叶窗 BL、供暖 HZ、插座 SD 等八种,再加上房间、朝向、灯具类型。ETS 导出的 XML 喂进来,逐段匹配冻结字典,解析完如果还有剩下的段,说明命名不合规,整条地址直接跳过。

更狠的是 copilot-instructions.md 里给 AI 编程助手立的铁律:KNX 工程文件是命名的唯一事实来源。名字错了,回 ETS 里改,改完重新导出。不许手改导出文件,不许在代码里补偿错误的元数据。见过太多项目是在代码里打补丁糊墙的,这种"源头治污"的纪律,说实话比任何架构图都更能说明工程质量。

二、MCP 工具层:六十九个工具,个个都写着"防呆"

数了一下,CasCap.Backend 里十个领域服务,一共暴露出六十九个 MCP 工具:KNX 总线二十一个、智能照明十五个、家电九个、前门八个、光伏七个......覆盖暖气、热水、百叶窗、门锁、摄像头、水族泵。

工具设计之较真,随便挑几个例子。

所有写操作都带 dryRun 参数。大模型可以先"空跑"一遍验证参数,确认没问题再真正下发。这是防 AI 手滑的第一道闸。

百叶窗移动是个长动作,物理上要走将近一分钟。硬等会拖死对话,所以做成了 tracked operation:工具立即返回"已开始移动",等行程结束再把完成消息推回聊天里。更细的是叶片角度的处理------垂直行程没停之前动叶片会出问题,所以代码里先把位置指令发出去,叶片指令延后发送。

工具的 Description 不是给人看的,是给大模型看的,里面直接塞领域知识。比如百叶窗工具的描述里白纸黑字写着:"注意 KNX 惯例,0 等于全开,100 等于全关。为隐私关闭百叶窗时,记得把叶片角度也设成 100。"连"关门磁传感器不是锁传感器,检测不了锁没锁"这种防幻觉的澄清,都写进了前门状态工具的描述里。

连校验失败的报错都是写给模型看的。你让一组百叶窗去某个位置,但组里没有位置控制地址,返回的错误消息会长这样:这个组没有 POS 地址,动不了;下面这些单独的百叶窗可以动,你挨个控制它们;或者先调 get_house_shutter_states 查清楚再说。错误消息本身就是给大模型的下一步指引------这个细节,我反正是头一回见到有人做得这么彻底。

除了工具,还有 MCP prompts:SummariseHeatingStatus、CheckHotWater、CheckSystemHealth 这类预制提示词,把"巡检供暖系统"这种高频任务固化成模板,用户一句话就能触发完整的检查流程。

三、代理层:系统提示词里的生活气息

用户的入口是 Signal 群聊。仓库里有张截图我来回看了好几遍(见下图,发布时可直接用):用户问了一句"暖气怎么样、能源情况如何、前门关了没",叫 Smart Haus 的编排代理跑在本地 Ollama 的 qwen3.5:9b 上,转头去咨询了三个子代理------供暖代理用 OpenAI gpt-5-mini,能源代理用 AzureOpenAI gpt-4o-mini,安防代理用本地的 qwen。58.7 秒,三次工具调用,23.3KB 上下文,最后拼出一份结构化的家庭状态报告。本地小模型当调度,云端模型当专家,这个搭配很务实。

代理的系统提示词(docs/agent-examples 里有公开示例)规矩立得特别有生活气息:

白天(早七点到晚十点)每小时最多主动发一条消息,夜里闭嘴------除非烟雾报警、设备故障这种安全事件。不重要的事件默默消化掉,重要的攒一批合并成一条简报。给用户提供选项时必须用投票工具,绝不许在文字里罗列选项;投票结果回来后先关投票再执行。只许提供自己真有工具能执行的动作------不许画饼。

语音消息进来先走 whisper(whisper.cpp 或 WhisperAsr 服务都行,配置里还能切 Azure)转成文字再喂给模型,回复可以用 TTS 转回语音发出去。摄像头拍到人,UniFi Protect 的 webhook 打进来,FFmpeg 剪出片段,还能发给视觉代理辨认"门口是谁"。

四、工程化:一个人维护出了团队的阵仗

十三个功能开关(Buderus、Comms、Knx、Fronius......)在配置里用逗号分隔,没硬件的功能不开,不会瞎初始化。

想体验不需要真设备:docker compose --profile demo up --build 一条命令,Redis、Azurite(Azure 存储的本地模拟器)、OpenTelemetry 全家桶起来,然后 curl 一个模拟的摄像头人形检测事件进去,就能看完整条事件链路。requests 目录里给每个控制器都备好了 .http 文件,拿 VS Code 的 REST Client 插件直接点。

部署侧是 Helm chart 加 Grafana 仪表盘,房屋总览、能耗、照明、暖通、水泵的看板 JSON 都随仓库发布。CI、SonarQube 质量门、pre-commit、每个集成库配测试工程,一样不缺。八个设备客户端库单独发 NuGet,你不想跑整套 SmartHaus,单独 dotnet add package CasCap.Api.Fronius 拿回去轮询自家逆变器也行。

还有两处"诚实得可爱"的地方。README 里有一句:This README does not report an executed demo------本 README 不宣称上面这个 demo 被实际跑通过。版本说明里写着:1.1 版本故意在小版本号里引入了不兼容变更,消费者请重新编译并迁移。见过太多项目把 breaking change 藏着掖着,这种坦白我反而愿意信任。

顺带一个 meta 层面的观察:这个项目自己就是 AI 辅助开发的样板间。copilot-instructions、共享指令库、设计文档里还有引进 Squad(人指挥的 AI 开发团队)和 speckit 的完整方案。一个人加一支 AI 队伍,维护一个跑在真实房子里的三万六千行代码的系统------这件事本身和项目内容一样值得琢磨。

泼几盆冷水

别急着 star 完就想上线,门槛得说清楚。

要跑全套,你得真有那堆欧洲硬件------Buderus 的热泵、Fronius 的逆变器、布了 KNX 总线的房子。没硬件只能玩 demo 档,而 demo 档不含 AI 对话,通信功能还得另外部署 agentizr 和 signalizr 两个服务,这俩虽然也开源,但生态基本就作者一个人。

那套组地址命名规范是作者自家的约定,你想复用 KNX 部分,自己的 ETS 工程得按他的规矩重新命名,否则解析器直接跳过你的地址。项目自己也说了,早期 PoC,接口随时变。

还有一点,整套体系的"智能"程度取决于外部代理运行时,SmartHaus 仓库本身不含任何模型编排代码------期待开箱即用的 Jarvis,会失望。

最后说两句

这两年看了太多"AI 智能家居"的演示,大多是大模型加个 function calling,背后裹一层现成平台的 API,演示完就没有然后了。SmartHaus 反过来:四层管道、领导选举、报文去重、干跑校验、行程跟踪,笨功夫全下在设备侧,大模型反而是整个体系里最可被替换的零件。

我挺认同这个次序的。AI 容易,房子难。工具层做得越严谨,上面接什么模型都能跑得稳;工具层糊弄,换再强的模型也只是在更快地产出错误的开关指令。

如果你是 .NET 背景,又对楼控、物联网或者 AI Agent 工程化有点兴趣,这个仓库值得放进收藏夹慢慢啃。哪怕不跑,光读 KNX 库和 MCP 工具层的代码,收获也不会小。觉得有用的话,欢迎点个 star 或者转发给身边搞智能硬件的朋友。

相关链接