当前位置: 首页 > news >正文

HireMind:从 0 到 1,用 LangGraph 打造 7 Agent 协作的智能招聘平台

一、引言

招聘场景中,HR 每天要面对大量简历,手工评估不仅耗时,而且标准不一。能不能让 AI 来完成这件事?从简历解析、技能匹配,到面试题生成、综合评分,全流程自动化——听起来简单,但每一步都需要不同的能力:解析需要抽取结构化信息,匹配需要检索知识库,评分需要综合判断。单次 LLM 调用无法覆盖这么长的链路。

HireMind 是我独立设计实现的一个开源项目,基于 DeepSeek + LangGraph 构建了 7 个专业 Agent 协作的智能招聘评估平台。如果要用一句话概括它的核心思路,那就是:把复杂任务拆成多个小步骤,每个步骤交给专门的 Agent 处理,用状态图编排它们之间的流转逻辑。

在线演示 👉 http://36.151.144.50

Git 仓库 👉 https://gitee.com/monan1122/hire-mind

二、架构总览

项目采用 Vue 3 + Java Spring Boot 3 + Python FastAPI 三层架构。前端负责交互,Java 层管理业务数据(用户、岗位、简历、报告),Python 层承载所有 AI 能力。Java 通过 WebClient 异步调用 Python 的/evaluate接口,拿到评估报告后存入 MySQL,前端轮询展示结果。

用户上传 PDF 简历 ↓ Java 业务层 (Spring Boot) ← MySQL / Redis ↓ WebClient POST /evaluate Python AI 层 (FastAPI) ↓ LangGraph StateGraph 编排 ↓ Resume → Desensitize → Skill → Router → Interview → Score → Report

三、7 Agent 协作 Pipeline

LangGraph 的StateGraph是整个 AI 层的骨架。每个 Agent 接收一个共享的AgentContext对象,处理完后更新状态,传递给下一个节点。关键设计在于条件边(Conditional Edge)——每个节点执行完后,根据结果走不同的分支。

builder.add_node("resume", resume_node) builder.add_node("resume_fallback", resume_fallback) builder.add_node("desensitization", desensitize_node) builder.add_node("skill", skill_node) builder.add_node("condition_router", condition_router_node) builder.add_node("interview", interview_node) builder.add_node("score", score_node) builder.add_node("score_fallback", score_fallback) builder.add_node("report", report_node) # Resume 节点:成功 → 脱敏,重试 → 再试,降级 → 正则兜底,失败 → 终止 builder.add_conditional_edges( "resume", lambda ctx: route_after_agent(ctx, "resume"), { "success": "desensitization", "retry": "resume", "fallback": "resume_fallback", "fail": "error_handler", } )

真正有意思的是Condition Router。如果候选人的技能匹配度不到 50%,说明他很可能不适合这个岗位,这时候再花 token 去生成面试题纯粹是浪费。于是我们在 Skill Agent 之后插入了一个纯规则节点:

async def condition_router_node(ctx: AgentContext) -> AgentContext: match_score = ctx.skill_result.match_score if ctx.skill_result else 0 ctx.skip_interview = match_score < 50 return ctx def route_after_condition(ctx: AgentContext) -> str: return "score" if ctx.skip_interview else "interview"

别看这只是个简单的 if-else,在每天 100 份简历的场景下,假设 30% 匹配度不达标,就省下了 30 次 LLM 调用。

四、异常处理:让 Pipeline 不会一碰就碎

Agent Pipeline 是串行的,任何一个节点挂了都可能让整个任务失败。我给每个 Agent 设计了三级异常处理:

  • Tier 1 可重试:API 超时、网络抖动 → 指数退避重试,最多 3 次
  • Tier 2 可降级:JSON 解析失败、字段缺失 → 规则兜底,不中断流程
  • Tier 3 终止:核心服务不可用 → 返回 PartialResult + 错误原因
async def call_deepseek_safe(prompt: str, node: str, ctx: AgentContext) -> str: max_retries = MAX_RETRIES.get(node, 1) for attempt in range(max_retries): try: content = await deepseek.chat(prompt, model=model) return content except Exception as e: ctx.retry_counts[node] = ctx.retry_counts.get(node, 0) + 1 if attempt < max_retries - 1: await asyncio.sleep(2 ** attempt) continue raise

Score Agent 的降级策略是一个典型的纯规则兜底:

async def score_fallback(ctx: AgentContext) -> AgentContext: match = ctx.skill_result.match_score if ctx.skill_result else 0 project_count = len(ctx.resume_desensitized.projects) if ctx.resume_desensitized else 0 raw_score = match * 0.7 + project_count * 5 ctx.score_result = { "final_score": min(raw_score, 100), "recommendation": "进入技术面试" if match >= 60 else "建议淘汰", "strengths": [], "weaknesses": ["评分系统暂时不可用"], } ctx.error = None return ctx

实践验证:即使 DeepSeek API 完全不可用,系统仍能基于关键词匹配和规则评分返回可用的评估结果——matchScore × 0.7 + 项目数 × 5,简单但有效。

五、混合检索 RAG

岗位知识库和面试题库的查询有两种截然不同的需求:有时是精确技能标签匹配("Java"、"Redis"),有时是语义描述匹配("有分布式系统经验")。纯 ES BM25 解决不了语义问题,纯 Milvus 向量对精确标签效果差。于是两边都查,然后 RRF 融合:

RRF_K = 60 def _rrf_fusion(es_results, milvus_results, k=RRF_K): scores = {} for rank, doc in enumerate(es_results): scores[hash(str(doc.get("id", "")))] = 1 / (k + rank + 1) for rank, doc in enumerate(milvus_results): key = hash(str(doc.get("id", ""))) scores[key] = scores.get(key, 0) + 1 / (k + rank + 1) merged = {} for doc in es_results + milvus_results: did = hash(str(doc.get("id", ""))) if did in scores: merged[did] = {**doc, "_fusion_score": scores[did]} return sorted(merged.values(), key=lambda x: x["_fusion_score"], reverse=True)

同时 Milvus 检索做了 3 秒超时保护,不可用时自动降级为纯 ES:

async def _safe_milvus_search(collection: str, query: str, top_k: int): try: return await asyncio.wait_for( milvus_client.search(collection, query, top_k), timeout=3.0 ) except (asyncio.TimeoutError, Exception): return []

六、数据脱敏与合规

所有发往 DeepSeek API 的数据在离开本地网络前必须脱敏。但教育和技能信息又必须保留——删掉学历和专业就没法正确匹配了。这是一个「选择性脱敏」的问题。

实现方式很直接:正则匹配 + 占位符替换,零外部依赖,单次 < 5ms。关键是在 Pipeline 中找准插入点——脱敏节点必须在 Resume Agent 之后、任何数据流向外部 API 之前:

async def desensitize_node(ctx): if ctx.resume_raw: ctx.resume_desensitized = desensitize_resume(ctx.resume_raw) return ctx

脱敏只针对 PII(个人身份信息),教育/技能/项目描述完整保留,不影响下游的匹配和评分准确度。

七、写在最后

这个项目从最初的单文件 Python 脚本,到后来加入 LangGraph 编排、Java 业务层、Vue 前端,经历了好几轮迭代。几个关键体会:

  1. 拆解比堆 prompt 更可靠:把大任务拆成 7 个小 Agent,每个只做一件事,调试和优化都更可控
  2. 降级比完美更重要:API 会超时、JSON 会解析失败、服务会不可用——每个节点都有 Plan B 比追求 100% 准确率更实际
  3. 脱敏不是可选项:从第一版就把脱敏做进 Pipeline,比后期补救省心得多

欢迎试用和提 Issue。在线演示 👉 http://36.151.144.50,管理员账号admin/admin123,候选人账号user1/user123

http://www.jsqmd.com/news/1093082/

相关文章:

  • GPU中专业术语
  • Visual C++运行库终极修复方案:5分钟彻底解决Windows软件启动问题的完整指南
  • With 注入通用属性
  • 动画角色机器人化:从《冰雪奇缘》Olaf看强化学习与机械设计创新
  • 基于复合粒子群优化的模糊神经预测控制的研究附Matlab代码
  • go-sqlmock
  • AI数字人平台热门十三问|必火AI数字人全维度专业解答
  • 如何高效优化电子书阅读体验:Kindle Comic Converter的完整漫画转换方案
  • 卡梅德生物技术快报|羊驼纳米抗体文库筛选实操全流程:天然 / 合成文库构建与淘选参数汇总
  • Windows虚拟显示器终极指南:Parsec VDD免费开源解决方案
  • 从 0 开始学 Python:装好环境,写一下demo实例
  • Kali Linux下使用apk2url从APK提取URL与IP的实战指南
  • 高效智能的网盘直链下载解决方案:一站式专业级工具LinkSwift深度解析
  • GPU硬件故障排查终极指南:5分钟完成显卡内存稳定性检测
  • 收藏!小白程序员必看:如何将大模型Agent从Demo成功落地工程实践?
  • 2026年大模型知识库优化实战?GEO策略如何重塑TOB品牌获客新路径
  • 收藏!小白程序员必看:一文搞懂AI Agent核心原理与实战代码
  • [Android] iVCam(手机变电脑摄像头)专业版
  • 01 TCP 协议是流式协议
  • Lean 4实战指南:5个步骤掌握下一代定理证明编程语言
  • Fatal error: require(): Failed opening required...” 以及如何彻底避免它再次出现
  • 2026年AI Agent大爆发!小白程序员必看:收藏这份从入门到精通指南,抓住时代红利!
  • 5个技巧轻松解决经典游戏兼容问题:开源dxwrapper完全指南
  • Vibe Coding:说人话就能做软件,超简单开发流程全讲明白
  • Netty 高性能网络编程:从零构建高并发服务器
  • 【TSP问题】基于帝企鹅算法AFO求解单仓库多旅行商问题MTSP附Matlab代码
  • XSS防御实战:从同源策略到CSP的纵深安全体系构建
  • Kafka2.4-Windows安装教程
  • 无需同看同一张图:跨被试神经表征对齐的VAE新范式
  • 一文吃透Java IO流!从底层原理到实战代码(新手必看)