跳到主要内容

我让 Claude Code 学会了一个它从未见过的框架

· 阅读需 9 分钟

我维护了一个自研 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 操作

设计原则:

  1. 入口即决策SKILL.md 只有 5 条核心原则 + 一张路由表。AI 读完就知道「简单 CRUD 不建 Service」「写操作必须 .execute() 结尾」
  2. 按需加载:AI 不会一次读完所有文件,而是根据当前任务去查对应的参考文档
  3. 硬约束前置: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 BootSolon
@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.mdSkill
结构单文件多文件 + 路由
加载方式全量加载按需加载
适合场景简单规则(几十行)完整框架知识(几百行+)
可复用性项目内跨项目复制

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 不认识」的问题,欢迎交流。点个 ⭐ 就是最大的支持。