> 面向 Trae 开发社区 | 案例 + 技巧 | 全文约 4000 字,读完约 12 分钟
> 所有数字可核验,仓库地址在文末。**含 4 个"AI 把我坑了"的真实案例**,和一份可直接粘贴的规则模板。
-–
## 0. 先说结论
**一个月,约 3000 元 token 费,27 天,从零到一个可开源发布的 ARM 边缘 LLM 推理引擎。**
- 纯 C11、零第三方运行时、单文件约 **0.8 MB**、**28 个 C 文件**
- 让一块 **15.6 GiB** 内存的 RK3588 开发板跑起 **17.66 GB** 的 Qwen3-30B-A3B
- 服务峰值内存 **14.23 GB → 1.11 GB(12.8×)**,且输出与全层档**逐位一致**
但这不是一篇"AI 真牛"的文章。
**真实的体感是:AI 把"写"和"查"的成本压到了接近零,然后我发现瓶颈全部搬到了"验"上。
我这 27 天里,大概有一半时间不在写代码,而在证明"它写的东西是对的"。**
而且最有意思的是——**它从没对我撒谎。它只是特别擅长把错的结论,写得像对的一样自信、
一样完整、一样有条理。**
下面是我被坑的 4 次,和我最后怎么活下来的。
-–
## 1. 它确实省掉了三件事(这是真的)
先把功劳说清楚,不然下面的吐槽会被误读成"AI 不行"。
**第一,它把"想法 → 可编译的 C"这段路压平了。** 手写 NEON 量化 GEMM、KV 分页、
mmap 逐层驱逐这些内核,我可以直接说"我要按层建页、算完 `MADV_DONTNEED`、只留 keep 层",
它给出的是能编译、能跑、结构合理的一版。**从 0 到能跑的初稿,快得离谱。**
**第二,它把"文档债"这条最容易欠的账还了。** 最后交付物里除了代码,还有
**11 页 Wiki、5 篇技术文档、90 篇教程**。这些东西如果靠我手写,会在"代码能跑"的那一刻
就永远躺在 TODO 里。
**第三,它能自己跑实验。** 我给它接了一条 SSH 通道(`tools/rk_remote.py`),
它就能上传脚本 → 在板端执行 → 读日志 → 改代码。**"改 → 跑 → 读 → 改"闭环一旦成立,
它就从"高级自动补全"变成了"能自己动手的结对工程师"。**
-–
## 2. 它坑我的 4 次(每个都真实发生,每个都很隐蔽)
### 案例一:同一个数字,我写了三遍,三遍都对,但都不一样
这是最典型的,也是最贵的。
我要在文档里写"逐层推理的 decode 代价"。AI 给了我一个数字:**+37%**。
我信了,写进文档。
后来我自己核对,发现同一个指标在不同口径下是:
| 口径 | decode 代价 |
|—|—|
| 冷页缓存 + `–threads 4` | **+86%**(207 → 385 ms/tok) |
| 冷页缓存 + `–threads 8` | **+37%**(429 → 586 ms/tok) |
| serve 稳态热档 | **+34.6%**(458.4 → 617.2 ms/tok) |
**三个数字都是真的。**
`+37%` 没错,`+86%` 也没错——**它们只是问的不是同一个问题**。
你看出问题了吗?**如果我当时只写 +37% 发出去,我就是在用一种口径里最漂亮的数字,
去描述一个跨口径的事实。** 这不是撒谎,但比撒谎更糟——因为它看起来严谨,还有数据支撑。
> **坑的本质**:AI 不负责"守住口径",它只负责"生成一个看起来对的数字"。
> 口径这件事必须由人来定义、并且**写下来**。
**我后来的做法**:三个数字全列,并注明口径。以及把口径规则写成文件。
-–
### 案例二:同样的代码,同样的板子,我测出的性能差了 2.3 倍
我在板子上测 8B 全层 decode,得到 **207 ms/tok**。
但我文档里之前记的是 **429 ms/tok**——**差了一倍多**。
第一反应是"我是不是测错了"。排查了半天,真相是:
| 档 | `–threads 4` | `–threads 8` | 比值 |
|—|—|—|—|
| 全层 decode | **186 / 189 ms/tok** | 431 / 433 ms/tok | 4 线程快 **2.3×** |
**RK3588 是 4×Cortex-A76 + 4×Cortex-A55 的 big.LITTLE。**
`–threads 8` 会把 4 个 A55 小核拉进 GEMM 并行区,**大核被小核拖住,整体反而慢 2.3 倍**。
这个坑之所以隐蔽,是因为它**在 x86 上是不会出现的**——
在服务器上"线程越多越快"是常识,在 big.LITTLE 上它是错的。
而 AI 的默认认知是前者。
> **通用教训**:**任何跨架构的性能结论,都必须标注线程绑定策略。**
> 网上一会儿说 429、一会儿说 186,两个都对——区别只在你喂了几个核。
-–
### 案例三:我一度以为"这个模型不会回答问题"
我让 30B 模型做一次简单问答,给出 32 个 token 的预算,
结果输出全是一堆思考过程,**一个字答案都没有**。
我当时判断:小预算下模型不产出答案。这结论被写进了文档。
后来查引擎源码才发现:**`enable_thinking` 默认是开的**
(引擎对齐 HuggingFace 的 `apply_chat_template` 默认行为),
32 个 token 全被思考段吃掉了,**根本没有机会写答案**。
**这不是模型的问题,也不是我的问题,是"默认值"的问题。**
而默认值这种东西,**AI 不会主动告诉你**——它会顺着你给的输入往下推,
然后把"输出里没有答案"解释成"模型在这个预算下不产出答案"。
> **修复**:显式传 `“enable_thinking”: false`,或者设 `VLLM_ENABLE_THINKING=0`。
> 那个档位下,30B 用 **24.5 秒**给出了完整答案——**从"不可用"变成"可用",
> 只差一个默认值。**
-–
### 案例四:工具链报了一个跟代码完全无关的错
`ld.exe: cannot open output file D:\项目\…\build-x64\vllm_kestrel_x64.exe: No such file or directory`
代码没问题、路径存在、权限也有。AI 的建议一轮轮来:检查权限、检查磁盘、检查杀软……
真相是:**MinGW 的 `ld` 处理不了 `-o` 参数里的非 ASCII(中文)绝对路径。**
旁证特别干脆:同一个中文目录下,另一个用**相对输出名**(`-o vqf_conv.exe`)的构建脚本
一次就过。**换成纯 ASCII 输出目录,立刻解决。**
> **教训**:AI 会把工具链的怪癖当成"需要排查的 bug",
> 而有些问题的答案是"这是工具链的历史遗留限制,绕开它"。
-–
## 3. 所以我给自己定了 7 条规则(可直接粘贴使用)
**下面这段可以直接粘进你的 Trae 项目记忆 / 规则文件。** 这是我用一个月、4 个坑换来的。
```markdown
## 项目规则
### 1) 数字口径:任何性能数字必须写全口径
- 必标四项:线程数 / 冷或热页缓存 / 纯权重或含 KV / 版本(v0 或 v1)
- 严禁把不同口径的数字放进同一张对比表
- 单位换算写死:内存用十进制 GB;内存与页缓存对比用 GiB;文件体积用十进制 GB
- 改任何数字之前,先读本文件
### 2) 自证日志:任何开关/优化必须打印一行能自证生效的日志
- 这一行必须包含"可对比的量"
- 例:[VQF-STREAM] enabled keep=1 nl=48 per-layer=334.1MB data=16847.2MB resident~808.4MB
- 验收方式:看日志,不听口头"已生效"
### 3) 自检门:功能必须先有 PASS/FAIL 自检,否则不许合入
- 自检必须是退出码 0/1,可无人值守跑
- 我只接受"退出码 + 日志原文",不接受"测试通过了"
### 4) 诚实边界:每个结论/每篇文档必须附"诚实边界"段
- 不写边界 = 视为未完成
- 边界要具体到"什么条件下不成立"
### 5) 汇报格式:说"改完了"必须同时给文件路径 + 行号
- 不接受"总结一下刚才做了什么"
- 我自己去看代码核对,而不是采信总结
### 6) 执行纪律
- 长命令拆成短轮询(一次性长时间阻塞容易被中断)
- 上传到 Linux 的 .sh 脚本先去 CR:sed -i “s/\r$//”
- 上传脚本 → 执行 → 读日志 → 再改,形成闭环
### 7) 坑清单:坑一复现,立刻写进 README/docs
- 三句式:现象 → 原因 → 解决
- 写进文件,不要只留在对话里(下次会话会忘光)
```
### 这 7 条里,最反直觉的是第 5 条
我最开始喜欢让 AI"总结一下刚才做了什么",它总结得又好又全。
后来我发现:**它会把没做的,也说成做了。** 不是撒谎,是它真的"以为"做完了。
**改成要"文件路径 + 行号"之后,一切变得干脆**——我去看一眼就知道真假。
这一条把我和它之间的信任成本降到了最低:
**不需要信它,只需要读它给的坐标。**
### 第 2 条是我最推荐给你的
如果你今天只做一件事,做这个:**让 AI 给它写的每个功能加一行"自证日志"。**
我的引擎在启用逐层推理时会打印:
```
[VQF-STREAM] enabled keep=1 nl=48 segs=11 per-layer=334.1MB data=16847.2MB resident~808.4MB rss=988kB
```
`data=16847.2MB` 是权重总量,`resident~808.4MB` 是实际常驻——
**这一行就是整个项目的核心思想,而且它无法被"假装生效"。**
有了这种日志,"AI 说它生效了"这个问题,就从**信任问题**变成了**读日志问题**。
-–
## 4. 人机分工:我最后承认的那件事
做到一半我想明白了一件事:
**AI 让"写"和"查"趋近于零成本,但"想清楚"和"做判断"的成本一点没变。**
所以最后我的时间大概是这么分的:
| 环节 | 时间占比 | 谁在主导 |
|—|—|—|
| 写代码、查资料、写文档 | ↓ 趋近很小 | **AI 主导** |
| 定义口径、设计 A/B、判断"这个结论能不能用" | ↑ 大部分 | **我主导** |
**结论是:规则应该写在"怎么验证"上,而不是写在"怎么生成"上。**
我见过太多人把提示词调得花里胡哨,却没定过一条"数字怎么标口径"的规则。
**前者影响它写得多快,后者决定它写的东西能不能用。**
-–
## 5. 这件事是可以核验的(不是自述)
我不要求你信我。Trae 的会话记忆是本地落盘的,而**记录里的东西可以和仓库里的代码对上**:
| 当时的会话记录 | 现在仓库里还在的代码 |
|—|—|
| “将 `vllm_batch.c` 中的轮询睡眠替换为基于 `pthread_cond_t` 的事件驱动机制”(09-01) | `src/serve/vllm_batch.c:209` 主循环真的就是 `pthread_cond_wait(&b->cv, &b->cv_mu)` |
| “改为手动加载模式…通过 `/admin/api/model/load` 手动填入模型路径”(08-31) | `src/serve/vllm_admin.c:1082` 真的实现了该路由 |
**这种"当时的记录 → 现在还在的文件"的对应,比任何自述都有说服力**——
因为代码是真实提交的产物,事后对不上就露馅。
- 仓库:Kestrel-LLM: Kestrel(红隼):纯 C11、零第三方依赖的 RK3588 边缘 LLM 推理引擎——原生小模型推理,加国密可信计算。
- 复现脚本:`sh tools/bench_value.sh`(**只依赖 Python 3 标准库**,会重跑内存 A/B + 热态稳定度 + 页缓存证据)
-–
## 6. 最后三句
**第一句**:AI 结对最值钱的地方不是"帮你写代码",
是**它愿意陪你做那些你一个人会偷懒跳过的脏活**——跑第 5 遍、写文档、记账。
**第二句**:但也正因为如此,**你必须成为那个守口径的人**。
它有无限耐心,但零责任心;它能生成任何形状的数字,但不会替你判断哪个能用。
**第三句**:如果你现在正在用 AI 写一个正经工程,
**请从今天起做两件事**——给它加"自证日志",给它加"口径规则"。
这两件事花的成本,比它坑你那一次便宜太多。
-–
*本文所有性能数字均标注口径;三档模型的完整数据、诚实边界与复现方法见仓库 README 与 Wiki。*
*项目采用 AGPL-3.0-or-later / 商业许可双许可,代码全部公开。*
-–
-–