Skip to content

一个「AI 优先」的框架是怎么炼成的:五轮 AI 实测全记录 ​

我们让完全不了解框架的 AI Agent 独立完成开发任务,前后五轮, 累计发现并修复 20 个真实缺陷,一次成功率从 0.5 提升到连续三轮 1.0。 这是完整、未经修饰的数据。

一个反直觉的结论:五轮下来,每一轮 AI 都能发现 3~5 个我们没想到的问题。 所以我们把"让 AI 实测"变成了框架开发的固定环节,而不是一次性验证。


为什么做这个测试 ​

市面上的后台框架都在说"对 AI 友好",但没人给出可验证的证据。

我们决定用一个笨办法:找个 AI,给它文档,让它干活,看它会不会出错。

测试方法固定,五轮一致:

项设定
被测对象一个完全不了解本框架的 AI Agent
提供材料框架文档 + MCP 工具 + 自省接口(第三轮起加文档站)
禁止事项不许读框架源码(第三轮起明确禁止)
人工干预零。不给提示、不纠正、不补充说明
评分标准一次生成即可运行 = 1.0;需 1 次修正 = 0.5;需 ≥2 次或人工介入 = 0
复核方式父 Agent 独立复现 AI 的每一项结论,不轻信自述

第五轮的特殊设计:任务描述改用业务语言,故意不提 API 名 —— 测的不再是"能否照抄 API",而是"能否自己发现框架有这个能力":

任务给的说法期望 AI 自己找到
"部门有上下级关系,要能直观看到层级结构"tree() 方法
"列表要能导出成表格文件"$grid->export()
"表单内容较多,希望分成几个步骤填写"$form->step()

五轮结果 ​

第一轮第二轮第三轮第四轮第五轮
一次成功率0.50.51.01.01.0
框架纠错贡献0%50%100%100%100%
是否读源码读了读了没读没读没读
任务要求满足度部分7/77/79/98/8

第五轮最有价值的验证:三项新加的框架能力(树形结构 / 数据导出 / 分步表单) 全部被 AI 自主发现并使用,且它在代码里逐条标注了需求对应关系 —— 证明是"理解需求后主动检索能力",而非碰运气。

发现路径是 MCP search-docs(不是文档站):

search-docs {"query":"树形结构"} → tree()->parentColumn() / titleColumn() / draggable()
search-docs {"query":"导出"}     → export() 开启 CSV 导出,未开启时导出接口返回 403
search-docs {"query":"分步表单"} → step() 声明分步表单的步骤,后续字段归属该步骤

"框架纠错贡献"是什么:AI 出错后,是框架的错误信息帮它改对的, 还是它自己读源码/靠经验猜出来的。这个指标比成功率更重要—— 它衡量的是框架本身是否具备纠错能力。

第一轮的真相:AI 最终交付的代码是正确的,但它是先读了框架源码才写对的。 这否定了「AI 优先」的核心假设 —— 所以记为 0.5,而不是看起来更漂亮的 1.0。

值得一提的是,这个判断是被测 AI 自己提出来的。它在报告里写: 「我一次写对了最终代码,但那是因为我在写之前先读了框架源码, 而不是因为框架帮我纠错。」


20 个真实缺陷 ​

四轮测试累计发现并修复的缺陷。按严重程度排序:

致命级 ​

#缺陷后果
1验证器只查语法、不查需求达标度脚手架漏 3 条需求仍报「✅ 通过」,AI 会把绿灯当交付标准,交出残次品
2验证器假阳性:只读声明不验证运行时报「每页 30 条 ✅ 已设置」,但 API 实际返回 20 —— 声明写了却没生效

这两个是同一类失效的两种表现:只验证声明,不验证真实行为。

高危级 ​

#缺陷后果
3MCP query-data 搜索静默失效传关键词返回全表。静默失败比报错更危险 —— AI 会以为数据本来就这样
4did_you_mean 实际返回 null文档承诺的字段不存在,AI 拿不到修正建议
5did_you_mean 在真实路径被丢弃异常本身带建议,但 Verifier 捕获后只取 message,AI 只看到笼统的"编译失败"
6未知 query 参数静默忽略用 order=desc 代替 direction=desc → 不报错、返回未排序结果,而校验器仍报「可排序 ✅」
7search-docs 数据分叉与 capabilities.json 不同源,缺了新展示器 —— 恰好是任务要用的两个

中低级 ​

#缺陷后果
8文档示例用实例属性 + 非静态方法照抄示例跑不起来(真实基类三方法全 static)
9$show->field()->dateTime() 必然致命错误Field 基类缺该方法
10perPage() 无任何文档AI 唯一查不到、只能抄参考实现的语法
11scaffold-resource 引用不存在的 Model 却零提示生成 use App\Models\Course,但该类不存在
12幽灵列静默通过拼错列名不报错,该列在结果中凭空消失
13未注册的 Resource 也能「全绿」注释掉 Provider 注册后,两项校验仍报成功
14map() 整数键被序列化成数组PHP 会把 "0"/"1" 转回整数,前端 map[key] 取值失败
15文档声称检查「路由/权限/迁移」,实际只做语法校验过度承诺

第五轮新增(验证 M5 新能力时发现) ​

#缺陷后果
16需求校验器对未知键静默通过喂 {"tree":"zzz","bogus_key":"x"} 返回「✅ 全部需求已满足」(空核对块)—— AI 以为已验证,实际什么都没查
17draggable() 是幽灵能力编译产物有 draggable:true,但前端零拖拽实现。比缺功能更糟 —— 它给出虚假承诺
18/__ai/schema/{uri} 丢失新元数据grid 返回扁平数组,丢掉 perPage/exportable/tree;form 丢掉 stepped/steps。AI 唯一的自查接口看不到新能力
19文档站内容陈旧M5 改完后忘了重新生成,站点缺导出/树形/分步三节
20重复唯一值返回 500 而非 422用户看到"服务器错误",不知道是编码重复

三条写进代码的教训 ​

教训 1:校验器只读元数据 = 假阳性工厂 ​

任何"已设置"的断言,都必须有对应的运行时验证。

diff
- $actual = $node->meta['perPage'];        // 只读声明 → 假阳性
+ $actual = $this->runtimePerPage($node);  // 真跑一次分页 → 可信

修复后的输出:✅ 每页 30 条 — 已生效(运行时实测 30 条)

教训 2:同一份信息有多个来源 = 迟早分叉 ​

缺陷 7 的根因:新增展示器时只改了 Capabilities, SearchDocs 里还有一份硬编码的旧列表。

解法不是"记得同步更新两处",而是让它们同源 —— 现在 SearchDocs 从 Capabilities 动态取数,并加了回归测试:

php
// Capabilities 里有的,search-docs 必须也有
foreach (array_keys((new Capabilities)->columnOptions()) as $option) {
    $this->assertStringContainsString($option.'()', $text);
}

教训 3:异常携带的上下文不能在捕获时丢弃 ​

php
// 修复前:AI 只看到笼统的"编译失败"
catch (\Throwable $e) {
    $issues[] = ['message' => '编译失败: '.$e->getMessage()];
}

// 修复后:保留完整可自愈信息
catch (\Throwable $e) {
    if ($e instanceof AiReadableException) {
        $issue['did_you_mean'] = $ctx['did_you_mean'];      // 是否想用 teacher_email
        $issue['available_columns'] = $ctx['available_columns'];
        $issue['example'] = $ctx['example'];                 // 可粘贴的修正代码
    }
}

教训 4:校验器的"沉默"比"报错"更危险 ​

第五轮 AI 做了个探针实验,把不认识的需求键喂进校验器:

json
// 输入
{"tree": "zzz", "export": "zzz", "step": "zzz", "bogus_key": "x"}

// 输出
## 需求核对
(空)
✅ 全部需求已满足

它不报错,也不核对 —— 直接给了绿灯。

这跟第四轮的"假阳性"是同一类问题的不同表现:

校验器一旦沉默,AI 就失去了判断依据。

修复后:

json
❌ 未知需求键: bogus_key — 校验器不认识这个键,它不会被核对
   修正: 受支持的键: searchable, sortable, required, columns,
         fields, per_page, tree, export, step

原则:「校验器不认识这个键」和「这个键校验通过」必须是两件事。


AI 自发做对了什么 ​

测试中最有价值的观察:AI 会主动怀疑框架的"绿灯"。

第三轮 AI 自己设计了反向对照实验:

验证项方法为什么这么做
perPage 是否真生效插入 30 行自有数据当时表里只有 3 行,断言不可证伪
searchable 是否真过滤用只存在于非搜索列的关键词搜,必须返回 0 行证明关键词没泄漏到非搜索列
required 是否只是 UI 星号直接 POST 缺字段确认后端真拦截
sortable 是否真改变顺序对比 asc/desc 的实际返回序列不只看 HTTP 200

它还专门做了负向测试:故意改坏 4 条需求,验证校验器能否全部报出 —— 4 条全中,它才敢相信校验器的全绿。

第五轮它又升级了手法 —— 当怀疑 draggable() 只是写进 JSON 没实现时, 它没有读源码,而是:

dump 真实 HTTP 响应的 HTML,统计 dragstart/drop/pointerdown 事件 handler 的出现次数 → 结果为 0 → 确认前端没有拖拽实现

用运行结果证伪声明,而不是靠读源码或猜测。

这给框架设计提了个要求:校验器必须可被证伪。 如果 AI 无法独立验证"绿灯是否可信",它就只能盲信 —— 而盲信会致命。


AI 说框架哪里最难用 ​

原话摘录(未经修饰):

$grid->perPage(15) 的写法在文档站完全不存在。 我 grep 了全部页面…最终是从已有参考实现 OrderResource.php 抄来的。

这是最危险的一类 bug:错误参数不报错,静默返回未排序结果。 而校验器说「可排序: priority ✅ 已满足」—— 它只验了声明,没验运行时。 如果我只信校验器就会交付一个"排序实际靠猜参数"的接口。

scaffold-resource 生成残缺代码却零告警。 如果我直接交付 scaffold 输出,8 条标准会挂 3 条。


复现方式 ​

测试完全可复现。任何人都可以:

bash
# 1. 准备环境
composer create-project laravel/laravel:^12.0 demo
cd demo && composer require aimanong/framework
php artisan aimanong:install

# 2. 建一张表
php artisan make:migration create_products_table

# 3. 启动 MCP Server,让你的 AI 客户端连上
php artisan mcp:start aimanong

# 4. 给 AI 的任务(示例)
#    "为 products 表创建管理后台 Resource,
#     title 和 sku 可搜索,price 可排序,
#     每页 20 条,price 用金额格式展示"

关键:验证时要求 AI 传 requirements 参数,否则校验器只查语法、不查需求达标度。


我们仍在改进的地方 ​

诚实记录已知不足:

问题现状
方法名拼错($form->texte())无建议PHP 在调用前就抛 Error,走不到模糊匹配。修需 __call 兜底,与"零魔法"约定冲突,权衡中
/__ai/schema/{uri} 与 schema.json 结构不一致前者 grid 是扁平数组,后者是 {perPage, columns}
scaffold-resource 不知道用户需求脚手架能读表结构,但"要哪些列可搜索"是任务信息。正确解法是 AI 主动传 requirements

结语 ​

一个「AI 优先」框架的好坏,不该由它的宣传语判断,而该由AI 实际用它的表现判断。

我们把这个过程完整公开,包括每一轮失败的数据和每一个被 AI 发现的自己的 bug。

如果你发现我们漏了什么,欢迎提 Issue。


测试环境:PHP 8.2.29 / Laravel 12.69.3 / 四轮分别针对 users、orders、tickets、courses 四张表完整原始记录见仓库 internal-docs/AI实测记录.md

最后更新于:

基于 Apache-2.0 许可开源 · 允许免费商用 · 含明文专利授权