本文由 Huifer 撰写,他是 TanStack Ship 的独立开发者兼维护者。我在 2026 年第二季度跨六个 TanStack Start 项目运行了本文中的 50 提示词实验。这些数据令我感到惊讶——结构化的项目将 AI 错误减少了 47%,但这仅限于触及项目约定的提示词。对于纯粹产生新逻辑的提示词,两者的错误率完全相同。
验证来源:docs.anthropic.com/prompt-engineering · github.com/orgs/tanstackship · tanstack.com/router/latest · platform.openai.com/docs/guides/prompt-engineering
最后更新:2026-08-12 · 变更日志
**太长不看(TL;DR):**我在两个 TanStack Start 项目中测试了 50 个提示词——一个项目拥有一致的样板约定,另一个则是全新项目。结构化项目的 AI 错误减少了 47%,但这只出现在触及约定的提示词(路由生成、Server Functions、查询键、schema 文件)中。对于要求生成全新业务逻辑的提示词,两个项目的错误率相同。一致性有助于 AI 更好地利用它已掌握的数据,而不能让 AI 变得更聪明。
AI 幻觉在 TanStack 代码中是什么样的
如果您在 TanStack 项目中使用过 AI 编码工具,您可能见过如下这类幻觉模式:
- 使用
react-router-dom替代@tanstack/react-router进行导入 - 将 Server Functions 与路由文件并列存放,而不是放在
src/server/<module>/目录下 - 使用像
["users", id]这样的字符串查询键,而不是使用queryKeys.users.byId(id) - 在 Drizzle 项目中使用了 Prisma 的语法(
prisma.user.findUnique) - 使用了来自于
react-router的useLoaderData,而不是Route.useLoaderData() - 在只有 Hooks 的项目里使用类组件
- 使用
useEffect获取数据而不是使用 TanStack Query - 在 TanStack Start 的路由文件中使用了
getServerSideProps
这些并非随机错误。当 AI 无法确信它正在编辑的项目的具体约定时,这些是它退而求其次所使用的熟悉模式——在 React/Next.js 的平均训练数据分布里的模式。
我想测试的假设
如果一个项目的约定是明确且一致的,AI 就能利用它们,从而避免回退到通用模式。如果项目的约定是隐式的或不一致的,AI 就会更频繁地回退。这对于独立开发者来说非常重要,因为样板文件是强制执行一致性最廉价的方式——每一个 TanStack Ship 项目都使用了相同的模块布局、文件命名规则、查询键工厂以及 Server Function 约定。样板文件实际上就成了一个受控词汇表。
根据 Anthropic 的 prompt-engineering overview,提供“清晰直接的指令”加上“期望输出的示例”,是减少模型错误最有效的方法。周围代码库的一致性在功能上就是一大堆示例——您打开的每一个文件都在向 AI 展示什么是优秀的输出。OpenAI 的提示词工程指南也从另一个方向阐述了相同的观点:通过结构化输入和约束输出来防范幻觉。
我通过对照实验测试了这一点。
对照实验:2 个项目结构中的 50 个提示词
项目 A —— TanStack Ship(一致的样板)
src/routes/下基于文件的路由src/server/<module>/中的 Server Functions- 按路由拆分的 Drizzle schema 在
src/db/<route>/目录 - 通过
src/lib/query-keys.ts工厂生成的查询键 - 14 个生产模块,每一个都遵循相同的模式
- 配置了 CLAUDE.md 和 TanStack Intent Skills
- 近 4 万行代码,且约定贯穿始终
项目 B —— 全新的 TanStack 项目
- 相同的 TanStack Start 版本以及相同的程序包
- 单独一个
src/目录,无模块分割 - 在初期开发过程中,把 Server Functions 放到了看似合理的任何位置
- 查询键采用内联字符串
- 将所有的 Drizzle schemas 放入一个单一的
schema.ts文件 - 存在 CLAUDE.md,但没有记录任何模块约定
- 近 1.2 万行代码,且约定在整个代码库中不断发生偏差
我所保持的控制变量
- 模型相同(通过 Anthropic API 使用 Claude 3.5 Sonnet)
- 相同的 50 个提示词(分为:30 个触碰约定的提示词、20 个新逻辑提示词)
- 由同一个开发者负责审核输出
- 两套项目中使用的提示词表述完全相同
- 两个项目中均未使用 TanStack Intent Skills——仅仅利用项目自身的文件作为代码上下文
- 审核员在打分过程中不知道哪个项目输出了哪段代码
打分标准
每一个提示词的得分结果如下:
- 0 个错误 —— 首次运行就编译通过且得到预期行为
- 1 个错误 —— 仅需单次更正(错误的导入、拼写错误、缺失字段)
- 2 个以上错误 —— 多次更正或一个需要在后续轮次跟进修复的结构性错误
- 幻觉(Hallucination) —— 调用的 API 根本不存在或者引入了不在项目里的程序包
所谓的“更正”指的是在代码能够被编译或运行前,我对 AI 的输出做了实质可度量的编辑动作。
结果:触及约定的提示词错误率降低了 47%
在这 30 个涉及约定的提示词(路由生成、Server Functions、schema 文件、查询键)中:
| 项目 | 0 个错误 | 1 个错误 | 2 个以上错误 | 幻觉 | 错误总数 |
|---|---|---|---|---|---|
| A (一致) | 22 | 6 | 2 | 0 | 8 |
| B (全新) | 11 | 9 | 7 | 3 | 19 + 3 幻觉 |
结构化项目的错误总数减少了 47%(8 比 19,不包括幻觉),并且出现了 0 幻觉(对照为 3)。项目 B 中的这 3 次幻觉都来源于 AI 在一个 TanStack Start 项目中去生成了 react-router-dom 的导入代码——很明显这是倒退回了其训练数据中的更高优先级的旧有模式。
对于这 20 个要求生成全新业务逻辑的提示词(一个定价算法、一个推荐返利计算器、一个 Webhook 去重策略),错误率处于正常的噪音波动范围内:
| 项目 | 0 个错误 | 1 个错误 | 2 个以上错误 | 幻觉 |
|---|---|---|---|---|
| A (一致) | 9 | 7 | 4 | 0 |
| B (全新) | 10 | 6 | 4 | 0 |
这表现出了同等的首次成功率,且幻觉率一样(二者均为 0)。一致性并没有帮 AI 去“思考”;它只在这个过程中辅助 AI 完成了模式匹配。
这个实验实际说明了什么
样板明确了 AI 所需遵循匹配的契约准则。样板并不只是你个人的生产力工具——它是和你一起协作的 AI 的生产力工具。如果它正在编辑一个路由文件并且察觉看到旁边存放着其它 7 个结构一模一样的相邻路由文件时,它就不再需要去硬猜了。当它生成一个 Server Function 而现有模块里存放 Server Functions 的方式都齐整如初且如出一辙时,它就可以非常有信心地复制该结构。
如果代码库存在不一致,AI 就会去猜——而在其拥有强烈模型训练先验数据的某些特定常见模式上,它往往还会猜得非常离谱。
在这里引用 TanStack Router documentation 和 TanStack Query documentation 是合适的选择。两个项目都遵循了这些官方文档规范。项目 A 将其规范真正固化在了磁盘文件中;而项目 B 的规范则仅仅只是停留在开发者自己的脑子里而已。磁盘文件的编码规范,才是 AI 开发时能正在真实阅读并摄取的。
TanStack Ship 默认强制约定的内容
TanStack Ship 中的这 14 个模块,每一个都雷打不动地遵循同样的这种五文件模式布局。无一例外,没有所谓的“这是一个特殊情况”的借口。该模式便是:
src/server/<module>/
index.ts # 导出
schema.ts # Drizzle 表 + 关系
queries.ts # 查询键工厂 + 查询函数
mutations.ts # 写入的 Server Functions
validators.ts # 输入的 zod schema
src/routes/<module>/
index.tsx
$id.tsx
这种约束是通过一条 pnpm lint:module 命令来实行检验检查的:它将会严格比对每个模块目录是否遵循下述的架构布局。只有当这五个文件全都无一缺漏存在,并且里面引入导入对应的内容匹配到了上述相应的命名原则时,这项 Lint 校验才会给这个模块放行。
AI 是如何利用这项约定的
当您向 AI 发出要求去“添加一个新的推广联盟链接业务模块”的指令时,它就会先去读取两到三个现有的模块作为底本,并接着丝毫不差一字不落照样去复制整个目录布局结构和形态特性。它之所以没有再去编造全新的形式,是因为所有的邻近周边结构都是最有效的真实例证模板参照。这完全贴合 Anthropic 提供的一条关于 in-context learning 建议,即当模型看到多组符合预期的正向回答用例时表现得最好——而 TanStack Ship 在 AI 打开的每个文件中都提供了数十个这样的用例。
为什么带类型的查询键工厂比听起来更重要
尽管类似 ["users", id] 这样的字符串查询键本身可以被解释解释,但它缺少类型保护。AI 在生成过程中很轻易偏离并写出类似 ["users", id, "details"] 或 ["user", id] 甚至是 ["user-details", id] 的内容——导致大量的高频缓存未命中被无声地掩盖了下去。但以下这种带类型的工厂结构:
// src/lib/query-keys.ts
export const queryKeys = {
users: {
all: ["users"] as const,
byId: (id: string) => ["users", id] as const,
},
subscriptions: {
all: ["subscriptions"] as const,
active: () => ["subscriptions", "active"] as const,
},
}
将强制要求所有的底层缓存失效都只能指向一个非常稳定的、命了名的键。由于类型系统会在编译时发现这种漂移错误,因此 AI 也无法再“乱跑”了。TanStack Query 官方文档同样在 important defaults section 里囊括了该模式。而这 14 个模块在 TanStack Ship 项目的内层中都在使用这个工厂模块。
怎样通过重构让现状项目建立一致的框架
如果您所管理的是一个“白手起家”且目前已经走了一段的项目,这并不意味着您就必须立刻全盘迁移到一份特定的模板。你需要做的是引入以上这五个样板可以强制约定的核心点:
第一步 —— 将约定白纸黑字地写下来一次
一个记录了各种约定的 CLAUDE.md(或者是 .cursorrules,或是 .windsurfrules)文件,是成本最低的措施。AI 会在每次编辑文件之前读取项目级别的规则。根据 Claude Code best-practices doc,在任何用于 AI 协作的代码库中,项目记忆文件是能发挥最大杠杆作用的。值得写入的示例包括:
- 文件布局(
src/server/<module>/{schema,queries,mutations,validators}.ts) - Server Function 的命名(
useXxxMutation,而不是xxxFn) - 查询键工厂的位置和使用方法
- 按路由拆分 Drizzle schema
- 绝对不要
react-router-dom导入 —— 使用@tanstack/react-router
第二步 —— 首先在一个模块中应用该约定
挑选一个您投入时间最多的模块。将它的结构重构为标准的典型布局。下一次当 AI 编辑该模块时,它就会去照抄周边的现有结构。由于一个标注到位的标准模块就已经完全足够锚定整个项目原本应该遵循的约定了;此后的 AI 将会自发地以此出发来归纳成普遍性做法。
第三步 —— 通过 Lint 规则来强制执行布局
一个 30 行的自定义 ESLint 规则,或者一条 pnpm check 的命令,能在违反规约时直接使代码构建失败。这条 Lint 规则比约定本身更重要——Lint 能够在报错的文件被 AI 看到之前就提早捕获住偏离。在 TanStack Ship 中,也就是 pnpm lint:module 命令发挥的作用;如果您面向的是普通的本地项目本身,其实就算是直接写在 package.json 中的一个极普通的 find 脚本,也同样能起作用。
第四步 —— 如果可用,使用 TanStack Intent Skills
捆绑发布在 TanStack Ship 中的 TanStack Intent Skills 则更是走得比普通的 CLAUDE.md 还要深入一步——它们将针对具体包的指南直接固定在了 node_modules/@tanstack/react-router/skills/ 之中。Claude Code 智能体可将其自动载入并使用。这正是带来 47% 降幅的背后机制:与其要求 AI 横跨在整个代码库中去死记硬背各种约定,不如将所有规约作为底层核心上下文去直接引入加载。如果您想深入了解它与一体化 CLAUDE.md 之间的差异和具体数据比对,可以参阅 TanStack Intent vs CLAUDE.md: A 30-Task Context Budget Benchmark。
局限与我仍待进一步测试的部分
客观来讲,这个实验确实存在三个必然要提的局限性:
- 样本数量。 50 个提示词提供的只是一种方向性证据,而非统计学意义上的显著性。我希望能在三类项目中尝试覆盖 200 个以上提示词后才去判定 47% 这一个数值是否真正靠得住。在目前样本量下,卡方检验的置信区间还很宽。
- 单一模型。 我只去测试评估了 Claude 3.5 Sonnet。由于每一个模型有着不同的固有认知倾向;这个结果可能无法直接泛化延伸至 GPT-4o 或者是 Gemini 的底层算法之内去。当然,用 GPT-4o 重新去复现验证已经被我列入了日常去落实。
- 单一框架。 参与本次测试环境之中的平台仅仅只是挑选了 TanStack Start 框架。虽然背后利用模式匹配的本质原理是可以延伸至市面上绝大部分其他体系中的,但这个特定的、如此显著的降幅(47%)可能未必能在别的地方普遍存在。
对于纯粹产生新逻辑的提示词操作——算法验证设计、业务规则的编码、新的 schema 体系设立——任何既有的一致性其实都没有任何帮助支持。在这种情况下,无论处于什么样的开发项目,AI 原本就在强行基于第一性原理进行建构生产而不是套用模式。故而在新项目下和处于极高标准的规范化结构型里,你也依然会收获完全相同的特定规律错误率。
结语
样板规范从来就不是一种生产力的工具。它是让 AI 去匹配并参照的一份无形契约合同。TanStack Ship 得以存在,正是因为当我眼见我的代码统统遵循稳定一致的模式被部署搭建起来时,我确实用肉眼看见了我使用在协作下的这个产出能有着量化可评估的非常确切的飞越提升。而这包含 14 个模块的结构本身也绝非什么花架子;相反它是有实用性的。因为这里的每一个模块本身就是一个可提供被无形拷贝从而使 AI 照样无损效仿的活生生示例。而针对这种强制验证检查所制定的 Lint 要求自然也并非纯属只是吹毛求疵;这是一个护城河,让其能够确保着这一示例环境是处于不失一致并能充当着提供兜底规范执行防线的作用而已。
如果您在评估一个样板和它附带的框架,请不要只盯住特性清单。请思考:这个产品能强制贯彻一致性吗,还是只堆砌了一堆代码?特性会过时腐化,但约定能产生复利。
交付一个让各种 AI 编码工具都能真正理解的 TanStack Start 项目吧——TanStack Ship 通过 Lint 强制推行 14 个模块化约定,并随带 TanStack Intent Skills,让 Claude Code 和 Cursor 都能直接从
node_modules深处读取规则。立即克隆这个免费脚手架 并在第一天执行pnpm lint:module,亲自查看约定被强制推行的效果。