""" 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')}")