
Resume Matcher 簡歷定制化改造Skill Verifier Loop 技能目標驗證閉環實現指南【免費下載鏈接】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本文基于倉庫內實現計劃文檔 docs/superpowers/plans/2026-05-06-resume-tailor-verifier-loop.md 展開圍繞技能目標驗證閉環Skill Verifier Loop這一主題通過把簡歷定制Tailoring拆成先產出技能目標計劃、再由本地驗證器過濾分類、最后交給差異生成的小步結構化 LLM 通道讓簡歷能新增 JD 對齊的技能、并在有本地校驗保護的前提下改寫工作/項目經歷措辭。讀完本文你將掌握add_skill差異動作的設計、SKILL_TARGET_PLAN_PROMPT與verify_skill_target_plan()的完整分類邏輯、以及它們如何接入既有的 Diff-Based 安全模型。背景單 Prompt 全量輸出為什么需要一次技能規劃通道在 Diff-Based Resume Improvement 設計文檔 中項目已經完成了從讓 LLM 輸出整份簡歷 JSON到讓 LLM 只輸出定向變更diff的架構遷移。其核心動機是整份輸出意味著 LLM 必須逐字復述它不想改動的字段——公司名、日期、每一條 bullet、技能列表——而每一次復述都是一次幻覺機會。Diff 模型用generate_resume_diffs() → apply_diffs() → verify_diff_result()的本地管線在結構上消滅了這一類問題原簡歷是程序化保留的基底LLM 只能通過白名單路徑做定點修改。但這個模型中仍有一個結構性缺口技能新增。原 Diff 模型對additional.technicalSkills只允許reorder重排不允許新增——因為沒有任何本地證據約束 LLM 新造出來的技能。如果放開新增就會退回LLM 編造技能的老問題原設計文檔 1.1 節幻覺向量 #2Fabricated skills。Skill Verifier Loop 正是為了補上這個缺口而設計。它不在現有 Diff 模型之外另起爐灶而是在 Diff 模型內部增加一次前置的、受本地驗證保護的技能規劃通道第一次 LLM 調用產出技能目標skill targets→ 本地驗證器過濾并分類 → 既有的差異生成調用拿到這份已驗證的目標計劃可以追加被允許的 JD 技能同時圍繞這些目標改寫 summary、work、project 的 bullet。從源碼結構看這一設計已完整落地于 apps/backend/app/services/improver.pygenerate_skill_target_plan()負責產出計劃verify_skill_target_plan()負責本地過濾分類apply_diffs()新增add_skill動作負責落地追加generate_resume_diffs()新增skill_targets參數負責把驗證結果注入差異 Prompt。整體架構一次先規劃、后驗證、再編輯的閉環實現計劃文檔給出的架構一句話概括保持既有 Diff 安全模型但把規劃與編輯拆開。extract_keywords ──→ generate_skill_target_plan ──→ verify_skill_target_plan LLM #1 LLM #2規劃 本地零 LLM 成本 │ 分類existing / jd_added / supported_by_resume / unsupported ▼ generate_resume_diffs ──→ apply_diffs ──→ verify_diff_result ──→ refiner LLM #3編輯 本地 本地 LLM #4對照倉庫中 apps/backend/app/routers/resumes.py 的_improve_preview_flow()編排該路由函數串聯了本輪全部新組件完整調用鏈為extract_job_keywords()提取 JD 關鍵詞保持不變generate_skill_target_plan()讓 LLM 產出緊湊的技能目標計劃target_skillsstrategy_notesverify_skill_target_plan()用純本地規則過濾、分類這些目標——這一步是Verifier Loop的核心確保進入編輯階段的技能都有據可依generate_resume_diffs(..., skill_targetsverified)把驗證過的目標寫進DIFF_IMPROVE_PROMPTLLM 據此追加技能、改寫 bulletapply_diffs()本地應用變更含add_skill動作verify_diff_result()做質量檢查warnings 經response_warnings流向前端。技術棧與計劃文檔聲明一致Python 3.13 FastAPI Pydantic v2LLM 調用統一走 LiteLLM 封裝的complete_json()測試使用 pytest。Task 1add_skill—— 讓技能新增成為受驗證的差異動作Schema 變更ResumeChange.action擴為四值在 apps/backend/app/schemas/models.py 中ResumeChange.action的合法值從三值擴展為四值action: Literal[replace, append, reorder, add_skill]同時該模型帶有一個關鍵的model_validator_list_original_only_for_reorderoriginal字段只有在reorder動作下才允許是列表LLM 回傳當前條目用于比對對文本動作必須是字符串或None——否則會靜默繞過 replace 的原文校驗關卡并導致后續編造指標檢查崩潰。這一防御在解析期Pydantic 校驗期就拒絕畸形輸入。apply_diffs()中的add_skill實現apps/backend/app/services/improver.py 中apply_diffs()對add_skill的約束非常嚴格逐條核對路徑鎖死path ! additional.technicalSkills直接拒絕——add_skill只能作用于技術技能列表不能作用于 languages / certifications / awards更不能作用于 summary 等文本字段目標必須是列表actual_value必須是 list否則拒絕值必須是非空字符串空串、非字符串一律拒絕去重大小寫不敏感用item.casefold()構建已存在集合new_skill.casefold() in existing即拒絕。測試test_add_skill_rejects_duplicate_case_insensitive驗證了resume 已有Python嘗試追加python會被拒、原列表仍只有 1 個Python必須命中已驗證目標_normalize_skill_key(new_skill)歸一化空白 casefold必須出現在allowed_skill_targets構建出的allowed_skill_keys集合中——這是Verifier Loop落地的最后一道閘門未經驗證器批準的技能無論 LLM 怎么輸出都會被拒。只有全部通過才會actual_value.append(new_skill)并計入applied。allowed_skill_targets是apply_diffs()新增的可選參數正是由verify_skill_target_plan()的輸出喂入。對應測試apps/backend/tests/unit/test_apply_diffs.py 中的TestApplyDiffsAddSkill覆蓋四條路徑test_add_skill_to_technical_skills允許列表含Kubernetes時追加成功test_add_skill_rejects_unverified_skillBananaDB不在允許列表拒絕且不進入結果test_add_skill_rejects_duplicate_case_insensitive大小寫不敏感去重test_add_skill_rejects_non_skill_path對summary路徑執行add_skill被拒。Task 2構建并驗證技能目標計劃SKILL_TARGET_PLAN_PROMPT一次只做規劃的輕量調用新的規劃 Prompt 定義在 apps/backend/app/prompts/templates.py核心指令是Return ONLY a JSON object. Do not rewrite the resume——它與編輯通道徹底解耦token 預算小generate_skill_target_plan()中max_tokens2048。其規則要點優先 JD 的 required / preferred 技能包含簡歷中與 JD 高度相關的既有技能允許包含簡歷技能列表里缺失的 JD 技能這正是新增的合法性來源不得包含與 JD 無關的技能不得包含證書certificationsreason 用輸出語言生成。Prompt 注入的上下文包括existing_skills現有技術技能列表、job_keywords格式化后的 JD 關鍵詞、job_description、original_resume結構化 JSON。輸出格式為{ target_skills: [ { skill: skill name, reason: why this skill should be emphasized } ], strategy_notes: brief notes for the next editing pass }generate_skill_target_plan()LLM 側封裝apps/backend/app/services/improver.py 中generate_skill_target_plan()的實現要點用complete_json(prompt..., system_promptYou are a resume skill planning agent..., max_tokens2048, schema_typediff)調用與 diff 通道共用同一 JSON 契約層入參自帶防御_sanitize_user_input()對 JD 做提示注入凈化對應 LLM-011 規則_INJECTION_PATTERNS覆蓋ignore previous instructions、system標簽等常見注入模式解析時同時接受字符串與 dict 兩種target_skills條目形態空目標直接丟棄非列表輸出記錄 warning 并返回空計劃。verify_skill_target_plan()本地分類器閉環的驗證器這是整個 Skill Verifier Loop 的規則核心。函數簽名def verify_skill_target_plan( raw_plan: dict[str, Any], original_resume_data: dict[str, Any], job_keywords: dict[str, Any], job_description: str | None None, ) - dict[str, list[dict[str, str]] | str]:它對 LLM 提出的每個技能目標按優先級分類為四類分類判定依據處理existing已存在于additional.technicalSkills歸一化后精確命中接受作為低風險目標jd_added存在于 JD 的required_skills/preferred_skills且若傳入job_description整詞出現在 JD 正文中接受——JD 要求的技能正是定制化產品的目的用戶會在差異預覽中審閱supported_by_resume未在技能列表但在簡歷 JSON 文本中整詞出現接受——技能背后有簡歷內容支撐unsupported既不在簡歷也不在 JD拒絕底層輔助函數保證了判定的嚴謹性_extract_skill_index()對技能列表做歸一化索引_normalize_skill_key空白折疊 casefold()_extract_jd_skill_index()只收錄同時滿足是 required/preferred 技能且整詞出現在 JD 正文的技能_skill_mentioned_in_text()用(?!\w)...(?!\w)詞邊界正則做整詞匹配避免PyTorch誤命中PyTorchLightning這類子串。歸一化也保證了original與jd_added的判定對大小寫、多余空白不敏感。輸出為{accepted: [...], rejected: [...], strategy_notes: ...}每個條目帶skill、source上述四類之一、reason。拒絕并非硬失敗——被拒條目不會進入編輯通道但結果整體仍可繼續。Task 3把已驗證目標接入差異生成generate_resume_diffs()新增skill_targets參數apps/backend/app/services/improver.py 中generate_resume_diffs()增加了可選參數skill_targets: list[dict[str, Any]] | None并在組裝 Prompt 時調用_prepare_skill_targets_for_prompt()把驗證結果格式化為- Kubernetes (jd_added): Required by JD - Python (existing): Already present in resume skills無目標時輸出No verified skill targets.。對應測試 apps/backend/tests/service/test_improver.py 的test_includes_verified_skill_targets_in_prompt斷言傳入了skill_targets[{skill: Kubernetes, source: jd_added, ...}]后實際發給 LLM 的 prompt 中同時出現Verified skill targets、Kubernetes與add_skill字樣。DIFF_IMPROVE_PROMPT的擴展編輯 Prompt 在 apps/backend/app/prompts/templates.py 中被擴展新增的關鍵規則規則 10Exception to rule 2: you may add a skill only if it appears in the verified skill targets below—— 這是技能新增的唯一合法出口規則 11要求 LLM 默認掃描 summary 與每一條 work / project / education 描述把已經體現 JD 關鍵詞或技能的內容用 JD 術語改寫restate而非新增職責/指標并保留候選人的真實成就規則 12保留原文大小寫REST、API、AWS 等專有名詞與縮寫不被改 casing允許路徑列表相應擴增additional.technicalSkills現在支持reorder或add_skilladditional.languages/certificationsTraining/awards新增reorder支持education[i].description新增 replace 支持它是單字符串而非列表故無[j]下標形態JSON 輸出示例中加入了add_skill動作的完整樣例original: null, value: verified skill target missing from the skills list。策略指令DIFF_STRATEGY_INSTRUCTIONS中full檔也被更新為You may rephrase bullets,add verified JD skills, and add new bullets that elaborate on existing work, but do not invent new responsibilities.——增加已驗證 JD 技能被明確為 full 定制策略的能力之一。_improve_preview_flow()中的編排順序按實現計劃_improve_preview_flow()的執行順序必須是先生成并驗證技能目標計劃再把它喂給差異生成。對應測試test_generate_skill_target_plan_parses_llm_output與test_verify_skill_target_plan_allows_existing_and_jd_skills分別驗證了解析與分類兩個環節。值得注意的實現細節_improve_preview_flow()還繼承了 Diff 模型的既有取舍——結構化簡歷數據僅在含月級日期時用于 Prompt_has_month_in_dates()檢測Jan、Feb等月份模式否則回退到原始 Markdown以避免丟失月份精度。Task 4預覽路徑驗證與測試策略實現計劃把最后一環定位為驗證閉環的整體正確性落地到倉庫中體現為兩層測試單元層diff applierapps/backend/tests/unit/test_apply_diffs.py 覆蓋add_skill的全部通過/拒絕分支以及reorder的邊界——尤其 issue #736 的救回salvage邏輯當 LLM 把新增/刪除的條目混進reorder時不整體丟棄該變更而是按請求順序保留原有條目、僅對技能列表插入通過驗證的新條目走與add_skill相同的allowed_skill_keys關卡未驗證的新條目一律丟棄原條目永不被靜默丟失。服務層prompt harnessapps/backend/tests/service/test_improver.py 覆蓋規劃輸出解析、驗證器分類existing / jd_added / supported_by_resume / unsupported、驗證目標注入 prompt、空 changes 與缺失changeskey 的降級處理、非法 action 被 Pydantic 拒絕、JSON月級日期優先于 Markdown 的輸入選擇、策略指令nudge / keywords / full選擇。計劃文檔要求最后運行pytest聚焦 improver、diff applier、refiner 相關用例若改動涉及前端才需要 lint否則跳過并說明原因。防御縱深與失敗模式Skill Verifier Loop 與既有安全網的關系是主閘 兜底主閘add_skill只能落在additional.technicalSkills且技能必須命中驗證器批準的allowed_skill_keysreorder的救回邏輯對非技能列表的新增條目直接丟棄兜底refine_resume()的_preserve_original_skills()等安全網繼續運行——即便 applier 出現 bug 放行了未授權技能下游仍會修正。失敗模式與既有 Diff 模型保持一致最壞情況是沒有任何變更被應用而非錯誤的變更被應用失敗情形處理LLM 未按 diff 格式輸出無changeskey記 warning返回 0 變更generate_resume_diffs()中strategy_notes注明原因全部 diff 被拒返回原簡歷不變verify_diff_result()提示 No changes were applied部分 diff 被拒部分改進生效rejected 列表生成 warning 流向前端add_skill目標不在驗證列表單項拒絕不影響其他變更文件變更總覽文件變更內容apps/backend/app/schemas/models.pyResumeChange.action增加add_skilloriginal列表形態僅限 reorder 的校驗器apps/backend/app/prompts/templates.py新增SKILL_TARGET_PLAN_PROMPT擴展DIFF_IMPROVE_PROMPT驗證目標、add_skill、大小寫保留、JD 術語改寫規則更新DIFF_STRATEGY_INSTRUCTIONSapps/backend/app/prompts/init.py導出SKILL_TARGET_PLAN_PROMPTapps/backend/app/services/improver.py新增generate_skill_target_plan()、verify_skill_target_plan()、_prepare_skill_targets_for_prompt()apply_diffs()實現add_skill與 reorder 救回generate_resume_diffs()新增skill_targets參數apps/backend/app/routers/resumes.py_improve_preview_flow()編排先規劃、驗證再生成差異apps/backend/tests/unit/test_apply_diffs.pyadd_skill通過/拒絕/去重/路徑約束測試reorder 救回測試apps/backend/tests/service/test_improver.py規劃解析、驗證器分類、prompt 注入測試小結Verifier Loop 的工程價值從實現計劃文檔到倉庫源碼Skill Verifier Loop 展示了一個值得借鑒的 LLM 應用模式不要讓 LLM 同時做決定改什么和決定怎么改而是用一次輕量的規劃調用 一次純本地的規則驗證把可追加的技能集合顯式收斂再交給編輯通道在這個集合內發揮。它沒有引入新的依賴、沒有推翻 Diff 安全模型、也沒有改變前端響應契約resume_preview、diff_summary、detailed_changes原樣輸出而是以最小侵入把技能新增從不可控的幻覺源變成了可審計、可拒絕、可預覽的受控變更。對需要兼顧定制效果與事實保真的簡歷類產品這套規劃-驗證-編輯-復核的閉環設計本身即是可直接復用的參考實現。【免費下載鏈接】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),僅供參考