项目复盘:从 0 到 1 构建 JD 智能分析 Agent
1.需求背景
在投简历的过程中发现三个高频痛点:
- 信息过载:招聘平台上海量 JD,人工逐条阅读、筛选效率极低
- 匹配模糊:凭感觉判断"合不合适",缺少结构化的差距分析,简历优化无从下手
- AI对话割裂:此前使用AI对话框形式分析JD,每个JD需重复向AI提问相同问题;会话长度超长后,需重建会话并重新发送个人背景和JD信息,上下文丢失导致分析流程割裂不顺畅
于是决定做一个提效的Agent:实现"JD分析 → 匹配评估 → 报告生成"全流程自动化
2.方案设计与系统架构
各层职责
- 输入层:支持文本 JD、图片 JD(招聘截图,可单张或批量)两种输入形态
- 决策层:FCAgent 主循环(qwen-max),根据用户输入自主决定调用哪个工具、以什么顺序调用,并设有系统边界——仅处理 JD 分析类请求,无关问题礼貌拒绝
- 工具层:3 个核心工具——ocr_image(图片识别:多模态大模型提取图片文字,区别于传统 OCR 引擎)、analyze_jd(JD解析+RAG匹配+报告生成)、sync_feishu(同步飞书:报告归档云文档 + 写入岗位梯队表)
- 基础设施层:Chroma 向量知识库(个人简历/技能/项目经历向量化)、Prompt 版本化管理(YAML 配置)、成本追踪
- 输出层:本地 Markdown 报告 + 同步飞书
另配有 Streamlit Web 界面,便于Demo演示
3.关键技术决策
决策一:输入类型判断交给 Function Call,而不是写死 if-else
早期版本用命令行参数显式指定输入类型(text/image):image 走"先图片识别再分析"、text 直接进 JD 分析,路由写死在代码里,LLM 不参与类型判断。改造为 FCAgent 后,用户随意输入(一句话、一个图片路径均可),由 LLM 逐轮规划调用链:判断是否先调 ocr_image → 拿到文本后调 analyze_jd → 分析完成后调飞书同步工具。取舍:FC 更灵活、更接近真实 Agent 形态;代价是每串联一个工具就要多一轮模型决策调用——if-else 路由下 LLM 只做分析这一件事,FC 模式下一次图片任务的完整链路约多出 3~4 次 qwen-max 调用,带来额外的 token 成本与延迟。
决策二:知识库检索坚持代码遍历,不强行套 Function Call
匹配分析需要"拿 JD 里的每个技能去知识库逐项检索证据"。这个环节用 for 循环遍历技能清单逐个检索,而不是让 LLM 决定检索什么。原因:检索路径是固定的(每个技能都必须查),代码遍历 100% 确定性、无遗漏;交给 LLM 决策反而可能漏检。Function Call 应该用在"路径不确定"的决策点上,而不是为了用而用。
决策三:批量处理 = 并发图片识别 + 串行分析 + 串行写Excel
批量分析 N 份 JD 图片时,图片识别阶段无资源竞争可并发提速;LLM 分析阶段受 API 限流约束必须串行;写飞书 Excel 阶段若并发会产生行号冲突,也必须串行。按各环节的资源约束分段选择并发策略,而不是一刀切。
决策四:报告与归档分离,飞书表格固定覆盖写
每份 JD 生成独立的飞书云文档(无冲突,可并行),岗位汇总表则用固定范围覆盖写(URL 不变,刷新即更新),避免每次修改创建新表导致链接失效、文件冗余。
决策五:图片转文字用视觉大模型,而不是传统 OCR
- 场景难点:招聘截图多为多栏排版 + 图标混排(顶部薪资摘要栏、悬浮按钮、底部 Tab 栏)
- 传统 OCR 的短板:只做文字区域检测 + 字符识别,按坐标拼接文本块——多栏场景阅读顺序容易错乱、小字与图标旁数字容易漏检,错误会传导给下游 JD 解析(垃圾进垃圾出)
- 视觉大模型的优势:理解版面语义,按正确阅读顺序转录,保证公司/薪资等关键信息不遗漏
- 代价与兜底:按 Token 计费(单次约 ¥0.01,可接受);配置多模型降级链(qwen-vl-max → qwen-vl-plus → Doubao-1.5-vision-pro → gpt-4o),单模型失效自动切换
决策六:图片识别只产出文本,不做 JD 的结构化
- 前提:Agent 的输入有两种形态——纯文本 JD、图片 JD。无论哪种形态,最终都要做 JSON 结构化
- 备选方案:视觉模型具备语义理解能力,可以在识别图片文字的同时直接输出结构化 JSON,省掉后续的解析环节
- 未采用的原因:纯文本 JD 输入后也还是需要做 JSON 结构化,如果让视觉模型"顺手"做结构化,2 条输入路径就有 2 套处理逻辑,代码实现需要增加额外的 if-else 判断,后续调字段还要两处同步维护
- 结论:JSON 结构化统一收敛在 JD 分析阶段,全局只有一份结构化逻辑。图片识别仅用于提示关键信息不遗漏,不承担字段化职责
4.踩坑与解决
坑 1:RAG 检索漏检已有技能。知识库里明明记录了 4 年 SQL 经验,检索时却没有命中对应证据。
解决:分析发现是 chunk 切分策略把技能描述切断、以及单次提问式检索覆盖率不足。改为"逐技能检索"(每个技能单独一次向量检索)并优化 chunk 策略,召回率从 62% 提升到 87%。同时保留人工核对机制兜底。
坑 2:LLM 输入原始 JSON 导致输出截断。把结构化 JD 的 JSON 直接喂给模型做分析时,模型会"过度解释"字段含义,输出过长被截断,下游解析报错。
解决:先把结构化数据转成自然语言段落再输入模型,问题消失。教训:LLM 的输入形式直接影响输出行为,数据预处理是 Prompt 工程的一部分。
坑 3:LLM 供应商额度频繁耗尽。主用通道的免费额度多次在分析中途耗尽,流程直接中断。
解决:重构 LLM 配置层,支持多 Provider(注释/取消注释即切换),文本任务与视觉任务分离配置,主通道失效时秒级切换备用通道。
坑 4:批量写飞书 Excel 行号冲突。多篇报告并发写入同一张表格时,追加行号互相覆盖,数据错乱。
解决:写入阶段串行化,并改为"整表范围覆盖写"模式,彻底消除行号竞争。
5.数据安全与脱敏
- 知识库本地化:个人简历、技能、项目经历等敏感数据仅存储在本地 Chroma 向量库,数据目录已通过 .gitignore 排除,不进入任何远程仓库
- 密钥管理:所有 API Key 统一存放在本地 .env 文件,代码与配置文件中无明文密钥
- 对外脱敏:本站展示的所有报告均已脱敏——真实姓名替换为化名、雇主名称泛化、薪资预期删除。JD 公司名保留真实(属公开招聘信息),以保证分析过程的真实性可验证