6897 words
34 minutes
Hr-Helper Agent
NOTE输入输出、节点流转、按需求拆分任务、多Agent框架、Sub-Agent 职责划分
多 Agent 协作职责划分
整体协作图
┌─────────────────┐ │ Orchestrator │ │ (Router) │ │ ───────────── │ │ 意图分类 │ │ 任务分发 │ │ 结果聚合 │ └────────┬────────┘ │ ┌───────────────────┼───────────────────┐ │ │ │ ▼ ▼ ▼ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ JD Worker │ │ Resume Worker │ │ Match Worker │ │ Pool (1-2) │ │ Pool (5-10) │ │ Pool (3-5) │ └────────┬───────┘ └────────┬───────┘ └────────┬───────┘ │ │ │ ▼ ▼ ▼ JDSchema ResumeSchema MatchResult1. Orchestrator (Router)
| 项目 | 内容 |
|---|---|
| 类型 | 调度者(不是执行者) |
| 模型 | Haiku 4.5(简单路由,便宜) |
| 职责 | 意图分类、任务分发、结果聚合 |
输入:
HR: “筛选 5 年以上 Python 工程师,重点看架构能力”
Orchestrator Think:
- 意图识别:这是一个「筛选请求」
- 需要 JD 解析 + 批量简历匹配
- 分发 JD Worker → 等待 JDSchema
- 分发 Resume Worker → 等待批量 ResumeSchema
- 分发 Match Worker → 等待 MatchResults
- 聚合结果返回给 HR
输出:
{ "intent": "screen_candidates", "tasks": [ {"task_id": "t1", "agent": "JDWorker", "input": "..."}, {"task_id": "t2", "agent": "ResumeWorker", "input": "resume_batch_001"}, {"task_id": "t3", "agent": "MatchWorker", "input": {"jd: "t1", "resumes": "t2"}} ], "wait_for": ["t3"]}2. JD Worker
| 项目 | 内容 |
|---|---|
| 类型 | 单任务执行者 |
| 模型 | Sonnet 4.6(深度理解 JD) |
| 副本 | 1-2 个(JD 解析不频繁) |
职责:将 JD 文本解析为结构化 JDSchema
输入:
“招聘高级 Python 后端开发,要求 5 年以上经验,熟悉 FastAPI、Django,有微服务架构经验优先,具备良好的团队协作能力和沟通能力,本科及以上学历”
处理流程:
- LLM 提取关键实体(技能、经验、学历)
- 构建技能树 + 权重
- 区分 required vs optional
- 异常检测(技能矛盾、要求不合理)
- 输出 JDSchema
输出:
{ "job_id": "jd_001", "job_title": "高级 Python 后端开发", "硬技能": [ {"skill": "Python", "weight": 0.3, "required": true, "level": "expert"}, {"skill": "FastAPI", "weight": 0.2, "required": true, "level": "advanced"}, {"skill": "Django", "weight": 0.15, "required": true, "level": "advanced"}, {"skill": "微服务架构", "weight": 0.1, "required": false, "level": "intermediate"} ], "软技能": [ {"skill": "团队协作", "weight": 0.15, "required": true}, {"skill": "沟通能力", "weight": 0.1, "required": true} ], "经验年限": {"min": 5}, "学历": {"min": "本科"}, "jd_embedding": [0.123, ...]}3. Resume Worker
| 项目 | 内容 |
|---|---|
| 类型 | 单任务执行者 |
| 模型 | Haiku 4.5(高吞吐,便宜) |
| 副本 | 5-10 个(简历量大) |
| 特点 | 无状态,可水平扩展 |
职责:解析简历为结构化 ResumeSchema
输入:
resume_file: PDF/DOCX/IMG
candidate_id: “cand_001”
处理流程:
1. 格式检测(PDF/DOCX/HTML/IMG) │ ▼2. 文本提取 - PDF: pdf-parse - DOCX: python-docx - IMG: Tesseract OCR │ ▼3. LLM 结构化提取 - 姓名、联系方式 - 工作经历(公司、职位、时间、描述) - 教育背景(学历、专业、毕业年份) - 技能清单(技能名、年限、级别) │ ▼4. 向量化(用于向量召回) │ ▼5. 输出 ResumeSchema输出:
{ "candidate_id": "cand_001", "name": "张三", "工作经历": [ { "company": "XX科技", "title": "高级工程师", "duration": "2020.03 - 至今", "years": 4, "description": "负责后端架构设计,使用 FastAPI 重构微服务...", "skills_extracted": ["Python", "FastAPI", "微服务"] } ], "教育背景": {"degree": "本科", "major": "计算机"}, "技能清单": { "Python": {"level": "expert", "years": 6}, "FastAPI": {"level": "advanced", "years": 3} }, "总工作经验": 6, "resume_embedding": [0.456, ...]}4. Match Worker
| 项目 | 内容 |
|---|---|
| 类型 | 单任务执行者 |
| 模型 | Sonnet 4.6(深度匹配+推理) |
| 副本 | 3-5 个 |
| 特点 | 核心计算模块,最耗时 |
职责:计算 JD 与简历的匹配度 + 生成解释
输入:
{ "jd": JDSchema, "candidate": ResumeSchema}处理流程:
1. 硬技能匹配 (40%) - 技能级别对比:Python expert ≥ JD 要求 expert ✓ - 技能是否存在:简历有 FastAPI,JD 要求 FastAPI ✓ │ ▼2. 软技能匹配 (20%) - 语义相似度计算 - 从工作描述推断软技能 │ ▼3. 经验年限匹配 (25%) - 简历 6 年 ≥ JD 要求 5 年 ✓ │ ▼4. 学历匹配 (15%) - 简历本科 ≥ JD 要求本科 ✓ │ ▼5. 优选条件加分 (5%) - 简历有微服务经验,属于优选条件 +20% 加分 │ ▼6. 综合得分计算 36.8 + 15.6 + 25.0 + 15.0 + 1.0 = 85.6 分 │ ▼7. Explanation 生成 - 为什么推荐/不推荐 - 匹配的优势 - 潜在风险 - 面试重点输出:
{ "candidate_id": "cand_001", "total_score": 85.6, "recommendation": "强烈推荐", "breakdown": { "硬技能": {"score": 92, "weighted": 36.8}, "软技能": {"score": 78, "weighted": 15.6}, "经验": {"score": 100, "weighted": 25.0}, "学历": {"score": 100, "weighted": 15.0}, "优选条件": {"score": 20, "weighted": 1.0} }, "explanation": { "summary": "综合得分 85.6,与岗位高度匹配", "strengths": [ "Python 专家级别(6年),超过岗位要求", "FastAPI/Django 技能与岗位吻合", "具备微服务架构经验(优选条件加分)" ], "concerns": ["软技能建议面试核实"], "missing": [], "interview_focus": ["微服务架构设计思路", "团队协作角色"] }, "confidence": 0.89}5. Worker 间协作时序
HR Request │ ▼┌─────────────────────────────────────────────────────────────────┐│ Orchestrator ││ 1. 意图分类:「筛选候选人」 ││ 2. 任务分发 │└─────────────────────────────────────────────────────────────────┘ │ ├──────────────────────────────┐ ▼ ▼┌──────────────┐ ┌──────────────┐│ JD Worker │ │ Resume Repo ││ │ │ (批量读取) ││ 解析 JD │ │ │└──────┬───────┘ └──────┬───────┘ │ │ │ JDSchema │ ResumeSchema[] │ │ │ ┌─────────────────┘ │ │ ▼ ▼┌──────────────────────────────────┐│ Match Worker Pool ││ ┌────────┐ ┌────────┐ ┌────────┐││ │Match #1│ │Match #2│ │Match #N│││ └────────┘ └────────┘ └────────┘││ 并行计算每个候选人的匹配度 │└──────────────────────────────────┘ │ │ MatchResults[] │ ▼┌──────────────────────────────────┐│ Orchestrator ││ 聚合 + 排序 + 分页 ││ 返回 Top-K 给 HR │└──────────────────────────────────┘6. 职责边界清晰化
| 问题 | 解答 |
|---|---|
| JD Worker 太多? | JD 解析不频繁,1-2 个足够 |
| Resume Worker 太少? | 简历量大,需要 5-10 个并行 |
| Match Worker 挂了? | Orchestrator 检测超时,重新分发 |
| JD 还没解析完就匹配? | Orchestrator 等待 t1 完成后再分发 t3 |
| 两个 Worker 抢资源? | Redis Queue 内部实现,不暴露给 Agent |
7. 与传统 Orchestrator-Workers 的区别
| 传统模式 | 本设计 |
|---|---|
| Orchestrator 决定「谁来执行」 | Orchestrator 只决定「做什么」,Queue 决定「谁来做」 |
| Worker 是通用执行者 | Worker 是专业化 agent(JD专用、Match专用) |
| 结果直接返回 | 统一经过 Orchestrator 聚合 |
这样设计的好处是:专业化比通用化更精准,JD Worker 专注理解招聘需求,Match Worker 专注匹配逻辑。
简历知识库存储什么?
1. 数据分层
┌─────────────────────────────────────────────────────────────────┐ │ 简历知识库四层架构 │ ├─────────────────────────────────────────────────────────────────┤ │ L1: 原始文件层 (Raw Files) │ │ - PDF/DOCX/HTML/IMG 原文件 │ │ - 用途: 存档、复核、OCR 重新处理 │ ├─────────────────────────────────────────────────────────────────┤ │ L2: 结构化数据层 (Structured Schema) │ │ - ResumeSchema (JSON) │ │ - 用途: 业务查询、筛选、排序 │ ├─────────────────────────────────────────────────────────────────┤ │ L3: 语义索引层 (Semantic Index) │ │ - 向量 embedding (技能、工作描述) │ │ - 用途: 语义相似召回 │ ├─────────────────────────────────────────────────────────────────┤ │ L4: 知识图谱层 (Knowledge Graph) │ │ - 技能实体关系 │ │ - 候选人-公司-技能-项目的图关系 │ │ - 用途: 跨维度推理、同义词扩展 │ └─────────────────────────────────────────────────────────────────┘2. 存储选型与数据内容
┌───────────────┬────────────────────────┬────────────────────────────────────────┐ │ 存储层 │ 技术选型 │ 存储内容 │ ├───────────────┼────────────────────────┼────────────────────────────────────────┤ │ L1 原始文件 │ S3 / Blob Storage │ PDF/DOCX/IMG 原始文件 │ ├───────────────┼────────────────────────┼────────────────────────────────────────┤ │ L2 结构化数据 │ PostgreSQL │ ResumeSchema JSON + 业务字段 │ ├───────────────┼────────────────────────┼────────────────────────────────────────┤ │ L3 向量索引 │ Weaviate / Pinecone │ skill_embedding, description_embedding │ ├───────────────┼────────────────────────┼────────────────────────────────────────┤ │ L4 知识图谱 │ Neo4j / Amazon Neptune │ 技能图谱、公司关系、项目关系 │ └───────────────┴────────────────────────┴────────────────────────────────────────┘3. 读写分离
┌─────────────────────────────────────────────────────────────────┐ │ 读写分离架构 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 写操作 (简历上传、JD创建) │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ Primary │ (PostgreSQL Primary) │ │ │ Write DB │ │ │ └──────┬──────┘ │ │ │ │ │ │ Streaming Replication │ │ ▼ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ Replica 1 │ │ Replica 2 │ │ │ │ (读) │ │ (读) │ │ │ └─────────────┘ └─────────────┘ │ │ │ │ 读操作 (筛选、搜索) │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ PgBouncer │ (读写分离路由) │ │ │ (连接池) │ │ │ └──────┬──────┘ │ │ │ │ │ ├─── Read 1 ──▶ Replica 1 │ │ │ │ │ └─── Read 2 ──▶ Replica 2 │ │ │ └─────────────────────────────────────────────────────────────────┘高可用
1. 基建
┌────────────────────┬─────────────────────┬──────────────┐ │ 组件 │ 高可用策略 │ 故障切换时间 │ ├────────────────────┼─────────────────────┼──────────────┤ │ 多区域部署 │ 主从 + 异步复制 │ 分钟级 │ ├────────────────────┼─────────────────────┼──────────────┤ │ Kubernetes Cluster │ 多 Master 节点 │ 秒级 │ ├────────────────────┼─────────────────────┼──────────────┤ │ 负载均衡器 │ 健康检查 + 自动摘除 │ 秒级 │ ├────────────────────┼─────────────────────┼──────────────┤ │ DNS │ TTL 多值 + 智能解析 │ 分钟级 │ └────────────────────┴─────────────────────┴──────────────┘2. 数据库层高可用
┌─────────────────────────────────────────────────────────────────┐ │ PostgreSQL 高可用架构 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ │ │ │ Client │ │ │ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ PgBouncer │ (连接池 + 读写分离路由) │ │ └──────┬──────┘ │ │ │ │ │ ├────────────────────┬────────────────────┐ │ │ ▼ ▼ ▼ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Primary │◀────│ Standby 1 │◀────│ Standby 2 │ │ │ │ (写) │ 同步 │ (热备) │ 同步 │ (热备) │ │ │ └──────┬──────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ │ Streaming Replication │ │ ▼ │ │ ┌─────────────┐ │ │ │ Read │ (读副本,用于报表/离线分析) │ │ │ Replica 3 │ │ │ └─────────────┘ │ │ │ │ 自动故障切换: PgPool-II 或 Patroni + etcd │ └─────────────────────────────────────────────────────────────────┘3. MQ高可用
redis cluster 哨兵4. 知识库高可用
读写分离高并发
4.1 简历处理高并发
┌─────────────────────────────────────────────────────────────────────────────┐ │ 简历处理高并发架构 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 简历上传 │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ API Gateway │ (限流 + Auth + 路由) │ │ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ Upload S3 │ (直接上传到对象存储,避开 API 服务器) │ │ │ Pre-signed URL │ │ │ └──────┬──────┘ │ │ │ │ │ │ Async Event │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ Redis Stream (消息队列) │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ resume:uploaded │ resume:uploaded │ resume:uploaded ... │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ ▲ ▲ ▲ │ │ │ │ │ │ │ │ │ │ └─────────┼────────────────────┼────────────────────┼───────────────┘ │ │ │ │ │ │ │ │ │ │ │ │ ┌────────┴───────┐ ┌────────┴───────┐ ┌────────┴───────┐ │ │ │ Worker Group A │ │ Worker Group B │ │ Worker Group N │ │ │ │ (处理分区 0-99) │ │ (处理分区 100-199)│ │ (处理分区 200-299)│ │ │ └────────┬───────┘ └────────┬───────┘ └────────┬───────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ Consumer Group (消费者组) │ │ │ │ 同一消费者组内的 Worker 竞争消费同一分区 │ │ │ │ 不同消费者组可以独立处理同一消息 │ │ │ │ │ │ │ │ Group A: 解析 ──▶ 写入 PG ──▶ 触发 Vector Agent │ │ │ │ Group B: 解析 ──▶ 触发 Graph Agent ──▶ 更新质量分 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘性能指标
┌─────────────────┬────────────────┬──────────┬─────────────┐ │ 指标 │ 正常范围 │ 告警阈值 │ 处理策略 │ ├─────────────────┼────────────────┼──────────┼─────────────┤ │ API P99 Latency │ < 500ms │ > 1s │ 扩容 + 降级 │ ├─────────────────┼────────────────┼──────────┼─────────────┤ │ API Error Rate │ < 0.1% │ > 1% │ 熔断 + 告警 │ ├─────────────────┼────────────────┼──────────┼─────────────┤ │ Queue Depth │ < 1000 │ > 5000 │ 扩容 Worker │ ├─────────────────┼────────────────┼──────────┼─────────────┤ │ CPU Utilization │ 40-70% │ > 80% │ 扩容 │ ├─────────────────┼────────────────┼──────────┼─────────────┤ │ Memory Usage │ 50-70% │ > 85% │ 扩容 │ ├─────────────────┼────────────────┼──────────┼─────────────┤ │ DB Connections │ < 80% │ > 90% │ 连接池调优 │ ├─────────────────┼────────────────┼──────────┼─────────────┤ │ Vector DB QPS │ < 80% capacity │ > 90% │ 扩容副本 │ └─────────────────┴────────────────┴──────────┴─────────────┘整体服务架构
┌─────────────────────────────────────────────────────────────────────────────┐ │ 全球多区域部署架构 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ Region A │ │ Region B │ │ Region C │ │ │ (主: 北京) │◀──────▶│ (备: 上海) │◀──────▶│ (备: 广州) │ │ │ │ 同步 │ │ 异步 │ │ │ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ │ │ │ └─────────────┼───────────────────────────┼───────────────────────────┼─────────┘ │ │ │ ▼ ▼ ▼ ┌───────────────────────────────────────────────────────────────────────────────┐ │ Global Load Balancer (GSLB) │ │ (DNS 智能解析 + 健康检查 + 流量调度) │ └─────────────────────────────────┬─────────────────────────────────────────────┘ │ ┌─────────────┴─────────────┐ ▼ ▼ ┌───────────────────┐ ┌───────────────────┐ │ API Gateway │ │ API Gateway │ │ (Kong/AWS API GW) │ │ (Kong/AWS API GW) │ │ ┌───────────────┐ │ │ ┌───────────────┐ │ │ │ Rate Limiter │ │ │ │ Rate Limiter │ │ │ │ Auth/JWT │ │ │ │ Auth/JWT │ │ │ │ Router │ │ │ │ Router │ │ │ │熔断器/Circuit │ │ │ │ 熔断器/Circuit │ │ │ └───────────────┘ │ │ └───────────────┘ │ └────────┬───────────┘ └─────────┬─────────┘ │ │ └─────────────┬─────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Kubernetes Cluster (K8s) │ │ ┌──────────────────────────────────┐ │ │ │ Ingress Controller │ │ │ └──────────────────────────────────┘ │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ API Pod │ │ API Pod │ │ API Pod │ │ │ │ (3+ replicas) │ │ │ │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ ┌──────────────────────────────────┐ │ │ │ HPA (自动扩缩容) │ │ │ │ 基于 CPU / Memory / RPS 指标 │ │ │ └──────────────────────────────────┘ │ │ │ └──────────────────────────────────────────┘ │ ┌──────────────────────┼──────────────────────┐ │ │ │ ▼ ▼ ▼ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ Resume Worker │ │ JD Worker │ │ Match Worker │ │ Pool (5-20) │ │ Pool (2-8) │ │ Pool (5-20) │ │ HPA enabled │ │ HPA enabled │ │ HPA enabled │ └───────────────┘ └───────────────┘ └───────────────┘ │ │ │ └──────────────────────┼──────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Redis Cluster (Queue + Cache) │ │ ┌────────┐ ┌────────┐ ┌────────┐ │ │ │Master │ │ Slave1 │ │ Slave2 │ │ │ └────────┘ └────────┘ └────────┘ │ │ Sentinel / Cluster Mode │ └────────────────────────────────────────┘ │ ┌──────────────────────┼──────────────────────┐ ▼ ▼ ▼ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ PostgreSQL │ │ Weaviate │ │ Neo4j │ │ (主从+读 replica) │ │ (多副本) │ │ (多副本) │ │ ┌───┐ ┌───┐ │ │ ┌───┐ ┌───┐ │ │ ┌───┐ ┌───┐ │ │ │Primary│ │Read │ │ │Node│ │Node│ │ │ │Primary│ │Read │ │ │ │ │Replica│ │ │ 1 │ │ 2 │ │ │ │ │ │Replica│ │ │ └───┘ └───┘ │ │ └───┘ └───┘ │ │ └───┘ └───┘ │ └───────────────┘ └───────────────┘ └───────────────┘生产环境架构选型与规划
1.1 Redis 作为 MQ 的核心问题
┌─────────────────────────────────────────────────────────────────┐ │ Redis 不适合做主力 MQ 的原因 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. 消息持久化问题 │─┐ │ ├── Redis 持久化是"建议性"的,断电可能丢消息 │ │ ├── AOF + fsync every sec 模式下,最坏丢 1 秒数据 │┤ │ └── 生产环境:简历数据丢了 = 灾难 ││ │ │──┤ │ 2. 消息堆积能力 │ │ │ ├── Redis 所有数据在内存,堆积消息 = OOM │┤ │ ├── 简历处理峰值 10万/小时,峰谷比 10:1 │ │ │ └── 专业 MQ 磁盘持久化,堆积百万级消息 │┤ │ │ │ │ 3. 消息顺序保证 │─┘ │ ├── Redis Stream 虽然有 consumer group,但无法保证全局顺序 │ │ ├── 批量筛选需要:JD 解析先于匹配,简历按序处理 │ │ └── Kafka/RocketMQ 靠分区保证顺序 │ │ │ │ 4. 事务与exactly-once │ │ ├── 简历解析完成 → 写入 PG → 写入向量库 → 发送通知 │ │ ├── Redis 无法保证跨系统的分布式事务 │ │ └── RocketMQ 事务消息 = 本地事务 + 消息投递原子性 │ │ │ │ 5. 延迟/定时消息 │ │ ├── 简历重试、HR 通知提醒、定时任务 │ │ ├── Redis 用 Sorted Set 模拟,复杂且不可靠 │ │ └── RocketMQ 延迟消息是原生支持 │ │ │ │ 6. 死信队列 / 消息回溯 │ │ ├── 消费失败 → 死信队列 → 人工处理 │ │ ├── Redis 需要自己实现 │ │ └── Kafka/RocketMQ 原生支持 │ │ │ └─────────────────────────────────────────────────────────────────┘ ┌────────────┬──────────────────────────────────┐ 1.2 消息队列选型对比 解决方案 │ ├────────────┼──────────────────────────────────┤ ┌──────────────┬──────────────┬────────────────────┬────────────────┐ │ 特性 │ RocketMQ │ Kafka │ Redis Stream │ ├──────────────┼──────────────┼────────────────────┼────────────────┤ │ 消息持久化 │ 磁盘,TB 级 │ 磁盘,PB 级 │ 内存优先 │ ├──────────────┼──────────────┼────────────────────┼────────────────┤ │ 吞吐量 │ 10万/秒 │ 100万/秒 │ 20万/秒 │ ├──────────────┼──────────────┼────────────────────┼────────────────┤ │ 消息堆积 │ 强,磁盘无限 │ 极强,保留 7-30 天 │ 弱,受内存限制 │ ├──────────────┼──────────────┼────────────────────┼────────────────┤ │ 顺序消息 │ 支持全局顺序 │ 分区内有序 │ 不保证 │ ├──────────────┼──────────────┼────────────────────┼────────────────┤ │ 事务消息 │ 原生支持 │ 需自研 │ 不支持 │ ├──────────────┼──────────────┼────────────────────┼────────────────┤ │ 延迟消息 │ 原生支持 │ 需插件 │ 模拟实现 │可用。 ├──────────────┼──────────────┼────────────────────┼────────────────┤ │ 死信队列 │ 原生 │ 原生 │ 需自研 │ ├──────────────┼──────────────┼────────────────────┼────────────────┤ │ 消息回溯 │ 支持 │ 支持 │ 不支持 │ ├──────────────┼──────────────┼────────────────────┼────────────────┤ │ 运维难度 │ 中 │ 高 │ 低 │ ├──────────────┼──────────────┼────────────────────┼────────────────┤ │ 单条消息成本 │ 中 │ 低 │ 低 │ └──────────────┴──────────────┴────────────────────┴────────────────┘1.3 推荐方案
┌─────────────────────────────────────────────────────────────────┐ │ 生产环境 MQ 选型建议 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────┐│ │ │ RocketMQ (推荐) ││ │ │ ││ │ │ ✅ 事务消息(简历处理链路的原子性) ││ │ │ ✅ 延迟消息(重试、通知调度) ││ │ │ ✅ 顺序消息(简历按序处理) ││ │ │ ✅ 消息堆积(峰值削峰) ││ │ │ ✅ 死信队列(失败处理) ││ │ │ ✅ Java 生态(团队技能匹配) ││ │ │ ││ │ │ 适用场景: ││ │ │ - 简历处理 Pipeline (解析→标准化→向量化→存储) ││ │ │ - 筛选任务分发 ││ │ │ - HR 通知消息 ││ │ └─────────────────────────────────────────────────────────────┘│ │ │ │ ┌─────────────────────────────────────────────────────────────┐│ │ │ Kafka (备选/大数据场景) ││ │ │ ││ │ │ ✅ 极高吞吐量(HR SaaS 多租户日志、审计日志) ││ │ │ ✅ 消息回溯(重新处理历史简历) ││ │ │ ✅ 生态丰富(与 Flink/Spark 集成) ││ │ │ ││ │ │ 适用场景: ││ │ │ - 简历原始文件上传事件 ││ │ │ - 审计日志/操作日志 ││ │ │ - 实时数据分析 (筛选漏斗、HR 行为分析) ││ │ └─────────────────────────────────────────────────────────────┘│ │ │ │ ┌─────────────────────────────────────────────────────────────┐│ │ │ Redis (仅限缓存,不做 MQ) ││ │ │ ││ │ │ ✅ Session 缓存 ││ │ │ ✅ 计算结果缓存 (JD Schema, Match Results) ││ │ │ ✅ 分布式锁 ││ │ │ ✅ HPA 指标存储 (队列深度采集) ││ │ │ ❌ 不做消息队列 ││ │ └─────────────────────────────────────────────────────────────┘│ └─────────────────────────────────────────────────────────────────┘二、其他架构改进点
2.1 向量数据库选型问题
┌─────────────────────────────────────────────────────────────────┐ │ Weaviate/Pinecone 生产问题 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 问题 1: Weaviate 是 Java 服务,内存消耗大 │ │ ├── 官方建议 16GB+ 内存 │ │ └── 多副本 = 内存成本乘以副本数 │ │ │ │ 问题 2: 混合搜索的局限 │ │ ├── BM25 + Vector 融合不如专业方案 │ │ └── RRF 融合是自己实现,后期维护成本高 │ │ │ │ 推荐方案: │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ Milvus (推荐) │ │ │ │ ├── 专为向量设计,性能优 │ │ │ │ ├── 混合搜索插件丰富 │ │ │ │ ├── GPU 加速支持 │ │ │ │ └── 国产开源,社区活跃 │ │ │ │ │ │ │ │ 或: │ │ │ │ │ │ │ │ PgVector (简单场景) │ │ │ │ ├── PostgreSQL 生态,运维简单 │ │ │ │ ├── <10万向量足够 │ │ │ │ └── 不足: 扩展性一般 │ │ │ └───────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘2.2 知识图谱必要性评估
┌─────────────────────────────────────────────────────────────────┐ │ 知识图谱真的是必须的吗? │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 当前设计: Neo4j 存储技能图谱 │ │ │ │ 生产问题: │ │ ├── 技能图谱数据从哪来?维护成本极高 │ │ ├── "微服务" 的同义词: [分布式系统, SOA, MSA] 谁来维护? │ │ ├── JD 和简历的技能匹配,用 embedding 语义相似度已经能解决 80% │ │ └── 引入 Neo4j = 多一套存储 + 多一套运维 │ │ │ │ 简化方案: │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ 用 Embedding + 同义词表替代知识图谱 │ │ │ │ │ │ │ │ skill_synonyms = { │ │ │ │ "微服务架构": ["分布式系统", "SOA", "MSA", "服务化"], │ │ │ │ "Python": ["Python3"], │ │ │ │ "机器学习": ["ML", "Machine Learning"] │ │ │ │ } │ │ │ │ │ │ │ │ 匹配时: │ │ │ │ 1. 直接匹配 │ │ │ │ 2. 同义词扩展匹配 │ │ │ │ 3. 向量语义相似度兜底 │ │ │ │ │ │ │ │ 收益: -Neo4j 依赖,-运维成本,+系统简单性 │ │ │ └───────────────────────────────────────────────────────────┘ │ │ │ │ 结论: 知识图谱适合前期 POC 验证,业务验证跑通后再考虑引入 │ │ │ └─────────────────────────────────────────────────────────────────┘2.3 多租户隔离问题
┌─────────────────────────────────────────────────────────────────┐ │ 当前架构多租户隔离不足 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 问题: │ │ ├── 所有租户共用一套 PostgreSQL/Weaviate/Redis │ │ ├── 租户 A 的简历可能通过向量相似度 召回 租户 B 的简历 │ │ ├── 数据泄露风险 │ │ └── 资源竞争: 租户 A 批量导入影响 租户 B 的筛选延迟 │ │ │ │ 改进方案: │ │ │ │ 方案 A: Schema 隔离 (简单场景) │ │ ├── PostgreSQL: schema per tenant │ │ ├── Weaviate: tenant_id 字段隔离 │ │ └── 优点: 简单,缺点: 跨租户查询困难 │ │ │ │ 方案 B: Namespace 隔离 (推荐) │ │ ├── 每个租户独立的 namespace │ │ ├── 消息队列: tenant_id 分区 │ │ ├── 向量库: namespace 隔离 │ │ └── 优点: 隔离性好,缺点: 资源成本 x N │ │ │ │ 方案 C: 资源配额 + 逻辑隔离 (成本敏感场景) │ │ ├── 租户级别资源配额 (QPS limit, storage quota) │ │ ├── 租户 ID 标记所有数据 │ │ ├── 查询时强制加 tenant_id 过滤 │ │ └── 优点: 成本低,缺点: 隔离性弱 │ │ │ │ 推荐: 初期用方案 C,验证业务后升级到方案 B │ │ │ └─────────────────────────────────────────────────────────────────┘三、生产环境关键改进
3.1 LLM 调用的高可用问题
┌─────────────────────────────────────────────────────────────────┐ │ LLM API 是单点故障 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 当前设计: 直接调用 OpenAI/Anthropic API │ │ │ │ 生产问题: │ │ ├── OpenAI API 故障 = 系统不可用 │ │ ├── API 限流 = 批量处理中断 │ │ ├── Token 成本不可预测 = 预算失控 │ │ └── API 响应时间波动大 = SLA 无法保证 │ │ │ │ 改进方案: │ │ │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ LLM Gateway 架构 │ │ │ │ │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ │ │ OpenAI │ │ Anthropic│ │ Azure │ ┌─────────┐ │ │ │ │ │ GPT-4 │ │ Claude │ │ OpenAI │ │ 本地模型 │ │ │ │ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────┼──────────────┼──────────────┘ │ │ │ │ │ │ │ │ │ │ ▼ ▼ │ │ │ │ ┌─────────────────────────────┐ │ │ │ │ │ LLM Gateway │ │ │ │ │ │ ┌─────────────────────────┐ │ │ │ │ │ │ │ 多模型路由 (策略模式) │ │ │ │ │ │ │ │ - Primary/Secondary │ │ │ │ │ │ │ │ - 成本优先 │ │ │ │ │ │ │ │ - 延迟优先 │ │ │ │ │ │ │ └─────────────────────────┘ │ │ │ │ │ │ ┌─────────────────────────┐ │ │ │ │ │ │ │ 熔断器 (每模型独立) │ │ │ │ │ │ │ │ - 失败率 > 50% → 切换 │ │ │ │ │ │ │ └─────────────────────────┘ │ │ │ │ │ │ ┌─────────────────────────┐ │ │ │ │ │ │ │ 限流 + 配额管理 │ │ │ │ │ │ │ │ - per tenant QPS │ │ │ │ │ │ │ │ - per model limit │ │ │ │ │ │ │ └─────────────────────────┘ │ │ │ │ │ │ ┌─────────────────────────┐ │ │ │ │ │ │ │ 响应缓存 (semantic cache)│ │ │ │ │ │ │ │ - 相同 JD 解析结果缓存 │ │ │ │ │ │ │ │ - LLM 调用 -50% │ │ │ │ │ │ │ └─────────────────────────┘ │ │ │ │ │ └─────────────────────────────┘ │ │ │ └───────────────────────────────────────────────────────────┘ │ │ │ │ 开源方案: │ │ ├── LiteLLM (Python) - 支持 50+ LLM API │ │ ├── PortKey (SaaS) - LLM 网关 + 分析 │ │ └── GPTCache - 本地语义缓存 │ │ │ └─────────────────────────────────────────────────────────────────┘3.2 简历处理链路的 Exactly-Once 问题
┌─────────────────────────────────────────────────────────────────┐ │ 简历处理 Exactly-Once 问题 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 当前设计的问题: │ │ │ │ 简历上传 → Resume Worker → Ingestion → Vector → Graph → 通知 │ │ │ │ │ │ │ │ │ │ 写入 PG 写入向量 写入图 发邮件 │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ │ │ 可能失败点: 任意一步失败,简历状态不一致 │ │ │ │ 问题场景: │ │ 1. Ingestion 成功,但 Vector Agent 失败 → PG 有数据,向量库没有 │ │ 2. 重试后,向量库重复写入 → 数据重复 │ │ 3. Graph Agent 失败 → 技能图谱不完整 │ │ │ │ 改进方案: RocketMQ 事务消息 │ │ │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ RocketMQ 事务消息处理: │ │ │ │ │ │ │ │ Producer Broker Consumer │ │ │ │ │ │ │ │ │ │ │ │ 发送 half 消息 │ │ │ │ │ │ │──────────────────────>│ │ │ │ │ │ │ │ │ │ │ │ │ │ 执行本地事务 │ │ │ │ │ │ │ (写入 PG) │ │ │ │ │ │ │ │ │ │ │ │ │ │ commit/rollback │ │ │ │ │ │ │──────────────────────>│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ 投递消息 │ │ │ │ │ │ │───────────────>│ │ │ │ │ │ │ │ │ │ │ │ │ │ 处理向量/图/通知 │ │ │ │ │ │ │ │ │ │ │ │ │ commit │ │ │ │ │ │ │<───────────────│ │ │ │ │ │ │ │ │ │ │ └───────────────────────────────────────────────────────────┘ │ │ │ │ 事务状态表设计: │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ resume_transaction │ │ │ │ ───────────────── │ │ │ │ transaction_id: UUID │ │ │ │ candidate_id: UUID │ │ │ │ status: PENDING|COMMITTED|ROLLBACK │ │ │ │ steps: { │ │ │ │ ingestion: DONE, │ │ │ │ vector: PENDING, │ │ │ │ graph: PENDING, │ │ │ │ notification: PENDING │ │ │ │ } │ │ │ │ created_at, updated_at │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘四、完整改进后的架构图
┌─────────────────────────────────────────────────────────────────────────────┐ │ 生产级改进架构 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 接入层 │ │ │ │ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ │ │ │ │ Kong API GW │ │ Kong API GW │ │ Kong API GW │ │ │ │ │ │ (区域 A) │ │ (区域 B) │ │ (区域 C) │ │ │ │ │ │ Rate Limit │ │ Rate Limit │ │ Rate Limit │ │ │ │ │ │ Auth │ │ Auth │ │ Auth │ │ │ │ │ │ WAF │ │ WAF │ │ WAF │ │ │ │ │ └───────────────┘ └───────────────┘ └───────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ LLM Gateway (LiteLLM) │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────────────────┐ │ │ │ │ │ OpenAI │ │Claude │ │ Azure │ │ 语义缓存 (GPTCache) │ │ │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ RocketMQ (事务消息 + 延迟消息) │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ resume:parse │ │ resume:sync │ │ match:batch │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ 延迟重试 │ │ 跨系统同步 │ │ 批量筛选 │ │ │ │ │ │ 死信队列 │ │ 顺序保证 │ │ 结果聚合 │ │ │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌──────────────────────────┼──────────────────────────┐ │ │ ▼ ▼ ▼ │ │ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ │ │ Resume Worker │ │ Ingestion │ │ Match Worker │ │ │ │ Pool (HPA) │───────▶│ Agent │───────▶│ Pool (HPA) │ │ │ │ │ │ │ │ │ │ │ │ - 格式解析 │ │ - 事务写入 │ │ - 向量召回 │ │ │ │ - LLM 提取 │ │ - 去重检测 │ │ - 深度匹配 │ │ │ │ │ │ - 质量评分 │ │ - 排序 │ │ │ └───────────────┘ └───────┬───────┘ └───────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 存储层 (多租户隔离) │ │ │ │ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ PostgreSQL │ │ Milvus │ │ Redis │ │ │ │ │ │ (读写分离) │ │ (向量存储) │ │ (缓存) │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ schema per │ │ namespace │ │ - Session │ │ │ │ │ │ tenant │ │ per tenant │ │ - Results │ │ │ │ │ │ │ │ │ │ - Metrics │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 可观测性层 │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ Jaeger │ │ Prometheus │ │ Grafana │ │ │ │ │ │ (Trace) │ │ (Metrics) │ │ (Dashboard)│ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘五、改进清单汇总
┌──────────────┬──────────────┬────────────────────────────────┬────────┐ │ 问题 │ 当前方案 │ 推荐改进 │ 优先级 │ ├──────────────┼──────────────┼────────────────────────────────┼────────┤ │ MQ 选型 │ Redis Stream │ RocketMQ (事务消息) │ P0 │ ├──────────────┼──────────────┼────────────────────────────────┼────────┤ │ LLM 可用性 │ 直接调用 API │ LLM Gateway (多模型+熔断+缓存) │ P0 │ ├──────────────┼──────────────┼────────────────────────────────┼────────┤ │ 向量库选型 │ Weaviate │ Milvus (性能+GPU支持) │ P1 │ ├──────────────┼──────────────┼────────────────────────────────┼────────┤ │ Exactly-Once │ 无 │ RocketMQ 事务消息 │ P0 │ ├──────────────┼──────────────┼────────────────────────────────┼────────┤ │ 多租户隔离 │ 逻辑隔离 │ Namespace 隔离 + 资源配额 │ P1 │ ├──────────────┼──────────────┼────────────────────────────────┼────────┤ │ 知识图谱 │ Neo4j │ 简化为同义词表 + Embedding │ P2 │ ├──────────────┼──────────────┼────────────────────────────────┼────────┤ │ 监控告警 │ 简单指标 │ 全链路追踪 + 业务指标 │ P1 │ ├──────────────┼──────────────┼────────────────────────────────┼────────┤ │ 成本控制 │ 无 │ LLM Gateway 配额 + 缓存 │ P1 │ └──────────────┴──────────────┴────────────────────────────────┴────────┘六、总结
┌────────┬──────────────┬───────────────────┐ │ 维度 │ 原设计 │ 改进后 │ ├────────┼──────────────┼───────────────────┤ │ MQ │ Redis Stream │ RocketMQ │ ├────────┼──────────────┼───────────────────┤ │ 向量库 │ Weaviate │ Milvus │ ├────────┼──────────────┼───────────────────┤ │ LLM │ 直连 API │ LLM Gateway │ ├────────┼──────────────┼───────────────────┤ │ 多租户 │ 逻辑隔离 │ Namespace + 配额 │ ├────────┼──────────────┼───────────────────┤ │ 事务 │ 无 │ RocketMQ 事务消息 │ ├────────┼──────────────┼───────────────────┤ │ 图谱 │ Neo4j │ 同义词表 │ ├────────┼──────────────┼───────────────────┤ │ 缓存 │ 无 │ GPTCache │ └────────┴──────────────┴───────────────────┘核心原则:
- 生产环境 Redis 不做 MQ - 用 RocketMQ
- LLM 调用必须加网关 - 可用性 > 成本
- 先简化再迭代 - 知识图谱不是必须项
- 多租户隔离要提前考虑 - 后期改造成本高
Hr-Helper Agent
https://sgjki547.top/posts/hrhelper-agent/