我让 Claude Code 学会了一个它从未见过的框架
我维护了一个自研 ORM 框架 DLZ-DB,核心代码不到 7000 行,跑在公司十几个项目里。问题是:AI 不认识它。于是我做了一套 Claude Code Skill,让 AI 能像用 MyBatis-Plus 一样流畅地写 DLZ-DB 代码。本文分享整个思路和实践。
一、问题:AI 写代码,遇到小众框架就「幻觉」
用 Claude Code 写 Spring Boot + MyBatis-Plus 的代码,体验非常好——它见过海量训练数据,生成的代码基本能跑。
但换成 DLZ-DB 就完全不行了:
// ❌ AI 幻觉:它会「脑补」一个不存在的 API
userMapper.selectList(new QueryWrapper<User>().like("name", "张"));
// ✅ DLZ-DB 实际写法:无 Mapper、无 XML、静态调用
DB.Pojo.select(User.class)
.like(User::getName, "张")
.queryBeanList();
AI 不是不聪明,是没见过你的框架。它会用最接近的知识去「猜」,结果就是一堆编译不过的幻觉代码。
这不是 DLZ-DB 独有的问题。任何公司的内部框架、私有 SDK、自研中间件,都面临同样的困境——AI 的训练数据里没有你的 API。
二、解法:Claude Code Skill —— 给 AI 写一份「教材」
Claude Code 的 Skill 机制允许你在项目里放一组 Markdown 文件,AI 在编码时会主动加载并遵守这些规范。
它不是普通的 README,而是一种结构化的知识注入:入口文件声明原则和路由,子文件提供 API 参考、编码规范、完整示例。AI 读完后,就像一个新员工读完了内部 Wiki,能按规矩写代码。
我为 DLZ 生态做的这套 Skill 就放在 dlz-ai-skill,覆盖了 DLZ-DB(ORM)和 DLZ-KIT(JSON 工具)两个核心库。
三、Skill 结构设计:像 API 文档一样精确,像教程一样好懂
.claude/skills/
├── SKILL.md ← 入口:5 条核心原则 + 文档路由表
├── README.md ← 安装指南
│
├── ref/ ← API 参考(精确到方法签名和返回值)
│ ├── dlz-db.md ← 7 个入口、条件方法、执行返回、预设 SQL
│ ├── dlz-kit.md ← JSONMap 路径取值、set/put、类型转换
│ └── constraints.md ← 10 条硬约束(违反 = 运行时报错)
│
├── guide/ ← 编码规范(告诉 AI「该怎么写」)
│ ├── entity.md ← Entity 注解 + 命名映射 + 逻辑删除
│ ├── controller.md ← Controller 模板 + 无 Service 规则
│ ├── service.md ← Service 何时建 + 事务/批量/多数据源
│ └── migration.md ← MyBatis → DLZ-DB 迁移指南
│
├── framework/ ← 框架集成(Spring Boot / Solon 差异)
│ ├── spring-boot.md
│ └── solon.md
│
└── examples/ ← 完整可运行示例
├── crud.md ← 增删改查
├── page.md ← 分页查询
├── sql.md ← 原生 SQL / 预设 SQL
├── transaction.md ← 事务
└── jsonmap.md ← JSONMap 操作
设计原则:
- 入口即决策:
SKILL.md只有 5 条核心原则 + 一张路由表。AI 读完就知道「简单 CRUD 不建 Service」「写操作必须.execute()结尾」 - 按需加载:AI 不会一次读完所有文件,而是根据当前任务去查对应的参考文档
- 硬约束前置:10 条最容易犯错的规则单独成文件,比如「
in()不可传单值」「占位符不混用」
四、核心设计细节
4.1 五条核心原则 —— AI 的「编码纪律」
1. **无 Mapper/DAO/XML**,直接用 DB.Pojo.xxx
2. **简单 CRUD → Controller 里直接写**,不建 Service
3. **复杂多表 / 可复用逻辑 → 才建 Service**
4. **写操作必须 .execute() 结尾**(insert 除外)
5. **事务**:Spring 用 @Transactional,Solon 用 @Tran,通用用 DB.Tx.run()
这 5 条是最高优先级。不管 AI 生成什么代码,都得过这 5 道关。特别是第 1 条——AI 见过太多 MyBatis 代码,本能地想生成 Mapper 接口,必须在入口就拦住。
4.2 硬约束 —— 10 个「地雷」
1. 无 Mapper/DAO/Wrapper 类,直接 DB.Pojo.*
2. 占位符不混用:DB.Jdbc 用 ?;DB.Sql 用 #{key}
3. 返回值区分:queryOne/List/Page 返回 ResultMap,要 Bean 用 queryBean 系列
4. 查询列用 columns() 不是 select()
5. insert 直接执行,无需 .execute()
6. 物理删除:绕过逻辑删除用 .ignoreLogicDelete(true)
7. 预设 SQL key 必须以 "key." 开头
8. in() 参数仅支持 List / CSV 字符串 / "sql:子查询",不可传单值
9. 批量操作用 DB.Batch.insert(users),不是 insertBatch()
10. #{key} 与 ${key} 不可混用
这些都是实际踩过的坑。不写出来,AI 大概率会踩。
4.3 迁移指南 —— 给 AI 一张「对照表」
很多项目是从 MyBatis-Plus 迁过来的。我专门写了一份迁移对照:
// ❌ MyBatis-Plus
userMapper.selectById(1);
userMapper.selectList(new QueryWrapper<User>().like("name", "张"));
// ✅ DLZ-DB
DB.Pojo.select(User.class).eq(User::getId, 1).queryBean();
DB.Pojo.select(User.class).like(User::getName, "张").queryBeanList();
AI 看到这种**「旧写法 → 新写法」**的对照后,能精确地做代码改造,而不是在两种风格之间反复横跳。
4.4 双框架支持
DLZ-DB 同时支持 Spring Boot 和 Solon。两个框架的 DB.xxx API 完全一致,差异只在注解和配置上:
| Spring Boot | Solon |
|---|---|
@RestController | @Controller |
@Transactional | @Tran |
@Autowired | @Inject |
@RequestBody | @Body |
分框架建文件,AI 根据项目依赖自动选择正确的写法。
五、效果对比
Before(无 Skill)
让 Claude Code 写一个用户分页查询接口:
// AI 生成的代码 —— 全是幻觉
@Autowired
private UserMapper userMapper;
public Page<User> getUsers(int page, int size) {
return userMapper.selectPage(
new Page<>(page, size),
new QueryWrapper<User>().orderByDesc("create_time")
);
}
问题:UserMapper 不存在、QueryWrapper 不存在、Page 构造参数不对。一行都编译不过。
After(有 Skill)
// AI 生成的代码 —— 直接能跑
@GetMapping("/page")
public R<Page<User>> page(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) Integer status) {
Page<User> p = DB.Pojo.select(User.class)
.eq(status != null, User::getStatus, status)
.orderByDesc(User::getCreateTime)
.page(pageNum, pageSize)
.queryBeanPage();
return R.data(p);
}
- ✅ 无 Mapper、无 Service,直接
DB.Pojo - ✅ 三参条件
eq(status != null, ...)自动跳过 null - ✅ 正确使用
queryBeanPage()返回 Bean 类型 - ✅ 符合 Controller 直写的编码规范
六、为什么不直接写 CLAUDE.md?
Claude Code 也支持在项目根目录放一个 CLAUDE.md。但它和 Skill 有本质区别:
| CLAUDE.md | Skill | |
|---|---|---|
| 结构 | 单文件 | 多文件 + 路由 |
| 加载方式 | 全量加载 | 按需加载 |
| 适合场景 | 简单规则(几十行) | 完整框架知识(几百行+) |
| 可复用性 | 项目内 | 跨项目复制 |
DLZ 的知识量太大了——7 个 DB 入口、20+ 条件方法、10 条硬约束、2 套框架集成、5 类示例。塞进一个文件,AI 上下文会溢出;拆成 Skill,按需加载,效果好得多。
七、给其他框架作者的建议
如果你也有自研框架,想让 AI 写出正确的代码,我总结了几点经验:
1. 先写硬约束,再写 API
AI 最容易犯的错不是「不会调 API」,而是**「用了不存在的 API」**。先把「不能做什么」写清楚,比「能做什么」更重要。
2. 用对比代替描述
❌ "DLZ-DB 使用静态方法进行数据库操作"
✅ "MyBatis: userMapper.selectById(1) → DLZ-DB: DB.Pojo.select(User.class).eq(User::getId, 1).queryBean()"
AI 看代码比看文字准确 10 倍。
3. 示例必须完整可运行
不要只贴片段,要贴从 Entity 到 Controller 的完整代码。AI 需要看到 import、注解、返回值的完整上下文。
4. 分文件,建路由
知识量超过 200 行就应该拆文件。入口文件只放原则和路由表,具体内容按场景分文件。AI 的上下文窗口是有限的,按需加载 >> 全量灌入。
5. 迭代 > 一次性
第一版不用完美。先覆盖最常用的 CRUD 和分页,跑几次看 AI 哪里出错,针对性补充约束。我的 10 条硬约束就是这么迭代出来的。
八、DLZ-DB 本身的亮点(安利一波)
既然都看到这了,顺便安利一下 DLZ-DB 本身:
🔍 SQL 日志能直接定位到业务代码行号
caller:(UserController.java:42) getList 15ms sql:SELECT * FROM user WHERE id = 1
不用再从 15 层异常栈里找 SQL 在哪。点一下就跳转到业务代码。
📦 核心代码不到 7000 行
没有 SqlSession、没有一级缓存、没有 Executor 分层。调用栈直通 JDBC,出 bug 能自己看源码。
🔌 多数据源:两行代码搞定
DB.Dynamic.setDataSource(prop); // 运行时注册
DB.Dynamic.use("slave", () -> DB.Pojo.select(...)); // 运行时切换
SaaS 多租户、动态数据源管理、ETL 作业——别人需要一圈配置的场景,这里两行解决。
🤝 同时支持 Spring Boot 和 Solon
DB.xxx API 完全一致,换框架不换业务代码。
九、如何使用
方式一:直接复制到你的 DLZ 项目(推荐)
git clone https://github.com/dingkui/dlz-ai-skill.git
cp -r dlz-ai-skill/.claude/skills/ your-project/.claude/skills/
然后在 Claude Code 中输入 /dlz 即可加载。
方式二:在 CLAUDE.md 中引用核心原则
## DLZ 开发规范
本项目使用 DLZ 生态。输入 /dlz 加载完整 API 参考。
核心原则:
1. 无 Mapper/DAO/XML,直接用 DB.Pojo.xxx
2. 简单 CRUD → Controller 里直接写,不建 Service
3. 复杂多表 / 可复用逻辑 → 才建 Service
4. 写操作必须 .execute() 结尾(insert 除外)
5. 事务:Spring 用 @Transactional,Solon 用 @Tran
十、总结
AI 不是不够聪明,是缺少你的知识。
Claude Code Skill 本质上是一种知识注入机制——把框架的 API、约束、编码规范结构化地写成 Markdown,AI 就能像一个读过完整文档的开发者一样工作。
对于自研框架来说,这可能是让 AI 真正可用的最短路径。不需要微调模型,不需要训练数据,只需要一组好的 Markdown 文件。
项目地址:
- AI Skill:github.com/dingkui/dlz-ai-skill
- DLZ-DB:github.com/dingkui/dlz-db
如果你也在自研框架或内部工具上遇到了「AI 不认识」的问题,欢迎交流。点个 ⭐ 就是最大的支持。