
























原版配置
AI化后的配置
RuleEngine这 5 个问题决定了项目的系统价值。
项目最终不是一个单纯的 AI 聊天壳子,而是一个有完整主链路的端侧规则系统。当前代码里已经有:
ChatViewModel:负责输入分流、上下文组织、流式请求和结果落地PromptBuilder:负责规则生成 prompt 和普通聊天能力 promptInputClassifier:把输入分成普通聊天和指令聊天NormalChatContextManager / CommandChatContextManager:分别维护两类上下文RuleRepo:用 Room 持久化规则RuleEngine:根据 Wi-Fi 变化读取规则、去重并执行MainActivity:作为事件入口,负责把用户输入和 Wi-Fi 变化接入系统。从提交历史看,项目在这 7 天里经历了几个明显阶段:先接入 prompt、上下文和 action 解析;再补输入分类、上下文管理类和 RuleEngine;最后补持久化配置、页面优化、代码清理和聊天能力 prompt。
最初的工具思路很直白:用户进入页面,手工选择 Wi-Fi、再手工配置音量、亮度、蓝牙之类的动作。
这种方式能用,但门槛很高,尤其是在“我只想说一句:以后在公司 Wi-Fi 下静音”这种场景里,表单输入的体验并不自然。
把“规则配置入口”从表单升级成自然语言:
这一步不是简单多了一个聊天窗口,而是把输入方式从“手工配置”改成了“意图配置”。
项目里专门做了一个 PromptBuilder.build(...),它会把:
拼成一条规则生成专用 prompt,让模型只返回类似下面这种结果:
{
"trigger": "公司WiFi",
"operation": "set_volume",
"params": {
"value": 0
}
}PromptBuilder 里可以直接看到这套结构:buildRecentHistory() 只取最近 4 条用户/AI 消息;规则 prompt 要求模型返回 trigger / operation / params.value 这样的固定结构。
很多 AI Demo 到这里就停了:模型能回一句“好的,我已经理解你的需求”。
但这对工具系统没有意义。真正的问题是:
模型怎么把一句自然语言,稳定变成可以落到本地执行链路里的结构化结果?
把模型输出目标从“文本回答”改成“JSON 结果”。
ChatViewModel 的流式请求结束后,不是简单把回复展示到 UI 就结束,而是:
currentTextCommandChatActionParser().parseActionDTO(currentText)ActionRuleRepo 保存并执行。这一段很关键,因为它让模型输出真正接入了本地系统,而不是停留在“AI 说了一句正确的话”。
它把项目从:
推进到了:
项目接入 AI 后,很快遇到一个很典型的问题:
如果所有输入都走一条链路、吃同一套上下文,结果会很糟:
先做输入分流,再做上下文分离。
InputClassifier 用一组简单规则把输入分成两类:
NormalChatCommandChat当前规则是关键词匹配,例如“帮我、自动、连上、设置、音量、打开、关闭、静音、亮度”等会被判成指令输入。
在 ChatViewModel 里,所有输入都会先进入统一入口:
fun handleUserInput(input: String, wifiSsid: String?) {
when (InputClassifier.classify(input)) {
InputType.NormalChat -> handleNormalChatInput(input, wifiSsid)
InputType.CommandChat -> handleCommandInput(input, wifiSsid)
}
}这一点在当前代码里已经明确落地。
项目没有继续沿用“一个消息列表管所有场景”的方案,而是拆成了两个上下文管理器:
NormalChatContextManager:保留最近 10 条普通消息CommandChatContextManager:保留最近 6 条指令相关消息。更重要的是,AI 回复在流式结束后,也会根据输入类型写回对应上下文,而不是只写到界面消息列表里。appendAssistantToContext(...) 就是在做这件事。
它把系统从“一个大聊天框”变成了两条清晰的业务链:
这是这个项目工程感开始明显提升的分水岭。
如果规则只是保存在内存里,并且只有用户按下发送按钮时才顺手跑一下,那它本质上还是个半成品。
真正需要解决的问题是:
当 Wi-Fi 环境变化时,系统能不能独立地读取规则、去重、执行,并把结果反馈回来?
抽出 RuleEngine,让“规则运行”变成一条独立链路,而不是附着在聊天链路上。
RuleEngine.run(ssid) 做了几件事:
ssid 从 RuleRepo 读取规则lastActions 比较SkippedDuplicateNoRuleSuccess(actions)。同时,MainActivity 里已经有了独立的环境入口 onWifiMaybeChanged():
ssidruleEngine.run(ssid)updateEnvFromRuleResult(result) 更新环境展示viewModel.onRuleRunResult(result)。另外,这个入口并不是只在发送消息时调用,它会在页面初始化、onResume()、Wi-Fi 权限回调后都触发。
项目真正拥有了第二条独立主线:
而不再只是:
如果规则每次重启应用就丢,那就算模型能生成规则,也只能算实验功能。
这个问题在 Day7 才真正补齐:
规则必须持久化。
把 RuleRepo 从内存容器升级成真正的本地存储层。
现在 RuleRepo 已经通过 Room 建了一个本地数据库:
RuleRepo.init(context) 里使用 Room.databaseBuilder(..., "rule_store.db")addRule(action) 调 dao.upsert(...)getRules(ssid) 调 dao.getByTrigger(ssid) 再转回 Action。MainActivity.onCreate() 里也已经在页面初始化时执行了 RuleRepo.init(applicationContext)。
项目从“会生成规则”升级成了“规则真的能被保存下来,并在下次启动时继续生效”。
项目加了聊天入口后,很自然会出现一个新问题:
如果没有明确约束,模型很容易回答得过宽,甚至超出项目真实能力边界。
为普通聊天链路单独加一条能力说明 prompt。
PromptBuilder 里增加了 buildAppCapabilityPrompt(),专门告诉模型:
这是这个 App 的助手
当用户询问能力边界时,要明确回答:
当前普通聊天上下文里,会 prepend 这条 capability prompt,再拼接普通聊天消息。这样普通聊天不只是在“能说话”,而是开始有了更清楚的产品边界。
flowchart TD
A[用户输入] --> B[MainActivity]
B --> C[ChatViewModel.handleUserInput]
C --> D[InputClassifier]
D -->|NormalChat| E[NormalChatContextManager]
D -->|CommandChat| F[CommandChatContextManager]
E --> G[普通聊天 Prompt]
F --> H[规则生成 Prompt]
G --> I[ChatRepo.streamChat]
H --> I
I --> J[流式文本拼接]
J --> K[更新 UI]
J --> L{是否为 CommandChat}
L -->|否| M[写回普通上下文]
L -->|是| N[ActionParser]
N --> O[RuleRepo.addRule]
O --> P[ActionExecutor.execute]
P --> Q[写回指令上下文]
flowchart TD
A[onCreate / onResume / Wi-Fi 权限回调] --> B[onWifiMaybeChanged]
B --> C[读取当前 SSID]
C --> D[RuleEngine.run]
D --> E{匹配结果}
E -->|NoRule| F[界面提示无匹配规则]
E -->|SkippedDuplicate| G[界面提示跳过重复执行]
E -->|Success| H[执行 Action 列表]
H --> I[更新环境状态与执行结果]
flowchart LR
UI[MainActivity / UI] --> VM[ChatViewModel]
VM --> Classifier[InputClassifier]
VM --> NCtx[NormalChatContextManager]
VM --> CCtx[CommandChatContextManager]
VM --> Prompt[PromptBuilder]
VM --> Repo[ChatRepo]
VM --> Parser[ActionParser]
Parser --> RuleRepo[RuleRepo]
UI --> RuleEngine[RuleEngine]
RuleEngine --> RuleRepo
RuleEngine --> Executor[ActionExecutor]
到当前阶段,这个项目已经具备:
它已经足够作为一个完整的 AI 端侧工程原型来讲。
如果继续往下做,比较自然的方向会是:
InputClassifier 增加更细粒度分类这个项目过程中最重要的变化,不是“多了几个类”,而是系统边界越来越清楚了:
最后得到的不是一个“AI 聊天 App”,而是一个:
AI 驱动的 Wi-Fi 场景规则生成与执行系统
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。