更新技能

This commit is contained in:
zk
2026-06-24 10:32:26 +08:00
parent 2f38c80207
commit 556302816b
4 changed files with 60 additions and 33 deletions
+29 -16
View File
@@ -44,7 +44,7 @@
3. 查岗位: 3. 查岗位:
-`bg_job` 拿 id、title、skill_tags -`bg_job` 拿 id、title、skill_tags
- 岗位不存在 → 报错 - 岗位不存在 → 报错
- skill_tags 为空 → 直接返回满分 10,missingSkills 为空数组 - skill_tags 为空 → 直接返回满分 10,gaps 为空数组
4. 查简历子表(拼 AI 输入): 4. 查简历子表(拼 AI 输入):
- `bg_user_resume_education` - `bg_user_resume_education`
- `bg_user_resume_work` - `bg_user_resume_work`
@@ -53,8 +53,8 @@
- `bg_user_resume_competition` - `bg_user_resume_competition`
5. 调 AI(一次): 5. 调 AI(一次):
- 输入:岗位 skill_tags 列表 + 简历 skills 字段 + 各子表经历描述 - 输入:岗位 skill_tags 列表 + 简历 skills 字段 + 各子表经历描述
- 输出:缺失技能的 JSON 数组,必须是 skill_tags 子集 - 输出:差距条目 JSON 数组,每条含 keyword(缺失技能,skill_tags 子集)、title(≤12字)、description(≤30字)
6. 计算匹配分:`score = (skill_tags总数 - missingSkills数) / skill_tags总数 × 10`,保留一位小数 6. 计算匹配分:`score = (skill_tags总数 - gaps条目数) / skill_tags总数 × 10`,保留一位小数
### 返回 ### 返回
@@ -71,7 +71,10 @@
"resumeName": "李华_产品经理", "resumeName": "李华_产品经理",
"targetPosition": "电商产品经理" "targetPosition": "电商产品经理"
}, },
"missingSkills": ["Python", "SQL", "数据分析", "跨部门沟通"] "gaps": [
{ "keyword": "SQL", "title": "缺少 SQL 关键词", "description": "岗位要求数据查询分析能力,简历未体现。" },
{ "keyword": "数据分析", "title": "数据分析证据不足", "description": "建议补充指标拆解与结论产出的经历。" }
]
} }
``` ```
@@ -81,13 +84,13 @@
|------|------| |------|------|
| 用户无简历 | 报错"请先创建简历" | | 用户无简历 | 报错"请先创建简历" |
| 岗位不存在 | 报错 | | 岗位不存在 | 报错 |
| skill_tags 为空 | 满分 10missingSkills 为空数组 | | skill_tags 为空 | 满分 10gaps 为空数组 |
| AI 调用失败 | 降级:全部标记为缺失,分数 0 | | AI 调用失败 | 降级:全部标记为缺失,分数 0 |
### AI Prompt ### AI Prompt
``` ```
你是一个技能匹配助手。给定岗位要求的技能标签列表和用户简历信息,判断用户简历中未覆盖的技能。 你是一个技能匹配助手。给定岗位要求的技能标签列表和用户简历信息,找出用户简历中未覆盖的技能并生成差距说明
【岗位技能标签】 【岗位技能标签】
{skill_tags} {skill_tags}
@@ -97,10 +100,15 @@
规则: 规则:
1. 逐个判断岗位技能标签,用户简历中是否体现了该技能(包括直接提及、经历中隐含的技能) 1. 逐个判断岗位技能标签,用户简历中是否体现了该技能(包括直接提及、经历中隐含的技能)
2. 只输出用户简历未覆盖的技能,必须是岗位技能标签的子集,原文输出不要修改 2. 只针对未覆盖的技能输出差距条目,已覆盖的技能不输出
3. 返回 JSON 数组格式,如:["Python", "SQL"] 3. 每个差距条目包含三个字段:
4. 如果全部覆盖,返回空数组 [] - keyword:缺失的技能,必须是岗位技能标签中的原文,不要修改
5. 只返回 JSON 数组,不要其他内容 - title:差距点的简短标题,不超过 12 个字(例如"缺少 SQL 关键词"
- description:结合岗位要求的一句说明或建议,不超过 30 个字
4. 返回 JSON 数组格式,如:
[{"keyword": "SQL", "title": "缺少 SQL 关键词", "description": "岗位要求数据查询分析能力,简历未体现。"}]
5. 如果全部覆盖,返回空数组 []
6. 只返回 JSON 数组,不要其他内容
``` ```
--- ---
@@ -128,7 +136,7 @@
- `resumeId`:以哪份简历为模板(来自差距分析返回的 resumeId,用户可能切换过简历) - `resumeId`:以哪份简历为模板(来自差距分析返回的 resumeId,用户可能切换过简历)
- `optimizeModules`:用户勾选要优化的模块,可选值:summary(个人概述)、skills(技能)、experience(过往经历) - `optimizeModules`:用户勾选要优化的模块,可选值:summary(个人概述)、skills(技能)、experience(过往经历)
- `addSkills`:用户勾选要新增的技能关键词(来自差距分析的 missingSkills - `addSkills`:用户勾选要新增的技能关键词(来自差距分析 gaps 的 keyword
### 处理流程 ### 处理流程
@@ -383,7 +391,7 @@
### 1. 差距分析 Prompt ### 1. 差距分析 Prompt
``` ```
你是一个技能匹配助手。给定岗位要求的技能标签列表和用户简历信息,判断用户简历中未覆盖的技能。 你是一个技能匹配助手。给定岗位要求的技能标签列表和用户简历信息,找出用户简历中未覆盖的技能并生成差距说明
【岗位技能标签】 【岗位技能标签】
{skill_tags} {skill_tags}
@@ -393,10 +401,15 @@
规则: 规则:
1. 逐个判断岗位技能标签,用户简历中是否体现了该技能(包括直接提及、经历中隐含的技能) 1. 逐个判断岗位技能标签,用户简历中是否体现了该技能(包括直接提及、经历中隐含的技能)
2. 只输出用户简历未覆盖的技能,必须是岗位技能标签的子集,原文输出不要修改 2. 只针对未覆盖的技能输出差距条目,已覆盖的技能不输出
3. 返回 JSON 数组格式,如:["Python", "SQL"] 3. 每个差距条目包含三个字段:
4. 如果全部覆盖,返回空数组 [] - keyword:缺失的技能,必须是岗位技能标签中的原文,不要修改
5. 只返回 JSON 数组,不要其他内容 - title:差距点的简短标题,不超过 12 个字(例如"缺少 SQL 关键词"
- description:结合岗位要求的一句说明或建议,不超过 30 个字
4. 返回 JSON 数组格式,如:
[{"keyword": "SQL", "title": "缺少 SQL 关键词", "description": "岗位要求数据查询分析能力,简历未体现。"}]
5. 如果全部覆盖,返回空数组 []
6. 只返回 JSON 数组,不要其他内容
``` ```
### 2. 定制简历 - summary 优化 Prompt ### 2. 定制简历 - summary 优化 Prompt
+13 -5
View File
@@ -28,17 +28,25 @@ _skill_gap_chain = (
) )
async def analyze_skill_gap(skill_tags: list[str], resume_json: str) -> list[str]: async def analyze_skill_gap(skill_tags: list[str], resume_json: str) -> list[dict]:
"""分析技能差距,返回缺失技能列表""" """分析技能差距,返回差距条目列表 [{keyword, title, description}]keyword 为缺失技能"""
try: try:
raw = await _skill_gap_chain.ainvoke({"skill_tags": str(skill_tags), "resume_json": resume_json}) raw = await _skill_gap_chain.ainvoke({"skill_tags": str(skill_tags), "resume_json": resume_json})
result = parse_llm_json(raw) result = parse_llm_json(raw)
if isinstance(result, list): if isinstance(result, list):
return [s for s in result if isinstance(s, str) and s in skill_tags] return result
return skill_tags # 解析异常降级:全部标记缺失 return _fallback_gaps(skill_tags) # 解析异常降级:全部标记缺失
except Exception as e: except Exception as e:
log.warning(f"AI技能差距分析失败: {e}") log.warning(f"AI技能差距分析失败: {e}")
return skill_tags # 降级:全部标记缺失 return _fallback_gaps(skill_tags) # 降级:全部标记缺失
def _fallback_gaps(skill_tags: list[str]) -> list[dict]:
"""降级:把全部岗位技能标记为缺失"""
return [
{"keyword": kw, "title": f"缺少{kw}", "description": "岗位要求该技能,简历暂未体现。"}
for kw in skill_tags
]
# ===== 定制简历 - summary 优化 ===== # ===== 定制简历 - summary 优化 =====
+10 -5
View File
@@ -3,7 +3,7 @@
注意:prompt 中的 JSON 示例花括号必须用 {{ }} 转义,避免被 ChatPromptTemplate 当作变量。 注意:prompt 中的 JSON 示例花括号必须用 {{ }} 转义,避免被 ChatPromptTemplate 当作变量。
""" """
SKILL_GAP_PROMPT = """你是一个技能匹配助手。给定岗位要求的技能标签列表和用户简历信息,判断用户简历中未覆盖的技能。 SKILL_GAP_PROMPT = """你是一个技能匹配助手。给定岗位要求的技能标签列表和用户简历信息,找出用户简历中未覆盖的技能并生成差距说明
【岗位技能标签】 【岗位技能标签】
{skill_tags} {skill_tags}
@@ -13,10 +13,15 @@ SKILL_GAP_PROMPT = """你是一个技能匹配助手。给定岗位要求的技
规则: 规则:
1. 逐个判断岗位技能标签,用户简历中是否体现了该技能(包括直接提及、经历中隐含的技能) 1. 逐个判断岗位技能标签,用户简历中是否体现了该技能(包括直接提及、经历中隐含的技能)
2. 只输出用户简历未覆盖的技能,必须是岗位技能标签的子集,原文输出不要修改 2. 只针对未覆盖的技能输出差距条目,已覆盖的技能不输出
3. 返回 JSON 数组格式,如:["Python", "SQL"] 3. 每个差距条目包含三个字段:
4. 如果全部覆盖,返回空数组 [] - keyword:缺失的技能,必须是岗位技能标签中的原文,不要修改
5. 只返回 JSON 数组,不要其他内容""" - title:差距点的简短标题,不超过 12 个字(例如"缺少 SQL 关键词"
- description:结合岗位要求的一句说明或建议,不超过 30 个字
4. 返回 JSON 数组格式,如:
[{{"keyword": "SQL", "title": "缺少 SQL 关键词", "description": "岗位要求数据查询分析能力,简历未体现。"}}]
5. 如果全部覆盖,返回空数组 []
6. 只返回 JSON 数组,不要其他内容"""
SUMMARY_OPTIMIZE_PROMPT = """你是一个简历优化助手。根据目标岗位信息,微调用户的个人概述。 SUMMARY_OPTIMIZE_PROMPT = """你是一个简历优化助手。根据目标岗位信息,微调用户的个人概述。
+8 -7
View File
@@ -79,19 +79,20 @@ class SkillGapService:
return self._gap_result(10.0, job, detail.resume, []) return self._gap_result(10.0, job, detail.resume, [])
# 4. 拼 AI 输入 # 4. 拼 AI 输入
resume_json = _build_resume_json(detail) resume_json = _build_resume_json(detail)
# 5. AI 分析 # 5. AI 分析(返回差距条目 [{keyword, title, description}]
missing = await analyze_skill_gap(skill_tags, resume_json) gaps = await analyze_skill_gap(skill_tags, resume_json)
# 6. 计算匹配分 # 6. 计算匹配分(缺失技能 = 差距条目的 keyword)
score = round((len(skill_tags) - len(missing)) / len(skill_tags) * 10, 1) missing_count = len(gaps)
return self._gap_result(score, job, detail.resume, missing) score = round((len(skill_tags) - missing_count) / len(skill_tags) * 10, 1)
return self._gap_result(score, job, detail.resume, gaps)
@staticmethod @staticmethod
def _gap_result(score: float, job: Job, resume: UserResume, missing: list[str]) -> dict: def _gap_result(score: float, job: Job, resume: UserResume, gaps: list[dict]) -> dict:
return { return {
"score": score, "score": score,
"job": {"jobId": str(job.id), "title": job.title, "skillTags": job.skill_tags or []}, "job": {"jobId": str(job.id), "title": job.title, "skillTags": job.skill_tags or []},
"resume": {"resumeId": str(resume.id), "resumeName": resume.resume_name or "", "targetPosition": resume.target_position or ""}, "resume": {"resumeId": str(resume.id), "resumeName": resume.resume_name or "", "targetPosition": resume.target_position or ""},
"missingSkills": missing, "gaps": gaps,
} }
# ===== 生成定制简历 ===== # ===== 生成定制简历 =====