从手动配置到自然语言生成规则:一个 Android AI 工具的重构实录

5 阅读9分钟

ChatGPT Image 2026年4月21日 17_09_39.png

先说结论,使用AI 驱动的 Wi-Fi 场景规则生成与执行系统,大幅简化了工具类App的配置使用门槛

原版配置

ef6b8f1c6e2619ad02c9c08156c05118.jpg

AI化后的配置

97907d9d48276af9eeb8c1c9781a5fc7.jpg

值得重点讲的 5 个问题

  1. 规则输入方式怎么从表单升级成自然语言
  2. 模型怎么从“会回答”变成“会产出结构化规则”
  3. 普通聊天和规则生成为什么必须分流
  4. 规则运行为什么要独立成 RuleEngine
  5. 规则为什么必须持久化,而不能只存在内存里

这 5 个问题决定了项目的系统价值。

可以带过的问题

  • 页面细节和视觉优化
  • 状态文案怎么命名
  • 某些小范围重构
  • 某次 prompt 的措辞细节调整

先说结果:项目最后长成了什么样

项目最终不是一个单纯的 AI 聊天壳子,而是一个有完整主链路的端侧规则系统。当前代码里已经有:

  • ChatViewModel:负责输入分流、上下文组织、流式请求和结果落地
  • PromptBuilder:负责规则生成 prompt 和普通聊天能力 prompt
  • InputClassifier:把输入分成普通聊天和指令聊天
  • NormalChatContextManager / CommandChatContextManager:分别维护两类上下文
  • RuleRepo:用 Room 持久化规则
  • RuleEngine:根据 Wi-Fi 变化读取规则、去重并执行
  • MainActivity:作为事件入口,负责把用户输入和 Wi-Fi 变化接入系统。

从提交历史看,项目在这 7 天里经历了几个明显阶段:先接入 prompt、上下文和 action 解析;再补输入分类、上下文管理类和 RuleEngine;最后补持久化配置、页面优化、代码清理和聊天能力 prompt。


问题 1:手工配置规则门槛太高,输入方式必须升级

最初的工具思路很直白:用户进入页面,手工选择 Wi-Fi、再手工配置音量、亮度、蓝牙之类的动作。
这种方式能用,但门槛很高,尤其是在“我只想说一句:以后在公司 Wi-Fi 下静音”这种场景里,表单输入的体验并不自然。

解决思路

把“规则配置入口”从表单升级成自然语言:

  • 用户负责表达意图
  • AI 负责把意图翻译成结构化规则
  • 本地系统负责保存和执行

这一步不是简单多了一个聊天窗口,而是把输入方式从“手工配置”改成了“意图配置”。

关键实现

项目里专门做了一个 PromptBuilder.build(...),它会把:

  • 当前 Wi-Fi
  • 最近 4 条相关消息
  • 当前输入
  • 固定 JSON 输出格式
  • 示例

拼成一条规则生成专用 prompt,让模型只返回类似下面这种结果:

{
  "trigger": "公司WiFi",
  "operation": "set_volume",
  "params": {
    "value": 0
  }
}

PromptBuilder 里可以直接看到这套结构:buildRecentHistory() 只取最近 4 条用户/AI 消息;规则 prompt 要求模型返回 trigger / operation / params.value 这样的固定结构。


问题 2:只有“聊天”不够,必须让 AI 产出可执行的结构化结果

很多 AI Demo 到这里就停了:模型能回一句“好的,我已经理解你的需求”。
但这对工具系统没有意义。真正的问题是:

模型怎么把一句自然语言,稳定变成可以落到本地执行链路里的结构化结果?

解决思路

把模型输出目标从“文本回答”改成“JSON 结果”。

关键实现

ChatViewModel 的流式请求结束后,不是简单把回复展示到 UI 就结束,而是:

  1. 继续持有完整的 currentText
  2. 如果这次输入属于 CommandChat
  3. 就进入 ActionParser().parseActionDTO(currentText)
  4. 校验 DTO
  5. 再转成本地 Action
  6. 交给 RuleRepo 保存并执行。

这一段很关键,因为它让模型输出真正接入了本地系统,而不是停留在“AI 说了一句正确的话”。

这一步解决了什么

它把项目从:

  • AI 会聊天

推进到了:

  • AI 能生成规则,并让规则落地

问题 3:普通聊天和规则生成混在一起,prompt 很容易被污染

项目接入 AI 后,很快遇到一个很典型的问题:

  • 有些输入是普通聊天,比如“你能做什么?”
  • 有些输入是规则指令,比如“以后在这里把音量调成 0”

如果所有输入都走一条链路、吃同一套上下文,结果会很糟:

  • 普通聊天会误触发规则生成
  • 规则生成会被闲聊历史污染
  • prompt 变得越来越不稳定

解决思路

先做输入分流,再做上下文分离。

关键实现 1:输入分流

InputClassifier 用一组简单规则把输入分成两类:

  • NormalChat
  • CommandChat

当前规则是关键词匹配,例如“帮我、自动、连上、设置、音量、打开、关闭、静音、亮度”等会被判成指令输入。

ChatViewModel 里,所有输入都会先进入统一入口:

fun handleUserInput(input: String, wifiSsid: String?) {
    when (InputClassifier.classify(input)) {
        InputType.NormalChat -> handleNormalChatInput(input, wifiSsid)
        InputType.CommandChat -> handleCommandInput(input, wifiSsid)
    }
}

这一点在当前代码里已经明确落地。

关键实现 2:上下文分离

项目没有继续沿用“一个消息列表管所有场景”的方案,而是拆成了两个上下文管理器:

  • NormalChatContextManager:保留最近 10 条普通消息
  • CommandChatContextManager:保留最近 6 条指令相关消息。

更重要的是,AI 回复在流式结束后,也会根据输入类型写回对应上下文,而不是只写到界面消息列表里。appendAssistantToContext(...) 就是在做这件事。

这一组改动解决了什么

它把系统从“一个大聊天框”变成了两条清晰的业务链:

  • 普通聊天链
  • 规则生成链

这是这个项目工程感开始明显提升的分水岭。


问题 4:规则不能只在“发送消息时顺手执行”,必须有独立运行引擎

如果规则只是保存在内存里,并且只有用户按下发送按钮时才顺手跑一下,那它本质上还是个半成品。
真正需要解决的问题是:

当 Wi-Fi 环境变化时,系统能不能独立地读取规则、去重、执行,并把结果反馈回来?

解决思路

抽出 RuleEngine,让“规则运行”变成一条独立链路,而不是附着在聊天链路上。

关键实现

RuleEngine.run(ssid) 做了几件事:

  1. 根据 ssidRuleRepo 读取规则
  2. 和上次执行的 lastActions 比较
  3. 如果 Wi-Fi 和动作都没变化,返回 SkippedDuplicate
  4. 如果没匹配规则,返回 NoRule
  5. 否则批量执行动作,并返回 Success(actions)

同时,MainActivity 里已经有了独立的环境入口 onWifiMaybeChanged()

  • 获取当前 ssid
  • 更新界面上的 Wi-Fi 状态
  • 调用 ruleEngine.run(ssid)
  • updateEnvFromRuleResult(result) 更新环境展示
  • 再把结果同步给 viewModel.onRuleRunResult(result)

另外,这个入口并不是只在发送消息时调用,它会在页面初始化、onResume()、Wi-Fi 权限回调后都触发。

这一步的意义

项目真正拥有了第二条独立主线:

  • 环境变化 → 规则运行 → 执行反馈

而不再只是:

  • 用户输入 → AI 回复

问题 5:规则只存在内存里,没有实际使用价值

如果规则每次重启应用就丢,那就算模型能生成规则,也只能算实验功能。
这个问题在 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)

这一步的意义

项目从“会生成规则”升级成了“规则真的能被保存下来,并在下次启动时继续生效”。


问题 6:用户问“这个 App 能做什么”时,AI 也需要有边界

项目加了聊天入口后,很自然会出现一个新问题:

  • 用户会问:“你能做什么?”
  • 或者:“这个软件到底是干嘛的?”

如果没有明确约束,模型很容易回答得过宽,甚至超出项目真实能力边界。

解决思路

为普通聊天链路单独加一条能力说明 prompt。

关键实现

PromptBuilder 里增加了 buildAppCapabilityPrompt(),专门告诉模型:

  • 这是这个 App 的助手

  • 当用户询问能力边界时,要明确回答:

    • 当前可以通过与 AI 聊天设置 Wi-Fi 环境下的手机配置
    • 支持音量、亮度、蓝牙开关
    • 并且下次启动时会自动应用这些已保存配置。

当前普通聊天上下文里,会 prepend 这条 capability prompt,再拼接普通聊天消息。这样普通聊天不只是在“能说话”,而是开始有了更清楚的产品边界。


最终架构长什么样

1)输入链路

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[写回指令上下文]

2)环境运行链路

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[更新环境状态与执行结果]

3)模块关系图

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 端侧工程原型来讲。 如果继续往下做,比较自然的方向会是:

  • 增加规则编辑、删除和覆盖策略的 UI
  • InputClassifier 增加更细粒度分类
  • 把日志和可观测性做得更系统
  • 再补一层更清晰的消息模式建模
  • 逐步引入更多系统能力和工具模块

结语

这个项目过程中最重要的变化,不是“多了几个类”,而是系统边界越来越清楚了:

  • 输入不是都一样的
  • 上下文不是都能混着用的
  • 模型输出不能只停留在文本
  • 规则运行不能挂在聊天链路上
  • 规则如果不持久化,就不是真正的工具能力

最后得到的不是一个“AI 聊天 App”,而是一个:

AI 驱动的 Wi-Fi 场景规则生成与执行系统

完整的代码可以点这里移步我的github查看