offer-catcher-agent-v2 / scripts /test_data_ingestion.py
hungryb's picture
v2: agent report + filtered corpus + evidence contract
54b5b64 verified
Raw
History Blame Contribute Delete
17.6 kB
"""
test_data_ingestion.py — 测试公开岗位数据接入管线
使用内置 30 条模拟公开岗位样本,测试 normalize、filter、deduplicate、merge 全链路。
可离线运行,不依赖网络。
输出 data/public_jobs_sample.json 和 data/jobs_merged.json。
"""
import json
import os
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT)
from src.public_job_ingestion import (
normalize_public_job,
infer_direction,
infer_stage,
_find_skills,
_quality_score,
deduplicate_jobs,
merge_jobs,
_safe_str,
)
# ---------------------------------------------------------------------------
# 内置 30 条模拟公开岗位样本(覆盖 AI/ML/LLM/Agent/推荐/后端/CV/NLP/数据分析)
# ---------------------------------------------------------------------------
FIXTURE_JOBS = [
# ----- LLM / Agent 方向 (8 条) -----
{"title": "LLM Application Engineer Intern", "company": "Anthropic", "location": "San Francisco",
"description": "Build LLM-powered applications using Claude API. Implement RAG pipelines and agent workflows. Requirements: Python, PyTorch, LangChain, experience with prompt engineering and vector databases.",
"employment_type": "Internship", "source": "greenhouse"},
{"title": "大模型算法实习生", "company": "字节跳动", "location": "北京",
"description": "负责大模型应用算法研发,包括 RAG 检索增强、Agent 工具调用、Prompt 优化。要求熟悉 Python、PyTorch、Transformer,有 LLM 项目经验优先。",
"employment_type": "实习", "source": "fixture"},
{"title": "AI Research Scientist - LLM", "company": "Meta", "location": "Menlo Park",
"description": "Research and develop large language models. Fine-tune models using LoRA/QLoRA. Build evaluation benchmarks. Requirements: PhD in CS/NLP, publications at top venues.",
"employment_type": "Full-time", "source": "lever"},
{"title": "Generative AI Engineer", "company": "Stability AI", "location": "London",
"description": "Develop generative AI applications with diffusion models and LLMs. Build RAG systems for document understanding. Requirements: Python, PyTorch, HuggingFace, LangChain.",
"employment_type": "Full-time", "source": "ashby"},
{"title": "Agent 应用开发工程师", "company": "Minimax", "location": "上海",
"description": "开发基于 LLM 的 Agent 应用,实现多轮对话和工具调用。要求熟悉 LangChain Agent 框架,有 Function Calling 实践,了解 ReAct 工作流。",
"employment_type": "社招", "source": "fixture"},
{"title": "NLP 算法实习生(大模型方向)", "company": "百度", "location": "北京",
"description": "参与文心大模型应用开发,进行 NER/文本分类/摘要等任务微调。要求:Python, PyTorch, BERT, HuggingFace Transformers,有 NLP 项目经验。",
"employment_type": "实习", "source": "fixture"},
{"title": "LLM Infrastructure Engineer", "company": "OpenAI", "location": "San Francisco",
"description": "Build scalable infrastructure for training and serving large language models. Optimize inference latency. Requirements: Python, CUDA, Triton, Ray, Kubernetes.",
"employment_type": "Full-time", "source": "greenhouse"},
{"title": "AI Application Developer", "company": "Notion", "location": "San Francisco",
"description": "Integrate AI features into Notion's product using LLMs. Build RAG-powered Q&A, summarization, and writing assistants. Requirements: TypeScript, Python, API design.",
"employment_type": "Full-time", "source": "lever"},
# ----- 推荐算法方向 (5 条) -----
{"title": "推荐算法实习生", "company": "快手", "location": "北京",
"description": "参与短视频推荐排序模型优化,包括召回、粗排、精排。要求熟悉推荐系统、排序模型、NDCG指标体系。Python、PyTorch、A/B Test 经验优先。",
"employment_type": "实习", "source": "fixture"},
{"title": "Machine Learning Engineer - Recommendations", "company": "Spotify", "location": "Stockholm",
"description": "Build recommendation systems for music discovery. Work on collaborative filtering, content-based recommendations, and multi-objective ranking. Requirements: Python, TensorFlow, Scala.",
"employment_type": "Full-time", "source": "greenhouse"},
{"title": "搜索推荐算法工程师", "company": "阿里巴巴", "location": "杭州",
"description": "负责电商搜索和推荐算法优化,提升 CTR/CVR。要求掌握排序模型(LTR/DNN)、召回策略、多目标优化。Python、TensorFlow 熟练。",
"employment_type": "社招", "source": "fixture"},
{"title": "推荐系统工程师", "company": "美团", "location": "北京",
"description": "优化外卖推荐系统,从召回-粗排-精排全链路优化。要求熟悉 Wide&Deep、DeepFM、多任务学习。Python、Spark、Hive 熟练。",
"employment_type": "社招", "source": "fixture"},
{"title": "Junior Data Scientist - RecSys", "company": "Delivery Hero", "location": "Berlin",
"description": "Develop recommendation models for food delivery. Apply collaborative filtering and deep learning to improve order recommendations. Requirements: Python, SQL, basic ML.",
"employment_type": "Entry Level", "source": "ashby"},
# ----- 后端/AI平台方向 (5 条) -----
{"title": "后端研发实习生(Go 方向)", "company": "腾讯", "location": "深圳",
"description": "参与 AI 平台后端服务开发,使用 Go 语言。要求掌握 Go、MySQL、Redis、消息队列,了解微服务架构和 gRPC。有分布式系统经验优先。",
"employment_type": "实习", "source": "fixture"},
{"title": "Backend Engineer - AI Platform", "company": "Stripe", "location": "Seattle",
"description": "Build backend services for Stripe's ML platform. Design APIs for model serving and feature store. Requirements: Go/Java, gRPC, Kubernetes, experience with ML infrastructure.",
"employment_type": "Full-time", "source": "lever"},
{"title": "云原生后端开发工程师", "company": "华为", "location": "深圳",
"description": "开发云原生 AI 平台后端服务,基于 Kubernetes/Docker 构建。要求 Go/Java/Python,熟悉微服务、容器化部署,了解 CI/CD 流程。",
"employment_type": "社招", "source": "fixture"},
{"title": "ML Platform Engineer", "company": "Databricks", "location": "San Francisco",
"description": "Build the platform that powers ML training and serving at scale. Design model registry, feature store, and experiment tracking. Requirements: Python, Scala, K8s, Spark.",
"employment_type": "Full-time", "source": "greenhouse"},
{"title": "推荐平台后端实习生", "company": "小红书", "location": "上海",
"description": "参与推荐平台后端开发,负责特征抽取服务和模型推理引擎。要求 Python/Go、gRPC、Docker,了解推荐系统架构。",
"employment_type": "实习", "source": "fixture"},
# ----- CV方向 (3 条) -----
{"title": "计算机视觉算法实习生(检测方向)", "company": "商汤科技", "location": "上海",
"description": "参与目标检测和图像分割算法研发,使用 YOLO/DETR/MaskRCNN 等模型。要求 Python、PyTorch、OpenCV,有 CV 项目经验优先。",
"employment_type": "实习", "source": "fixture"},
{"title": "Computer Vision Engineer", "company": "Tesla", "location": "Palo Alto",
"description": "Develop computer vision algorithms for autonomous driving. Work on object detection, lane detection, and scene understanding. Requirements: Python, PyTorch, OpenCV.",
"employment_type": "Full-time", "source": "lever"},
{"title": "图像识别算法实习生", "company": "旷视科技", "location": "北京",
"description": "参与图像分类、OCR 识别算法研发。要求 Python、PyTorch、CNN/ResNet/ViT 等模型经验,有模型轻量化和部署经验优先。",
"employment_type": "实习", "source": "fixture"},
# ----- NLP方向 (3 条) -----
{"title": "NLP Research Intern", "company": "Google", "location": "Mountain View",
"description": "Research in natural language processing. Work on multilingual models, text generation, and dialogue systems. Requirements: PyTorch, HuggingFace, publications.",
"employment_type": "Internship", "source": "greenhouse"},
{"title": "搜索 NLP 算法实习生", "company": "搜狗", "location": "北京",
"description": "参与搜索 Query 理解和排序优化,包括意图识别、Query 纠错改写。要求 NLP 基础,Python,PyTorch/BERT,有搜索/对话系统项目优先。",
"employment_type": "实习", "source": "fixture"},
{"title": "文本生成算法工程师", "company": "阅文集团", "location": "上海",
"description": "负责网络文学 AI 创作和文本生成模型优化。要求 NLP 背景,熟悉 GPT/BART 等生成式模型,有模型微调和部署经验。",
"employment_type": "社招", "source": "fixture"},
# ----- 数据分析方向 (3 条) -----
{"title": "校招数据分析实习生", "company": "滴滴出行", "location": "北京",
"description": "参与出行平台数据分析,设计指标体系、构建用户画像。要求 SQL 熟练,Python 数据分析,有 A/B 实验和可视化经验优先。",
"employment_type": "校招", "source": "fixture"},
{"title": "推荐数据分析实习生", "company": "Bilibili", "location": "上海",
"description": "分析视频推荐效果指标,包括 CTR/CVR/用户停留时长。要求 SQL/Python 数据分析,了解推荐系统指标体系,有 Hadoop/Spark 经验优先。",
"employment_type": "实习", "source": "fixture"},
{"title": "Data Analyst - Growth", "company": "Canva", "location": "Sydney",
"description": "Analyze user growth metrics and build dashboards. Design A/B tests and measure impact of product changes. Requirements: SQL, Python/R, Tableau.",
"employment_type": "Full-time", "source": "lever"},
# ----- 搜索方向 (2 条) -----
{"title": "智能搜索算法实习生", "company": "知乎", "location": "北京",
"description": "优化知乎内容搜索排序,包括向量检索、文本相关性排序。要求 NLP/搜索背景,Python/PyTorch,了解 ES/向量数据库。",
"employment_type": "实习", "source": "fixture"},
{"title": "校招人岗匹配算法实习生", "company": "BOSS直聘", "location": "北京",
"description": "参与人岗匹配算法研发,包括简历-岗位语义匹配、Embedding 召回、排序模型优化。要求 NLP/推荐系统背景,有向量检索和排序项目经验。",
"employment_type": "校招", "source": "fixture"},
# ----- 游戏AI/其他方向 (1 条) -----
{"title": "游戏 AI 算法实习生", "company": "网易游戏", "location": "广州",
"description": "参与游戏 AI 研发,包括 NPC 行为决策、强化学习训练。要求 Python、PyTorch、强化学习基础,有游戏 AI 项目经验优先。",
"employment_type": "实习", "source": "fixture"},
]
# ---------------------------------------------------------------------------
# 测试函数
# ---------------------------------------------------------------------------
def test_normalize():
"""测试 normalize_public_job 对每条 fixture 输出合法 schema。"""
print("[TEST] normalize_public_job ...")
required_fields = ["id", "title", "company", "city", "stage", "direction",
"skills", "project_signals", "jd", "interview_themes",
"source", "source_url", "posted_at", "data_quality_score"]
passed = 0
failed = 0
for i, raw in enumerate(FIXTURE_JOBS):
job = normalize_public_job(raw)
if job is None:
print(f" [WARN] fixture[{i}] returned None")
failed += 1
continue
missing = [f for f in required_fields if f not in job]
if missing:
print(f" [FAIL] fixture[{i}] '{job.get('title', '?')}' missing fields: {missing}")
failed += 1
else:
passed += 1
ok = passed > 0 and failed == 0
print(f" [{'PASS' if ok else 'FAIL'}] normalize: {passed} passed, {failed} failed")
def test_direction():
"""测试方向推断。"""
print("[TEST] infer_direction ...")
cases = [
("大模型应用算法实习生", "负责 LLM 应用开发,包括 RAG 和 Agent 工作流。", "LLM"),
("推荐算法实习生", "优化推荐排序模型,提升 CTR 和 NDCG。", "推荐算法"),
("计算机视觉算法实习生", "使用 YOLO 进行目标检测和图像分割。", "计算机视觉"),
("后端研发实习生", "开发 Go 微服务,使用 gRPC 和 K8s。", "后端开发"),
("数据分析实习生", "分析用户数据,设计指标体系。", "数据分析"),
]
passed = 0
for title, desc, expected_dir in cases:
direction = infer_direction(title, desc)
if expected_dir in direction:
passed += 1
else:
print(f" [WARN] '{title}' -> '{direction}', expected '{expected_dir}'")
ok = passed >= 4
print(f" [{'PASS' if ok else 'FAIL'}] direction infer: {passed}/{len(cases)}")
def test_skills():
"""测试技能提取。"""
print("[TEST] _find_skills ...")
desc = ("Requirements: Python, PyTorch, LangChain, FAISS, Docker. "
"Experience with RAG and Agent workflows. Knowledge of BERT, GPT, and Transformers.")
skills = _find_skills(desc)
expected_subset = ["python", "pytorch", "langchain", "faiss", "docker", "bert", "gpt"]
hits = sum(1 for s in expected_subset if s in skills)
ok = hits >= 5
print(f" [{'PASS' if ok else 'FAIL'}] Found {len(skills)} skills, {hits}/{len(expected_subset)} expected present")
def test_quality():
"""测试质量评分。"""
print("[TEST] _quality_score ...")
s1 = _quality_score("LLM Engineer", "Short desc.", "LLM", ["python"])
s2 = _quality_score("大模型应用算法实习生", "这是一段很详细的岗位描述," * 10, "LLM",
["python", "pytorch", "langchain", "faiss"])
ok = s2 > s1
print(f" [{'PASS' if ok else 'FAIL'}] short JD score={s1}, long JD score={s2}")
def test_dedup_move():
"""测试去重。"""
print("[TEST] deduplicate_jobs ...")
jobs = [
{"title": "A", "company": "X", "city": "北京"},
{"title": "A", "company": "X", "city": "北京"},
{"title": "B", "company": "Y", "city": "上海"},
]
result = deduplicate_jobs(jobs)
ok = len(result) == 2
print(f" [{'PASS' if ok else 'FAIL'}] dedup: {len(jobs)} -> {len(result)}")
def test_merge():
"""测试合并。"""
print("[TEST] merge_jobs ...")
builtin = [
{"title": "大模型应用算法实习生", "company": "字节跳动", "city": "北京"},
{"title": "推荐算法实习生", "company": "快手", "city": "北京"},
]
public = [
{"title": "LLM Engineer", "company": "OpenAI", "city": "SF"},
{"title": "大模型应用算法实习生", "company": "字节跳动", "city": "北京"}, # 重复
]
merged = merge_jobs(builtin, public)
ok = len(merged) == 3
print(f" [{'PASS' if ok else 'FAIL'}] merge: {len(builtin)}+{len(public)} -> {len(merged)} (dup removed)")
def generate_sample_files():
"""生成非破坏性的测试样例文件,不覆盖正式岗位库。"""
data_dir = os.path.join(ROOT, "data")
os.makedirs(data_dir, exist_ok=True)
# 标准化所有 fixture
normalized = []
for raw in FIXTURE_JOBS:
job = normalize_public_job(raw)
if job:
normalized.append(job)
# 去重
deduped = deduplicate_jobs(normalized)
# 至少保底取前 30 条(去重后可能少于30)
sample = deduped[:min(len(deduped), 30)]
# 保存到 test_* 文件,避免覆盖正式 data/public_jobs_sample.json。
sample_path = os.path.join(data_dir, "test_public_jobs_sample.json")
with open(sample_path, "w", encoding="utf-8") as f:
json.dump(sample, f, ensure_ascii=False, indent=2)
# 合并
builtin_path = os.path.join(data_dir, "jobs.json")
if os.path.exists(builtin_path):
with open(builtin_path, "r", encoding="utf-8") as f:
builtin = json.load(f)
else:
builtin = []
merged = merge_jobs(builtin, sample)
merged_path = os.path.join(data_dir, "test_jobs_merged.json")
with open(merged_path, "w", encoding="utf-8") as f:
json.dump(merged, f, ensure_ascii=False, indent=2)
print(f"[OK] test_public_jobs_sample.json: {len(sample)} jobs")
print(f"[OK] test_jobs_merged.json: {len(merged)} jobs (builtin {len(builtin)} + public {len(sample)} = {len(builtin)}+{len(sample)} -> {len(merged)} after dedup)")
# ---------------------------------------------------------------------------
# main
# ---------------------------------------------------------------------------
def main():
print("=" * 60)
print(" test_data_ingestion.py")
print("=" * 60)
test_normalize()
test_direction()
test_skills()
test_quality()
test_dedup_move()
test_merge()
generate_sample_files()
print("\n[OK] All tests completed.")
if __name__ == "__main__":
main()