这一节不是让 AI 一次吐出一大段代码,而是带你走完一轮可复现的 Vibe Coding:先说清目标和边界,再分两轮实现、验证、调试和交付。最后你会得到一个能把结构化个人信息转换成 Markdown 简历的 Python 项目。
第 1 节 · 免费文字实战
核心路径约 90 分钟;AI API 加餐约 20 分钟。没有 API Key 也可以完成全部必做任务。
这一节会完成什么
课程内容清单:
- Python基础环境搭建
- AI API调用入门
- 简历模板设计
- 数据处理和格式化
- 项目打包和分享
- 实践项目: 个人简历自动生成器
最终的数据流是:
脱敏的 resume_input.json
↓
Python 读取、校验和格式化
↓
离线模板 ─────────────→ resume.md(必做)
↓
结构化 Prompt ────────→ resume_prompt.txt(必做)
↓
OpenAI Responses API ─→ resume_ai.md(可选)
完成不等于“程序没有报错”。你需要同时交出:生成文件、测试结果、一次失败修复记录、README 和独立迁移挑战。
开始之前:先定义完成
Goal
做一个命令行简历生成器:读取 JSON 中的真实经历,输出结构清楚的 Markdown 简历。
Context
- 学习者刚开始接触 Python;
- 输入来自一个 UTF-8 编码的 JSON 文件;
- 输出先使用确定性的离线模板;
- AI API 只作为可选增强,不影响核心项目完成;
- 示例数据必须脱敏,不能把身份证号、住址、电话或真实密钥放进项目。
Constraints
- 不使用 Web 框架、数据库、登录和部署;
- 不让模型虚构经历、数字、技能或求职结果;
- API Key 只从环境变量读取;
- 每次只修改一个小目标,修改后立即验证;
- 参考结果只能用于完成后的复核,不能代替自己的实现过程。
Acceptance
项目完成时必须满足:
python app.py --mode template能生成resume.md;python app.py --mode prompt能生成resume_prompt.txt;- 缺少必填字段时,程序给出能定位问题的错误;
- 输出中的事实都能在输入 JSON 中找到;
- 没有 API Key 时,离线模式仍可正常完成;
- 项目包不包含 Key、虚拟环境、缓存或真实隐私数据;
- 在一个新对话中独立增加一个字段,并重新写出验收标准。
先把上面四段复制到你的 WORKLOG.md。它们是你与 AI 编码工具的工作合同。
Python基础环境搭建
本课建议使用 Python 3.10 或更高版本。先打开终端,不要急着安装一堆框架。
1. 检查 Python
macOS 或 Linux:
python3 --version
Windows PowerShell:
python --version
看到 Python 3.10、3.11、3.12 或更高版本即可继续。若 python 找不到而 python3 可用,后续命令统一把 python 换成 python3。
2. 建立独立项目目录和虚拟环境
macOS 或 Linux:
mkdir vibe-resume-generator
cd vibe-resume-generator
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
Windows PowerShell:
mkdir vibe-resume-generator
cd vibe-resume-generator
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
激活后,终端行首通常会出现 (.venv)。再运行一次:
python --version
python -m pip --version
把两行真实输出记进 WORKLOG.md。不要只写“环境完成”。
3. 建立最小文件结构
vibe-resume-generator/
├── app.py
├── resume_input.json
├── tests/
│ └── test_app.py
├── README.md
├── WORKLOG.md
├── ACCEPTANCE.md
└── .gitignore
.gitignore 先写这些内容:
.venv/
__pycache__/
*.pyc
.env
resume.md
resume_ai.md
resume_prompt.txt
先探索,再让 AI 给计划
在 AI 编码工具里开一个新对话,提供 Goal / Context / Constraints / Acceptance,然后只授权只读探索:
先不要修改文件。请检查当前目录和规格,回答:
1. 现在有哪些文件;
2. 为完成最小离线简历生成器还缺什么;
3. 给出不超过 3 步的实现计划;
4. 每一步准备如何验证。
审计划时删掉不属于本节的内容。若计划出现网页界面、数据库、账号系统、PDF 排版或自动投递,直接要求收窄。你需要的是一条短而可验证的主线:
读取与校验 → 模板与保存 → 测试与交付
这一步训练的是“审计划后授权”,不是把决定权交给 AI。
简历模板设计
1. 把 JSON 当作唯一事实源
在 resume_input.json 中写入脱敏示例:
{
"name": "小林",
"target_role": "数据分析实习生",
"education": "某大学 信息管理专业",
"skills": ["Python", "Pandas", "SQL"],
"projects": [
{
"name": "订单数据分析",
"description": "清洗脱敏订单数据,分析销售趋势并输出图表"
}
],
"experience": [
"使用 Python 整理课程数据",
"为分析结果编写 Markdown 说明"
]
}
输入合同包含 6 个必填字段:
| 字段 | 类型 | 用途 |
|---|---|---|
name | 字符串 | 简历标题 |
target_role | 字符串 | 目标岗位 |
education | 字符串 | 教育背景 |
skills | 字符串列表 | 技能清单 |
projects | 对象列表 | 项目名称与描述 |
experience | 字符串列表 | 经历亮点 |
2. 先定输出合同
resume.md 必须按固定顺序包含:
# 姓名
目标岗位:岗位名称
## 教育背景
## 技能
## 项目经历
## 经历亮点
离线模板的价值是结果稳定、可测试、无网络费用。AI 输出可以优化表达,但不能替代事实合同。
3. 给 Prompt 加事实边界
后续 API 模式使用的 Prompt 至少包含:角色、任务、输入、输出格式和禁止事项。最重要的一句是:
只能使用输入中明确提供的事实;不要虚构经历、数字、技能或成果。
这句是约束,不是保证。任何模型输出都还要人工逐条核对。
数据处理和格式化
现在进入第一轮 Build → Verify:只实现读取与校验,不急着接 API。
Build 1:读取 JSON 并尽早失败
在 app.py 中先写:
import json
from pathlib import Path
from typing import Any
REQUIRED_FIELDS = {
"name": str,
"target_role": str,
"education": str,
"skills": list,
"projects": list,
"experience": list,
}
def load_profile(path: Path) -> dict[str, Any]:
with path.open("r", encoding="utf-8") as file:
profile = json.load(file)
if not isinstance(profile, dict):
raise ValueError("简历输入必须是一个 JSON 对象。")
for field, expected_type in REQUIRED_FIELDS.items():
if field not in profile:
raise ValueError(f"缺少必填字段:{field}")
if not isinstance(profile[field], expected_type):
raise ValueError(f"字段 {field} 应为 {expected_type.__name__}。")
if not all(isinstance(item, str) and item.strip() for item in profile["skills"]):
raise ValueError("skills 中的每一项都必须是非空字符串。")
for index, project in enumerate(profile["projects"], start=1):
if not isinstance(project, dict):
raise ValueError(f"第 {index} 个项目必须是 JSON 对象。")
if not isinstance(project.get("name"), str) or not project["name"].strip():
raise ValueError(f"第 {index} 个项目缺少字符串字段 name。")
if not isinstance(project.get("description"), str) or not project["description"].strip():
raise ValueError(f"第 {index} 个项目缺少字符串字段 description。")
return profile
Verify 1:先看成功,再制造一个失败
临时在文件末尾加:
profile = load_profile(Path("resume_input.json"))
print(profile["name"], profile["target_role"])
运行:
python app.py
然后复制一份 JSON,删掉 target_role 再运行。你应该看到包含 缺少必填字段:target_role 的错误。把命令、错误和原因记录到 WORKLOG.md,再恢复正确输入。
如果看到 JSONDecodeError,先检查上一行末尾的逗号、英文双引号和大括号是否成对。先修输入,不要用宽泛的 try/except 把错误吞掉。
Build 2:格式化 Markdown
继续在 app.py 中增加:
def render_template_resume(profile: dict[str, Any]) -> str:
lines = [
f"# {profile['name'].strip()}",
"",
f"目标岗位:{profile['target_role'].strip()}",
"",
"## 教育背景",
"",
profile["education"].strip(),
"",
"## 技能",
"",
]
lines.extend(f"- {skill.strip()}" for skill in profile["skills"])
lines.extend(["", "## 项目经历", ""])
for project in profile["projects"]:
lines.extend([
f"### {project['name'].strip()}",
"",
f"- {project['description'].strip()}",
"",
])
lines.extend(["## 经历亮点", ""])
lines.extend(f"- {item.strip()}" for item in profile["experience"])
return "\n".join(lines).rstrip() + "\n"
再增加一个稳定的 Prompt 生成函数:
def build_prompt(profile: dict[str, Any]) -> str:
profile_json = json.dumps(profile, ensure_ascii=False, indent=2)
return f"""你是一名求职简历顾问。
请根据下面的个人信息生成 Markdown 中文简历。
要求:
1. 结构清楚,目标岗位明确;
2. 只能使用输入中明确提供的事实;
3. 不要虚构经历、数字、技能或成果;
4. 信息不足时保守表达;
5. 只输出 Markdown 正文。
个人信息:
{profile_json}
"""
最后把临时的两行运行代码替换成命令行入口:
import argparse
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="生成 Markdown 简历。")
parser.add_argument("--input", type=Path, default=Path("resume_input.json"))
parser.add_argument("--output", type=Path, default=Path("resume.md"))
parser.add_argument("--mode", choices=("template", "prompt", "openai"), default="template")
parser.add_argument("--prompt-output", type=Path, default=Path("resume_prompt.txt"))
return parser.parse_args()
def main() -> None:
args = parse_args()
profile = load_profile(args.input)
if args.mode == "prompt":
args.prompt_output.write_text(build_prompt(profile), encoding="utf-8")
print(f"已生成 Prompt:{args.prompt_output}")
return
content = render_template_resume(profile)
args.output.write_text(content, encoding="utf-8")
print(f"已生成简历:{args.output}")
if __name__ == "__main__":
main()
Verify 2:运行两条真实命令
python app.py --mode template
python app.py --mode prompt
检查:
resume.md能正常显示中文;- 姓名、岗位、教育、技能、项目和经历都出现;
resume_prompt.txt中的 JSON 没有变成\u4e00这类转义;- 输出没有出现输入中不存在的数字或成果。
AI API调用入门
这一段是可选进阶。API 调用需要你自己的 OpenAI API Key、联网环境和可用额度,并可能产生费用。网站不会索取、代存或替你运行 Key。
1. 安装官方 Python SDK
保持虚拟环境已激活:
python -m pip install openai
2. 只在环境变量中设置 Key
macOS 或 Linux 当前终端:
export OPENAI_API_KEY="<仅在本机粘贴>"
Windows PowerShell 当前窗口:
$Env:OPENAI_API_KEY="<仅在本机粘贴>"
不要把 Key 写入 app.py、JSON、.env.example、README、截图或 Git 提交。终端共享屏幕前也要确认历史记录里没有敏感值。
3. 使用 Responses API
在 app.py 顶部增加 import os,再增加:
def generate_with_openai(prompt: str, model: str) -> str:
if not os.getenv("OPENAI_API_KEY"):
raise RuntimeError("未设置 OPENAI_API_KEY;请先使用 template 或 prompt 模式。")
try:
from openai import OpenAI
except ImportError as error:
raise RuntimeError("未安装 openai;请先运行 python -m pip install openai。") from error
client = OpenAI()
response = client.responses.create(model=model, input=prompt)
content = response.output_text.strip()
if not content:
raise RuntimeError("API 返回了空内容,请检查模型权限、额度和请求日志。")
return content + "\n"
在 parse_args() 里增加模型参数:
parser.add_argument(
"--model",
default=os.getenv("OPENAI_MODEL", "gpt-5.6-luna"),
)
把 main() 生成内容的部分改成:
if args.mode == "openai":
content = generate_with_openai(build_prompt(profile), args.model)
else:
content = render_template_resume(profile)
运行前,把 resume_input.json 换成不含真实身份信息的测试数据:
python app.py --mode openai --output resume_ai.md
模型名称、账户可用范围和价格会变化;如果默认模型不可用,请从官方模型目录选择你账户可用的文本模型,并通过 OPENAI_MODEL 或 --model 显式指定。当前 SDK 的安装、环境变量和 Responses API 结构以 OpenAI API 快速入门 为准,模型选择以 OpenAI 模型目录 为准。
4. API 结果必须人工验收
逐项问自己:
- 每条经历都来自输入吗?
- 模型有没有补出不存在的年份、指标或公司?
- 模型有没有把“正在学习”写成“熟练掌握”?
- 标题和 Markdown 结构是否完整?
- 失败时,离线模式是否仍然可用?
Prompt 中写“不要虚构”不等于模型绝不会虚构。API 输出不能直接当作求职事实。
测试、Debug 与 Final Review
1. 建立最小自动测试
在 tests/test_app.py 中写:
import json
import tempfile
import unittest
from pathlib import Path
from app import build_prompt, load_profile, render_template_resume
class ResumeGeneratorTests(unittest.TestCase):
def setUp(self) -> None:
self.profile = {
"name": "小林",
"target_role": "数据分析实习生",
"education": "某大学 信息管理专业",
"skills": ["Python", "SQL"],
"projects": [{"name": "订单分析", "description": "清洗脱敏订单数据"}],
"experience": ["使用 Python 整理课程数据"],
}
def test_template_contains_supplied_facts(self) -> None:
output = render_template_resume(self.profile)
for fact in ("小林", "数据分析实习生", "订单分析", "Python"):
self.assertIn(fact, output)
def test_prompt_forbids_fabrication(self) -> None:
prompt = build_prompt(self.profile)
self.assertIn("不要虚构", prompt)
def test_missing_field_fails_early(self) -> None:
invalid = dict(self.profile)
invalid.pop("target_role")
with tempfile.TemporaryDirectory() as temp_dir:
path = Path(temp_dir) / "invalid.json"
path.write_text(json.dumps(invalid, ensure_ascii=False), encoding="utf-8")
with self.assertRaisesRegex(ValueError, "target_role"):
load_profile(path)
if __name__ == "__main__":
unittest.main()
运行:
python -m unittest discover -s tests -v
2. 做一次真实 Debug 闭环
不要只看全部通过。主动把 resume_input.json 的 skills 改成字符串:
"skills": "Python, SQL"
按以下格式记录:
复现命令:python app.py --mode template
实际结果:字段 skills 应为 list
预期结果:skills 必须是 JSON 数组
根因:输入类型违反合同
最小修复:把 skills 恢复为字符串数组
回归证据:全部测试通过,两种离线模式再次成功
调试的目标不是“让红字消失”,而是保留一条别人能复现、能理解、能回归的证据链。
3. Final Review
在交付前检查最终改动:
- 需求外功能是否被移除;
- 是否还有硬编码路径或个人信息;
- 所有输出是否使用 UTF-8;
- API Key 是否只来自环境变量;
- 自动测试和两条真实命令是否都运行过;
- API 若没有真实调用,是否明确写为“未验证”;
- README 是否让一个新用户能从零复现。
项目打包和分享
1. 写清 README
README 至少包含:项目目标、环境版本、文件结构、安装步骤、三种模式、测试命令、输入字段、隐私边界和已知限制。
推荐公开结构:
vibe-resume-generator/
├── app.py
├── resume_input.example.json
├── tests/
│ └── test_app.py
├── requirements.txt
├── README.md
├── WORKLOG.md
└── ACCEPTANCE.md
把脱敏示例重命名为 resume_input.example.json。真实简历输入、生成结果和 Key 不进入公开包。
2. 生成依赖说明
离线模式只使用 Python 标准库。若你完成了 API 加餐,在 requirements.txt 中写:
openai
同时在 README 说明依赖和模型可用性会变化,安装时应核对官方文档。不要把整个虚拟环境打进压缩包。
3. 在干净副本中复跑
分享前新建一个临时目录,只复制公开结构中的文件,然后重新运行:
python app.py --input resume_input.example.json --mode template
python app.py --input resume_input.example.json --mode prompt
python -m unittest discover -s tests -v
如果干净副本不能运行,项目还不能交付。
4. 压缩或上传 GitHub
macOS 或 Linux:
zip -r vibe-resume-generator.zip vibe-resume-generator \
-x "*/.venv/*" "*/__pycache__/*" "*/.env" "*/resume*.md"
Windows PowerShell:先确认目录中没有 .venv、.env 和真实输入,再运行:
Compress-Archive -Path vibe-resume-generator\* -DestinationPath vibe-resume-generator.zip
上传 GitHub 前再执行一次:
git status --short
逐个检查准备提交的文件。不要用一句“应该没有”代替检查。
实践项目: 个人简历自动生成器
现在关掉当前 AI 对话,开一个新对话,从空白上下文完成下面的挑战。
必做任务
- 用自己的脱敏信息重写输入 JSON;
- 独立说明 Goal、Context、Constraints 和 Acceptance;
- 让 AI 先只读探索并提交不超过 3 步的计划;
- 分两轮完成“读取与校验”“模板与保存”;
- 复现一次真实失败,完成最小修复和完整回归;
- 生成
resume.md与resume_prompt.txt; - 完成 README、测试记录和干净项目包;
- 可选:使用脱敏输入运行一次 API 模式并人工核对。
独立迁移挑战
不提供现成 Prompt。请在新对话中把 links 字段加入输入合同,让简历增加“作品链接”章节。你必须先写出新的验收标准,再允许 AI 修改代码。
迁移完成的最低证据:
- 没有
links时给出清楚错误,或明确把它设计为可选字段; - 有 1–3 个链接时能稳定输出;
- 测试同时覆盖空列表和正常列表;
- README 已同步更新;
- 你能解释为什么选择必填或可选。
提交清单
-
app.py -
resume_input.example.json -
tests/test_app.py -
README.md -
WORKLOG.md -
ACCEPTANCE.md - 测试通过的终端记录
- 离线生成的脱敏结果
- 一次失败复现与回归记录
- 独立迁移挑战说明
当前限制与完成标准
这节课的必做路径只生成 Markdown,不处理 PDF 样式、ATS 评分、岗位投递或录取预测。API 输出也不保证比离线模板更好。
David Studio 当前已验证课程配套 Python 项目的离线生成、Prompt 导出、输入错误和无 Key 安全失败;没有在本次发布中使用真实 Key 进行联网 API 冒烟测试。学习者需要根据自己的账户、模型权限、额度和所在地区自行验证 API 模式。
真正的完成标准是:另一个人拿到你的干净项目包,可以按 README 在新环境中复现;你也能说明每一个范围取舍、失败和验证证据。