受控代码生成

本页指导如何安全地利用 AI Agent 或模板系统生成代码:通过允许路径约束、生成→diff→审查→测试→合并的审查流程、生成区域标注与测试策略,确保生成代码可审计、可回滚、不覆盖手工改动。

生成前声明目标文件与禁止触碰区(密钥、生成代码目录)。补丁格式统一(unified diff),大改动拆 PR。依赖新增需锁文件与许可证扫描。

自动执行仅限本地沙箱或 CI:禁止模型直接连生产。SKILL 要求输出「变更说明 + 自测命令 + 风险」三段式摘要。

代码生成安全与模板系统

把「能写哪里、能执行什么、能访问哪些网络」写成显式策略:允许路径与命令白名单优先于「尽量别乱动」的口头约定。密钥、证书、.env、生产配置应落在拒绝列表,并由 pre-commit / CI 做秘密扫描双保险。

  • 执行面:生成与测试在隔离工作目录或容器内运行;禁止将仓库 token、云凭据注入 Agent 上下文。
  • 依赖面:新增包必须可审计(锁文件、许可证、供应链来源);拒绝未钉版本的「顺手装一个」。
  • 数据面:日志与评测数据脱敏;不向模型回传含 PII 的栈或 dump。
  • 人在环:敏感模块双人审阅;生成或机器改动的文件打标(注释、OWNERS、CODEOWNERS)便于追溯。

Handlebars 模板代码生成示例(生成 REST Controller):

// templates/controller.hbs(Handlebars 模板)
// @generated by codegen v1.2.0 — DO NOT EDIT MANUALLY
// @generated-source: schemas/{{resourceName}}.yaml
// @generated-at: {{generatedAt}}

import { Router, Request, Response } from 'express';
import { {{ResourceName}}Service } from '../services/{{resourceName}}.service';
import { create{{ResourceName}}Schema } from '../schemas/{{resourceName}}.schema';

const router = Router();
const service = new {{ResourceName}}Service();

{{#each endpoints}}
router.{{method}}('{{path}}', async (req: Request, res: Response) => {
  {{#if hasBody}}
  const body = create{{../ResourceName}}Schema.parse(req.body);
  {{/if}}
  const result = await service.{{operationId}}({{params}});
  res.json(result);
});
{{/each}}

export default router;

// 生成脚本(Node.js)
const Handlebars = require("handlebars");
const fs = require("fs");
const yaml = require("js-yaml");

function generate(schemaPath, templatePath, outputPath) {
  const schema = yaml.load(fs.readFileSync(schemaPath, "utf8"));
  const template = Handlebars.compile(fs.readFileSync(templatePath, "utf8"));
  const code = template({ ...schema, generatedAt: new Date().toISOString() });
  fs.writeFileSync(outputPath, code);
  console.log(`Generated: ${outputPath}`);
}
回滚与开关:大改动用特性开关或分阶段发布;数据库迁移保持可逆或具备清晰降级路径,避免「生成一次就锁死」。

Diff 审阅与防止覆盖手工改动

统一使用 unified diff(或平台等价的逐行对比),审阅顺序建议:删改意图 → 边界条件 → 测试与类型。Agent 产出的大补丁应拆成多个小 PR,每个 PR 只服务一个可叙述的目标,便于 bisect 与回滚。

  • 先看路径与文件数:是否超出事先声明的允许范围;是否误触生成物目录或仅应机器写的文件。
  • 再看行为变化:公开 API、序列化格式、权限检查、错误码是否与文档/调用方一致。
  • 最后看噪音:无关格式化、重排 import、大面积重命名应单独 PR,避免与安全修复混在同一 diff。

生成区域标注(防止覆盖手工修改)与审查流程:

// ✅ 生成区域标注模式:用注释标记机器生成的范围

// user.service.ts
export class UserService {
  // @codegen-start — 以下代码由生成器管理,勿手动改动
  async findById(id: string) { ... }
  async findAll(filter: UserFilter) { ... }
  async create(data: CreateUserDto) { ... }
  // @codegen-end

  // ✋ 以下为手工扩展,不会被生成器覆盖
  async findByEmailWithProfile(email: string) {
    // 复杂查询,不适合自动生成
  }
}

// 生成器脚本:仅替换标记区域
const CODEGEN_START = "// @codegen-start";
const CODEGEN_END = "// @codegen-end";
function replaceGeneratedSection(fileContent, newCode) {
  const start = fileContent.indexOf(CODEGEN_START);
  const end = fileContent.indexOf(CODEGEN_END) + CODEGEN_END.length;
  if (start === -1 || end === -1) throw new Error("未找到生成标记");
  return fileContent.slice(0, start) + CODEGEN_START + "\n" +
    newCode + "\n  " + CODEGEN_END + fileContent.slice(end);
}

// 生成审查流程(CI pipeline)
// 1. codegen --dry-run --output-diff     # 只输出 diff,不写文件
// 2. git diff --exit-code                # CI 验证无意外文件改动
// 3. 人工 review diff PR
// 4. npm test / go test ./...            # 测试必须通过
// 5. 合并(squash merge 保留 @generated 标注)

门禁建议:格式化与 lint 在 CI 自动通过;人类审阅聚焦语义与风险。可辅以 golden diff、契约测试或变异测试,捕获「能编译但行为错了」的生成结果。

允许路径预览

在落地到 CI 或 Agent 配置前,可用下方工具快速核对「允许前缀 / 目录」是否覆盖预期路径。规则为前缀匹配:每行一条,使用正斜杠;以 * 结尾表示「该前缀下任意子路径」。以 # 开头的行视为注释。禁止列表优先:命中任一禁止前缀则判为拒绝。

示例:src/tests/docs/*


            

生成代码的测试策略

对生成器本身和生成产物分别测试:

// 1. 测试生成器(快照测试)
import { generate } from "./codegen";

test("生成 UserController 应匹配快照", () => {
  const code = generate("./schemas/user.yaml");
  expect(code).toMatchSnapshot();  // 快照变化时需人工 review
});

test("生成代码应包含所有 CRUD endpoints", () => {
  const code = generate("./schemas/user.yaml");
  expect(code).toContain("router.get('/users'");
  expect(code).toContain("router.post('/users'");
  expect(code).toContain("router.put('/users/:id'");
  expect(code).toContain("router.delete('/users/:id'");
});

// 2. 测试生成产物(集成测试,不修改生成文件)
// 生成的 Controller 通过 supertest 做接口测试
import request from "supertest";
import app from "./app";

describe("Generated UserController", () => {
  it("GET /users 应返回用户列表", async () => {
    const res = await request(app).get("/users");
    expect(res.status).toBe(200);
    expect(Array.isArray(res.body)).toBe(true);
  });
});
---
name: controlled-code-generation
description: 模板系统、审查流程与生成代码保护策略
---
# 本页主题
- 利用 Handlebars/Jinja2 模板系统生成样板代码
- 生成→diff→审查→测试→合并的完整流程
- 生成区域标注防覆盖手工改动
- 对生成器与生成产物分别测试

# 允许路径约束
- 生成代码只能写入声明的目录(src/generated/, __generated__/)
- 密钥、.env、生产配置在拒绝列表
- pre-commit 做 secret 扫描双保险

# 生成审查流程
1. codegen --dry-run --output-diff    # 输出 diff 不写文件
2. 人工 review diff PR(聚焦语义与风险)
3. CI: lint + typecheck + 单测必须通过
4. 合并后保留 @generated 标注与来源注释

# 防覆盖策略
- 文件头 @generated 注释(工具层检查)
- @codegen-start / @codegen-end 区域标注
- 生成器只替换标记区域,手工代码不受影响

# 测试策略
- 生成器快照测试:输出变化时需 review
- 生成产物集成测试:接口合约验证
- 变异测试:检测生成代码的逻辑正确性

# 依赖策略
- 新增依赖必须有锁文件 + 许可证扫描
- 拒绝未钉版本的「顺手装一个」

# 回滚
- 大改动用特性开关
- 数据库迁移保持可逆

返回技能库 更多技能入口