1. 项目概述:一个不“胡说八道”的AI后端生成器

最近在做一个新项目,需要快速搭建一个用户管理模块的后端。和往常一样,我打开了某个流行的AI代码生成工具,输入了需求:“生成一个基于Node.js和Express的用户注册、登录和JWT验证的REST API。” 几秒钟后,代码生成了,乍一看挺像那么回事。但当我仔细审查时,问题来了:它生成的JWT签名密钥是一个硬编码的简单字符串“secret”,用户密码居然以明文形式存储在数据库的返回结果里,路由中间件的顺序也有问题,认证逻辑放在了一些公开路由之后。这让我不得不停下来,花大量时间去调试、修正这些AI“臆想”出来的错误和安全隐患。

我相信很多开发者都有过类似的经历。AI代码生成工具在带来效率革命的同时,其固有的“幻觉”问题也成了我们信任和采纳它的最大障碍。它可能会生成语法正确但逻辑荒谬的代码,引用不存在的库,或者像我的例子一样,忽视最基本的安全最佳实践。这种不确定性,使得我们无法放心地将生成的代码直接用于生产环境,所谓的“效率提升”最后往往变成了“漏洞排查”的额外负担。

正是被这个问题反复折磨,我决定自己动手,构建一个“不胡说八道”的AI后端生成器,我把它命名为 Archon Specs 。Archon意为“执政官”,寓意着规则与秩序;Specs即“规格说明书”。这个项目的核心目标不是生成最“聪明”或最“创新”的代码,而是生成最“正确”、最“可靠”、最符合行业既定规范和项目特定约束的代码。它更像一个严格遵循蓝图和建筑规范的工程师,而非一个天马行空的艺术家。接下来,我就详细拆解一下我是如何实现这个目标的,以及在这个过程中积累的一些关键思考。

2. 核心设计思路:用“规则引擎”约束“生成模型”

要让AI不“幻觉”,最直接的想法就是给它戴上“紧箍咒”。传统的代码生成模型(如基于大型语言模型的工具)主要依赖其在海量代码数据上学到的概率分布来生成下一个token。这种方式创造力强,但可控性差,容易偏离具体项目的上下文和技术栈要求。Archon Specs的设计哲学截然不同,它的核心是一个 “规则优先,生成为辅” 的双层架构。

2.1 架构总览:规范与生成的协同

整个系统可以看作是一个精密的流水线,如下图所示(概念示意):

用户需求输入
        ↓
[规范解析与增强层]
        ↓
[结构化约束生成器] → [规则与知识库]
        ↓
[代码生成模型 (LLM)]
        ↓
[静态分析与验证层]
        ↓
可部署的、规范的代码输出

第一层:规范解析与增强层。 这是入口。用户输入的不再是简单的自然语言描述,而是一份结构化的“规格说明书”(Spec)。这份说明书可以通过YAML、JSON或一个引导式UI来生成。它必须明确包含:

  • 技术栈 :Node.js + Express + PostgreSQL, 还是 Python + FastAPI + MongoDB?版本号是多少?
  • 数据模型 :每个实体(如User, Product)的字段名、类型、是否必填、默认值、唯一性约束、关联关系。
  • API端点 :每个端点的路径、HTTP方法、需要的路径/查询/请求体参数、预期的响应格式、需要的身份验证与授权级别。
  • 业务规则 :密码强度要求、邮件发送逻辑、状态流转条件等。
  • 项目规范 :代码风格(ESLint/Prettier配置)、目录结构、命名约定(是 camelCase 还是 snake_case )。

这个层级的目的是将模糊的需求转化为机器和AI都能无歧义理解的精确指令。如果用户输入不够详细,系统会通过预设的问题进行交互式补全,确保没有模糊地带。

第二层:结构化约束生成器。 这是系统的“大脑”。它接收解析后的规范,并结合内置的 “规则与知识库” ,生成一份极度详细的、带有强制约束的“生成任务描述”。这个知识库是我投入大量精力构建的,包括:

  • 安全规则 :密码必须加盐哈希存储(例如使用bcrypt);JWT密钥必须足够长且建议从环境变量读取;SQL查询必须使用参数化查询或ORM以防止注入;CORS、Helmet等安全中间件必须配置。
  • 性能规则 :数据库连接需要池化;分页查询必须实现;N+1查询问题必须避免。
  • 最佳实践 :错误处理中间件必须放在所有路由之后;环境配置必须与代码分离;日志记录需要结构化。
  • 框架特定约定 :例如,在Express中,路由定义的最佳顺序是什么;在Prisma中,如何正确定义模型关系。

这个生成器输出的不是代码,而是一份给下层AI模型的“不容置疑的指令清单”。例如:“在 /api/auth/register 的POST处理器中,你必须:1. 从请求体中验证 email password 字段。2. 使用 bcrypt.hash 对密码进行哈希,成本因子为10。3. 使用Prisma Client将用户数据存入 User 表,确保 email 字段有唯一约束。4. 绝对不能在响应中返回密码哈希值。5. 返回201状态码和创建的用户ID及邮箱。”

第三层:代码生成模型。 这里才会调用大语言模型(如GPT-4、Claude 3或专门微调的代码模型)。但关键区别在于,我们提供给模型的提示词(Prompt)是经过前两层精心构造的。这个Prompt包含了:

  1. 严格的角色定义 :你是一个资深后端工程师,严格遵守所有给定的规范和约束。
  2. 清晰的上下文 :项目技术栈、目录结构、已存在的相关文件(用于生成导入语句和避免冲突)。
  3. 具体的任务 :即第二层生成的“指令清单”。
  4. 输出格式要求 :必须只输出要求的代码块,不能有任何解释性文字。

通过这种方式,我们将AI的创造力引导到一个高度受限的解空间内,极大地降低了它“自由发挥”并产生幻觉的可能性。

第四层:静态分析与验证层。 这是安全网。即使在前三层严格控制下,生成的代码仍会经过自动化检查:

  • 语法检查 :使用语言本身的编译器或解释器(如 node -c )检查语法错误。
  • 规则匹配检查 :使用简单的AST(抽象语法树)分析器或正则表达式,扫描生成的代码,确保没有出现“硬编码密码”、“ console.log 敏感信息”、“未处理的Promise”等明确禁止的模式。
  • 依赖检查 :确保 package.json 中声明的依赖版本与规范一致,并且没有引入不必要或存在已知安全漏洞的包。

只有通过所有检查的代码才会被最终输出。任何一步失败,都会将错误信息和上下文反馈给第二层,尝试调整指令重新生成,或直接向用户报告无法满足的约束。

2.2 为什么选择“约束生成”而非“微调模型”?

一个常见的思路是:为什么不直接微调一个代码大模型,让它学习“正确”的代码模式?我确实考虑过,但最终放弃了,原因如下:

  1. 成本与敏捷性 :收集和清洗一个覆盖所有技术栈、所有最佳实践的高质量代码数据集成本极高。而业务规则、项目规范是千变万化的,每次更新都需要重新微调,不现实。
  2. 规则的可解释性 :基于规则的系统,每一条约束都是清晰、可管理、可追溯的。我可以很容易地添加一条新规则:“所有REST API响应必须包裹在 { data: ..., message: ... } 的结构中”。而在微调模型中,这条规则需要多少训练样本才能被稳定学习?无从得知。
  3. 混合策略的优越性 :当前架构结合了规则引擎的确定性与LLM的灵活性。规则负责“必须怎么做”,LLM负责“如何优雅地实现”。当遇到规则库未覆盖的复杂业务逻辑时,LLM依然能在给定的框架内发挥其理解能力,而基础的安全性、结构性规则已经被牢牢锁死。

注意 :这个架构的核心是“规则库”的质量和完备性。它不是一个一蹴而就的东西,而需要随着项目生成、社区反馈和最佳实践的发展而持续迭代。我的做法是,每次手动修复AI生成代码的bug时,都会思考:“这个错误能否被总结成一条规则,加入到Archon Specs的知识库中?” 这使它具备了自我进化的能力。

3. 核心组件深度解析:规则库与提示词工程

要让Archon Specs真正可靠,两个核心组件的设计至关重要:一是静态的 规则与知识库 ,二是动态的 提示词构造策略 。它们共同决定了生成代码的“底线”和“上限”。

3.1 构建可扩展的规则与知识库

我的规则库不是一堆散乱的 if-else 语句,而是一个结构化的、可插拔的系统。它主要分为几个维度:

3.1.1 安全规则集 这是最高优先级的规则。每条规则都包含“模式”、“严重级别”、“修复建议”和“适用技术栈”。

- rule_id: SEC-001
  name: 硬编码密钥检测
  pattern: (['\"])(?:[A-Za-z0-9+/=]{20,}|secret|password|key)(['\"])
  description: 检测代码中是否存在看似硬编码的密钥、密码或令牌。
  severity: CRITICAL
  action: REJECT # 直接拒绝生成,或强制替换为环境变量读取语句
  replacement_template: `process.env.{{ENV_VAR_NAME}}`
  tech_stack: ["nodejs", "python", "go"]

- rule_id: SEC-002
  name: SQL注入风险检测
  pattern: (?:query|execute|run)\s*\(\s*[`'\"].*?\$\{.*?\}.*?[`'\"] # 简化示例,实际更复杂
  description: 检测字符串拼接形式的SQL查询。
  severity: HIGH
  action: SUGGEST_REWRITE
  suggestion: "使用参数化查询或ORM的安全方法。例如,在Prisma中使用 `prisma.user.findUnique({ where: { email: inputEmail } })`"

3.1.2 框架约定规则集 这部分确保生成的代码符合特定框架的“味道”。例如,对于Express.js:

  • 规则:路由定义应集中在一个 /routes 目录下,并通过 app.use 挂载。
  • 规则:错误处理中间件必须定义在所有路由之后,签名应为 (err, req, res, next)
  • 规则:控制器逻辑应尽量精简,复杂业务应抽取到 /services 层。

对于数据库ORM(如Prisma):

  • 规则:模型定义中,关系字段需使用 @relation 装饰器明确标注。
  • 规则:查询时应默认排除敏感字段(如 passwordHash ),除非显式指定。

3.1.3 项目模板与脚手架规则 这是可定制的部分。Archon Specs允许用户定义或选择“项目模板”。一个“企业级REST API模板”可能强制要求:

  • 目录结构必须包含 src/controllers , src/services , src/middlewares , src/utils
  • 必须使用Winston或Pino进行结构化日志记录。
  • 必须包含统一的响应封装器 ApiResponse 和错误类 AppError
  • 必须配置Dockerfile和 docker-compose.yml 用于本地开发。

当用户选择这个模板后,所有生成的代码都会自动遵循这些结构性和风格性约定。

3.2 动态提示词构造:从规范到精确指令

这是将死板的规则“注入”AI思维的关键环节。我的提示词是一个多部分的模板:

你是一个经验丰富的{{TECH_STACK}}后端开发专家,正在严格遵循“{{PROJECT_NAME}}”项目的开发规范。

**项目上下文:**
- 项目根目录:`{{PROJECT_ROOT}}`
- 现有主要文件结构:[列出相关文件,如现有的模型定义、工具类等]
- 代码风格:{{CODE_STYLE_CONFIG}}

**你的任务:**
根据以下精确要求,生成{{FILE_PATH}}文件的代码。你必须严格遵守所有约束。

**技术要求与约束(必须遵守):**
1.  **安全要求:**
    - 所有密码必须使用bcrypt库进行哈希处理,成本因子为10。
    - 任何密钥、数据库连接字符串必须从环境变量`process.env`读取,严禁硬编码。
    - 所有用户输入在用于数据库操作前必须经过验证。
2.  **框架要求:**
    - 使用Express.js。路由定义在`/routes`目录,控制器逻辑在`/controllers`目录。
    - 使用Prisma作为ORM。数据库模型定义见`schema.prisma`文件。
    - 使用JWT进行身份验证,令牌在HTTP Only Cookie中发送。
3.  **具体实现要求:**
    - 在`src/routes/auth.js`中,创建一个新的路由`/register` (POST)。
    - 该路由的控制器函数应放在`src/controllers/authController.js`中,命名为`register`。
    - 控制器逻辑必须:
        a. 从请求体接收`email`和`password`。
        b. 验证邮箱格式和密码长度(>=8位)。
        c. 检查邮箱是否已存在。
        d. 使用`bcrypt.hash`哈希密码。
        e. 使用Prisma Client将用户记录创建到数据库。
        f. 生成一个JWT令牌,包含用户ID。
        g. 将令牌设置在HTTP Only Cookie中。
        h. 返回201状态码和用户基本信息(不含密码)。
    - 必须包含完整的错误处理,使用项目约定的`AppError`类和错误处理中间件。

**请只输出要求的代码,不要有任何额外的解释、注释或Markdown格式。从文件的第一行开始输出。**

这个提示词的特点在于:

  • 角色清晰 :将AI定位为“遵循规范的专家”,而非“自由创作者”。
  • 上下文具体 :提供了文件路径、现有结构,避免了生成孤立、无法融入现有项目的代码。
  • 约束显式且优先 :将“必须遵守”的条款放在最前面,并用数字列表强调,强化其重要性。
  • 任务极度具体 :不是“生成注册功能”,而是“在A文件创建B路由,调用C控制器,控制器必须完成步骤a-h”。
  • 输出格式严格 :要求“只输出代码”,避免了模型附加不必要的自然语言描述,便于后续的自动化处理。

通过这种精细化的提示词工程,我能够将规则的遵守率从传统方式的“可能”提升到“几乎必然”。

4. 实战演练:从Spec到可运行后端的全流程

让我们通过一个具体的例子,看看Archon Specs是如何工作的。假设我们要为一个简单的博客系统生成后端。

4.1 第一步:编写Archon Spec(规格说明书)

用户可以通过YAML文件或Web界面定义Spec。以下是一个简化的YAML示例:

project:
  name: "simple-blog-api"
  tech_stack:
    runtime: "nodejs@18"
    framework: "express"
    orm: "prisma"
    database: "postgresql"
    auth: "jwt"
  structure_template: "standard-express-api"

models:
  - name: "User"
    fields:
      - name: "id"
        type: "Int"
        attributes: ["@id", "@default(autoincrement())"]
      - name: "email"
        type: "String"
        attributes: ["@unique"]
      - name: "name"
        type: "String"
      - name: "passwordHash"
        type: "String"
        excludeFromDefaultOutput: true # 关键规则:默认查询不返回此字段
  - name: "Post"
    fields:
      - name: "id"
        type: "Int"
        attributes: ["@id", "@default(autoincrement())"]
      - name: "title"
        type: "String"
      - name: "content"
        type: "String?"
      - name: "published"
        type: "Boolean"
        default: false
      - name: "authorId"
        type: "Int"
      - name: "author"
        type: "User"
        relation: "authorId -> User.id"

apis:
  - endpoint: "/api/auth/register"
    method: "POST"
    auth_required: false
    request_body:
      - field: "email"
        type: "string"
        required: true
        validation: "email"
      - field: "password"
        type: "string"
        required: true
        validation: "minLength:8"
    response:
      status: 201
      body:
        success: true
        data:
          id: "number"
          email: "string"
    controller: "authController.register"
  - endpoint: "/api/posts"
    method: "GET"
    auth_required: false
    query_params:
      - field: "page"
        type: "number"
        default: 1
      - field: "limit"
        type: "number"
        default: 10
    response:
      status: 200
      body:
        success: true
        data: "Post[]"
        pagination:
          page: "number"
          limit: "number"
          total: "number"
    controller: "postController.getPublishedPosts"

rules:
  security:
    - "password-hashing:bcrypt"
    - "jwt-in-http-only-cookie"
    - "sql-parameterization:prisma" # 使用Prisma本身已免疫注入
  validation:
    - "request-validation:joi" # 指定使用Joi进行请求验证

4.2 第二步:系统处理与代码生成

  1. 解析与增强 :系统读取YAML,检查完整性。发现 Post 模型关联了 User ,但 /api/posts 的响应里可能需要作者信息。系统会提示用户:“在 getPublishedPosts 的响应中,是否需要包含作者的基本信息(如name)?” 用户确认后,这条信息被补充到规范中。
  2. 约束生成 :规则引擎启动。
    • 根据 tech_stack ,选择 nodejs-express-prisma 规则集。
    • 根据 security 规则,生成指令:“所有涉及 User 创建的密码字段,必须使用 bcrypt.hash 处理,成本为10。JWT令牌必须通过 res.cookie(‘token’, token, { httpOnly: true, secure: process.env.NODE_ENV === ‘production’ }) 设置。”
    • 根据 structure_template ,确定目录结构为 src/controllers , src/routes , src/middlewares , src/utils , prisma/
    • 根据 validation 规则,决定在控制器前使用Joi中间件进行验证。
  3. 调用LLM生成代码 :针对每个API端点,构造类似上一节的详细提示词,调用模型生成 authController.js , postController.js , authRoutes.js , postRoutes.js 等文件。同时,还会生成核心的 prisma/schema.prisma 模型文件、 src/middlewares/authMiddleware.js src/utils/AppError.js src/utils/catchAsync.js (用于包装异步控制器)等基础构件。
  4. 静态分析与输出 :生成的所有代码文件会经过ESLint(根据项目规范配置)、一个简单的安全规则扫描器(检查是否有 console.log(password) 之类的明显问题)和Prisma格式检查。全部通过后,系统会输出完整的项目文件夹,并附带一个 README.md ,说明如何安装依赖、设置环境变量和运行项目。

4.3 第三步:生成代码示例

以下是系统可能生成的 src/controllers/authController.js 的部分代码。请注意其如何体现规则约束:

const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const { PrismaClient } = require('@prisma/client');
const { AppError } = require('../utils/AppError');
const catchAsync = require('../utils/catchAsync');

const prisma = new PrismaClient();

const register = catchAsync(async (req, res, next) => {
  // 1. 数据验证(由Joi中间件已完成,此处直接使用)
  const { email, password } = req.body;

  // 2. 检查邮箱是否唯一(规则:业务逻辑检查)
  const existingUser = await prisma.user.findUnique({
    where: { email },
    select: { id: true } // 规则:只查询必要字段
  });

  if (existingUser) {
    return next(new AppError('Email already in use', 409)); // 规则:使用统一错误类
  }

  // 3. 哈希密码(规则:SEC-001, 使用bcrypt)
  const passwordHash = await bcrypt.hash(password, 10);

  // 4. 创建用户(规则:使用Prisma,自动参数化)
  const newUser = await prisma.user.create({
    data: {
      email,
      name: req.body.name || null, // 处理可选字段
      passwordHash, // 规则:存储哈希值,而非明文
    },
    select: { // 规则:明确指定返回字段,排除passwordHash
      id: true,
      email: true,
      name: true,
      createdAt: true,
    },
  });

  // 5. 生成JWT令牌(规则:密钥从环境变量读取)
  const token = jwt.sign(
    { userId: newUser.id },
    process.env.JWT_SECRET,
    { expiresIn: process.env.JWT_EXPIRES_IN }
  );

  // 6. 设置Cookie(规则:JWT in HTTP Only Cookie)
  res.cookie('token', token, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    maxAge: 24 * 60 * 60 * 1000, // 1 day
  });

  // 7. 发送响应(规则:统一响应格式)
  res.status(201).json({
    success: true,
    data: newUser,
  });
});

module.exports = { register };

可以看到,生成的代码严格遵循了Spec和规则库中的所有要求:密码哈希、环境变量、Prisma安全查询、字段排除、统一错误处理、JWT Cookie设置和响应格式。几乎没有给“幻觉”留下任何空间。

5. 优势、局限与未来思考

经过一段时间的开发和内部试用,Archon Specs展现出了明显的优势,但也暴露出一些局限性。

5.1 核心优势

  1. 极高的可靠性 :这是最大的卖点。生成的代码可以直接运行,并且符合安全与最佳实践,大大减少了审查和调试时间。对于构建标准化的CRUD API、管理后台接口等场景,效率提升是数量级的。
  2. 强一致性与可维护性 :无论项目由谁发起,或在不同时间生成不同模块,只要使用相同的Spec模板和规则集,代码风格、目录结构、错误处理方式都完全一致。这极大降低了团队协作成本和后续维护成本。
  3. 知识沉淀与传承 :规则库成为了团队或社区最佳实践的载体。一位架构师可以将安全规范、性能要点固化到规则中,确保所有生成的项目都自动继承这些知识,避免了新手重复踩坑。
  4. 专注业务逻辑 :开发者可以将精力从重复的、易错的样板代码(如用户认证、数据验证、错误处理)中解放出来,专注于真正创造价值的复杂业务逻辑。

5.2 当前局限性与挑战

  1. 复杂业务逻辑的乏力 :对于高度复杂、非标准的业务流(例如一个涉及多状态机、复杂事件驱动的工作流引擎),当前的规则+提示词方法可能不够用。模型可能无法深刻理解业务内涵,需要人工编写大量定制化规则,或者最终还是需要开发者手动实现核心部分。
  2. 规则库的维护负担 :构建和维护一个全面、准确、与时俱进的规则库是一项持续的工作。新的框架、新的安全漏洞、新的最佳实践都需要及时更新到规则库中。
  3. “过度约束”可能抑制创新 :在极端追求“不幻觉”的过程中,可能会把代码风格限制得过于死板,有时会排除掉一些虽然不符合旧有规则但更优雅的新解决方案。需要在“规范”和“灵活”之间找到平衡。
  4. 初始Spec编写成本 :要求用户编写结构化的Spec,本身就有一定的学习成本。虽然比直接写代码快,但对于一个非常简单的想法,用户可能会觉得“杀鸡用牛刀”。提供更智能的、交互式的自然语言到Spec的转换工具是未来的方向。

5.3 实践心得与避坑指南

在开发Archon Specs的过程中,我积累了一些关键经验:

  1. 规则的设计要“语义化”,而非“字面化” :早期我写了一条规则“禁止在代码中出现 password 字符串”。结果它错误地拦截了包含 password 的日志信息(如 ‘Invalid password provided’ )。后来改为检测“在赋值语句右侧或数据库查询中出现的、未经哈希处理的 password 变量”,准确率才大幅提升。规则要理解代码的意图。
  2. 分层验证比单次验证更有效 :不要指望一次生成就完美。我的流程是:LLM生成 -> 基础语法/规则检查 -> 如有问题,将错误信息作为上下文反馈给LLM进行修正(最多2次)-> 最终输出。这种“生成-反馈-修正”的循环比单纯提高第一次生成的要求更可靠。
  3. 提供“逃生舱口” :对于某些确实无法用现有规则描述的复杂需求,系统应该允许开发者在生成的代码中手动添加“豁免注释”,例如 // archon-ignore-next-line SEC-005 ,并记录原因。这既保证了流程的严谨,又提供了必要的灵活性。
  4. 持续从真实错误中学习 :建立一个反馈循环。当用户发现生成的代码仍有问题时,可以提交报告。这个报告应该被分析,并判断是规则缺失、提示词不明确还是LLM自身的问题,然后针对性优化系统。让系统在实际使用中越变越聪明。

构建Archon Specs的过程,让我深刻认识到,现阶段让AI完全替代人类编程是不现实的,但让它成为一个 高度自律、永不犯错的高级助手 则完全可行。它的价值不在于替代思考,而在于消除那些因粗心、遗忘或知识盲区而导致的低级错误,让我们能把宝贵的创造力集中在真正需要智慧的地方。这个项目还在持续迭代中,但它的核心理念—— 用确定性的规则约束不确定性的智能 ——我认为是当前AI辅助开发领域一条非常务实且有效的路径。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐