
1. 为什么 EasyClick 大型项目需要工程化1.1 从五百行脚本到十二个业务流程先说一段真实经历。最早接触 EasyClick 时一个项目基本就是单个几百行的脚本文件。控件、业务逻辑、异常处理全部放在一起也没觉得有什么问题。真正难堪的是后来有一个项目业务流程从两个扩展到十二个还要兼容三种不同分辨率。我当时打开项目文件光是想找到“首页那个按钮的控件定义在第几行”就花了大半天。那时候的体验用一个词来形容就是“连坐”。改一个控件名可能牵动首页判断、登录跳转、订单确认三条业务逻辑改一个等待时间可能让十几个步骤全部重新排队。更头疼的是因为界面和逻辑混在一起完全没法并行开发。两个人同时改同一个文件Git 合并时满屏冲突与其解决冲突不如重写一遍。这种痛点的本质不是脚本语言不好也不是工具不够强而是我们一直用“写小脚本”的思维去做大项目。传统软件开发里早就验证过的分层思想、模块化、单一职责在自动化脚本领域常常被人忽略。我后来花了很大力气做工程化改造才慢慢把这套大脚本从“能跑就行”变成“能维护、能扩展”。1.2 工程化前后做同一件事的差距我做的第一件改革是先把项目按三个层次拆开UI 层只管描述界面定义所有控件和可操作元素业务层只管流程编排负责判断、调度、重试基础设施层封装日志、等待、重试、设备配置等低频变化的部分。改造后我做了个对比屏幕适配新增一个方案开发成本从修改十几个文件变成只新增一个 UI 描述文件业务流程新增一个入口只需要新建一个业务模块引用已有控件就好。类似的需求变化改动量大概降了一个数量级。工程化在短期看起来是“绕远路”因为要多维护目录结构、要写规范、要考虑接口约定。但只要你遇到过一次“因为一个控件变更整个项目都要重新测一遍”的情况就会知道这条路省下来的时间是实实在在的。尤其是当 AI 参与开发后一个清晰的工程边界本身就等于给 AI 画好了干活的范围它能少出很多错。1.3 工程化的本质是降低“调用成本”随手贴一张改造前后的项目文件对比可能更直观。改造前是main.js ├── 打开 App ├── 判断登录 ├── 找到首页搜索框 ├── 输入关键词 ├── 点击搜索按钮 ├── 等待结果列表 ├── ...改造后项目变成app/ ├── ui/ │ ├── home.js │ ├── login.js │ └── order.js ├── flows/ │ ├── task_login.js │ ├── task_search.js │ └── task_order.js └── libs/ ├── logger.js └── auto.js两者对比改造后的每一层文件都短了很多每个文件的职责非常清楚。识别一个页面就去ui/对应的文件里找跟界面无关的流程调度就去flows/里找。这种“一眼就能定位”的体验其实比代码本身的优化更能提升效率。2. 核心架构UI 与业务分离的分层设计2.1 一份可以直接参照的目录结构上面已经出现一份简化的目录我再把完整版展开方便直接复制参考project_root/ ├── app/ │ ├── ui/ │ │ ├── home.js │ │ ├── login.js │ │ ├── order.js │ │ └── common.js │ ├── flows/ │ │ ├── task_main.js │ │ ├── task_order.js │ │ └── task_exception.js │ ├── libs/ │ │ ├── auto.js │ │ ├── logger.js │ │ └── retry.js │ └── config/ │ ├── device.js │ ├── settings.js │ └── env.js ├── assets/ │ └── images/ ├── rules/ │ ├── spec.md │ └── .coderules.json └── dist/ └── release.js关键点在于ui/、flows/、libs/三个目录的边界。ui/里的文件只描述界面不写流程flows/里的文件只写流程不直接操作原生控件libs/里放所有跨项目可复用的东西。这个边界一开始就得靠规则守住否则时间一长又会变得一坨。2.2 UI 层写什么东西不写什么东西UI 层一个文件的实例大概是这样的以首页为例// app/ui/home.js const HomeUI { entry: id(main_entry), searchInput: id(search_input), searchBtn: text(搜索), profileTab: desc(我的标签页), floatingAction: id(float_action), }; function openSearchPage(timeout 10) { return waitClick(HomeUI.searchBtn, timeout); }它允许做的事定义选择器和坐标提供hasXxx、tapXxx、inputXxx等基础操作处理坐标换算和多分辨率适配。它不允许做的事不允许判断“如果订单已完成就不点击搜索”不允许操作返回值继续驱动流程不允许读写业务数据。一句话UI 层是“界面字典加手势服务”它不关心用户最终想完成什么业务只关心当前屏幕上有哪些东西、怎么点。这个认知非常重要因为我见过不少项目嘴上说分层实际 UI 文件里全是业务判断最后等于没分。2.3 业务层如何与 UI 层协作业务层的代码风格也相对固定// app/flows/task_search.js function runSearchTask(keyword) { const inHome ui.home.hasHomeEntry(); if (!inHome.found) return { success: false, reason: not_in_home }; ui.home.openSearchPage(); ui.home.inputSearch(keyword); ui.home.tapSearchBtn(); const list ui.search.waitResultList(15); if (!list.found) return { success: false, reason: search_timeout }; return { success: true, count: list.count }; }它通过ui.*暴露出来的方法一步步推进自身的判断集中在“应该走哪一步”“上一步有没有成功”“失败要怎么处理”上。至于控件是id还是text、在屏幕哪个位置业务层完全不知道。这样一旦界面改版只需要回到 UI 层更新定义业务层的判断和流程不用动。2.4 两层之间的统一返回结构为了让两层协作顺畅我定了一条硬规则UI 层方法统一返回带found和reason的对象而不是直接返回null或难懂的异常。例如// 找不到时的返回 { found: false, reason: timeout } // 找到时的返回 { found: true, reason: ok, element: selector }这么做有三个好处业务层拿到结果后直接用if (!res.found)判断不需要捕获各种异常AI 生成代码时不会因为空对象而臆造奇怪的兼容逻辑日志里能清晰看到每一步失败的原因方便定位问题。这条规范同样写进rules/spec.mdAI 和人工开发都必须遵守。不要小看这个返回结构约定它其实是整个分层架构能稳定运行的“通信协议”。2.5 多分辨率适配怎么处理手机自动化最烦的就是分辨率适配。以前我在业务代码里到处写“1080 分辨率时 x5002340 分辨率时 x300”最后自己都会搞混。分层之后我把所有坐标换算都收敛到 UI 层。具体做法是在config/device.js里记录当前设备的基准分辨率UI 层定义坐标时只写逻辑坐标再提供一个convertPoint()函数做换算// app/libs/auto.js function convertPoint(x, y, fromWidth 1080, fromHeight 2340) { const sw device.width / fromWidth; const sh device.height / fromHeight; return { x: Math.round(x * sw), y: Math.round(y * sh) }; }业务层请求某个坐标时UI 层负责把缩放换算好。这样换设备时只需要改设备配置不需要满项目搜索坐标。这个做法在多 AI 并行时尤其重要因为 AI 不会记得你哪个坐标写在了哪一行但统一的换算入口能把错误范围限制住。3. 多 AI IDE 并行开发工具选型与角色分工3.1 一个编码助手解决不了所有环节我前面说“工程化是基础”但真正让这个模式跑得飞快的其实是多 AI IDE 并行。为什么要用多个我举几个真实感受有的助手擅长长上下文总结能站在全局分析项目结构有的助手优点在于代码风格统一生成大量重复 UI 定义时很稳定有的助手逻辑推理更强适合写复杂流程但它的长上下文能力弱不适合让它读整个项目。如果整个项目只用其中一个要么 UI 层写得还行但流程一复杂就开始乱要么业务逻辑挺强但控件命名五花八门。所以我把任务按角色拆开把不同 IDE 当成不同风格的程序员各管一摊。不要指望同一个工具所有场景都最强分工之后反而能发挥每一个的长处。3.2 典型角色分工表为了避免涉及具体产品我用助手 A、B、C 来代表不同能力的 AI IDE角色适合承担的模块能力要求主要产出助手 AUI 层、控件定义风格稳定、结构化输出ui/*.js、坐标换算、截图素材说明助手 B业务层、流程实现逻辑推理、异常处理flows/*.js、状态判断、重试方案助手 C全局审查、代码巡检长上下文、规则理解风险清单、模块接口一致性检查这里想强调一点角色分工会随模型能力变化而调整。没有永恒固定的“哪个助手负责哪一层”只要你觉得某类任务换另一个工具产出更稳定就随时换。唯一不变的是“角色边界”本身界面归界面流程归流程。3.3 用一份规则文档约束所有 AI多工具协作最怕各写各的。我解决的办法是在项目根目录放一份rules/spec.md并且把它作为每个 AI IDE 的“项目规则”注入。关键规则如下# 项目协作规则rules/spec.md - UI 层禁止写业务判断业务层禁止直接写原生选择器。 - 控件命名一律使用“页面前缀 功能名”如 homeSearch、loginBtn。 - UI 方法统一返回 {found, reason} 结构禁止返回 null。 - 等待操作必须调用 libs/auto.js 的 waitClick禁止自行写 while sleep。 - AI 输出代码时必须说明函数来源本文件定义 / ui 目录 / libs 目录。 - 如果 AI 认为需要新增函数必须先输出新增说明再在代码中实现。 - 所有 AI 生成的代码默认假设会接受另一个助手审查。这份文档相当于团队的“代码规范 接口协议”。每次新开对话我会先让 AI 读一遍这份文件再让它干活。用同一个规范约束多个模型出来的代码才会有统一的风格。规则不在多在于能被强制检查。4. 拿来即用的三种 AI 提示模板4.1 UI 层助手提示模板如果你要请一个 AI 专门生成 UI 层可以直接复制下面这段提示词把其中的页面信息替换一下你是 EasyClick 项目的 UI 层代码生成助手。 项目规则见 rules/spec.md请先阅读并严格遵守。 现有页面首页 页面结构描述 - 顶部搜索框占位文案“搜索你想找的商品” - 中间 Banner 区域resource-id 为 home_banner - 底部 Tab 栏依次为首页、分类、购物车、我的 - 右下角有悬浮入口resource-id 为 float_action 请输出 app/ui/home.js - 只定义选择器和 UI 操作封装 - 不允许写任何业务判断 - 每个方法返回 {found, reason} - 控件命名以 home 前缀开头。这个模板的关键是给了 AI 三个东西角色定位、页面事实、输出约束。角色定位让它知道自己是 UI 层不会跑偏去写流程页面事实是真实信息避免 AI 编造控件输出约束则对齐了整体架构。4.2 业务层助手提示模板业务层更适合把需求写成一个“用例式流程”让 AI 基于流程来编排你是 EasyClick 项目的业务层代码生成助手。 项目规则见 rules/spec.md请先阅读项目规则。 流程每日签到任务 前置条件App 已登录首页已完成加载 步骤 1. 如果首页存在“签到中心”入口点击进入。 2. 进入后查找“立即签到”按钮并点击。 3. 点击后 3 秒内页面应出现“签到成功”提示。 4. 如果提示未出现重新点击一次再次失败则截图并记录日志。 5. 完成后返回首页并保存本次签到状态到日志文件。 请输出 flows/task_sign.js - 只调用 ui.* 模块禁止直接写原生选择器 - 所有点击使用 waitClick 等待 - 返回结果统一为 {success, reason}。“用例式”写法比“帮我做个签到”清晰得多。AI 能把每个判断点翻译成代码不会漏掉关键步骤也不会脑补需求。如果你需求里有多条分支一定要在流程里写清楚 when / then。4.3 全局审查助手提示模板第三类模板用于代码审查它是整个多 AI 并行体系里的“保险丝”你是一名自动化项目代码审查员。 项目规则见 rules/spec.md请先阅读。 请检查 app 目录 1. 是否存在 UI 层写业务判断、业务层写原生选择器的情况 2. 是否存在重复定义的控件名 3. 所有点击操作是否都有超时保护 4. 是否有 AI 直接调用不存在的函数 5. 业务层返回结果是否符合 {success, reason} 约定 最后按严重程度输出风险清单。这类审查类提示词建议固定成项目级模板不要每次重写。审查助手不需要很强的代码生成能力但需要能跨文件找矛盾。实际用下来它的精确度相当高。4.4 模板设计里的几个细节有几个细节值得留意每个模板都先要求 AI “阅读规则文件”这能拉齐所有工具对项目的认知模板里明确列出“允许做什么、禁止做什么”比只让 AI 自由发挥稳定得多模板末尾要求“统一返回结构”是为了让审查环节有固定的检查依据模板里不要用模糊词比如“尽量、大概、可能”这些词会让 AI 产生不稳定的判断。这些模板不是一成不变的。随着项目变大可以继续往里面追加“设备配置说明”“日志约定”等章节但核心的三大块——角色、事实、约束——始终不要丢。5. 实操篇多 IDE 并行的完整工作流5.1 UI 层截图、控件树、描述三件套拿 UI 层举例标准操作步骤是打开界面分析工具导出目标页面的控件树 XML或直接截一张清晰的页面图。把截图、XML、以及一两句“人话”需求同时发给助手 A。比如“首页顶部有搜索框占位文案是搜索你想找的商品底部标签栏是首页、分类、购物车、我的”。让助手 A 输出符合rules/spec.md的 UI 文件。人工逐项核对资源 id、text、desc 与界面实际是否一致。核对完的文件提交到app/ui/。这五步里最关键的是第 4 步。AI 偶尔会脑补一个不存在的控件或者把两个控件混在一起。因为 UI 文件是业务层的地基这里出错后期会很头痛。5.2 业务层用“用例式描述”驱动流程实现业务层给 AI 的输入不要直接说“给我写个函数执行订阅任务”而是给一份接近测试用例的流程描述。比如流程搜索商品 前置条件已登录首页加载完成 步骤 1. 点击首页顶部搜索框。 2. 输入关键词“蓝牙耳机”。 3. 点击键盘上的搜索按钮。 4. 等待结果列表出现最多等 15 秒。 5. 如果列表为空重新触发一次搜索再次为空则截图并记录日志。然后把这段描述交给助手 B加上一句“按 rules/spec.md 实现到 flows/task_search.js”。这种“用例式”写法的好处是流程里的每个判断点都会被 AI 明确翻译成代码逻辑。它比“帮我做个搜索”这种口头需求清晰得多AI 不至于漏掉关键步骤也不会自己脑补一些奇怪的需求。5.3 审查环节长上下文助手当“巡查员”当 A 和 B 分别产出一批代码后我会在助手 C 的工作区打开整个项目目录给它一个固定角色“你是一名自动化项目代码审查员”。让它检查 UI 层是否写了业务判断、业务层是否直接写了原生选择器、有没有重复控件名以及所有点击是否带超时。这相当于给项目安排了一个客观的“代码评审”。助手 C 不太擅长从零写功能但让它跨文件找矛盾往往很精准。有几次两个层级的代码各自看着都对一对接就报错都是它提前抓出来的。审查出的问题不要直接让 C 改最好是收回对应模块让 A 或 B 修职责边界不能乱。5.4 分支隔离四个工具并行时的版本管理除了逻辑上分层物理上也要隔离。我强烈建议用 Git 分支管理不同 AI 的工作区main分支始终是可运行的稳定版本。feature/ui-xxx分支助手 A 的改动。feature/flow-xxx分支助手 B 的改动。feature/review分支助手 C 审查意见汇总和修复分支。每个分支只改自己的目录合并时重点检查接口部分冲突大大减少。之前没做分支隔离时两个 AI 同时在根目录写文件覆盖核心公共函数的情况出现过不止一次教训挺深刻。6. 多 AI 协作的翻车与排查实录6.1 控件名漂移A 定义的B 找不到最常遇到的问题就是命名漂移。助手 A 在ui/home.js里写了homeTopSearch助手 B 在业务代码里预期的是searchEntry两者一看引用不存在的控件。后来我在app/ui/index.js里做了一次“控件登记”把所有 UI 方法统一导出。然后要求业务层助手必须先从这个入口 import不允许自创控件名。这就等于给 IDE 加了一个中央注册表问题根除。6.2 等待逻辑乱写死等和抢跑AI 生成代码时特别喜欢自己写sleep(1000)和while(true)这在自动化里非常危险。页面还没出来就点击叫抢跑无脑死等叫死等。我的应对是封装libs/auto.js的waitClick和waitSelector并规定所有 AI 代码只能调这两个函数不许自己写等待循环。// libs/auto.js function waitClick(selector, timeout 10) { let waited 0; while (waited timeout) { if (exists(selector)) { click(selector); return { found: true, reason: ok }; } sleep(1000); waited; } return { found: false, reason: timeout }; }有同学会问用系统自带的 click 不就行了吗问题是系统自带的 click 在你调用时元素可能还没渲染出来而 AI 又特别喜欢在前后两个操作之间补一个随机 sleep。与其让 AI 乱来不如集中管理等待策略。6.3 幻觉函数调用了一个不存在的 API业务层 AI 有时候会输出clickButtonByText(立即签到)看着像模像样但项目里根本没这个函数。规范里的一条就是“函数必须有来源”。AI 只能调用三类函数当前文件内定义的、从 ui 模块导入的、从 libs 导入的。如果它觉得缺函数必须先说明并新增再调用。这招基本治住了 AI 自嗨。如果你遇到某个 AI 助手总会写幻觉函数建议在提示词里加一句“如果调用不存在的函数将导致运行失败你必须严格使用已有函数”。多数情况下加上这句话之后情况会明显改善。6.4 并行合并冲突三个物理预防手段物理目录隔离UI 和业务、公共库放不同的目录Git 分支隔离不同助手在不同分支作业统一发布构建用构建脚本把多个模块打包成单个dist/release.js不手动在发布脚本里改代码。有了这三层合并冲突从“每次都有”变成“很少出现”。剩下的零散冲突多半是修改了同一个公共方法走一次代码审查就能解决。7. 工程化之后的长期红利7.1 把自己从“写脚本”升级成“搭框架”工程化做到位之后项目的每个模块其实都可以沉淀下来。我现在做新项目第一件事不是从零写代码而是先复制一套自己的libs/和rules/然后针对具体业务填 UI 层和流程层。一个基础框架复用三四个项目之后开发速度会变得肉眼可见地快。公共层代码慢慢变成自己的“资产”不再是一个项目一份代码。7.2 preflight 自检浪费十几分钟之前先排雷页面改版是自动化项目最大的敌人。AI 时代也一样谁能更快发现“元素不存在”谁就省时间。我在业务层加了一个启动自检函数function preflight() { const required [ ui.home.hasHomeEntry(), ui.login.hasLoginInput(), ui.order.hasOrderList(), ]; const failed required.filter((r) !r.found); if (failed.length 0) { logger.error(preflight failed, failed); return false; } return true; }每次正式执行前先跑一遍preflight。如果没有自检一个小页面改版就能让脚本白跑十几分钟有了它几秒钟就能判断该不该继续。这个习惯很浅显但不少项目都忽略了。7.3 规则文档同时约束 AI 也约束新人很多人以为rules/spec.md只是给 AI 看的其实不是。它的价值在于让所有参与项目的人都在同一套约定下工作。新同事或新伙伴接手项目先读规则文档再读代码上手速度快不少。可以说这份文档是整个工程从“个人脚本”走向“可以被协作维护的项目”的标志。提示规则文档建议放在项目根目录命名固定为rules/spec.md。这样不管谁来接手打开项目第一眼就能看到“这个项目是怎么被约束的”。8. 最后分享几个自己的习惯把多 AI IDE 并行这套玩法跑顺之后我印象最深的不是生成速度而是“收尾成本”降下来了。以前改一处功能要全局搜代码现在只要顺着 UI 和 flows 的边界找定位通常在一分钟内。我个人比较受用的几个习惯新项目启动时先花半天写清楚rules/spec.md后面省下的时间绝对大于这半天每个 AI 助手开新对话前都重新粘贴一次项目结构和规则文件别指望它记住上次的会话不同层级的代码交给不同工具不是固定的哪一环节产出不稳定就换工具试试自动化脚本的核心依然是可维护性不要为了追求“一次生成完”而跳过审查。如果你手头正好有一个规模上来的 EasyClick 项目建议先别急着让 AI 帮你加功能。先把目录拆开定好 UI 与业务的边界再引入多工具并行。这套组合跑顺之后你大概率也会觉得传统那种把所有代码堆一个文件里的做法真的回不去了。