尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

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

HireMind:从 0 到 1,用 LangGraph 打造 7 Agent 协作的智能招聘平台
📅 发布时间:2026/6/30 2:06:01

一、引言

招聘场景中,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。

相关新闻

  • 深入解析TSB83AA23芯片:总线仲裁、PCI配置与驱动开发实战
  • GPU中专业术语
  • 动画角色机器人化:从《冰雪奇缘》Olaf看强化学习与机械设计创新

最新新闻

  • 深入了解进程:C++开发者不可不知的关键知识点
  • Codex切换ChatGPT账号与第三方API后报错、会话不可见的处理方法
  • 【实测】基于 K100-AI 部署 Hermes Agent 跑自主智能体操作指南
  • 什么让 CUDA 程序性能大幅提升?GPU 寄存器与固定内存的秘密大公开
  • JumpServer+MaxKB联合方案:打破运维僵局,实现安全与效率双赢!
  • Prompt设计6策略:从一次性生成到多轮迭代的工程方法

日新闻

  • 【计算机毕业设计案例】基于 Spring Boot+Vue 的电影售票系统设计与实现 前后端分离架构下影院在线购票管理平台(程序+文档+讲解+定制)
  • 到底 TMD 用哪个: npm, pnpm, Yarn, Bun, Deno? 傻瓜, 当然用 npm 啦
  • Google限制Meta使用Gemini模型 凸显AI授权竞争白热化

周新闻

  • Windows字体自定义终极方案:No!! MeiryoUI完全指南
  • Deepin Boot Maker:告别命令行,3分钟制作Linux启动盘的智能解决方案
  • Plain Craft Launcher 2:重新定义你的Minecraft游戏体验

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号