第 7 课:代码生成模式
什么是"代码生成模式"?
这是 resume-generator 最有意思的设计模式。AI 不是"直接"生成 Word 文件,而是走了一条间接路线:
graph LR
subgraph "普通思路(做不到)"
P1["AI"] -. "直接输出" .-> P2["resume.docx"]
end
subgraph "实际做法(很聪明)"
R1["AI"] --> R2["写一段 JS 代码"] --> R3["执行这段代码"] --> R4["代码输出 resume.docx"]
end
为什么不能直接生成?因为 .docx 本质上是一个 ZIP 压缩包,里面是一堆 XML 文件。AI 没法直接"吐"出一个二进制文件。但 AI 可以写代码,而代码可以生成二进制文件。
代码生成的完整流程
graph TD
A["AI 搜集资料(WebSearch + WebFetch)"] -->|"搜集到了人物背景信息"| B["AI 读取技能文件(Skill + Read)<br>学会 docx 库的用法"]
B -->|"知道了怎么用代码生成 Word"| C["AI 写一段 JavaScript 代码(Write 工具)<br>generate_resume.js"]
C -->|"代码写好了"| D["AI 执行这段代码(Bash 工具)<br>node generate_resume.js"]
D -->|"代码运行,生成了文件"| E["resume.docx - 最终产出"]
AI 写的代码长什么样?
当你运行 resume-generator 后,打开 agent/custom_scripts/generate_resume.js,你会看到 AI 自动生成的代码。大致结构是这样的(每次运行可能不同):
const { Document, Paragraph, TextRun, Packer, HeadingLevel,
AlignmentType, LevelFormat } = require('docx');
const fs = require('fs');
// AI 把搜集到的信息硬编码进了代码
const doc = new Document({
styles: {
default: {
document: { run: { font: "Arial", size: 20 } } // 10pt
}
},
numbering: {
config: [{
reference: "bullets",
levels: [{
level: 0,
format: LevelFormat.BULLET,
text: "•",
alignment: AlignmentType.LEFT,
style: { paragraph: { indent: { left: 720, hanging: 360 } } }
}]
}]
},
sections: [{
properties: {
page: {
margin: { top: 720, right: 720, bottom: 720, left: 720 } // 0.5 inch
}
},
children: [
// 名字(24pt 居中)
new Paragraph({
alignment: AlignmentType.CENTER,
children: [
new TextRun({ text: "Elon Musk", bold: true, size: 48 }) // 24pt
]
}),
// 联系信息
new Paragraph({
alignment: AlignmentType.CENTER,
spacing: { after: 200 },
children: [
new TextRun({ text: "CEO, SpaceX & Tesla | ...", size: 20 })
]
}),
// 职业简介
new Paragraph({
children: [
new TextRun({ text: "PROFESSIONAL SUMMARY", bold: true, size: 24 })
]
}),
new Paragraph({
children: [
new TextRun({
text: "Visionary entrepreneur and engineer...",
size: 20
})
]
}),
// 工作经历(最多 3 份,每份 2-3 个要点)
// ...
// 教育背景
// ...
// 技能列表
// ...
]
}]
});
// 导出为 .docx
Packer.toBuffer(doc).then(buffer => {
fs.writeFileSync(
__dirname + '/resume.docx',
buffer
);
console.log('Resume generated successfully!');
});
注意几个关键点:
- AI 严格遵守了系统提示词的约束:0.5 inch margins(720 DXA),24pt 名字,10pt 正文
- AI 正确使用了 docx 库的 API:因为它读了 docx-js.md 这份参考手册
- AI 把搜集到的真实信息编码进了代码:工作经历、教育背景等都是搜出来的
- AI 遵守了 "不用 \n 换行" 的规则:每一行都是单独的 Paragraph
为什么要用"AI 写代码"这个模式?
原因一:AI 不能直接生成二进制文件
Word 文档是二进制格式,AI 只能输出文本。但代码是文本,代码可以生成二进制文件,所以"AI 写代码 → 代码生成文档"是一个巧妙的间接方案。
原因二:代码可以精确控制排版
直接让 AI "输出一段排版好的内容"很难精确。但用代码,你可以精确到每个字号、每个间距:
// 精确控制:名字 24pt,标题 12pt,正文 10pt
new TextRun({ text: "Name", size: 48 }) // 48 半磅 = 24pt
new TextRun({ text: "Header", size: 24 }) // 24 半磅 = 12pt
new TextRun({ text: "Body", size: 20 }) // 20 半磅 = 10pt
原因三:可验证、可调试
AI 生成的代码你可以看、可以改。如果简历排版不对,你可以看 generate_resume.js 找原因,甚至手动修改后重新运行。
# 看看 AI 写的代码
cat agent/custom_scripts/generate_resume.js
# 手动修改后重新运行
node agent/custom_scripts/generate_resume.js
"代码生成模式"的通用公式
这个模式不只是用在简历生成上。通用公式是:
任何 "AI 没法直接输出" 的格式,都可以用这个模式:
AI 搜集/分析信息
↓
AI 写一段代码(利用对应的库)
↓
执行代码 → 产出目标文件
例子:
Word 文档 → AI 写 JS 代码(用 docx 库)
Excel 表格 → AI 写 Python 代码(用 openpyxl 库)
PDF 文件 → AI 写代码(用 reportlab 库)
PPT 演示 → AI 写代码(用 python-pptx 库)
图表图片 → AI 写代码(用 matplotlib 库)
错误处理:代码执行失败怎么办?
AI 写的代码不一定能一次跑通。好消息是,AI 有多轮操作机会(maxTurns: 30),所以它可以:
第 1 次尝试:
AI 写代码 → Bash 执行 → 报错!
AI 看到错误信息:
"TypeError: Cannot read property 'BULLET' of undefined"
第 2 次尝试:
AI 修改代码(import LevelFormat)→ Bash 执行 → 成功!
这就是 maxTurns 留余量的好处。AI 有"试错 → 修复"的空间。
本课小结
- "代码生成模式":AI 不直接输出目标文件,而是写代码 → 执行代码 → 产出文件
- 这么做是因为 AI 不能直接生成二进制文件,但代码可以
- AI 写的代码质量取决于 Skill 技能文件和系统提示词
- 这个模式可以推广到所有"需要用代码来生成"的文件格式
- maxTurns 给了 AI 试错修复的空间
课后练习
- 运行一次后,打开
agent/custom_scripts/generate_resume.js,看看 AI 的代码风格 - 故意在 SYSTEM_PROMPT 里把 "Body 10pt" 改成 "Body 8pt",看 AI 是否会遵守
- 试着手动修改
generate_resume.js(比如换个名字),然后node generate_resume.js重新运行