NLCC设计哲学:用自然语言写代码
NLCC设计哲学:用自然语言写代码
NLCC = Natural Language Code Compiler
专业级自然语言编译器,将自然语言描述编译为可执行代码
前言
在探索Claude Code、Clawdbot等AI辅助编程工具的过程中,我逐渐意识到一个核心问题:传统的软件工程实践,可能并不适合AI时代。
传统的代码追求复用、模块化、可维护性——这些当然重要。但在AI能够快速生成大量代码的今天,我认为代码本身变得cheap了,真正昂贵的是AI的注意力。
基于这个核心洞察,我设计了NLCC(Natural Language Code Compiler)——一套专门为AI编程优化的编译系统。本文将详细阐述这个系统的设计哲学。
核心洞察:代码是Cheap的
AI时代的成本结构
在AI辅助编程中:
- 代码是cheap的:AI生成代码极快(几秒钟生成几百行)
- module随时可以替换:没搞好就再做一个,代价很低
- 完全不需要提前过度设计:让实际需求驱动架构演化
- AI的上下文窗口:昂贵(有限的注意力)
- AI理解复杂系统:困难(容易遗漏细节)
- AI修改迭代:昂贵(需要重新理解整个上下文)
新的成本函数:
总成本 = AI生成代码的时间(低)
+ 人类review的时间(中)
+ AI修改迭代的时间(高)
+ 系统复杂度导致的AI错误率(极高)结论:在AI时代,应该牺牲代码复用来换取AI注意力的聚焦。
深入理解:为什么要牺牲代码复用?
让我用具体例子说明这个观点。
传统方式:追求代码复用
场景:统一错误处理
// include/common/error.h
typedef struct {
int code;
char message[256];
void* context; // 通用的上下文
int context_size;
} Error;
void error_handle(Error* err); // 超级通用的错误处理传统优势:
- ✅ 只写一次错误处理逻辑
- ✅ 统一的错误格式
- ✅ 易于维护
传统成本:
- 需要设计"万能"的Error结构
- 需要考虑所有使用场景
- 人类工程师能理解这个复杂抽象
AI时代的问题
让AI写使用Error的代码时:
AI的思维过程:
1. 需要处理错误
2. 用Error结构
3. Error有哪些字段?code、message、context、context_size...
4. context是什么类型?void*?
5. 需要设置context_size吗?多大?
6. 有初始化函数吗?
7. 错误码是多少?
8. 要检查NULL吗?
...(AI注意力被分散到10个细节上)AI实际生成(很可能出错):
void network_send(Network* self, const char* data) {
if (send_failed) {
Error err; // ❌ 忘记初始化
err.code = 1000;
strcpy(err.message, "send failed"); // ❌ 忘记检查长度
// ❌ 忘记设置context
error_handle(&err); // ❌ 可能段错误
}
}问题:AI注意力被分散到Error结构的细节上,核心逻辑反而出错。
AI方式:牺牲代码复用
// 不设计通用的Error结构
// src/network/impl.c
void network_send(Network* self, const char* data) {
if (send_failed) {
// 简单直接,网络模块自己的错误处理
fprintf(stderr, "[network] send failed: %s\n", strerror(errno));
if (self->error_callback) {
self->error_callback("send failed");
}
}
}
// src/storage/impl.c
void storage_write(Storage* self, const char* data) {
if (write_failed) {
// 存储模块自己的错误处理(完全不同)
fprintf(stderr, "[storage] write failed: %s\n", strerror(errno));
if (self->retry_count < 3) {
self->retry_count++;
storage_write(self, data); // 自己的重试逻辑
}
}
}AI生成这个:
AI的思维过程:
1. 需要处理错误
2. fprintf打印
3. 调用回调(如果有)
4. 完成
AI只需要关注3个细节,专注度高!成本对比
传统方式(追求复用):
设计通用Error:4小时
AI出错率:30%
人类review:每个module 30分钟
修复时间:20分钟 × 3轮 = 60分钟
10个module总成本:4 + 15 = 19小时AI方式(牺牲复用):
设计通用Error:0小时
AI出错率:5%
人类review:每个module 5分钟
修复时间:5分钟 × 1轮 = 5分钟
10个module总成本:0 + 1.6 = 1.6小时
代码重复:每个module都写fprintf(但没关系!)节省时间:17小时!
NLCC架构设计:依赖倒置 + 零循环依赖
设计目标
- 零循环依赖:AI不需要理解复杂的依赖关系
- 清晰的边界:每个模块的职责单一明确
- 可组合性:像搭积木一样组装复杂系统
- 可测试性:每个模块可以独立测试
目录结构设计
project/
├── AGENTS.md # 顶层意图描述(main函数)
├── modules/
│ ├── logger/
│ │ ├── AGENTS.md # 模块意图
│ │ └── skills/ # 按需加载的细节
│ │ ├── output.md
│ │ ├── thread_safety.md
│ │ └── json_format.md
│ ├── network/
│ │ ├── AGENTS.md
│ │ └── skills/
│ │ ├── http.md
│ │ └── ssl.md
│ └── storage/
│ ├── AGENTS.md
│ └── skills/
└── build/ # AI生成的一次性代码
├── include/ # 公共接口层(零依赖区)
│ ├── common_types.h # POD数据结构
│ ├── logger.h
│ ├── network.h
│ └── storage.h
├── src/ # 实现层
│ ├── logger/
│ │ ├── impl.c
│ │ └── internal.h
│ ├── network/
│ └── storage/
└── api.h # 唯一对外头文件依赖关系图
┌─────────────────┐
│ AGENTS.md │ ← 顶层意图
│ (项目main函数) │
└────────┬─────────┘
↓ 指向
┌────────┴─────────┐
│ modules/*/AGENTS │ ← 模块意图
│ - 清晰边界 │
│ - 职责单一 │
└────────┬─────────┘
↓ 指向
┌────────┴─────────┐
│ modules/*/skills │ ← 按需细节
└────────┬─────────┘
↓ NLCC编译
┌────────┴─────────┐
│ build/include │ ← 代码接口层
│ build/src │ ← 代码实现层
└──────────────────┘关键约束:
- ✅
modules/*/AGENTS.md绝对禁止互相引用 - ✅
skills/*.md只能被对应的AGENTS.md引用 - ✅
build/下的代码自动生成,手工修改会被覆盖
核心原则:自然语言是源码
提示词架构
NLCC采用AI原生提示词架构,让自然语言(Markdown文档)成为源码,代码成为编译后的中间产物。
1. AGENTS.md:MD文档的main函数
核心原则:每个AGENTS.md是唯一入口,描述"做什么",不描述"怎么做"。
项目级AGENTS.md
# AGENTS.md - 项目全局视图
## 项目背景
这是一个高性能网络服务器,支持并发连接和事件驱动架构。
## 核心功能
- 网络连接管理(TCP/SSL)
- 异步事件处理
- 日志记录
- 数据持久化
## 主要模块
- [logger/](modules/logger/AGENTS.md):日志记录模块
- [network/](modules/network/AGENTS.md):网络通信模块
- [storage/](modules/storage/AGENTS.md):数据存储模块
## 对外接口
应用通过`api.h`提供统一的接口:void app_start();
void app_stop();
## 设计原则
- 零循环依赖
- 编译期隔离
- AI注意力优先模块级AGENTS.md
# Logger Module - AGENTS.md
## 功能定位
记录应用日志到文件和控制台,支持多种日志级别和输出格式。
## 核心接口void logger_log(Logger logger, const char message);
void logger_set_level(Logger* logger, LogLevel level);
void logger_set_output(Logger* logger, OutputTarget target);
## 主要职责
1. 接收日志消息
2. 格式化输出(文本/JSON)
3. 写入目标(文件/控制台)
4. 线程安全保证
## 依赖
- 依赖:无(零依赖模块)
- 被依赖:network、storage
## 细节文档
- [输出策略](skills/output.md):如何支持多种输出格式
- [线程安全](skills/thread_safety.md):如何保证并发安全
- [性能优化](skills/performance.md):如何优化日志性能
## 注意事项
- 不要在这个模块中添加网络功能
- 不要阻塞主线程2. Skills目录:按需加载的细节
核心原则:skills只描述意图和需求,不描述具体实现。
示例:logger/skills/json_format.md
# JSON格式日志输出
## 需求描述
日志需要支持JSON格式输出,便于日志收集和分析系统(如ELK)解析。
## 输出格式{
"timestamp": "2026-02-15T10:30:00Z",
"level": "ERROR",
"module": "network",
"message": "Connection failed",
"context": {
"peer": "192.168.1.100:8080",
"error_code": 110}
}
## 使用场景
- 生产环境日志收集
- 自动化日志分析
- 结构化查询
## 性能要求
- JSON序列化时间 < 100μs
- 不能阻塞主线程
## 不需要关心
- 具体用哪个JSON库(AI自己选择)
- 如何优化性能(AI自己考虑)关键点:
- ✅ 描述"做什么":支持JSON格式
- ✅ 描述"为什么要做":便于日志分析
- ✅ 描述"使用场景":生产环境
- ❌ 不描述"怎么做":不用告诉AI用哪个库
3. 按需加载策略
核心思想:不是一次性加载所有MD,而是根据AI的注意力范围动态加载。
场景1:AI生成logger模块代码
步骤1:读取 logger/AGENTS.md
→ AI理解模块的整体意图
步骤2:AGENTS.md引用了 skills/output.md
→ AI读取这个文件,了解输出需求
步骤3:AI开始生成代码
→ 只关注当前模块,不受其他模块干扰场景2:AI需要理解logger如何被network使用
步骤1:读取 logger/AGENTS.md
→ 了解logger提供的接口
步骤2:读取 network/AGENTS.md
→ 发现network依赖logger
步骤3:按需加载 skills/json_format.md
→ 了解network需要JSON格式的日志NLCC工作流程
编译流程
# 1. 人类修改意图
vim modules/logger/skills/json_format.md
# 2. 触发"编译"
nlcc build
# 3. NLCC自动执行
Reading modules/logger/AGENTS.md...
→ Loading skills/output.md...
→ Loading skills/json_format.md...
Generating code...
→ build/src/logger/impl.c
→ build/include/logger.h
Compiling...
→ gcc -c build/src/logger/impl.c -o build/obj/logger.o
Testing...
→ All tests passed
Build complete!
# 4. 查看生成的代码(可选,就像今天看汇编)
cat build/src/logger/impl.c
# 5. 运行
./build/bin/app与传统编译器的对比
| 传统编译器(GCC) | 自然语言编译器(NLCC) |
|---|---|
| 输入:C代码 | 输入:自然语言(AGENTS.md) |
| 输出:机器码/汇编 | 输出:C代码 |
| 错误:语法错误、链接错误 | 错误:意图模糊、逻辑矛盾 |
| 优化:寄存器分配、指令调度 | 优化:模块拆分、依赖管理 |
| 调试:看汇编代码(-S) | 调试:看生成的C代码 |
| 工具链:预处理 → 编译 → 汇编 → 链接 | 工具链:意图分析 → 代码生成 → 传统编译 |
目标用户:会写代码的程序员
NLCC的目标用户是会写代码的程序员,而不是完全不懂编程的人。
小张 - 后端开发工程师
- 熟练掌握 Python/Java
- 经常写重复性代码(CRUD、API接口)
- 想提高效率:"我只想描述需求,不想手写每一行代码"
痛点:
- "写 CRUD 很枯燥,但又不能不做"
- "有时候想法很简单,实现起来要写很多代码"
- "想快速验证想法,不想浪费时间在样板代码上"
小李 - 全栈开发者
- 前后端都能写
- 经常需要快速开发原型
- 想要自然语言生成完整功能
痛点:
- "项目初期,只是想快速验证功能"
- "手写前端+后端很累"
- "如果能说清楚需求就自动生成该多好"
王工 - 算法工程师
- 擅长算法和数据处理
- 不想花时间在 Web 开发上
- 需要快速搭建演示系统
痛点:
- "我的算法很复杂,但界面很简单"
- "不想学 React/Vue,只想展示结果"
- "只要能跑起来,怎么实现都行"
共同点:
- ✅ 会编程,懂技术
- ✅ 想提高开发效率
- ✅ 能准确描述需求
- ✅ 能读懂生成的代码,可以二次修改
- ❌ 不想重复写样板代码
- ❌ 不想被框架和工具链束缚
核心设计原则
1. 代码是cheap的,AI注意力是昂贵的
不惜增加代码量来降低每个模块的复杂度。用代码重复换取逻辑简单。
2. 零循环依赖
modules/*/AGENTS.md绝对禁止互相引用- 每个模块职责单一明确
- AI不需要理解复杂的依赖关系
3. 自然语言描述意图,代码描述实现
- AGENTS.md 描述"做什么"
- skills/*.md 描述需求和约束
- 生成的代码 是"怎么做"的具体实现
4. 按需加载,降低上下文消耗
- 不是一次性加载所有MD
- 根据当前任务动态加载相关模块
- 降低AI的上下文窗口压力
5. MD是源码,代码是中间产物
- 人类维护
modules/下的MD文档 build/下的代码是自动生成的,可随时重新编译- 就像今天维护C代码,不关心生成的汇编
与传统编程的对比
传统编程
学习编程 → 学习语言 → 设计架构 → 编写代码 → 调试 → 部署
↓ 6个月 ↓ 3个月 ↓ 2周 ↓ 2周 ↓ 1周 ↓ 1天
总时间:约1年才能做出像样的软件NLCC方式
设计架构 → 编写AGENTS.md → NLCC编译 → 运行
↓ 1周 ↓ 2天 ↓ 10分钟 ↓ 0秒
总时间:约2周就能用上技术演化的视角
历史规律:抽象层不断上升
┌─────────────┬──────────────┬─────────────┬──────────────┐
│ 时代 │ 程序员关注 │ 工具 │ 输出物 │
├─────────────┼──────────────┼─────────────┼──────────────┤
│ 机器码时代 │ 0101序列 │ 手工拨开关 │ 机器码 │
│ 1940s │ │ │ │
├─────────────┼──────────────┼─────────────┼──────────────┤
│ 汇编时代 │ 助记符 │ 汇编器 │ 机器码 │
│ 1950s │ (MOV, ADD) │ │ │
├─────────────┼──────────────┼─────────────┼──────────────┤
│ 高级语言 │ 算法逻辑 │ 编译器 │ 汇编/机器码 │
│ 1970s+ │ (for, if) │ │ │
├─────────────┼──────────────┼─────────────┼──────────────┤
│ AI时代 │ 业务意图 │ 自然语言 │ 代码 │
│ 2020s+ │ (自然语言) │ 编译器 │ │
└─────────────┴──────────────┴─────────────┴──────────────┘类比:代码 = 未来的汇编
今天我们如何看待汇编?
场景1:应用开发
工程师:我要写一个Web服务器
选择:Go、Python、Java
绝不会考虑:手写汇编
场景2:系统编程
工程师:我要写Linux内核
选择:C语言,偶尔内联汇编
不会全用:纯汇编
场景3:极致优化
工程师:这个加密循环性能不够
考虑:手写汇编优化这一小段明天我们如何看待代码?
场景1:应用开发
工程师:我要写一个Web服务器
做法:写AGENTS.md描述意图
工具:自然语言编译器(NLCC)
生成:Go/Python代码(自动)
绝不会考虑:手写Go代码
场景2:系统编程
工程师:我要写操作系统内核
做法:写关键模块的AGENTS.md
生成:C代码(自动)
偶尔手工优化:极少数性能关键路径
场景3:极致优化
工程师:AI生成的这段代码性能不够
考虑:手工优化这一小段结论:
- 汇编依然存在,但适用范围极小(1%)
- 代码依然存在,但适用范围会大大降低(10% → 1%)
- NLCC不会淘汰代码,就像高级语言不会淘汰汇编
人类工程师的职责演化
现在(高级语言时代)
人类工程师的日常:
1. 设计系统架构(模块划分)
2. 定义接口(.h文件)
3. 实现逻辑(.c文件)
4. 调试、测试、优化
5. 维护代码(修复bug、添加功能)
6. Code Review时间分配:
- 架构设计:10%
- 写代码:60%
- 调试测试:20%
- 维护:10%
未来(NLCC时代)
人类工程师的日常:
1. 设计系统架构(模块划分)
2. 编写意图描述(AGENTS.md)
3. 审查AI生成的代码(偶尔,5%时间)
4. 处理AI搞不定的极端场景(1%时间)
5. 监控系统运行状态时间分配:
- 架构设计:20%
- 编写MD:30%
- 审查代码:5%
- 极端场景:1%
- 监控、优化、学习:44%
核心转变:
- 从"如何实现" → "要实现什么"
- 从"细节控制" → "意图表达"
- 从"代码维护" → "意图维护"
愿景
让程序员更高效地创造软件
NLCC 的使命是提升开发效率,让程序员能够:
- ✅ 用自然语言描述需求,不再手写每一行样板代码
- ✅ 快速验证想法,从需求到可运行代码只需几分钟
- ✅ 保持完全掌控,生成的代码清晰可读,可以二次修改
- ✅ 专注核心逻辑,把时间花在真正重要的地方
不是低代码平台,不是玩具工具,而是专业级编译器
NLCC 将自然语言视为一种编程语言,通过编译器技术将其转换为可执行代码。生成的代码与手写代码无异,性能、可维护性、可扩展性完全一致。
实践建议
如何开始
1. 从小项目开始
- 不要在大型遗留项目上尝试
- 选择新项目、工具项目、实验性项目
2. 设计MD架构
- 划分模块:哪些功能是独立的?
- 编写项目级AGENTS.md
- 编写模块级AGENTS.md
- 编写skills/*.md
3. 验证提示词质量
- 给另一个AI看你的AGENTS.md
- 它能理解这个模块做什么吗?
- 它知道如何使用这个模块吗?
4. 维护MD,而不是代码
- 需要修改功能时,编辑AGENTS.md
- 运行 nlcc build 重新生成
- 不要直接编辑 build/ 下的代码
结语
NLCC 是专业级自然语言编译器
让小张能快速生成 CRUD 接口
让小李能高效搭建前后端原型
让王工能专注算法而非界面
核心理念:
- 自然语言是合法的编程语言
- 编译器负责从需求到代码的转换
- 程序员保持对生成的完全掌控
用自然语言写代码,像说话一样自然
这是继《AI时代的代码架构》、《AI原生提示词架构设计》后的实践思考。NLCC不是愿景,而是正在实现的系统。核心观点是:在AI时代,自然语言将成为新的源码,代码将成为中间产物。 这不是科幻,而是编程语言演化的必然趋势。
本文由作者 twg2020 创作,使用 AI 辅助润色
首发于:somingbai.com
时间:2026-02-19