去年,业务下滑,这几年 AI 越来越旺,我也一直在寻求出路(瞎折腾)。那段时间我一直在想一件事:我得降本增效了。
过去做一个项目,我通常先跟客户沟通,整理需求,写 PRD,做原型。UI 效果图确定后,再找前端和后端,拉群、拆任务、解释业务、联调测试。项目规模再大一点,还要增加设计、测试或运维。这里面每个人都只做一段,而我作为项目经理,串联前后。这套方法我用了很多年,它能交付,但成本也摆在那里。项目一多,沟通本身就会变成一项很重的工作;项目一少,维持团队开支又会形成压力。
2026 年,我开始做一个比较直接的实验:不再为了每个项目组一支开发团队,改成由我负责需求、产品判断、测试和上线,让 Claude Code、Codex 这些编程 Agent 承担大部分读代码和编码工作。对于“AI 会不会写一个页面” 这件事早已没什么悬念,我真正想知道的是,一个过去需要前端、后端和项目负责人配合的功能,现在能不能由一个人带着 AI,从需求一路做到正式环境,并且真的有人使用(年初风靡的小龙虾, 当时也是和 AI 一起搞过,但就落地,苦不堪言!)。
点读教材制作 GUI 软件,是这个实验里最完整的一次落地。它不是一场为了展示 AI 能力而做的 Demo。软件已经在真实教材制作中使用,产出的内容也进入了正式业务。可如果只看最后的界面,很容易把这个过程想得太简单。这套工具从第一版走到 v1.32,难的并不是把按钮画出来,而是无数次的反馈打磨。
开始阶段:为了提高效率产出
事情一开始,并不是“我要开发一套可视化的教材点读软件”。我先在自己的 AI 英语系统里做了同步课文点读。学生打开一本教材,看到的是接近纸质书的整页画面。点击页面上的一句英文,这句话会高亮,同时播放发音;打开翻译后,还能看到对应的中文。站在学生这一侧,这个功能很好理解。点一句,读一句。
但是教材内容制作工作量不小,每一句话背后都要准备好几样东西:教材整页图、英文文本、文字在页面上的位置、中文翻译、音频地址,以及它属于哪一本书、哪个单元、哪个板块。只要其中一项错了,学生点到的内容就可能对不上,或者根本没有反应。2026 年 6 月 27 日,第一版学员端点读功能和内容管线跑了起来。当时的做法主要依赖 python 命令行脚本:把教材 PDF 交给 MinerU 解析,抽出文字和坐标;再用 AI 模型生成翻译和 TTS 发音;最后整理成数据库可以导入的 SQL。这条链路能跑,却很快暴露出一个问题。
MinerU 对普通印刷文字处理得不错,但英语教材里有很多文字并不规规矩矩地排在正文区。有些对白画在漫画气泡里,有些单词跟插图做在一起,还有艺术字标题、手写体、表格、填空线。它们在视觉上很清楚,解析工具却可能把整块当成图片,里面的英文一句也抽不出来。后来我接入了 Qwen 的视觉模型,让它直接看教材页面,补出 MinerU 漏掉的对白和文字。这样确实找回了大量内容。以一本实际教材为例,MinerU 在某一页只拿到 4 句,视觉模型能补到 9 到 10 句,漫画里的整段对话终于回来了。可新的麻烦也跟着出现了:文字大体是对的,框的位置却经常偏大、重叠,甚至盖到旁边的插图上。
最早我只挑成段的课文、韵文和故事做点读。后来重新考虑学生的使用场景,我把口径改成了:教材上能读出来的英文,尽量都做成可点击热区。板块标题、单词标签、表头、人名、练习句、填空句,都应该能点。同一本译林三年级上册,旧口径是 31 页、198 句;扩大范围后变成了 67 页、872 句。多出来的不是一个抽象数字,而是几百个需要确认的框。哪个框压住了文字,哪个漏了,哪个把题号也框进来了,哪个英文少了一个词,都得在教材页面上看。

命令行可以告诉我“这一页识别了 16 句”,却不能让我方便地看到第 7 个框到底压在什么地方。用 JSON 文件手工改四个小数也不是长久办法。一页两页还可以忍,上百页、近千句以后,这种制作方式已经支撑不了真实生产。于是,GUI 可视化界面 成了下一步必须补上的东西。
第一版 GUI 没有重做管线,只是先让人能操作
2026 年 7 月 4 日,我把需求重新整理了一遍。我要的不是一个单独的图片标注工具,也不是把几条命令换成漂亮按钮。它必须接住整本教材的制作流程:从解析结果开始,完成 AI 识别、内容融合、人工校对、图片上传、翻译、发音,最后导出可以进入正式系统的 SQL。还有一条很重要:已经跑通的底层脚本不能推倒重来。
当时的管线已经处理了不少复杂工作。如果为了 GUI 再复制一套逻辑,命令行和桌面工具很快就会变成两个版本。同一个问题修了一边,另一边仍然会错,后面的维护反而更麻烦。所以第一版采用了一个很简单的结构:GUI 负责收集参数、展示页面和响应操作,每个阶段仍然调用原来的 Python 脚本。命令行和桌面工具读写同一份工作文件,产物完全互通。
Claude Code 完成了第一版主要代码。它使用 Python 自带的 tkinter 做界面,没有再引入一套前端工程。初版六百多行,把整个过程串成了五个阶段:抽取、AI 识别、融合、上传与翻译发音、导出 SQL。教材加载进来后,左边显示整页图片和热区框,右边可以修改英文、译文并试听发音。框漏了,可以直接拖出一个新框;位置不对,可以移动或重画;多出来的框可以删掉。每一页校对完成后,还能标记“本页已核”。

从第一版开始,我就要求 AI 把几条原则写成程序守卫,也就是程序错误兜底:英文为空,不能导出;热区坐标非法,不能导出;还有页面没确认,要列出页码提醒;SQL 生成后,数据库句子数必须和画布上的热区框数量一致。AI 可以补内容,但工具不提供“让 AI 随便改写英文原文”的功能,教材上的英文最终仍以原书和人工确认为准。这些设计不是为了追求功能齐全,原因很现实:一旦错误内容进入正式数据库,再回头找是哪一步出了问题,成本会高很多。
7 月 4 日当天,GUI 的基本能力已经具备,本地代码检查也通过了。按照很多 AI 开发案例常见的写法,故事写到这里就可以结束:一天做出桌面软件,AI 提升开发效率。问题是,它还没有在 Windows 上完整走过一次。后面发生的事情,才是这次开发里更值得写的部分。
开发环境和运行环境差异性带来的问题
这套 GUI 最终要在 Windows 上使用,而 AI 开发和代码分析是 在 macOS 上。第一次放到 Windows 运行,很快就出了问题。脚本会在控制台输出箭头和对勾,macOS 上一切正常,Windows 的 GBK 控制台却无法编码这些字符,程序直接崩溃。后来给五个脚本统一设置 UTF-8 输出,基础流程才跑通。
接着又遇到跨盘符问题。GUI 和脚本在 D 盘,MinerU 解析出来的教材在 C 盘。程序想把教材目录转换成相对路径,Windows 发现两个路径不在同一个盘符,直接抛出异常。界面上看起来只是“点了登记按钮,什么也没发生”。还有一次,教材图片按照宽度放大后,高度超过了窗口。下面的日志区被挤到屏幕之外,用户看不到程序到底在做什么。给画布加滚动条以后,又必须把鼠标坐标转换成滚动后的画布坐标,否则人在页面中间画出的框,保存时会整体偏移。
音频试听也出过问题。Windows 的播放接口不支持原来写死的设备类型,最初的代码又把系统错误码吞掉了,界面只会笼统提示“打不开 MP3”。后来改成依次尝试不同的音频设备,取回系统的真实错误信息,再用默认播放器兜底,播放链路才稳定下来。这些问题没有哪一个靠“再写一遍提示词”就能提前解决。
Claude Code 能读代码、找调用关系、提出修改方案,但它没有坐在我的 Windows 电脑前,也没有替我点击那些按钮。我的工作是把实际现象说准确:我点了什么,界面出现了什么,日志最后停在哪里,教材文件放在哪个盘,预期结果是什么。然后它根据代码和日志定位,我更新文件,再回到 Windows 验证。
早期这种协作很像传话:我看到一个错误,把零散信息发给 AI;AI 猜测原因,改完以后我再试。如果信息不够,一次本来十分钟能看明白的问题,可能来回几轮。后来每一个管线步骤都会生成独立日志,GUI 自己也保存会话日志;版本号旁边明确标“待测试”还是“已验证”;每次更新要求整个工具目录一起替换,启动时还会检查 GUI 和底层脚本是否配套。这些都不是一开始规划出来的“最佳实践”,而是在反复丢上下文、漏文件、看不到报错之后逐渐补上的。
我以为该换更强模型,后来发现是坐标读错了
7 月 7 日,我在日记里做过一次复盘:GUI 功能基本都有了,最大问题还是需要人工校准的热区太多。当时使用的是 qwen-vl。我查到它已有升级型号 qwen3-vl-plus 和 qwen3-vl-flash,第一反应很自然:新模型空间理解更强,也许换模型就能让框更准。于是工具加上了视觉模型选择,默认切到 qwen3-vl-plus,还专门处理了不同模型可能使用的 0—1000 坐标范围。

新模型跑完,问题并没有按预期消失。有些框仍然很大,有些互相重叠,还有的直接溢出了教材页面。

如果这时候继续把原因归结为“模型还不够强”,下一步大概就是再换一个模型、再烧一次调用费用。我们停下来检查了模型返回的真实数据,问题终于露出来了。视觉模型给出的坐标格式是 [x1, y1, x2, y2],意思是左上角和右下角两个点。整条后续链路却一直按 [x, y, w, h] 使用,也就是左上角、宽度和高度。
例如某个框返回的是 [0.262, 0.452, 0.359, 0.472]。正确的宽度应该是 0.359-0.262=0.097,高度是 0.472-0.452=0.020。旧程序却直接把 0.359 当宽、把 0.472 当高。一个本来只压住一行文字的细框,就被撑成了覆盖大片页面的巨框。我们拿 744 条真实识别结果核对,绝大多数都符合“两角坐标”的规律。修正转换方式后,原来 445 个溢出页面的框降到了 0,框高的中位数也回到了接近一行文字的正常范围。到这里我才确认,前面大量的手工删框、改框,有相当一部分并不是模型能力逼出来的,而是程序把模型的答案理解错了。
这件事对我的提醒很大。现在做 AI 应用,很容易在结果不好时先怀疑模型。模型换得更快,参数调得更多,调用费也跟着增加。但一个功能从模型到用户,中间还隔着提示词、数据格式、解析代码、坐标转换、前端展示和运行环境。任何一层理解错了,最后看起来都像“AI 不行”。模型选型当然重要,不过在真实项目里,判断问题究竟出在哪一层,比知道最新模型叫什么更重要。
持续的迭代优化
坐标问题修好以后,人工校准量明显下降,但这套工具仍然没有到“可以放心生产”的程度。从 v1.8 到 v1.32,后面的修改大致可以分成三类。第一类是操作问题。

每次启动都重新填写百炼和 OSS 凭据很麻烦,于是增加了本地 .env 保存,并确保密钥文件不会进入代码仓库。新书的编号容易跟旧书冲突,工具就根据现有登记自动建议下一组号段。操作人员不知道当前进行到哪一步,五个阶段按钮便增加了“已完成、下一步、未开始”的状态。教材图太高,要不停上下滚动,于是加入“整页适配”;日志区占空间,就允许折叠;发音原来固定一种音色,后来增加四种音色选择和试听缓存。做完一本教材后才发现中间漏了一页,以前只能重新融合整本,人工校对的框会被覆盖,后来增加了“补页”,只插入指定页面,其他内容一个字也不动。这些功能单独看都不大,却决定了工具是只能由开发者勉强操作,还是能稳定拿来干活。
第二类是内容正确性。教材解析经常会碰到很细的小问题。比如 Public ____ used to be slow...,上游抽取时可能变成 Publicused to be slow...。原因是两个文本块之间没有自动补空格,教材上画出来的填空横线又没有被当成文字。工具后来根据文字块间距判断应该直接拼接、补空格,还是还原成 ____,并且规定下划线只能在发音时转换成停顿,原文和译文都必须保留。翻译也不是“调用一次大模型”就结束。
我们遇到过原文 There is no ____,模型在中文里直接补成“毫无争议”。从语言上看它可能很通顺,对练习题来说却是把答案 doubt 直接告诉了学生。还有带两个填空的句子,模型可能只保留一个空,或者调整前后顺序。后来翻译前先给每个填空换上独立占位符,翻译完成后再恢复,并由程序核对数量和顺序。否定词、关键数字、长段未翻译英文也会进入检查。模型第一次不合格,重试时会明确告诉它上一轮错在哪里;多次仍不过,就留空并打上标记,交给人处理,不能带着错误继续生成音频和 SQL。
还有一类错误藏得更深:每一页的框、英文、译文都没问题,学员端目录却是错的。重制一本八个单元的教材时,我们在导出后发现,七个单元开头的扉页、Big question 和 Cartoon time 被归到了上一个单元,一共错了 21 页。原因是 MinerU 没把扉页上的艺术字 Unit 5 识别成标题,系统直到三页后的普通页眉才意识到已经换了单元。
一开始我们为 GUI 增加了人工重贴标签的功能。再往下查,发现视觉模型其实早就识别出了七个单元的扉页,只是融合脚本从来没有使用这份结果。后来程序改成从页面顶部的第一个短框中识别 Unit N,同时用位置和长度限制防止把正文误当成单元标题。真实数据复跑后,七个扉页全部命中,跟人工逐页核对的起始位置一致。这类问题很能说明真实软件的麻烦:你需要的信息可能已经在系统里,只是没有被正确地接进下一步。
第三类是交付安全。识别、翻译、发音和上传都会产生费用。工具每次执行计费步骤前,先计算页数或句数,让人确认后再调用。翻译没有检查干净时不生成发音,否则译文改完还要再跑一次音频,钱会重复花。
导出的 SQL 也从一份普通数据文件变成了单本教材安装包。执行前先备份这本书的旧内容,只删除指定教材的数据;目标数据库里不存在这本教材时,一行也不插,防止产生孤儿数据;执行后另有校验脚本核对教材是否存在、页面数、句子数和已激活用户数。真出了问题,回滚语句直接写在文件开头。到了 v1.32,我们又遇到一次“第一页消失”。操作时明明设置了页面范围,但范围起点那一页恰好被视觉模型识别成 0 句,融合规则便把整页静默丢掉。与此同时,界面要求填写的是从 0 开始计算的 PDF 页序,人却容易照教材上印刷的页码填写,两者还可能相差两三页。
修复以后,只要用户明确把一页放进范围,即使 AI 一句也没认出来,工具也要保留这页并告警,留给人工补框。界面同时显示 PDF 页码换算,并增加“看首页”按钮,登记前先确认起点是不是想要的那一页。这里没有复杂的新算法。改变的是规则:AI 没认出来,不等于这一页不存在;程序不能在用户已经明确选择的范围里,悄悄替人删内容。
截至 2026 年 8 月 14 日,与同步教材项目相关的 Git 提交一共 66 次。当前五个核心脚本和测试代码合计约 6300 行,132 项离线测试全部通过。GUI 已在 Windows 上完成实际制作验证,并用于真实教材制作上线。生产力工具,终于落地了。

这套软件里,我和 AI 到底分别做了什么
回头看这段开发,我可以很明确地说:AI 写了所有代码。Claude Code 和 Codex 读取已有仓库,寻找接口和数据结构,完成 GUI、管线、测试和一轮轮修复。它们也会根据日志分析原因,把新的操作方法和已知问题写回 RUNBOOK。换成过去的工作方式,这些任务会分散在前端、后端、测试和项目文档之间。但软件为什么要做、要收哪些教材内容、哪些页面应该进入正式库、计费步骤什么时候能跑、什么错误必须阻断上线,这些判断仍然由我完成。
我还要在 Windows 上实际操作,以真实使用者的身份,把模糊的“不好用”变成可以定位的信息;要比较模型结果,判断是继续换模型,还是检查坐标和解析;要确认 SQL 能不能进测试站,正式站出现问题怎样回滚;最后还要打开学员端,真的点一下教材上的句子,听到正确发音,看到正确翻译。AI 可以给出修改,但它不会替我承担错误教材进入正式环境的后果。
这也是我现在理解的人机分工。不是人负责提一个想法,剩下全部交给 AI;也不是人必须重新逐行写一遍,才能算自己完成。人的工作往上游和下游移动了:上游要把问题定义清楚,下游要对真实结果负责,中间的大量实现可以交给 AI 加速。
这次开发,也是我对 OPC 和 FDE 的一次验证
我之前写过 FDE 的介绍文章,也就是前线部署工程师。我没有在 Palantir 或 OpenAI 担任这个职位,也不准备因为做完一个项目就给自己换一张名片。我更在意的是,这个角色描述了一种正在变得重要的工作方式:面对一团还没说清的业务问题,既要理解现场,也要亲手把解决方案送进真实环境。我和AI 这次合作开发的落地软件,很符合这个过程。
需求不是从一张定义完整的任务单开始的。最初只是“同步教材要能点读”。等功能上线后,才发现限制规模的不是学员端页面,而是教材怎样制作;GUI 做出来以后,又发现人工框太多、Windows 跑不稳、翻译可能泄露答案、目录和漏页在画布上看不出来。问题是一层一层从实际使用中显现的。如果开发边界停在“代码已经生成”,这些问题都不属于开发任务。可对业务来说,任何一个没解决,工具就无法稳定生产教材。
OPC,One Person Company,一人公司,对我来说也不是一个人把所有活都自己做完。过去,我想扩大交付能力,首先想到的是增加人。现在,我可以把一些原本只存在于自己脑子里的经验,逐步写进软件:哪些步骤会花钱,什么时候需要停下来确认;哪些错误可以提醒,哪些必须阻断;上线前查什么,出问题怎样退。
当这些经验变成按钮、日志、测试和守卫以后,它们就不再只属于某一次人工操作,而是成了下一本教材还能继续使用的生产能力。这是 AI 对一人公司更实际的价值。它不只是减少一次外包费用,也不只是让我少写几行代码。它让一个人有机会把自己的业务判断更快地固化成工具,再由工具支撑下一次交付。当然,边界也很清楚。
一人公司不代表一个人什么都会。模型服务、MinerU、OSS、Python 和现有 Java 系统,都是这套工具能够成立的基础。AI 也没有消除测试、部署、成本和经营风险。相反,当过去分散在几个人手里的工作集中到一个人身上,最后的责任会更清楚地落回来。
v1.32 不是终点
现在,这套点读教材制作 GUI 已经可以完整走完一本教材的生产流程,也确实进入了真实业务。以前需要组织多人、反复传文件和解释规则才能推进的内部工具,现在可以由我带着 AI 持续迭代。这个结果证明了“我+AI”的交付方式可行,但还没有证明它已经足够轻松。
热区识别的人工校准已经比早期少了很多,仍然没有完全消失。不同出版社、不同年级的教材版式差异很大,模型和规则还会遇到新情况。下一步不是宣布软件大功告成,而是继续拿真实教材使用,把人工参与压到合理范围,同时守住内容正确和上线安全。这次开发让我确认:AI 确实把代码变便宜了,但软件能不能进入业务,取决于有没有人愿意把需求、测试、环境和结果接起来。
如果你也在尝试用 AI 做产品,你觉得最容易卡在哪一步?是把需求说清楚、把代码做出来,还是让它在真实环境里稳定运行?
夜雨聆风