从零生成主简历:Resume-Matcher 的 AI Resume Wizard 全流程设计解析
发布时间:2026/9/11 15:47:35 作者:尧图编辑部 阅读量:1,286

从零生成主简历Resume-Matcher 的 AI Resume Wizard 全流程设计解析【免费下载链接】Resume-MatcherThe #1 AI Harness for Building Resumes, PDFs, Cover Letters more, locally with 100 LLMs support.项目地址: https://gitcode.com/GitHub_Trending/re/Resume-Matcher导读Resume-Matcher 是一款在本地运行、支持 100 LLM 的开源简历构建工具。对于尚未持有 PDF/DOCX 简历的用户项目提供了一条全新的入口AI Resume WizardAI 简历向导——通过一次一个问题的自适应 AI 对话从零构建一份结构化的通用主简历master resume并最终沉淀为与上传解析路径完全一致的下游数据形态。本文基于 Resume Wizard 设计文档结合仓库中已落地的后端路由、服务、Schema、Prompt 模板与前端页面源码完整讲解该功能的架构设计、数据契约、交互流程、容错机制与测试策略读者读完可以掌握如何在项目内实现并验证一个AI 引导 应用管控的问答式简历生成管线。一、设计目标为没有简历的人补上主简历创建路径Resume-Matcher 的核心工作流围绕主简历master resume 职位描述JD的匹配与定制展开。此前dashboard 将缺少主简历作为初始化入口用户通过POST /api/v1/resumes/upload上传 PDF/DOCX系统创建一条普通简历记录并在不存在健康主简历时将其标记为 master同时把master_resume_id写入本地存储从而放行用户进入后续功能。Wizard 设计文档的核心目标正是为手上没有现成 PDF/DOCX的用户提供第二条路径通过一次一步的 AI 问答从零构建一份真实truthful的结构化主简历其下游产物与上传并解析出的主简历完全等价字段上传解析路径Wizard 最终产物is_mastertrue当无健康主简历时true当无健康主简历时processing_statusreadyreadycontent_typejsonjsonprocessed_data兼容ResumeData兼容ResumeData经校验也就是说Wizard 输出的不是一份游离的草稿文件而是一条已持久化、可用于后续 tailor职位定制流程的标准主简历记录。二、产品流程上传 or AI 向导二选一当不存在主简历时dashboard 的设置磁贴setup tile会弹出一个Swiss 风格二选一对话框对应前端组件 master-resume-choice-dialog.tsx上传已有简历继续复用ResumeUploadDialog走原有上传解析快路径用 AI Wizard 从零创建跳转到/resume-wizard路由。// apps/frontend/components/dashboard/master-resume-choice-dialog.tsx DialogContent classNamemax-w-2xl bg-background border-2 border-black shadow-[4px_4px_0px_0px_#000000] ... DialogHeader.../DialogHeader div classNamegrid gap-4 bg-background p-6 md:grid-cols-2 section{/* Upload 选项Upload 图标 ResumeUploadDialog */}/section section{/* Wizard 选项Bot 图标 路由到 /resume-wizard */}/section /div /DialogContentWizard 页面位于 apps/frontend/app/(default)/resume-wizard/page.tsx/resume-wizard/page.tsx)是一个客户端组件use client实际渲染逻辑在 resume-wizard-page.tsx 中。Wizard 的引导式开场Intro设计文档规划了一个简短的 AI 引导开场源码中通过_INTRO_QUESTION常量resume_wizard.py 服务与前端INTRO_QUESTIONlib/api/resume-wizard.ts保持一致Hi — Ill help you build your master resume. Whats your name, and what kind of role are you going for?询问用户身份与目标角色方向即使回答是口语化的如 Hi, Im James.也能提取出姓名用姓名个性化下一条问题So James, where would you like to begin?展示章节选择Work Experience、Internships、Education、Projects、Skills、Review。姓名提取在服务端由extract_intro_name()实现services/resume_wizard.py它使用三条正则依次匹配_INTRO_NAME_PATTERNS ( re.compile(r\bI(?:| a)m\s([A-Z][A-Za-z](?:\s[A-Z][A-Za-z])?)), re.compile(r\b[Mm]y name is\s([A-Z][A-Za-z](?:\s[A-Z][A-Za-z])?)), re.compile(r\b[Nn]ame(?:s| is)?\s([A-Z][A-Za-z](?:\s[A-Z][A-Za-z])?)), )注意一个细节关键字my name/name允许大小写但捕获的姓名必须以大写字母开头因此用[Mm]/[Nn]显式限定而非re.IGNORECASE——否则会误把 domain name facebook is 这类句子中的小写词捕获成姓名。该提取作为intro 回答后姓名仍为空时的确定性兜底防止 LLM 未能正确解析姓名见run_ai_turn中的 fallback 逻辑services/resume_wizard.py。章节行为与基线输出Wizard 的章节与简历 Schema 的映射关系在设计中明确给出服务端_VALID_SECTIONS与_SECTION_PROMPTSservices/resume_wizard.py落地了这一映射用户可见章节ResumeData 字段章节提示词节选Work ExperienceworkExperiencetitle、company、dates、what you did、measurable impactInternshipsworkExperience合并层映射title、company、dates、what you worked on、what changedEducationeducationschool、degree、dates、honors、standout courseworkProjectspersonalProjectswhat you built、why it mattered、tech、resultsSkillsadditional.technicalSkills languages / certificationsTraining / awardstools、technologies、skillsContact / Summary / ReviewpersonalInfo/summary/ 审查联系方式、职业描述、缺口检查基线输出Prompt 模板中同样强制见 prompts/resume_wizard.py每段工作/实习经历 3 条 bullet每个项目 2 条 bullet技能从用户回答中持续推断并在 finalize 前实时显示在技能章节。用户可以在 finalize 之前跳过任意章节并随时返回Review 步骤会识别缺失但有用的信息——源码中build_review_warnings()services/resume_wizard.py产生确定性提示def build_review_warnings(data: ResumeData) - list[str]: if not info.name.strip(): warnings.append(Add your name — its required to create your resume.) if not any(value.strip() for value in contact): warnings.append(Add at least one contact method (email, phone, or a link).) if not data.workExperience and not data.personalProjects: warnings.append(Add at least one experience, internship, or project.) if not data.education: warnings.append(Education is empty — skip only if thats intentional.) if not data.additional.technicalSkills: warnings.append(Skills are empty — add tools or technologies youve used.)其中姓名是 finalize 的唯一硬性要求请求缺少姓名会直接 422因此在 review 阶段就提前提示避免用户在最终提交时遭遇笼统报错。三、AI Harness结构化回合制状态机设计文档明确了 AI Harness 的核心原则后端暴露resume-wizardAPI 命名空间接收当前 wizard 状态 最新用户动作/回答永远返回结构化响应而不是自由文本。应用自身掌控 Schema 与允许的章节动作——AI 可以写 bullet、提取事实、推断技能、改写措辞、追问澄清问题但每一轮返回的简历草稿在交给客户端前都必须通过ResumeData校验。回合状态模型状态 Schema 定义在 schemas/resume_wizard.py由以下核心模型组成ResumeWizardSection Literal[intro, contact, summary, workExperience, internships, education, personalProjects, skills, review] ResumeWizardStep Literal[intro, question, review, complete] ResumeWizardAction Literal[start, answer, skip, back, review] class ResumeWizardState(BaseModel): step: ResumeWizardStep intro resume_data: ResumeData Field(default_factoryResumeData) current_question: ResumeWizardQuestion Field(default_factoryResumeWizardQuestion) history: list[ResumeWizardHistoryEntry] Field(default_factorylist) asked_count: int 0 inferred_skills: list[str] Field(default_factorylist) is_complete: bool False progress: ResumeWizardProgress Field(default_factoryResumeWizardProgress) warnings: list[str] Field(default_factorylist)其中history记录每个已答问题的回答前草稿快照resume_data_before这是back动作能够确定性还原上一问与草稿的基础。asked_count服务端自增配合进度计算compute_progress()services/resume_wizard.py让进度条永远由服务端计算绝不信任模型输出。回合动作路由POST /api/v1/resume-wizard/turn的动作分发在 routers/resume_wizard.pyaction request.action if action start: return ResumeWizardTurnResponse(statebuild_initial_wizard_state()) if action back: return ResumeWizardTurnResponse(stateapply_back(request.state)) if action review: return ResumeWizardTurnResponse(stateapply_review(request.state)) # 成本护栏达到问题数上限后不再调用 LLM直接引导进入 review if request.state.asked_count RESUME_WIZARD_MAX_QUESTIONS: return ResumeWizardTurnResponse(stateapply_review(request.state)) if action skip: state await run_ai_turn(request.state, , skipTrue) ... state await run_ai_turn(request.state, answer_text, skipFalse)值得注意的工程细节RESUME_WIZARD_MAX_QUESTIONS 15services/resume_wizard.py是一道成本护栏一旦asked_count达到 15后续 answer/skip 回合不再产生 LLM 调用直接路由到 review避免无上限消耗 tokenback与review是纯确定性操作无 LLM 调用back通过弹栈 history 快照还原review仅计算温柔提示skip也调用 LLM但 Prompt 中注入的是固定指令——(The user skipped this question. Do NOT modify resume_data. Ask the next most useful question for a different section.)让模型转向另一个章节提问。AI 回合核心逻辑run_ai_turn()services/resume_wizard.py是整条管线的枢纽按序完成序列化当前草稿→json.dumps(state.resume_data.model_dump(modejson), ensure_asciiFalse)净化用户回答非 skip 时对回答先做_sanitize_user_inputPrompt 注入模式剥离再_scrub_secrets脱敏sk-…/AIza…/Bearer …等凭证样式 token避免敏感信息进入 LLM组装 Prompt并调用complete_json(prompt, max_tokens8192, schema_typeresume)解析与校验结果必须是 dictresume_data经过normalize_wizard_resume_data→ResumeData.model_validate严格校验分区合并_merge_section()只把 LLM 输出合并进当前活动章节绝不覆盖其他章节。关键设计分区合并与条目去重_merge_section()services/resume_wizard.py按章节分发合并策略intro/contact仅当新值非空时覆盖personalInfo各字段summary非空时替换摘要workExperience/internships/education/personalProjects调用_merge_entries()做签名去重合并skillsmerge_unique_skills()保留首次出现的拼写与顺序大小写不敏感去重casefold同时合并languages、certificationsTraining、awards未知/review章节绝不改动resume_data。_merge_entries()的注释解释了动机模型有时只回显用户刚描述的一条记录而非完整列表若直接整体替换就会抹掉早前录入的条目。因此采用内容签名而非id因为 wizard 条目的 id 默认为 0作为联合键模型省略的旧条目保留、同签名条目原位替换、真正的新条目追加。def _merge_entriesT - list[T]: # 签名键如 titlecompanyyears 的 casefold 元组 # 省略的保留 / 同签名替换 / 新条目追加合并后还会执行_assign_entry_ids()services/resume_wizard.pyLLM 省略id字段导致条目 id 全为 0而下游的 live preview React key 与 builder 的Math.max(...ids)1逻辑都依赖唯一 id因此按位置确定性重编 1-based id。下一问的选择_next_question()services/resume_wizard.py优先采用模型返回的next_question其section必须钳制到合法枚举valid_section()兜底为review若模型未提供则回退到_next_gap_section()——按工作经历 → 教育 → 项目 → 技能 → review的顺序自动定位第一个明显为空的章节。def _next_gap_section(data: ResumeData) - str: if not data.workExperience: return workExperience if not data.education: return education if not data.personalProjects: return personalProjects if not data.additional.technicalSkills: return skills return review前端 resume-wizard-page.tsx 的firstGapSection()复刻了同一启发式用于 review 后继续补充Keep Adding时定位下一个内容缺口——注释特别指出review章节在后端合并中是 no-op若目标设为其会静默丢弃回答。四、Prompting结构化简历写作助手的约束体系设计文档要求新增 resume-wizard Prompt 模板让模型扮演结构化简历写作助手核心规则包括构建通用主简历而非针对特定职位的定制简历不得虚构公司、日期、指标、工具、学位、奖项或技能回答含糊或缺关键事实时必须追问澄清优先产出基于用户事实的精炼行动导向 bullet使用配置的内容语言{output_language}只输出请求的 JSON 对象保留既有草稿数据除非用户明确修改。这些规则完整落在 prompts/resume_wizard.py 的RESUME_WIZARD_TURN_PROMPT中。模板额外强调了语言边界人类可读文本下一问、标题、bullet、摘要用{output_language}输出但结构化值保持原样——next_question.section必须是精确的英文枚举值日期保持给定格式不翻译章节键与日期。模板中的输出 JSON 骨架每轮必须返回{ resume_data: { personalInfo: {name: , title: , email: , phone: , location: , website: , linkedin: , github: }, summary: , workExperience: [], education: [], personalProjects: [], additional: {technicalSkills: [], languages: [], certificationsTraining: [], awards: []}, sectionMeta: [], customSections: {} }, next_question: {text: Your next concise question, section: workExperience}, inferred_skills: [Skill], is_complete: false }其中is_complete仅是建议信号提示前端亮起 Review finish 提示step始终停留在question绝不自动 finalize——是否进入 review 由客户端决定见 services/resume_wizard.py 的注释。章节提示词同样落地work experience 要求 title、company、dates、responsibilities、tools、scale、impactprojects 要求 what was built、why it mattered、technologies used、user/usage context、links见_SECTION_PROMPTS。五、Backend APIturn 与 finalize 两个端点设计文档规划的端点与源码一一对应挂载于 apps/backend/app/main.py 的app.include_router(resume_wizard_router, prefix/api/v1)POST /api/v1/resume-wizard/turn请求体ResumeWizardTurnRequest——state完整往返的状态action 可选的answeranswer的text约束为min_length1, max_length6000且必须非空白字段校验器拒绝纯空白action answer时必须携带answer否则模型校验器抛错422响应体ResumeWizardTurnResponse——新的state含更新后的resume_data、进度、当前章节、推断技能、下一问、警告、完成状态。POST /api/v1/resume-wizard/finalizefinalize 端点 执行校验终稿 → 创建主简历幂等保护先查当前主简历若已存在processing_status ready的主简历直接 409 拒绝A master resume already exists. Delete it before creating a new one.Schema 校验normalize_resume_data(request.state.resume_data.model_dump(modejson))后再ResumeData.model_validate保证落库的是严格合法的ResumeData原子创建db.create_resume_atomic_master(...)——content为规范 JSONensure_asciiFalse, sort_keysTruecontent_typejsonfilenamefAI Resume Wizard - {name}.jsonprocessing_statusready并同步设置title。注释指出title 放在原子创建内避免已提交但无标题的主简历在重试时触发 409 死锁最终校验若创建结果is_masterFalse竞态下主简历已被占用删除刚创建的非 master 记录并返回 409做到非破坏性拒绝。最终响应{message, request_id, resume_id, processing_status: ready, is_master}。若已存在主简历finalize 拒绝创建——显式的替换replacement流程不在本次实现范围内Out of Scope。错误处理完整上下文留服务端浏览器只见通用消息后端所有异常统一处理routers/resume_wizard.pyValueError→ 422 Could not update the resume draft.其余异常 → 500 Resume wizard failed. Please try again.同时logger.error记录详细上下文。模型异常细节、provider 密钥、原始堆栈永不进入浏览器。JSON 修复与重试设计文档要求模型返回非法 JSON 时后端使用既有complete_json行为 仅基于 prompt 的 JSON 修复指令重试仍失败则服务端记录详细错误并返回通用客户端错误。这正对应run_ai_turn中complete_json(prompt, max_tokens8192, schema_typeresume)的调用——complete_json是项目 LLM 层的既有能力app/llm.py。六、Frontend UI聚焦工具的 Swiss 设计设计文档明确了/resume-wizard页面的视觉规范前端 resume-wizard-page.tsx 与 question-card.tsx 逐条落地设计规范源码实现Canvas 背景#F0F0E8主题 tokenbg-background方形圆角、黑色边框border-2 border-black rounded-none硬偏移阴影shadow-[4px_4px_0px_0px_#000000]shadow-sw-lg衬线标题、无衬线正文、等宽元数据font-serif/font-sans/font-mono无装饰渐变、无圆角卡片、无营销 hero网格布局lg:grid-cols-[minmax(0,1fr)_360px]首屏即向导工具本身页面结构为两栏左栏QuestionCard——顶部服务端计算的进度条roleprogressbar每格为黑/白方块、章节标签等宽蓝色小字、衬线大字号问题、答案Textarea、操作按钮组右栏LivePreview——实时结构化预览展示已收集的姓名/头衔、经历title · company、年份、bullet、项目、教育、技能标签。交互操作矩阵QuestionCard根据step渲染不同的操作question 步骤ContinueEnter 提交、ShiftEnter 换行见handleKeyDown、Skip、Review、Back有 history 时显示review 步骤CreatecanFinalize为 false 时禁用即无姓名时不可创建、Keep Adding回到 question 并定位到下一个缺口章节isComplete为 true 时在 question 步骤显示绿色 ready 提示resumeWizard.readyHint。键盘交互遵循仓库惯例Enter 永不冒泡到父级表单/对话框event.stopPropagation()ShiftEnter 插入换行。Live Preview 的技能推断展示live-preview.tsx 将technicalSkills与inferred_skills合并展示dedupeSkills()使用toLowerCase()与后端casefold对齐注释专门指出toLocaleLowerCase会在土耳其语等 locale 上因点/无点 I 产生分歧本轮新推断的技能用绿色边框 ✓ 标记用户可在 finalize 前实时核对 AI 推断是否越界。本地草稿持久化与刷新恢复resume-wizard-page.tsx将草稿持久化到localStorage的resume_wizard_draft键。读取时做深度容错归一化readSavedDraft/normalizeDraftResumeDatastep/section 钳制到合法枚举、personalInfo每字段强制字符串防止数字型 name 让.trim()抛错陷入刷新死循环、列表字段强制数组并重编 1-based id。写入是 best-effort——配额/序列化失败静默忽略绝不让草稿保存问题崩溃向导。Finalize 后的落点handleFinalize()resume-wizard-page.tsxlocalStorage.setItem(MASTER_RESUME_KEY, response.resume_id); // master_resume_id localStorage.removeItem(DRAFT_STORAGE_KEY); incrementResumes(); setHasMasterResume(true); setState((current) ({ ...current, step: complete })); router.push(/builder?id${response.resume_id});即存入master_resume_id→ 更新状态缓存 → 路由到/builder?idresume_id供用户最终人工审查与编辑。这是设计文档强调的刻意安排——AI 向导产出强初稿但用户在使用简历进行 tailor 之前仍应能检查与编辑。七、Frontend 数据契约与 API 助手设计文档要求前端在 apps/frontend/lib/api/resume-wizard.ts 增加 API 助手源码完全对应postResumeWizardTurn(payload)→POST /resume-wizard/turnfinalizeResumeWizard(state)→POST /resume-wizard/finalizecreateInitialResumeWizardState()生成本地初始状态与后端build_initial_wizard_state字段一致step: intro、空resume_data、intro 问题、progress: {current: 0, total: 8}。前端类型ResumeWizardState/ResumeWizardSection/ResumeWizardAction与后端 Pydantic Schema 字段一一对应构成可往返的契约。所有 i18n 文案走useTranslations()的resumeWizard.*键并由 check_locale_parity.py 检查各 locale 键一致。八、错误处理与恢复动作前端侧设计文档要求客户端错误以 Swiss 风格警示呈现并附带恢复动作前端落地为红框警示区border-2 border-red-600 bg-red-100rolealert 错误翻译键resumeWizard.errors.turnFailed/finalizeFailed用户可执行的恢复路径包括重试 AI 回合再次点击 Continue/Skip/Review继续编辑当前回答setErrorKey(null)后重新输入返回 dashboard页面右上角 Back to Dashboard 按钮转而走上传路径dashboard 的 Upload 选项。九、测试策略与质量门禁设计文档列出的测试点全部有落地文件后端apps/backend/tests/integration/test_resume_wizard_api.pyWizard Schema 校验与经ResumeData的强制类型转换intro 回答提取姓名并产出章节选择章节更新产生基线 bullet 数量技能推断仅使用用户提供的事实finalize 在无主简历时创建 ready 主简历finalize 在主简历已存在时拒绝。前端apps/frontend/tests/resume-wizard-api.test.tsAPI 助手请求/响应形状resume-wizard-page.test.tsx初始渲染、章节切换、finalize 存储master_resume_id并路由到 builderresume-wizard-question-card.test.tsxquestion/review 步骤操作矩阵resume-wizard-live-preview.test.tsx技能去重与推断标记。质量门禁前端改动须在apps/frontend下运行npm run lint与npm run format后端改动运行针对新 router/service 的定向 pytest。十、Out of Scope明确的边界设计文档与实现保持一致以下内容不在 Wizard 范围内不做职位定制Wizard 构建的是通用主简历JD 定制仍走既有 tailor 管线不替换既有上传解析器、tailor 流程、enrichment 流程或 builder不改动CI、Docker 或 GitHub workflow 文件不实现主简历的显式替换流程finalize 遇到已存在主简历时以 409 非破坏性拒绝。结语一份AI 引导、应用管控的可信简历管线Resume-Matcher 的 AI Resume Wizard 展示了混合式问答生成的成熟工程范式AI 负责对话与起草应用负责 Schema、状态机与校验。从 intro 的姓名提取、分区合并防覆盖、内容签名去重、服务端进度计算与 15 问成本护栏到 review 的确定性缺口提示与 finalize 的原子建库再到前端 Swiss 风格工具化界面与 localStorage 刷新恢复——整条管线强调诚实生成、可追溯、可回退、可最终人工编辑。它没有把简历生成交给黑盒而是把 LLM 的能力约束在结构化契约之内这也正是设计文档中truthful structured master resume从原则走向实现的完整路径。【免费下载链接】Resume-MatcherThe #1 AI Harness for Building Resumes, PDFs, Cover Letters more, locally with 100 LLMs support.项目地址: https://gitcode.com/GitHub_Trending/re/Resume-Matcher创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考