Matching Engine Architecture

RecruitMatch의 매칭은 단일 LLM 호출이 아닙니다. 5단계 결정론적 파이프라인이며, 각 단계의 출력은 다음 단계의 입력 스키마를 만족해야 합니다.

Parse
LLM 추출
Normalize
Skill Graph
Embed
pgvector + HNSW
Score
4축 가중 + LightGBM
Explain
Evidence 링크

Stage 1 · Parse

입력 문서는 형식에 따라 다른 어댑터가 처리합니다. PDF는 layout-aware OCR을 거쳐 텍스트 블록 + 좌표로 변환되고, LLM이 JSON 스키마로 추출합니다.

json{
  "candidate_id": "C-1207",
  "experiences": [
    {
      "company": "Coupang", "role": "Senior Backend Eng.",
      "start": "2021-03", "end": null,
      "achievements": ["Kafka 기반 이벤트 시스템 운영 (14k QPS)"],
      "source": { "page": 1, "line_range": [12, 28] }
    }
  ],
  "skills": [
    { "name": "Kafka", "proficiency": "expert", "years": 3 }
  ]
}

Stage 2 · Normalize

추출된 raw skill 문자열은 18만 노드 스킬 그래프에 join됩니다. "Kotlin", "kotlin", "코틀린"은 동일 ID로 매핑됩니다.

Title Leveling

"Sr. Eng", "Senior Engineer", "L5 SWE", "책임 엔지니어"는 IC4 노드로 정규화됩니다.

Stage 3 · Embed

JD의 모든 요건 문장과 이력서의 모든 achievement 문장은 text-embedding-3-large(3,072 dim)로 인코딩되어 pgvector에 저장됩니다.

sqlCREATE INDEX ON embeddings
  USING hnsw (vec vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

SET hnsw.ef_search = 80;

Stage 4 · Score

다음 페이지 (4-axis Scoring) 참조.

Stage 5 · Explain

모든 매칭은 왜 그 점수가 나왔는지 근거를 가집니다. JD의 어떤 문장이 이력서의 어떤 문장과 어떤 강도로 연결되는지 span pointer로 저장됩니다.