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绕过, 算法排序, 逆向工具