Murari-10/RedRob-The_Data_and_AI_Challenge-Team_BuildX

GitHub: Murari-10/RedRob-The_Data_and_AI_Challenge-Team_BuildX

一套面向大规模候选人排名的多阶段 RAG 检索增强系统,在纯 CPU、离线、5 分钟的严格预算内完成十万简历的检索、打分与重排。

Stars: 0 | Forks: 0

# Redrob Hackathon — 多阶段 RAG 候选人排名器 针对 100,000 份候选人档案与 Redrob *Senior AI Engineer* 职位 描述进行排名,并输出前 100 名作为提交的 CSV。 该系统是一个**本地、仅限 CPU、离线**的检索增强排名 pipeline:dense bi-encoder → 本地向量存储 → BM25 → 结构化的 "recruiter-brain" 打分 → cross-encoder 重排。在排名路径上,没有任何环节 会开启网络 socket,并且使用预构建的 artifacts,排名步骤可以在 5 分钟的 预算内轻松完成。 - **深入了解 pipeline 和各项打分权重:** [docs/PIPELINE.md](docs/PIPELINE.md) 和 [docs/SCORING.md](docs/SCORING.md) - **提交元数据:** [submission_metadata.yaml](submission_metadata.yaml) ## 问题描述 针对 100,000 份候选人档案与固定的 *Senior AI Engineer* JD 进行排名,并输出 前 100 名 —— 限制在 **5 分钟、仅限 CPU、离线** 的排名时间预算内,运行于 16 GB 内存的机器上。候选人池中还包含刻意设置的、不可能被录用的 "honeypot" 档案(根据 [docs/submission_spec.docx](docs/submission_spec.docx) 约有 80 份),这些 必须通过常规逻辑进行过滤,而不能对个别记录进行特殊处理。 ## 快速开始 所有命令均**从仓库根目录**运行。需要 Python 3.11+。 ``` pip install -r requirements.txt ``` ### 路径 A — 在新的 / 更新的 `candidates.jsonl` 上运行(先进行重建) 每当输入文件不是构建已发布 artifacts 的那个原始文件时,请使用此路径。分为两个步骤: ``` # 1) 为此文件预计算 artifacts(离线;可能超过 5 分钟 — 允许) python build_index.py --candidates ./data/candidates.jsonl # 2) 生成提交的 CSV(仅 CPU,无网络,< 5 分钟) python rank.py --candidates ./data/candidates.jsonl --out ./submission.csv ``` 步骤 1 会针对给定文件重建并覆盖 `artifacts/` 中的所有内容; 步骤 2 是计入预算的排名步骤,负责生成 [submission.csv](submission.csv)。 在**两个**命令中,请将 `./data/candidates.jsonl` 替换为您自己文件的路径。 ### 路径 B — 复现我们的精确提交结果(artifacts 已完全匹配) 如果您使用的是构建已发布 artifacts 的**同一个** `candidates.jsonl`,则无需进行构建步骤 —— 只需运行单一的排名命令: ``` python rank.py --candidates ./data/candidates.jsonl --out ./submission.csv ``` 这会加载已提交的 [artifacts/](artifacts/),并在 5 分钟预算内复现我们精确的 [submission.csv](submission.csv)。 ### 验证输出 ``` python scripts/validate_submission.py ./submission.csv ``` ## 关于这两个步骤的说明 - **`build_index.py`(预计算,不计入预算)。** 解析每份档案,编码 dense + BM25 通道,并将它们写入 `artifacts/`。规则明确 允许此步骤超过 5 分钟。它会**覆盖**任何现有的 artifacts,因此 在新文件上重新运行它总是安全的。`artifacts/build_meta.json` 会记录 哪个 encoder 以及多少名候选人 (`n`) 生成了当前的 artifacts —— 这是 确认它们与您的输入匹配的一种快捷方式。 - **`rank.py`(排名,预算 < 5 分钟)。** 加载 `artifacts/` 并生成 CSV。如果 `artifacts/` 完全缺失,它会自动回退到在预算内计算 通道的模式,因此 pipeline 依然可以在任何机器上运行 —— 但对于新的 输入,**推荐**的路径是先运行 `build_index.py`(路径 A),这能 保持排名步骤的快速并确保分数正确。 ## 仓库结构 ``` . ├── rank.py # ← THE reproduce entrypoint (candidates.jsonl → submission.csv) ├── build_index.py # offline pre-computation of retrieval artifacts (unbudgeted) ├── app.py # Streamlit sandbox for a small sample (demo) ├── submission.csv # the produced top-100 submission ├── requirements.txt # pinned dependencies ├── submission_metadata.yaml # portal metadata │ ├── src/ # source package │ ├── config.py # single source of truth: all weights & thresholds │ ├── reasoning.py # deterministic (non-LLM) reasoning strings │ ├── data_processing/ # parsing, features, behavioral, honeypot │ ├── retrieval/ # dense encoder + vector store, BM25, hybrid fusion │ └── reranking/ # recruiter-brain composite + cross-encoder │ ├── artifacts/ # pre-computed retrieval artifacts (loaded at rank time) │ ├── dense.f16.npy # (100K, dim) profile embeddings │ ├── ids.npy # candidate_id order aligned to dense.f16.npy │ ├── bm25_scores.npy # BM25 scores vs the fixed JD query │ ├── jd_dense.npy # mean-pooled JD query vector │ └── build_meta.json # which encoder/backend produced the artifacts │ ├── data/ # inputs & samples │ ├── candidates.jsonl # official 100K input — NOT committed (see .gitignore) │ ├── candidate_schema.json │ ├── sample_candidates.json │ └── sample_submission.csv │ ├── docs/ │ ├── PIPELINE.md # architecture / pipeline write-up │ ├── SCORING.md # exact formula, weights, and rejected approaches │ ├── job_description.docx # original hackathon briefs │ ├── redrob_signals_doc.docx │ └── submission_spec.docx │ └── scripts/ └── validate_submission.py # checks the CSV against the submission spec ``` ## Sandbox 演示 ``` streamlit run app.py ``` 将其指向一个小的(几百份或更少)候选人样本 —— 例如 `data/sample_candidates.json` —— 并观察多阶段 pipeline 如何生成一份 带有扎实的、分阶段分数明细的排名列表。 ## 设计决策与权衡 塑造 pipeline 的那些选择,以及每种选择的代价: - **默认使用 Hashing dense-encoder,而非语义 BGE。** `DENSE_ENCODER_MODE = "hash"` ([src/config.py](src/config.py))。真正的 BGE-small 在我们的 CPU 上编码 100K 档案 需要约 2.75 小时;而确定性的 hashing encoder 构建相同的索引 只需约 5 分钟。我们接受了较弱的 dense 通道,因为 `S_struct`(0.62 权重) + BM25 以及阶段 4 的 cross-encoder 承担了排名质量。真正的 BGE 路径已经 实现并经过验证 —— 切换为 `"auto"` 即可使用它(参见 [docs/PIPELINE.md](docs/PIPELINE.md),“已发布 artifacts 的状态”)。 - **`role_gate` 对技能列表是盲区的。** 它只读取 title ∪ 职业经历证据 ([src/data_processing/features.py](src/data_processing/features.py))—— 这恰恰切断了那些靠堆砌关键词的人试图爬升的途径。 - **我们持久化存储的是 BM25 的 *score vector*,而不是 postings index。** 对于 100K 文档,tokenized 索引 pickled 后约为 130 MB,超过了 GitHub 单个文件 100 MB 的限制; 由于 JD 查询在构建时是固定的,因此每个候选人的 score vector 就是 `rank.py` 所需的全部内容 ([src/config.py](src/config.py))。 - **Cross-encoder 仅在前 300 名的 shortlist 上运行** (`SHORTLIST_N`) —— 头部排名是 决定 NDCG@10 的关键,而在全量候选人池上运行 cross-attention 会超出预算。 ## 我们尝试过并拒绝的方法 这两种方法都是在*针对真实的 100K 候选人池进行测量之后*被移除的 —— 这些 负面结果也是工程实践的一部分(完整细节见 [docs/SCORING.md](docs/SCORING.md)): - **将 GitHub 活动作为“仅限闭源项目”的否决条件。** 拒绝原因: `years_of_experience≥5 AND github_activity_score≤0` 会命中池中 43% 的候选人 (即使限制在目标头衔中也有 16%)。在这个数据集中,优秀的 候选人中没有 GitHub 简直太常见了 —— 它不是一个可靠的参考标准。 - **Honeypot 规则“任何技能持续时间 > 总职业月数”。** 拒绝原因:它 标记了 13,581/100,000(13.6%)的真实候选人 —— 因为技能往往是 自学的,或者早于被记录的职业历史。 ## 已知限制 - **已发布的** dense 通道是 hashing 回退方案,而不是语义 BGE(参见 `artifacts/build_meta.json`, `encoder_mode`)。 - `trajectory_score()` 已被计算并附加到诊断信息中,但**未** 并入最终得分中(见 [docs/SCORING.md](docs/SCORING.md) 以及 [src/data_processing/features.py](src/data_processing/features.py) 中的注释)。 - 分数归一化是**相对于候选人池的**(在被评分的池中进行稳健的 min-max 归一化)。在微小的样本上(例如 Streamlit 演示),它会将极端值拉伸至 0.0/1.0,这与在 10 万完整规模下的表现不同。 ## 测试与验证方法 - [scripts/validate_submission.py](scripts/validate_submission.py) 会在上传前根据提交规范检查 输出的 CSV。 - **确定性 (Determinism)** 在这里是一项正确性属性:所有内容都设定了种子 (`src/config.py: SEED`),因此相同的输入会产生字节完全相同的输出。 - 规则阈值(honeypot、否决条件)是通过**在全量 10 万候选人池上进行经验性扫描**调整出来的,而非事先主观设定的 —— 参见上文被拒绝方案的 测量结果。 ## AI 辅助(透明度声明) AI 被作为工具使用于由人类主导的工程流程中: - **起点。** 最初的多阶段 RAG + 重排架构蓝图 是借助 LLM 起草的,并由团队进行了审查。 - **团队负责的工作。** 将每一个托管的云端组件调整为 符合护栏要求的本地/离线替代方案;设定并审查所有的 评分权重、role-gating 和否决规则;设计 honeypot 逻辑;并针对真实的 10 万候选人池对规则进行经验性调整和拒绝 (见“我们尝试过并拒绝的方法”)。这些基于实测数据的决策属于 团队,而非模型。 - **AI 完成的工作。** Claude Code 根据经过审查的设计编写并重构了 实现代码。 完整声明请参见 [submission_metadata.yaml](submission_metadata.yaml)。 ## 保证 - **确定性。** 所有内容都设定了种子 (`src/config.py: SEED`);相同的输入 会产生字节完全相同的输出。 - **排名时无需网络。** 不会有任何候选人数据被发送到任何托管的 LLM/API。 - **仅限 CPU,< 16 GB 内存,< 5 分钟**,即可使用已发布的 artifacts 处理 10 万规模的候选人池。 - **没有隐藏步骤。** 提交的 `submission.csv` 正是 `rank.py` 处理 `data/candidates.jsonl` 后输出的结果。
标签:Kubernetes, Python, RAG, 人工智能, 向量检索, 招聘系统, 无后门, 用户模式Hook绕过, 算法排序, 逆向工具