hungryb's picture
Deploy Offer Catcher final public demo
c974f5d verified
Raw
History Blame Contribute Delete
8.74 kB
"""
graph.py - 技术方向匹配分析(重新实现)
不再生成虚假岗位,专注分析简历适合的技术方向
"""
import json
import os
import re
import requests
from pathlib import Path
from dotenv import load_dotenv
def _get_api_key():
"""每次调用都重新读 .env,避免模块缓存问题"""
env_path = Path(__file__).resolve().parent.parent / ".env"
if env_path.exists():
load_dotenv(dotenv_path=env_path)
api_key = os.getenv("DEEPSEEK_API_KEY", "")
if not api_key:
api_key = os.getenv("deepseek_api_key", "")
return api_key
def call_llm(prompt: str, max_tokens: int = 2000, timeout: int = 90) -> str:
"""调用 DeepSeek API,返回纯文本"""
api_key = _get_api_key()
if not api_key:
return json.dumps({"error": "未配置 DEEPSEEK_API_KEY"})
base_url = os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com/v1")
model = os.getenv("MODEL", "deepseek-chat")
try:
resp = requests.post(
f"{base_url}/chat/completions",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
json={
"model": model,
"messages": [{"role": "user", "content": prompt}],
"max_tokens": max_tokens,
"temperature": 0.3,
},
timeout=timeout,
)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"]
except Exception as e:
return json.dumps({"error": f"API 调用失败: {e}"})
def run_pipeline(resume: str, goal: str) -> dict:
"""
核心技术方向匹配分析
返回:技术方向匹配度、优势、不足、改进建议
"""
prompt = f"""你是专业的求职顾问和技术方向分析师。
## 用户简历
{resume[:3000]}
## 求职目标
{goal}
## 任务
分析这份简历,找出它适合的技术方向,并给出详细的匹配分析和改进建议。
## 技术要求
你需要分析以下常见技术方向(根据简历内容选择最相关的 3-5 个方向):
1. AI/LLM 算法(大模型、NLP、Transformer、PyTorch)
2. 推荐算法(推荐系统、CTR 预估、排序算法)
3. 数据分析(SQL、Python、统计分析、可视化)
4. 前端开发(React、Vue、TypeScript、HTML/CSS)
5. 后端开发(Python、Java、Go、微服务)
6. 计算机视觉(CV、目标检测、图像分割、OpenCV)
7. 强化学习(RL、游戏 AI、机器人)
8. 数据工程(大数据、Spark、Hadoop、数据仓库)
## 输出格式
以 JSON 格式返回,结构如下(注意字段名必须完全一致,确保 JSON 格式正确):
```json
{{
"resume_analysis": {{
"skills": ["技能1", "技能2"],
"projects": ["项目1", "项目2"],
"experience": ["经历1", "经历2"],
"education": "教育背景字符串",
"strengths": ["优势1", "优势2"],
"weaknesses": ["不足1", "不足2"]
}},
"direction_matches": [
{{
"direction": "技术方向名称(如:AI/LLM 算法)",
"match_score": 85,
"level": "高匹配/中匹配/低匹配",
"description": "这个方向的一般要求(1-2句话)",
"your_advantages": ["你的优势1", "你的优势2"],
"your_gaps": ["你的不足1", "你的不足2"],
"how_to_improve": [
{{
"action": "具体行动(如:做一个 RAG 项目)",
"time_needed": "需要的时间(如:2 周)",
"expected_gain": "预期提升(如:+15 分)"
}}
],
"reference_resources": ["学习资源1", "学习资源2"]
}}
],
"overall_suggestions": [
"总体建议1",
"总体建议2"
],
"action_plan": {{
"today": ["今天应该做什么"],
"this_week": ["本周应该做什么"],
"this_month": ["本月应该做什么"]
}}
}}
```
## 要求
1. direction_matches 应该包含 3-5 个最相关的技术方向
2. 每个方向都要有具体的改进建议(actionable)
3. 改进建议要具体、可执行(不要说"学习 XX",要说"做一个 XX 项目")
4. 只返回 JSON,不要其他解释
5. 确保 JSON 格式正确,不要有 trailing commas
"""
# 调用 LLM 分析
result = call_llm(prompt, max_tokens=4000, timeout=120)
# 调试:保存原始返回
import time
debug_dir = Path(__file__).resolve().parent.parent / "debug"
debug_dir.mkdir(exist_ok=True)
with open(debug_dir / f"llm_raw_{int(time.time())}.txt", "w", encoding="utf-8") as f:
f.write(result)
# 解析结果
try:
# 检查是否是错误信息
if result.strip().startswith("{"):
err_obj = json.loads(result)
if "error" in err_obj:
return {
"error": err_obj["error"],
"direction_matches": [],
"resume_analysis": {},
}
# 提取 JSON(支持 ```json 代码块 或 裸 JSON)
json_match = re.search(r"```json\s*([\s\S]*?)\s*```", result)
if not json_match:
json_match = re.search(r"\{[\s\S]*\}", result)
if json_match:
json_str = json_match.group(1) if json_match.lastindex else json_match.group()
# 清理可能的 markdown 代码块标记
json_str = re.sub(r"^```json\s*", "", json_str)
json_str = re.sub(r"\s*```$", "", json_str)
# 尝试修复常见的 JSON 错误
json_str = re.sub(r",\s*\}", "}", json_str)
json_str = re.sub(r",\s*\]", "]", json_str)
report = json.loads(json_str)
else:
return {
"error": "LLM 返回格式错误,无法解析 JSON",
"raw": result[:500],
"direction_matches": [],
"resume_analysis": {},
}
# 确保有必要的字段
if "direction_matches" not in report:
report["direction_matches"] = []
if "resume_analysis" not in report:
report["resume_analysis"] = {}
if "overall_suggestions" not in report:
report["overall_suggestions"] = []
if "action_plan" not in report:
report["action_plan"] = {"today": [], "this_week": [], "this_month": []}
return report
except json.JSONDecodeError as e:
return {
"error": f"解析 LLM 返回失败: {e}",
"raw": result[:500],
"direction_matches": [],
"resume_analysis": {},
}
except Exception as e:
return {
"error": f"处理 LLM 返回失败: {e}",
"raw": result[:500],
"direction_matches": [],
"resume_analysis": {},
}
if __name__ == "__main__":
test_resume = """张三
北京邮电大学 - 计算机科学与技术 - 本科 - 2026 届
手机:13800000000 | 邮箱:zhangsan@bupt.edu.cn
教育背景
2022.09-2026.06 北京邮电大学 计算机科学与技术 本科
GPA:3.8/4.0 专业排名:15/200
实习经历
2025.06-2025.09 腾讯 大模型算法实习生
- 参与混元大模型训练,负责 RLHF 数据处理
- 使用 PyTorch 实现 DPO 算法,提升模型效果 5%
- 负责数据清洗与质量评估,处理 10 万+ 条训练数据
项目经历
2024.09-2025.06 基于 RAG 的简历匹配系统
- 使用 LangChain + DeepSeek API 构建简历-岗位匹配系统
- 实现向量检索(FAISS),召回准确率达 85%
- 部署 Streamlit 应用,支持 PDF 简历解析
技能
Python, PyTorch, LangChain, DeepSeek API, Git, Linux
"""
test_goal = "北京 AI 大模型算法实习"
print("开始测试 run_pipeline(新版本:技术方向匹配分析)...")
result = run_pipeline(test_resume, test_goal)
print("\n" + "="*50)
print("测试结果:")
print(json.dumps(result, ensure_ascii=False, indent=2))
if "direction_matches" in result and result["direction_matches"]:
print("\n" + "="*50)
print("技术方向匹配分析:")
for i, dm in enumerate(result["direction_matches"], 1):
print(f"\n{i}. {dm.get('direction')} - 匹配度: {dm.get('match_score')}%")
print(f" 级别: {dm.get('level')}")
print(f" 你的优势: {', '.join(dm.get('your_advantages', []))}")
print(f" 你的不足: {', '.join(dm.get('your_gaps', []))}")
print(f" 改进建议: ")
for imp in dm.get('how_to_improve', []):
print(f" - {imp.get('action')} ({imp.get('time_needed')}) → {imp.get('expected_gain')}")