我如何靠指挥 AI 智能体、而不是自己写代码,做出了 OMLA
- 发布日期
- 2026年7月11日
- 作者
- Jacob Lloyd —— 项目完成后,在 AI 协助下撰写
- 阅读时长
- 约 10 分钟阅读
简单来说: 这是一个真实的故事:一个人,没有程序员团队,靠指挥 AI 助手,为一家非营利组织做出了一套真正上线的授权平台。文章讲的是具体的工作方法:写清楚的书面计划、层层复核,以及一条铁律——没有人类批准,任何东西都不能上线。这是一份用 AI 打造正经软件的实用蓝图。
这是我用来指挥 AI 编程智能体、几乎独自建成 OMLA(一家非营利组织的版税授权后端)的完整流程。方法尽管拿去用。做出来的这套后端,不过是这套流程确实有效的证据。
tl;dr
- 这是什么: 让 Claude Code 和一群本地 AI 智能体去建成一个真实非营利后端的工作流,我扮演的是架构师和把关人,不是打字员。
- 花多少钱: 基本没多花什么钱。一份编程智能体订阅,也许再加一块闲置 GPU 用来听第二意见。真正的成本是你自己审查代码的时间。
- 需要什么: 一个编程智能体、真正写下来的规格说明(而不是凭感觉),外加一条铁律——人类不点头,什么都别想部署。
- 最终得到什么: 一套有据可查的可用后端。多层审计、针对真实本地环境跑的测试,以及一个没有人类"GO"就拒绝出货的部署脚本。
最终得到什么
在讲怎么做之前,先看看最后做出了什么:OMLA,Open Model Licensing Association(开放模型授权协会)。一套面向开放 AI 模型的版税授权后端和公开网站。
| 项目 | 结果 |
|---|---|
| 状态 | 正在以华盛顿州非营利组织身份注册,501(c)(3) 申请中。网站故意挂着"Beta 0.9.0"的标记。 |
| 规则 | 个人、研究、教育用途完全免费。商业用途需支付"可归因收入"或"模型运行成本"两者中较大者的 30%。 |
| 数据库 | 核心 12 张表,Supabase 上的 Postgres 17,全面启用行级安全,金额一律用整数分计算(绝不用浮点数) |
| 身份认证 | ed25519 + ML-DSA-65 混合签名,经典加后量子双保险,即便未来量子计算机破解了经典签名,系统也不会崩溃。没有验证通过的签名,就没有版税结算单。 |
| 审计留痕 | 只追加不可改,SHA-256 哈希链,每小时校验一次,链尾的"锚点"每天存到数据库之外的地方 |
| 前端 | 约 30 个纯静态 HTML 页面,不用任何框架,支持 13 种语言,跑在普通的共享主机上 |
| 由谁做的 | 一个负责指挥的人类,负责写代码的 Claude Code,以及负责做审查和杂活的本地开源模型智能体群 |
| 目前进度 | 已完全就绪并测试通过。部署仍只是 dry-run,除非我亲手敲下"GO"。 |
大致的时间线,免得你以为这是一个周末赶出来的:
- 2026 年 5 月下旬——从一个个人聊天机器人的副业项目转向做一家授权非营利组织,同时敲定最初的 12 张表的 schema
- 几天之内——后量子签名加固、涉及资金路径的边缘函数,以及"非托管"重写(详见下文)
- 6 月初——后端推上生产环境(是数据库,不是公开的前端网站)
- 6 月 16 日——法律条款重写完成,License v1.0 生效
- 7 月 2 日——网站和后端的全面刷新,加上 13 个语种的重新翻译,都已就绪并测试完成,写这篇文章的时候仍在等我点"GO"
一个好玩的起源小插曲:OMLA 最初的全称是"Open Machine Learning Assistant"(开放机器学习助手),是个人聊天机器人项目。基础设施的选型直接沿用到了后来转型做授权业务的版本上;那个聊天机器人本身则被降级成一个功能受限的文档助手,在生产环境里被刻意关闭。一个业余聊天机器人变成授权基础设施——这点范围蔓延,应该也算正常吧?
让其余一切成为可能的那条规则
在写任何代码之前,有一个设计决定就干掉了大半的重活:OMLA 从不移动、持有或转账资金。它只计算应付金额,并公开收款方自己提供的钱包地址或收款信息。商业用户直接、点对点地把钱付给创作者本人。没有支付处理,没有托管账户,没有"OMLA 余额"这种东西。钱包标识符只是路由信息,不是账户。
许可协议的措辞把这点说得很直白:"OMLA publishes; OMLA does not pay."(OMLA 只负责公开信息;OMLA 不负责付款。)
为什么要这么费劲?成为一个资金转移方,本身就是在授权业务的监管难题之上,再叠加一整套监管噩梦。跳过资金托管,KYC、制裁审查、税务这些负担就转移给了实际在交易的那两方——这本来在法律上也该是他们的责任。正是这一个决定,才让一个业余爱好者敢去尝试这个项目。
而且这甚至都不是最初的设计。开发到一半的时候,schema 里仍然暗含着资金托管的意味,于是一张原名就叫 payments 的表被改名成了 royalty_statements——列、索引、触发器、RLS 策略、枚举类型,整整一次迁移,就为了让数据库不再对自己实际做的事撒谎。旧的措辞后来还是回来咬了我一口(见后面"容易踩的坑")。
下面是一美元的版税,在 OMLA 从未经手的情况下走过的路径。
第 4 步就是关卡:没有第 1 步那个通过验证的签名,永远不会有版税结算单。OMLA 公开金额和钱包地址之后,剩下的就是另外两方之间的事——就跟我告诉你"某个朋友欠你二十块钱",然后让你们两个自己去解决一样。
实际的循环:人类、智能体、验证、部署
这套工作流并不复杂。它只是对"谁做什么"这件事极度严格,而且每次有变动都要重走一遍。
重点不在这些框本身,而在于没有哪个框可以被跳过。每次改动都从我把"应该存在什么、为什么"写下来开始,而不是"改得好点就行"。智能体按这份规格起草,别的智能体在我看到之前先审查这份草稿,而测试则跑在表现得像生产环境的基础设施上。我读 diff,永远是最后一步。
整个后端本地优先运行:一套真正的 Supabase 技术栈,Postgres 17 加边缘函数,跑在我自己机器上的无根容器里。生产环境只是一个部署目标,从来不是真相来源。如果本地和线上不一致,那在我推送之前,错的都是线上那边。
质量标准写在项目配置里,不只是在我脑子里:"Audit-ready by default: ship work that can withstand external audit."(默认即可审计:只出货能经得起外部审计的成果。) 所有工作都假定将来会有一个独立的评审者,不管是 AI 还是人类,会来找漏洞。
针对真实环境测试,而不是模拟环境
如果测试本身在骗你,上面说的这一切就毫无意义,所以这里绝不会对着模拟数据库出货。所有测试都跑在真实的、用完即弃的本地环境上。三层结构,全部变绿之后才允许往部署方向推进:
deno test # 13 unit tests for the edge functions
./test/run.sh # isolated scratch DB per run, migrations w/ ON_ERROR_STOP,
# smoke tests (PQ signatures, wrong-key rejection, audit
# hash-chain, payout gate), RLS cross-tenant isolation,
# an adversarial suite, then rollback + reapply
./test/integration.sh # the full money path against the live local stack:
# register → tampered payload rejected → wallet verify →
# splits → usage report → statement published → replay
# (asserts zero duplicate writes) → admin gate → compliance
那行"重放并断言零重复写入"比听起来重要得多。使用报告来自别人的系统,总有一天会有人不小心重复提交同一份;这个测试要确认的是,重放同一份报告会产生零条新的结算单,而不只是"没有崩溃"。这套测试后来被加固成了断言结果、而不只是记录日志——听起来很吹毛求疵,直到你有一天真去调试一个"通过了"却什么都没做的测试。
审计的审计
让一个 AI 审查自己的成果,不过是走个过场。所以审计是故意分层的:先由本地的智能体群做一轮修复,再由一个更强的模型作为独立的最终审计关卡。
这道最终关卡不是走形式。有一次它验证了本地智能体的修复,同时还发现了一个 search_path 绕过漏洞,外加第一轮遗漏的列、签名和血缘方面的漏洞。这后来变成了一次专门的加固迁移:十二条编号的发现,每一条都随附一个回归测试,确保它不会悄悄地卷土重来。
规模跟改动大小相匹配。日常改动交给一个 3 智能体的评审小组。前端和 i18n 的全面翻修则动用了一个 24 智能体的群体审查,结果它发现网站的内容已经跟现实脱节(还在描述老的聊天机器人产品,而不是它已经变成的授权后端),逼着团队重新做出一个符合现实的版本。
部署关卡:我不敲下去,就一直是 dry-run
部署运行器只有一个任务:确保上面这一切不会不小心跑到生产环境去。每次都默认 dry-run;只有带上明确的标志,才会真正碰生产环境:
./deploy.sh # dry run (default) — shows exactly what WOULD happen
./deploy.sh --go # only a human runs this, only when ready
一个带引导的包装脚本还要更进一步:在任何会改动生产环境的步骤之前,你得真的敲下"GO"这个词。给智能体的常设规则也写下来了,免得它变成一句可有可无的建议:没有人类的明确指示,绝不添加 --go。
其他一些保证不只是写在纸面上的政策,而是直接焊进了运行器本身:
- 前端同步绝不使用
--delete,所以它不可能把主机上它不管的文件也一并抹掉 - 任何前端覆盖之前,都会先做一次服务器端备份
- 后端会拒绝动作,除非连接的项目和预期的生产项目完全匹配——这是防止推错数据库的硬性防护
- 推到远程的永远只是追加型迁移,绝不做 reset
- 文档聊天机器人的函数在原则上被排除在每一次部署之外
- 密钥只存在仓库之外一个 mode-600 的文件里;一次纯文本检查会确认 service-role 密钥绝不会出现在出货内容里
说实话,这个项目的部署日志,大部分内容其实就是我一次次临阵退缩的日记:一次又一次的 dry run,而一个已经完全就绪的重新设计,就在旁边等着一句话。这不是 bug。"带门槛"本来就应该是这种感觉。
容易踩的坑
真正疼过的那些部分:
- 零宽字节。迁移用的 SQL 必须是纯 ASCII。一个肉眼看不见的非 ASCII 字节,就能把一个
$$美元引号 token 拆开,悄无声息地把两条 SQL 语句合并成一条。修复办法后来成了固定仪式:每次都 grep 一遍非 ASCII 字节(要求为零),再数一遍$$token 的数量(要求是偶数)。 - 百分号会咬人。在
RAISE EXCEPTION的格式化字符串里,%%才是字面意义上的一个百分号,每个变量都恰好需要一个%。这个坑踩过一次,现在写下来的唯一原因就是这个。 - SECURITY DEFINER 必须固定 search_path。每一个 definer 函数和触发器函数都固定了自己的 search_path,这样谁都没法在
pg_temp里伪造一张同名表,再喂进去错误的数据。对抗性测试会数一遍固定了 search_path 的函数数量,数量一旦下降就报错。 - 时灵时不灵的 superuser。一次非正常重启之后,本地 Postgres 的角色有时候会在重启后失去 superuser 权限。修复办法是干净地停止再启动整套环境,而不是排查问题。写下来是为了让智能体们别每次都重新发现一遍。
- i18n 会悄悄覆盖 HTML。翻译层会完全覆盖页面文案,所以就算"修好了"HTML,只要对应的语言文件没修,照样会送出过时的文案。这正是那些已经"删除"的托管时代旧文案一再复活的原因。
- 审计链是可以被骗过的,而且是故意留的,好让我们知道这一点。一个完全掌控数据库的攻击者,可以删掉哈希链触发器,重建一段自洽的假历史;单靠内部检查是抓不住的。所以每天有一个任务会把链尾的哈希值锚定到数据库完全之外的地方,而测试套件在一次运行里就证明了这两点:内部检查被骗过了,而外部锚点仍然能抓到这次篡改。
- 线上网站不是已就绪的那个版本。写这篇文章的时候,公开网站上仍然显示着一些重新对齐之前的旧文案(一份旧的许可协议草案、一张过时的支付渠道卡片),因为那次重新设计只是就绪状态,还没有部署。另外,公开的
/about这个网址其实是个裸的目录索引;AI 使用披露页面在它下面一层。