批量生成器负责把已写好的内容可靠地变成页面。它不会替你证明每条内容有独立价值。本例先写三条不同的借用规则,再让 Codex 管数据校验、输出与失败处理。
三条数据分别回答三个问题
以下为虚构“社区工具柜”教学规则,不是任何机构实际制度。保存为 pages.json:
json
[
{"slug":"borrow","title":"怎样借用工具","description":"借用前确认库存、提交日期,并在领取时核对配件。","answer":"先确认工具编号和所需日期的库存;填姓名、工具编号、领取与归还日期。管理员确认可用后,领取时双方核对配件与现状,再登记交接。未收到确认不能视为已预约。","sourceLabel":"教学制度第1条","related":["renew"]},
{"slug":"renew","title":"归还日前怎样申请续借","description":"续借需在到期前提出,并确认后续无人预约;未获同意仍按原日期归还。","answer":"在原归还日前告知管理员工具编号和拟延长日期。管理员检查后续预约;无冲突并明确同意后更新台账。已有预约或未收到确认时,原归还日期继续有效。","sourceLabel":"教学制度第2条","related":["borrow","missing-part"]},
{"slug":"missing-part","title":"归还时发现配件缺失怎么办","description":"先记录缺失配件与借用时清单,再交管理员核查;不自行猜测费用。","answer":"归还时对照领取清单,记下缺少的配件、发现时间和已查找位置。把现有工具与记录交管理员核对,保留借用时照片。赔付或补配按另行确认的制度处理,本教学规则没有给出金额。","sourceLabel":"教学制度第3条","related":["borrow"]}
]
三个问题有不同触发条件和结果,不能把一段“联系管理员”换三个标题就算三篇。sourceLabel 是透明标明的教学来源,不是假装存在的外部机构文件。
一个完整生成器,写入前检查整批
保存为 generate.mjs。只需要 Node.js,无第三方包。
js
import fs from 'node:fs';
import path from 'node:path';
const input=process.argv[2]||'pages.json';
const out=path.resolve(process.argv[3]||'preview-pages');
const pages=JSON.parse(fs.readFileSync(input,'utf8'));
if(!Array.isArray(pages)||!pages.length)throw Error('Expected nonempty array');
const seen=new Set();
for(const p of pages){
for(const k of ['slug','title','description','answer','sourceLabel']){
if(typeof p[k]!=='string'||!p[k].trim())throw Error('Missing '+k);
if(/\bTODO\b|待补充/.test(p[k]))throw Error('Placeholder in '+k);
}
if(!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(p.slug))throw Error('Invalid slug');
if(seen.has(p.slug))throw Error('Duplicate slug: '+p.slug);
seen.add(p.slug);
if(!Array.isArray(p.related))throw Error('related must be array');
}
for(const p of pages)for(const s of p.related){
if(typeof s!=='string'||!seen.has(s)||s===p.slug)throw Error('Invalid related: '+s);
}
const escape=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
const map=new Map(pages.map(p=>[p.slug,p]));
const files=pages.map(p=>{
const links=p.related.map(s=>'<li><a href="'+s+'.html">'+escape(map.get(s).title)+'</a></li>').join('');
const html='<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>'+escape(p.title)+'</title><meta name="description" content="'+escape(p.description)+'"><style>body{max-width:48rem;margin:2rem auto;padding:0 1rem;line-height:1.8;overflow-wrap:anywhere}</style></head><body><main><h1>'+escape(p.title)+'</h1><p>'+escape(p.answer)+'</p><p>来源:'+escape(p.sourceLabel)+'</p><ul>'+links+'</ul></main></body></html>';
return [p.slug+'.html',html];
});
if(fs.existsSync(out))throw Error('Output exists; choose a new directory');
fs.mkdirSync(out,{recursive:true});
for(const [name,html] of files)fs.writeFileSync(path.join(out,name),html,{flag:'wx'});
console.log(JSON.stringify({output:out,files:files.map(([name])=>name)}));
运行 node generate.mjs pages.json preview-01。预期生成 borrow.html、renew.html、missing-part.html 三页,互链目标都在这批数据中。再次使用 preview-01 会拒绝,避免覆盖人工检查结果;需要重试时换 preview-02。磁盘写入中断可能留部分文件,程序没有承诺文件系统事务,失败目录不作为完整交付。
给 Codex 的工作应包括错误输入
text
阅读 pages.json 三条正文,先说明每条独立回答什么;没有足够内容的条目提出修订,不凑数量。
运行 generate.mjs 到新预览目录,核对每页 title、description、H1、正文和关联目标。
在独立坏数据副本验证:重复 borrow slug、删除 answer、related 指向 absent,分别应在创建输出目录前报错。
另外在测试副本把标题放入 <b>测试 & 样例</b>,检查页面显示字面文本而不是执行标签。
保留实际命令和退出码。只报告这些测试覆盖的结构与转义,不宣称 SEO 或内容质量达标。
结构检查无法判断正文是否真能解决借用问题,所以仍应人工逐条读:申请条件、批准条件和缺少制度时的边界是否写清。要接到实际网站时,沿用该站 canonical、URL、公共模板和底栏;本练习不为你的正式站点新定索引策略,也不提交搜索引擎。
下一步
继续阅读与你当前问题相关的教程:


