Skip to main content

Resume Flow

基于 LangGraph 的简历筛选应用,使用 LLM + 规则引擎双重验证机制自动化评估候选人。

核心特性

  • 五维评估体系:姓名有效性、目标公司经历、学历毕业年份、软件技术岗位、技术方向
  • 双重验证机制:每个 LLM 判断后紧跟规则引擎交叉验证,冲突时以规则为准
  • 技术方向重试循环:第二个 LLM 独立审核,不一致时自动重试(最多 3 次)
  • 断点续传:基于 SqliteSaver 的 Checkpoint 持久化,中断后可从断点恢复
  • 多格式输出:JSON 单文件结果 + _summary.json 汇总 + Rich 终端美化 + Excel 报表
  • YAML 配置config_default.yaml 内置默认值,config.yaml 用户覆盖,Prompt 文件独立管理
  • 注册表驱动STAGES 列表声明评估维度,graph/finalize/output/cli 自动适配
  • 极简扩展:新增评估维度只需 3 步(写 prompt + 写节点 + 加 Stage 声明)

目录


一、项目概述

Resume Flow 是一个自动化简历筛选工具,面向技术招聘场景。它读取 JSON 格式的简历文件,通过 LangGraph 构建的状态图工作流,依次对候选人进行五个维度的评估,最终判定是否符合条件并为合格候选人生成招聘邮件。

设计理念:LLM 擅长理解语义但可能产生幻觉,规则引擎确定性强但无法处理模糊表述。两者结合,取各自所长,实现高准确率的自动化筛选。

输入resume_json/ 目录下的 JSON 简历文件

输出

  • resume_json_eval/<filename>.json — 每份简历的详细评估结果
  • resume_json_eval/_summary.json — 汇总统计
  • resume_json_eval/eval_resume.xlsx — Excel 报表

二、架构说明

2.1 模块结构

resume_flow/
├── __init__.py              # 包初始化
├── __main__.py              # python -m resume_flow 入口
├── cli.py                   # Typer CLI (init / run 子命令)
├── config.py                # 日志初始化、.env 加载、常量定义
├── config_default.yaml      # 内置默认配置(随包分发)
├── config_loader.py         # YAML 配置加载、深度合并、Prompt 渲染
├── state.py                 # LangGraph State 定义 (ResumeState)
├── graph.py                 # LangGraph StateGraph 构建(从 STAGES 自动组装)
├── llm.py                   # LLM 客户端封装 (OpenAI 兼容)
├── output.py                # JSON / Excel / Rich 终端输出
├── prompts/                 # Prompt 模板文件(自动发现)
│   ├── check_name.txt
│   ├── check_company.txt
│   ├── check_grad_year.txt
│   ├── check_title.txt
│   ├── check_tech.txt
│   ├── verify_tech.txt
│   └── generate_email.txt
└── nodes/                   # 工作流节点
    ├── __init__.py          # 节点函数导出
    ├── _template.py         # 新增节点模板(copy 即用)
    ├── llm_helpers.py       # LLM 工具函数 + run_check + run_verify
    ├── registry.py          # Stage 注册表 + STAGES + make_initial_state
    ├── eval_logic.py        # 评估逻辑 (is_all_pass / get_fail_reasons)
    ├── load_resume.py       # 加载简历 JSON
    ├── check_name.py        # 姓名判断 + 验证
    ├── check_company.py     # 公司判断 + 验证
    ├── check_grad.py        # 毕业年份判断 + 验证
    ├── check_title.py       # 职位判断 + 验证
    ├── check_tech.py        # 技术方向判断 + 验证 (含重试循环)
    ├── finalize.py          # 汇总结果
    └── generate_email.py    # 生成招聘邮件

各模块职责

模块 职责
config_loader.py YAML 加载、深度合并、Prompt 自动发现与渲染、Settings 单例管理
config_default.yaml 内置默认配置:目标公司、学历年份、关键词、LLM 参数、路径等
nodes/llm_helpers.py call_llm_json / call_llm_text LLM 调用封装;run_check / run_verify 节点模板函数
nodes/registry.py Stage dataclass + STAGES 列表 + make_initial_state + build_check_outputs
nodes/eval_logic.py is_all_pass / get_fail_reasons / has_llm_error 等评估逻辑
graph.py STAGES 自动组装 LangGraph 工作流

2.2 LangGraph 工作流

graph.pySTAGES 列表自动构建工作流:

load_resume
    │
    ▼
check_name ──→ verify_name
    │
    ▼
check_company ──→ verify_company
    │
    ▼
check_grad_year ──→ verify_grad_year
    │
    ▼
check_title ──→ verify_title
    │
    ▼
check_tech ──→ verify_tech ──┬── confirmed ──────────→ finalize
                              │── conflict_need_retry ─→ check_tech (重试)
                              │── failed_after_retry ──→ finalize
                              │
                              ▼
                          finalize ──────┬── 全 pass ──→ generate_email ──→ END
                                         │── 任一 fail ─→ END

节点和边从 STAGES 列表自动生成,无需手动注册。唯一需要特殊处理的是 tech 维度的重试循环。

2.3 注册表驱动机制

nodes/registry.py 中的 STAGES 列表是整个系统的核心声明:

STAGES = [
    Stage(name="name", check=check_name, verify=verify_name,
          fail_reason="姓名无效", extra_fields={"detected_name": ""}),
    Stage(name="company", check=check_company, verify=verify_company,
          fail_reason="不在目标公司", extra_fields={"company_matched": ""}),
    Stage(name="grad", state_prefix="grad_year", check=check_grad_year, ...),
    Stage(name="title", check=check_title, verify=verify_title, ...),
    Stage(name="tech", check=check_tech, verify=verify_tech, ...),
]

STAGES 驱动以下自动化:

  • graph.py:自动添加 check/verify 节点和连边
  • make_initial_state():自动生成初始 state 字段
  • build_check_outputs():自动生成输出 dict
  • eval_logic.py:自动计算 all_pass / fail_reasons
  • finalize.py:自动汇总所有维度结果

2.4 双重验证机制

每个评估维度由一对节点完成:check(LLM 判断)→ verify(规则验证)。

  • LLM 擅长理解模糊表述,但可能产生幻觉
  • 规则引擎基于确定性逻辑,不会幻觉但无法处理复杂语义
  • 两者交叉验证,冲突时以规则为准

run_checkrun_verify 辅助函数消除了 check/verify 的样板代码:

# check 函数只需写"发什么消息给 LLM"和"看哪个字段判断通过"
def check_name(state: dict) -> dict:
    return run_check(
        stage_name="name",
        state=state,
        user_message=f"候选人姓名: {state['name']}\n...",
        pass_field="is_valid_name",
        extra={"detected_name": "detected_name"},
    )

# verify 函数只需写"规则是什么",比较逻辑由 run_verify 处理
def verify_name(state: dict) -> dict:
    rule_pass = ...  # 规则判断
    return run_verify(prefix="name", llm_result=..., rule_pass=rule_pass, ...)

三、开发指南

3.1 环境搭建

# 1. 创建虚拟环境
conda create -n resume_flow python=3.10 -y
conda activate resume_flow

# 2. 安装项目
pip install -e .

# 3. 初始化工作环境(生成 .env + config.yaml + 创建输入/输出目录)
resume-flow init

# 4. 编辑 .env,填入真实 API Key
# 5. 将简历 JSON 文件放入 resume_json/ 目录
# 6. 运行
resume-flow run

resume-flow init --with-prompts 可同时复制 prompt 模板到本地 prompts/ 目录,方便自定义。

3.2 配置文件说明

.env(LLM API 配置)

LLM_API_KEY: sk-your-real-api-key
LLM_API_URL: https://api.openai.com/v1/chat/completions
MODEL: gpt-4o-mini

config_default.yaml(内置默认配置)

随包分发,包含:目标公司、学历年份、学位关键词、职位关键词、邮件解析规则、LLM 参数、路径配置等。请勿直接修改

config.yaml(用户覆盖配置)

resume-flow init 生成,只需保留想修改的配置项:

# 覆盖目标公司
target_companies:
  - Apple
  - Google

# 覆盖学历年份
bachelor_years: [2015, 2016, 2017, 2018]

配置加载顺序config_default.yamlconfig.yaml(深度合并)← .env(LLM 连接参数)

Prompt 文件

resume_flow/prompts/*.txt 中的文件被自动发现,无需在配置中注册路径。用户可通过 config.yamlprompts: 段覆盖指向自定义 prompt 文件。

3.3 CLI 参数说明

# 初始化工作环境
resume-flow init
resume-flow init --with-prompts    # 同时复制 prompt 到本地

# 运行
resume-flow run
resume-flow run -i /data/resumes -o /data/results
resume-flow run --config /path/to/config.yaml    # 指定配置文件
resume-flow run --fresh                            # 清除旧结果重新运行
resume-flow run --no-checkpoint                    # 内存模式,不持久化
选项 说明
--input / -i 输入目录(默认 <项目根>/resume_json
--output / -o 输出目录(默认 <项目根>/resume_json_eval
--excel / -e Excel 文件路径
--config 指定用户配置文件路径
--fresh 清除输出目录和 checkpoint,从头运行
--no-checkpoint 内存模式,不使用 SqliteSaver

四、扩展教程 — 费曼学习法

4.1 什么是节点

用工厂流水线理解:

简历进厂 → 质检员A检查 → 质检员B检查 → ... → 全合格则发邮件 → 出厂

节点 = 质检员。每个质检员做两件事:LLM 判断 + 规则验证。LLM 会犯错,规则是兜底裁判。

代码里,一个节点就是两个普通函数

def check_xxx(state):    # 质检员A:调 LLM 做判断
    return run_check(...)

def verify_xxx(state):   # 质检员B:用规则验证 A 的判断
    return run_verify(...)

run_checkrun_verify 是辅助函数,帮你处理调 LLM、处理错误、比较结果等重复劳动。你只写"判断什么"和"规则是什么"。

节点涉及 3 个文件:

prompts/check_xxx.txt   ← 工作手册(告诉 LLM 怎么判断,自动发现,不用注册路径)
nodes/check_xxx.py      ← 工作能力(函数实现,用 run_check/run_verify 简化)
nodes/registry.py       ← 花名册(一行注册,自动排工位、发工资、做考核)

关键registry.py 是"HR"——你把员工登记到花名册,HR 自动安排流水线位置、初始化工作表、汇总考核结果。

4.2 新增节点(3 步)

以新增"地理位置检查"为例。

第 1 步:写工作手册

创建 resume_flow/prompts/check_location.txt

你是简历分析专家。判断候选人是否在目标城市工作。
返回JSON:
{
    "is_target_city": true/false,
    "matched_city": "匹配到的城市",
    "reason": "判断理由"
}

放到 prompts/ 目录即可,系统自动发现,不需要在任何配置文件中注册路径。

第 2 步:写工作能力

复制模板 resume_flow/nodes/_template.pyresume_flow/nodes/check_location.py,把 xxx 替换成 location,填入逻辑:

"""Node: 地理位置判断 + 验证"""

from __future__ import annotations

from resume_flow.config_loader import get_settings
from resume_flow.nodes.llm_helpers import run_check, run_verify


def check_location(state: dict) -> dict:
    """LLM 判断是否在目标城市"""
    resume = state["resume_data"]
    return run_check(
        stage_name="location",
        state=state,
        user_message=f"所在地: {resume.get('location', '')}\n请判断是否在目标城市。",
        pass_field="is_target_city",
        extra={"location_matched_city": "matched_city"},
    )


def verify_location(state: dict) -> dict:
    """规则验证:直接搜城市名"""
    cfg = get_settings()
    target_cities = cfg["location"]["target_cities"]
    resume = state["resume_data"]

    all_locs = [resume.get("location", "")] + [
        exp.get("location", "") for exp in resume.get("experience", [])
    ]
    rule_matched = [c for c in target_cities if c in " ".join(all_locs)]
    rule_pass = len(rule_matched) > 0

    return run_verify(
        prefix="location",
        llm_result=state["location_check_result"],
        rule_pass=rule_pass,
        rule_reason=f"匹配={rule_matched}" if rule_matched else "无匹配",
        extra={"location_matched_city": ", ".join(rule_matched) if rule_matched
               else state.get("location_matched_city", "")},
    )

就这么多。 run_check 帮你调 LLM、处理错误、解析结果。run_verify 帮你比较规则和 LLM 的结论。你只写"发什么消息"和"规则是什么"。

第 3 步:登记到花名册

编辑 resume_flow/nodes/registry.py,加两处:

# 顶部加 import
from resume_flow.nodes.check_location import check_location, verify_location

# STAGES 列表末尾加一行
STAGES = [
    # ... 现有的 5 个维度 ...
    Stage(
        name="location",
        check=check_location,
        verify=verify_location,
        fail_reason="不在目标城市",
        extra_fields={"location_matched_city": ""},
    ),
]

state_prefix 省略了——默认等于 name,即 "location"。只有 grad 维度需要显式指定 state_prefix="grad_year"

完成。 graph/finalize/output/cli 自动适配。

验证

python -m py_compile resume_flow/nodes/check_location.py
ruff check resume_flow/nodes/check_location.py resume_flow/nodes/registry.py

4.3 run_check 和 run_verify 在做什么

run_check:LLM 判断的标准流程

你调用 run_check(...) 时,它帮你做 5 件事:

1. 查注册表,找到 stage_name 对应的 state_prefix
2. 从 prompts/ 加载 check_{stage_name}.txt 作为 system 消息
3. 调用 LLM,传入 system + user_message
4. 如果 LLM 报错,返回 fail + 错误原因
5. 如果正常,从 LLM 结果提取 pass_field 判断通过,提取 extra 中的额外字段

你只需要告诉它:检查什么维度、发什么消息、看哪个字段判断通过、提取什么额外字段

run_verify:规则验证的标准流程

你调用 run_verify(...) 时,它帮你做 1 件事:

比较 rule_pass 和 llm_result:
  一致 → confirmed(规则和 LLM 达成共识)
  不一致 → conflict(以规则为准,覆盖 LLM 的结论)

你只需要告诉它:前缀是什么、LLM 说了什么、规则说了什么、额外返回什么字段

有特殊需求的维度不用这两个辅助函数。check_companycheck_grad 的 check 有自定义逻辑,verify_gradverify_tech 有特殊处理(unknown / 重试循环),保留原写法。辅助函数是帮忙的,不是强制的。

4.4 删除节点(2 步)

  1. 编辑 registry.py,删除 Stage(name="title", ...) 那一行和对应 import
  2. 删除 nodes/check_title.py

完成。 流水线自动重新连接。

4.5 修改节点

需求 改哪个文件 改什么
改 LLM 指令 prompts/check_xxx.txt 直接编辑文本,无需改代码
改规则参数 config.yaml 覆盖默认配置,无需改代码
改判断逻辑 nodes/check_xxx.py 编辑函数体,所见即所得
改流水线顺序 nodes/registry.py 调整 STAGES 列表中 Stage 的顺序

注意tech 维度有重试循环,目前假设它是最后一个维度。如果要把其他维度放到 tech 后面,需检查 graph.py 中 tech 的条件边处理。

4.6 Stage 参数说明

Stage(
    name="location",           # 维度名(日志、prompt 文件名、失败原因标识)
    check=check_location,      # LLM 判断函数
    verify=verify_location,    # 规则验证函数
    fail_reason="不在目标城市",  # 失败原因(字符串或函数)
    extra_fields={             # 额外 state 字段及默认值
        "location_matched_city": "",
    },
    # state_prefix="location",  # 省略则默认等于 name,只有 grad 需要显式指定
)

state_prefix 自动生成三个 state 字段:{prefix}_check_result{prefix}_check_reason{prefix}_check_verified,加上 extra_fields 中的额外字段。初始值由 make_initial_state() 自动设置。

fail_reason 支持两种写法:

fail_reason="不在目标公司"          # 字符串:固定原因(大多数维度)
fail_reason=_grad_fail_reason      # 函数:动态原因(接收 state,返回 str 或 None)

4.7 常见坑

症状 修复
忘记注册到 STAGES 节点函数写了但运行时没执行 在 STAGES 列表加一行 Stage(...)
prompt 文件名不匹配 run_check 报错找不到 prompt 文件必须叫 check_{stage_name}.txt
config 段名与代码不匹配 KeyError: 'location' YAML 段名与 cfg["xxx"] 键名保持一致
pass_field 写错 所有结果都是 fail pass_field 必须与 prompt 中要求的 JSON 字段名一致

4.8 速查表

操作 步数 改哪些文件
新增节点 3 prompts/check_xxx.txt + nodes/check_xxx.py + nodes/registry.py
删除节点 2 nodes/registry.py(删一行)+ nodes/check_xxx.py(删文件)
改 Prompt 1 prompts/check_xxx.txt
改规则参数 1 config.yaml
改判断逻辑 1 nodes/check_xxx.py
改流水线顺序 1 nodes/registry.py(调整 STAGES 顺序)

五、LLM 调用场景

大模型在 7 个节点中被调用,分为两类:

# 节点 调用方式 温度 返回 JSON 键 用途
1 check_name chat_json 0.1 is_valid_name 判断姓名是否有效
2 check_company chat_json 0.1 is_target_company 判断是否在目标公司工作过
3 check_grad_year chat_json 0.1 is_target_year 判断毕业年份是否在目标范围
4 check_title chat_json 0.1 is_software_tech 判断当前职位是否为软件技术岗
5 check_tech chat_json 0.1 is_tech 综合判断是否技术方向
6 verify_tech chat_json 0.1 is_consistent LLM 自我审核技术判断
7 generate_email chat 0.3 生成招聘邮件(唯一文本生成)

规律:判断类调用使用 chat_json + 低温度(0.1),保证确定性;文本生成使用 chat + 中温度(0.3),允许创造性。

两层错误处理

机制 行为
LLMClient 内部 指数退避重试 RateLimitError/APITimeoutError 自动重试(最多 3 次,等待 2^n 秒)
call_llm_json/text 包装 异常降级 捕获所有异常,返回 {"error": ...}"[LLM调用失败]" 标记,不中断流程

多模型支持

切换模型只需修改 .env,无需改代码:

LLM_API_KEY: sk-xxx
LLM_API_URL: https://api.deepseek.com/v1/chat/completions
MODEL: deepseek-chat

支持任何 OpenAI 兼容服务(OpenAI、DeepSeek、本地 Ollama 等)。


六、设计哲学

LLM + 规则引擎双验证

LLM 判断(语义理解)  ←→  规则引擎验证(确定性判定)
         ↓                        ↓
     覆盖模糊边界            覆盖明确规则
         ↓                        ↓
         └──── 冲突时以规则为准 ────┘
                    ↓
              最终判定结果
  • 为什么不用纯 LLM? LLM 会幻觉、不稳定、有成本
  • 为什么不用纯规则? 规则无法覆盖语义边界("技术 Lead" 是技术岗还是管理岗?)
  • 冲突处理:规则引擎是"底线",LLM 是"增强",冲突时信任确定性更高的规则

全量评估,不短路

五个维度全部判断,不因前面失败而跳过。便于完整诊断、数据积累和断点续传。

错误降级,不中断

单次 LLM 失败不中断批处理。失败简历标记为 error,可后续重试。

结构化输出优先

所有判断类调用要求 LLM 返回 JSON({"is_xxx": true, "reason": "..."}),布尔字段直接用于逻辑判断,reason 提供可解释性。


七、run_check / run_verify 速查

run_check

run_check(
    stage_name="xxx",       # 维度名,决定用哪个 prompt 和 state 前缀
    state=state,            # LangGraph state
    user_message="...",     # 发给 LLM 的 user 消息
    pass_field="is_xxx",    # LLM JSON 中判断通过的字段名
    extra={"field": "json_key"},  # 额外提取字段 {state字段: json字段}
)

run_check 做的事:查注册表找 state_prefix → 加载 prompt → 调 LLM → 处理错误 → 解析结果 → 返回 state 更新。

run_verify

run_verify(
    prefix="xxx",           # state 前缀
    llm_result="pass",      # LLM 判断结果
    rule_pass=True,         # 规则引擎判断是否通过
    rule_reason="...",      # 规则引擎的原因
    extra={"field": "value"},  # 额外返回字段
)

run_verify 做的事:比较 rule_pass 和 llm_result → 一致返回 confirmed → 不一致返回 conflict(以规则为准)。

有特殊需求的维度不用这两个辅助函数。check_companycheck_grad 的 check 有自定义逻辑,verify_gradverify_tech 有特殊处理(unknown / 重试循环),保留原写法。


八、init 命令详解

resume-flow init                  # 生成 .env + config.yaml + 创建目录
resume-flow init --with-prompts   # 同时复制 prompt 模板到本地 prompts/

init 执行 4 步:

步骤 做什么
1. 生成 .env 交互式输入 API Key / URL / Model
2. 生成 config.yaml 带注释的用户配置模板(目标公司、学历年份等高频项)
3. 创建目录 resume_json/(输入)+ resume_json_eval/(输出)
4. 复制 prompts(可选) --with-prompts 时复制 7 个 prompt 到本地 prompts/

九、常见修改场景

需求 修改文件 说明
修改目标公司列表 config.yamltarget_companies 无需改代码
修改学历年份范围 config.yamlbachelor_years 无需改代码
修改 LLM prompt prompts/check_xxx.txt 直接编辑文本文件
修改规则引擎逻辑 nodes/check_xxx.pyverify_xxx 所见即所得
修改技术重试次数 config.yamltech_max_retry 无需改代码
修改 LLM 模型/温度 .envconfig.yamlllm 无需改代码
新增评估维度 3 个文件 第四章
删除评估维度 2 个文件 registry.py 删一行 + 删 check_xxx.py

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

resume_flow-0.1.6.tar.gz (124.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

resume_flow-0.1.6-py3-none-any.whl (50.3 kB view details)

Uploaded Python 3

File details

Details for the file resume_flow-0.1.6.tar.gz.

File metadata

  • Download URL: resume_flow-0.1.6.tar.gz
  • Upload date:
  • Size: 124.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.7

File hashes

Hashes for resume_flow-0.1.6.tar.gz
Algorithm Hash digest
SHA256 ad1beeae2bbab2433e6ebffc4c39aac30d19fd27df29922b9c7cd207c6e3d3d8
MD5 38d093cf613ee96e5961430350ab21f3
BLAKE2b-256 97fa019cdcf04877c21b084be2223306065c37a755221c31e30a490f07a860f2

See more details on using hashes here.

File details

Details for the file resume_flow-0.1.6-py3-none-any.whl.

File metadata

  • Download URL: resume_flow-0.1.6-py3-none-any.whl
  • Upload date:
  • Size: 50.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.7

File hashes

Hashes for resume_flow-0.1.6-py3-none-any.whl
Algorithm Hash digest
SHA256 a80c7eae9bd539f5334b4ba073b62f82561924b1d4d3185b1be1a51439ccd2ca
MD5 7d5e3f3715dd01b849ef911233eb9045
BLAKE2b-256 f4368f1054802aa9544b0df42b035ffbbc7c1994d42f31d7ee86fc6e158ff4da

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.6 This release

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page