一个真实的教学工具项目,从需求到上线全程由 AI Agent 协作完成。本文不吹能力,只讲做成了什么、怎么做的、踩了哪些坑——尤其是那些文档里不会写、但会让你卡住两小时的细节。
我手上有五科 DSE 的词汇表(生物、化学、科学、物理、数学),散在 PDF 和 Word 里。学生背单词全靠纸质名单,老师想知道全班掌握情况只能靠提问。
需求拆开来其实就四条:
同时有个硬约束:老师自己要能改词库。如果每次加个词都要我改代码、重新部署,这个项目就废了。
最后的结果:一个 504KB 的 HTML 单文件(双击即用)+ 一个微信小程序(四个页面)+ 一个云函数后端,五科共 52 本课本 / 265 个单元 / 9545 个词条全部打通。
模块 | 说明 |
|---|---|
英译汉 | 看英文选中文,四选一 |
汉译英 | 看中文选英文,四选一 |
拼写 | 按 QWERTY 虚拟键盘逐字母拼写,按键带剩余次数限制 |
消消乐 | 英文-中文配对消除,统计犹豫率(点了又取消) |
单词泡泡 | Canvas 打泡泡游戏,只对词尾两个字母做判定 |
Reveal | 背诵模式:只看英文,点一下揭开中文,词表可行内增删改 |



每次练习退出时给出一份本次练习的完整报告,而不是只丢一个分数:
标红粒度的设计原则是只标真正判错的字符:
实际效果(一次拼写练习的真实结算):

能力 | 实现方式 |
|---|---|
加/减课本 | HTML 里的「➕ / 🗑」按钮,改的是本机 localStorage |
重命名课本 | 同上 |
编辑词条 | Reveal 界面行内 ✏️ / 🗑 / ➕,改完可导出 .txt |
一键发布 | 双击 发布词库.cmd,词库上云,小程序下次启动自动同步 |
全程统计 | 独立教师页输入口令,拉全班汇总 |

为什么用 HTML 当"数据库"? 因为老师会用的工具只有浏览器。把词库放在一个能双击打开的 HTML 里,她就能自己改;放在数据库里,她必须等我。数据源头选在"用户能直接编辑"的地方,是这个项目最重要的一个决定。
为什么用对象存储而不是数据库? 词库是"整份替换"的只读数据,天然适合对象存储。CDN + 公有读,小程序端 downloadFile 拿到的就是最新版,没有任何查询需求。省掉了数据库的钱和运维,也省掉了"表结构迁移"这类麻烦。
为什么发布脚本零依赖? 老师双击的是 .cmd,环境里没有 npm install 这一步。所以 COS 上传是手写 XML API 签名实现的——只用 Node 内置的 crypto / https。
词库不是 JSON,是一种类 Markdown 的纯文本,因为它要同时满足"人能读能改"和"程序能解析":
规则很简单,但有三条必须守住:
, / , / Tab)。少一个逗号,这行就变成"死词条"——不报错,但永远练不到。这是最难排查的一类问题。## 下面直接写词,归入「(本節導言)」;空小节往返不丢。const UNITS 结构要能互认,避免升级时词库丢失。两端必须用同一套解析器。这里我没靠"小心一点",而是写了一个 quiz-text-test:同一份文本,两端解析结果必须完全一致(导入→导出→再导入,结果不变)。测试通过才算对齐。
腾讯云 COS 的 PutObject 签名算法不复杂,手写一遍就能省掉整个 SDK 依赖。核心就三步:
三个容易翻车的点:
k=v 列表里键必须小写并按字典序排序,值用 encodeURIComponent,但空格要还原成 +;x-cos-* 头需要进签名,普通头(Content-Length、Content-Type)不签更省事;密钥全部走配置,tools/cos.config.json 不进版本库——这类文件一旦截图或提交,等于把桶送人。
小程序启动时并发拉取各科词库文件,落地到本地缓存后并入科目列表。
这里有一个几乎每个人都会踩一次的坑:wx.downloadFile 的合法域名配置必须带 https:// 前缀。少写协议头,域名校验直接不通过——而报错信息并不会明确告诉你是前缀的问题。
统计的难点不是计算,而是语义统一。比如"跳过"到底算不算错?
最后定下来的规则是:
两端的行为必须一致,否则同一份数据在 HTML 和小程序里得出的正确率不一样。
数据结构上,一次练习维护一个 sessWords:
speakSims 用数组而不是取 max,是产品的硬要求:学生读三次、只对一次,和读一次就对,不该得同样的反馈。所以每次尝试都记、都展示,低于 70% 标红。
键盘是 QWERTY 26 键 + 底部空格 + 删除键。关键设计是 spellPool 二维数组:每个字符按它在单词里出现的次数分配"可点次数",次数用完变灰、点不动,而不是报错。
这样学生不会"因为按错键"被扣分,只有拼错字母才算错——符合学习场景,而不是打字考试。
数据量很小(每个学生每个科目一条记录),为此开一个云数据库不划算。做法是:同一个 COS 桶,另开一个私有读的对象前缀当 KV 用。
云函数里只用环境变量存密钥,代码里不落任何凭据:
教师端要口令才给汇总,学生端只能读写自己的 openid 文件。
这部分是我认为本文最有价值的内容。每一条都是真实卡过的。
ERR_INVALID_RESPONSE,curl 却正常现象:浏览器打开函数 URL 直接失败,curl 同样的地址返回 200。
原因:云函数的"参数兼容模式"网关会自动补一个 Content-Type 头,而函数代码里又自己设了一个,于是响应里出现重复的 Content-Type。curl 容忍这种不合规响应,Chrome 严格拒绝。
解法:删掉函数代码里手动设的 Content-Type(交给网关补),重新打包上传。
排查这类问题的经验:当浏览器报错而 curl 正常时,先怀疑响应头不合规,而不是网络、VPN。
函数 URL 形如 https://<appid>-<随机串>.ap-guangzhou.tencentscf.com。我把随机串里的一个字符抄错了(74jdyq5yp 抄成 74jygxi5yp),表现为"部署成功但访问不通",排查了很久。
教训:URL、AppID、桶名这类标识符,永远复制不要手打。
const 声明的变量不挂到 window在 jsdom 里跑页面脚本时,顶层 const xxx = ... 不会变成 window.xxx(只有 var 和函数声明会)。所以测试里不能写 window.state 断言。
解法:断言改走 DOM 结构,或者读函数源码字符串(fn.toString())做静态断言。
现象:结算页里泡泡模式的读音匹配率永远是空数组。
原因:泡泡的逐词记录是在"拼完这个词"的那一刻快照生成的,但读音校验发生在那之后。快照时 speakSims 还不存在,于是存进去一个新建的空数组;之后读音回调往 pot.speakSims 推数据,推的是另一个数组。结算读的还是那个空数组。
解法:在生成记录之前先把数组落到对象上,两边存同一个引用。而拼写模式因为是同一个活对象引用,从来没出过问题——这类 bug 只在"一部分路径共享引用、另一部分不共享"时出现,最难发现。
CSS 里写了:
本意是"错的词标红",但它盖住了我在字母级加的标红规则,效果变成整词红色。而需求是"只标错的那个字母"。
解法:加一个更高优先级的覆盖规则,把字母级标红的作用域保护起来:
HTML 端也有同款坑(.rw.bad .rw-en)。同一个错误在两个技术栈各犯一次——说明问题不在框架,在"命名粒度没有对齐设计意图"。
拼写键盘的空格键,直接输出 {{item.val}} 会渲染成一片什么都没有的空白,看起来像 bug。
解法:显式替换成可见符号:
写内联脚本统计词库时,反引号(模板字符串 / 正则里的反引号)被 shell 抢先解释,报 unexpected EOF while looking for matching。
解法:复杂脚本写成文件再执行,不要内联。这条后来成了整个项目的通用做法——所有 HTML 改造都写成独立的补丁脚本。
.cmd 必须纯 ASCII + CRLFWindows 批处理对编码很敏感。文件里带中文、或用 LF 换行,双击就报莫名其妙的错。
解法:.cmd 文件强制 纯 ASCII + CRLF,中文提示语只放在被调用的 Node 脚本里输出。
在对 500KB 单文件 HTML 做批量字符串手术时,遇到过编辑工具返回成功、但内容实际没变的情况。
解法:每次编辑后立刻用 grep 验证关键标记是否存在。这个习惯救过我好几次——否则会带着"以为改了"的状态往下走,最后表现成一个很难定位的功能缺失。
消消乐的核心逻辑保存在两个地方(HTML 内嵌一份、小程序独立一份),改了一处忘了另一处,表现是"电脑上正常、手机上不对"。
解法:凡是两端都要用的逻辑,用同一个测试文件覆盖两端,改完同时跑;测试不过就不算改完。
这个项目的特殊之处:主体是一个 504KB 的 HTML 单文件,加上小程序四个页面、二十多个工具脚本、十几个测试文件。手改这种规模的文件是不现实的,所以整个改造过程是围绕"让 AI 安全地改大文件"来设计的。
每次功能改造都写成一个独立的补丁脚本(tools/xxx.js),它必须满足:
.bak-*;new Function() 把内嵌 JS 跑一遍语法检查,不通过就回滚。这样每次改造都是"可复现、可回退、可审计"的,而不是一堆不可追溯的手工修改。
这条经验值得推广:让 AI 改大文件时,不要让它直接输出"新文件",而是让它写一个幂等的迁移脚本。文件太大时,全文重写有概率引入静默错误;而补丁脚本的每一步都是可以验证的。
我在这个项目里累计写了 11 个测试文件、约 3300 行断言代码,覆盖:
关键点:测试是让 AI 写的,但验收标准是我定的。 需求一旦变成断言,"改了没有"就不再是主观判断。我后面几轮改动,都是先让测试红、改完看着变绿。
最省积分(也最省时间)的方式,是一次性把完整规则讲清楚,而不是"先改一半看看"。例如标红规则,我最终是这么给的:
单词泡泡只统计最后两个字母,输入一次就算错误,这个单词的其余字母都是黑色;单词拼写同样逻辑,但错误的定义是:连续两次输入某个字母都错、或者使用了提示字母才算错,只输错一次不算;两个游戏都要输出每次读音的匹配率,每一次都要输出,低于 70% 标红。
这段话包含 4 条独立规则,一次说完,两端一次改到位。如果拆成四轮,光上下文重建就要多做四次。
项目跨了十几天、几十轮对话。把"词库格式约定""发布流程""已知的坑"这些沉淀到项目记忆里,新会话起来不用重新解释背景——这对长周期项目是决定性的。
我遇到过至少两次"AI 报告修改成功、实际文件没变"。所以流程里固定了一条:改完必须用 grep 验证标记落地,必须跑测试,必须两端都核一遍。信任,但要验证。
指标 | 数值 |
|---|---|
词库规模 | 5 科 / 52 本课本 / 265 单元 / 9545 词条 |
HTML 单文件 | 504 KB(含全部词库与逻辑,零外部依赖) |
小程序 | 4 个页面(选科目 / 练习 / 消消乐 / 泡泡) |
工具脚本 | 20+ 个(合并、发布、上架、补丁、校验) |
测试代码 | 11 个文件 / 3300+ 行 / 全部通过 |
服务器成本 | 对象存储 + 云函数按量,实际接近 0 |
老师改词库的成本 | 双击一个 .cmd |
回归测试最终结果:
回头看,这个项目能跑起来,靠的不是"AI 能写代码",而是三件事:
如果你也在做"轻量、离线优先、用户自己能维护"的工具,欢迎交流。踩坑清单里如果有哪条正好卡住你,那这篇文章的目的就达到了。
本文使用 WorkBuddy 完成需求梳理、代码实现与测试编写。项目全程真实落地,相关数据可在本地复现。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。