Files
offerpai_python_ai/app/ai/skill_gap_analyzer/analyzer.py
T
2026-06-24 10:32:26 +08:00

174 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""技能差距分析 AI 引擎
差距分析 + 定制简历优化 + Agent 规划/执行。
依赖:LLM 枚举、skill_gap_analyzer/prompts
"""
import asyncio
import time
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from app.ai.model_config import SkillGapModel
from app.ai.skill_gap_analyzer.prompts import (
SKILL_GAP_PROMPT, SUMMARY_OPTIMIZE_PROMPT, EXPERIENCE_OPTIMIZE_PROMPT,
AGENT_PLAN_PROMPT, AGENT_MODULE_EDIT_PROMPT, AGENT_MODULE_ADD_PROMPT, MODULE_SCHEMAS,
)
from app.core.logger import log
from app.tool.json_helper import parse_llm_json
# ===== 差距分析 =====
_skill_gap_chain = (
ChatPromptTemplate.from_messages([("system", SKILL_GAP_PROMPT), ("human", "请开始分析。")])
| SkillGapModel.ANALYSIS
| StrOutputParser()
)
async def analyze_skill_gap(skill_tags: list[str], resume_json: str) -> list[dict]:
"""分析技能差距,返回差距条目列表 [{keyword, title, description}]keyword 为缺失技能"""
try:
raw = await _skill_gap_chain.ainvoke({"skill_tags": str(skill_tags), "resume_json": resume_json})
result = parse_llm_json(raw)
if isinstance(result, list):
return result
return _fallback_gaps(skill_tags) # 解析异常降级:全部标记缺失
except Exception as e:
log.warning(f"AI技能差距分析失败: {e}")
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_optimize_chain = (
ChatPromptTemplate.from_messages([("system", SUMMARY_OPTIMIZE_PROMPT), ("human", "请开始优化。")])
| SkillGapModel.SUMMARY
| StrOutputParser()
)
async def optimize_summary(job_title: str, add_skills: list[str], original_summary: str) -> str:
"""优化个人概述,融入技能关键词"""
t0 = time.monotonic()
try:
result = await _summary_optimize_chain.ainvoke({
"job_title": job_title, "add_skills": "、".join(add_skills) if add_skills else "无",
"original_summary": original_summary or "暂无",
})
log.info(f"AI优化summary完成 ({round(time.monotonic() - t0, 2)}s)")
return result
except Exception as e:
log.warning(f"AI优化summary失败: {e} ({round(time.monotonic() - t0, 2)}s)")
return original_summary
# ===== 定制简历 - experience 优化 =====
_experience_optimize_chain = (
ChatPromptTemplate.from_messages([("system", EXPERIENCE_OPTIMIZE_PROMPT), ("human", "请开始优化。")])
| SkillGapModel.EXPERIENCE
| StrOutputParser()
)
async def optimize_module(job_title: str, job_description: str, module_data: str) -> list | dict | None:
"""优化单条经历描述,返回修改后的记录数据"""
t0 = time.monotonic()
try:
raw = await _experience_optimize_chain.ainvoke({
"job_title": job_title, "job_description": job_description or "",
"original_module_data": module_data,
})
result = parse_llm_json(raw)
log.info(f"AI优化经历模块完成 ({round(time.monotonic() - t0, 2)}s)")
return result
except Exception as e:
log.warning(f"AI优化经历模块失败: {e} ({round(time.monotonic() - t0, 2)}s)")
return None
# ===== Agent - 规划 =====
_plan_chain = (
ChatPromptTemplate.from_messages([("system", AGENT_PLAN_PROMPT), ("human", "请分析用户指令。")])
| SkillGapModel.AGENT_PLAN
| StrOutputParser()
)
async def plan_edit(job_title: str, job_description: str, resume_json: str,
chat_history: str, instruction: str) -> dict | None:
"""Agent 规划:分析用户指令,返回原子操作列表或对话回复"""
try:
raw = await _plan_chain.ainvoke({
"job_title": job_title, "job_description": job_description,
"resume_json": resume_json,
"chat_history": chat_history, "instruction": instruction,
})
result = parse_llm_json(raw)
return result if isinstance(result, dict) else None
except Exception as e:
log.warning(f"AI规划失败: {e}")
return None
# ===== Agent - 单条记录修改 =====
_record_edit_chain = (
ChatPromptTemplate.from_messages([("system", AGENT_MODULE_EDIT_PROMPT), ("human", "请执行修改。")])
| SkillGapModel.AGENT_EDIT
| StrOutputParser()
)
async def execute_record_edit(job_title: str, job_description: str, instruction: str,
chat_history: str, module_schema: str,
record_data: str) -> dict | list | None:
"""修改单条记录:根据指令修改指定记录数据"""
try:
raw = await _record_edit_chain.ainvoke({
"job_title": job_title, "job_description": job_description,
"instruction": instruction, "chat_history": chat_history,
"module_schema": module_schema, "record_data": record_data,
})
return parse_llm_json(raw)
except Exception as e:
log.warning(f"AI单条记录修改失败: {e}")
return None
# ===== Agent - 新增记录 =====
_record_add_chain = (
ChatPromptTemplate.from_messages([("system", AGENT_MODULE_ADD_PROMPT), ("human", "请生成新记录。")])
| SkillGapModel.AGENT_ADD
| StrOutputParser()
)
async def execute_record_add(job_title: str, job_description: str, instruction: str,
chat_history: str, module_schema: str) -> dict | None:
"""新增一条记录:根据指令生成新记录"""
try:
raw = await _record_add_chain.ainvoke({
"job_title": job_title, "job_description": job_description,
"instruction": instruction, "chat_history": chat_history,
"module_schema": module_schema,
})
result = parse_llm_json(raw)
return result if isinstance(result, dict) else None
except Exception as e:
log.warning(f"AI新增记录失败: {e}")
return None