GitHub 트렌딩 딥다이브 · 2026-06-24

interviewstreet/hiring-agent 딥다이브
— 이력서 PDF를 '공정하고 근거 있는 점수'로 바꾸는 AI 채점 에이전트

interviewstreet/hiring-agentHackerRank(인터뷰스트리트)가 공개한, 지원자의 이력서 PDF를 읽어 점수로 바꾸는 AI 에이전트입니다. PDF를 구조화된 JSON으로 파싱하고, GitHub 실데이터(저장소·스타·커밋 수)로 주장을 검증한 뒤, 고정된 채점 기준(rubric)에 따라 항목별 점수 + 근거 + 가점/감점을 LLM이 매깁니다. 핵심은 두 가지 — 로컬에서 무료로(Ollama) 돌아가 이력서가 밖으로 안 나가고, 이름·성별·학교·학점·지역으로는 절대 점수를 매기지 않도록 공정성을 코드와 프롬프트에 박았다는 점. (저장소: interviewstreet/hiring-agent · 언어 Python 84% + Jinja 16% · ★ 약 1.2천 · 라이선스 MIT · HackerRank)

목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석 (이력서 PDF가 점수가 되기까지)
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

"키워드 매칭 ATS가 아니다. 이력서를 '이해'하고, GitHub로 '검증'하고, 근거와 함께 '채점'한다."

한 줄로

hiring-agent는 이력서 한 장을 받아, 규칙표를 들고 항목마다 점수와 그 근거를 적어 내려가는 '꼼꼼한 1차 서류 심사관'이다.

채용에서 가장 고된 일 중 하나가 수천 장의 이력서를 1차로 걸러내는 것입니다. 사람이 일일이 깊게 읽기는 불가능하고, 기존 ATS(지원자 추적 시스템)는 키워드만 맞춰 거르니 조잡합니다. hiring-agent는 그 사이를 메웁니다 — 이력서를 의미로 이해해 프로젝트의 복잡도까지 보고, "GitHub에 그 프로젝트가 진짜 있고, 커밋을 몇 개나 했는지" 실데이터로 확인한 다음, 정해진 100점 만점 기준표에 따라 항목별 점수 + 그 점수의 증거 문장을 남깁니다.

용어
에이전트(agent) — 여기선 '하나의 판단을 끼운 파이프라인'
거창한 프레임워크(LangChain 등)를 쓰지 않습니다. 이 프로젝트의 '에이전트다움'은 GitHub 저장소 중 '평가에 쓸 상위 7개'를 LLM이 직접 고르는 단계 하나에 있습니다. 나머지는 곧게 흐르는 파이프라인(PDF→파싱→검증→채점)이고, 그 가운데 '도구를 써서 정보를 모으고 → 그걸 보고 결정'하는 한 매듭이 에이전트의 핵심을 보여줍니다. "에이전트=무거운 프레임워크"가 아님을 알려 주는 좋은 예입니다.
용어
채점 기준표 (rubric, 루브릭)
루브릭은 '무엇을 보고 몇 점을 주는가'를 적어 둔 평가 기준표입니다(선생님이 수행평가 채점할 때 쓰는 표를 떠올리세요). hiring-agent의 루브릭은 resume_evaluation_criteria.jinja라는 프롬프트 파일에 자연어로 적혀 있습니다 — 오픈소스 기여 35점, 개인 프로젝트 30점, 실무 경험 25점, 기술 폭 10점, 가점 최대 20점. 기준표 자체가 곧 프롬프트라는 점이 이 프로젝트의 묘미입니다.

전체는 파이썬 CLI 스크립트 하나로 돕니다 — python score.py 이력서.pdf. 웹 서버도, 데이터베이스도, 프런트엔드도 없습니다. 기본 LLM은 로컬 Ollama(예: gemma3:4b)라 인터넷 없이, 무료로, 이력서를 내 컴퓨터 밖으로 내보내지 않고 채점할 수 있고, 설정만 바꾸면 클라우드 Gemini로도 돕니다. 이 문서는 사용법보다 그 안의 설계 — "PDF를 어떻게 믿을 수 있는 JSON으로 바꾸나", "어떻게 LLM의 산수를 안 믿고 코드로 점수를 가두나", "공정성을 어떻게 강제하나"를 파고듭니다.

2왜 주목받는가

"채용 테크 회사가 '자기들이 지원자를 채점하는 실제 로직'을 통째로 오픈소스로 공개했다."

먼저, AI 이력서 채점은 왜 어렵고 위험한가

AI로 이력서를 거르는 시도는 많았지만 악명도 높습니다 — 과거 아마존이 만든 이력서 AI는 여성 지원자에게 불리한 편향이 드러나 폐기됐습니다. 또 지원자는 이력서에 과장을 적기 마련이라, 글자만 믿으면 '튜토리얼 따라 한 프로젝트'와 '진짜 오픈소스 기여'를 구분하지 못합니다. hiring-agent는 이 두 난제를 정면으로 겁니다.

관점hiring-agent흔한 대안
(수동 심사 · 키워드 ATS)
이해 방식의미 + 복잡도까지 LLM이 이해키워드 일치 여부만
주장 검증GitHub 실데이터(커밋·기여자 수)로 대조이력서 글자를 그대로 신뢰
공정성이름·성별·학교·학점·지역 사용 금지를 명시심사자/필터에 따라 들쑥날쑥
투명성점수마다 근거(evidence) 문장 + 오픈소스 프롬프트왜 떨어졌는지 알기 어려움
프라이버시로컬 Ollama면 완전 오프라인클라우드 전송이 흔함
함정
LLM에게 '점수 계산'을 통째로 맡기면 안 된다

LLM은 그럴듯하게 점수를 매기지만 산수를 틀리고, 시키지 않은 기준(학교·이름)에 은근히 영향받습니다. 점수표 합계가 100을 넘거나, 자기 저장소뿐인데 오픈소스 점수를 후하게 주는 식의 사고가 납니다.

해결
프롬프트로 규칙 + 코드로 한 번 더 가두기 (이중 방어)

hiring-agent는 공정성·상한 규칙을 시스템 메시지와 기준표 프롬프트 양쪽에 반복해 넣고, 그 위에 코드로도 각 항목 점수를 min(점수, 상한)으로 자르고 총점을 120으로 클램프합니다. "자기 저장소만 있으면 오픈소스 점수는 10점 이하"처럼 구조적 규칙을 GitHub 데이터로 강제하기도 합니다. 모델의 출력을 결코 그대로 믿지 않는다는 태도가 일관됩니다.

그리고 결정적으로 — 이걸 만든 곳이 HackerRank입니다. 기술 채용·코딩 테스트 플랫폼을 운영하는 회사가 '지원자를 어떻게 점수화하는지'의 실제 기준을 통째로 공개한 셈이라, 채용 현업과 학습자 모두에게 화제가 됐습니다. 활동도 활발해 별 약 1.2천에 포크 353개, 열린 PR이 89개에 이릅니다(사람들이 직접 고쳐 보며 기여 중).

3기술 스택 전체 지도

"무거운 AI 프레임워크가 하나도 없다. 파이썬 표준 + PyMuPDF + Pydantic + Jinja2 + Ollama가 전부다."

의외로 단출합니다. LangChain·LlamaIndex·벡터 DB·임베딩이 전혀 없습니다(파일명이 pymupdf_rag.py지만 실제로는 PDF→마크다운 변환일 뿐, 의미 검색 RAG는 어디에도 없습니다). 무게중심은 'PDF를 믿을 수 있는 JSON으로 바꾸기'와 '스키마로 출력 강제하기'에 있습니다.

① 언어 · 실행

기술역할한 줄 설명
Python 3.11+전체 언어웹 프레임워크 없음. python score.py <pdf>로 도는 순수 CLI.
Ollama 0.5기본 LLM(로컬)gemma3:1b/4b/12b 등. 컨텍스트 32K로 강제. 오프라인·무료.
google-generativeai 0.4대체 LLM(클라우드)설정만 바꾸면 Gemini로 전환. 응답을 Ollama 모양으로 변환해 코드 공통화.

② 문서 파싱 · 구조화

기술역할
PyMuPDF 1.26 + pymupdf4llm 0.0.27PDF를 마크다운으로 변환(제목·표·링크 보존). 원시 PDF를 바로 LLM에 던지지 않는 게 핵심.
Pydantic 2.11스키마의 심장. JSON 이력서 구조와 채점 결과(EvaluationData)를 정의하고, .model_json_schema()를 LLM에 '출력 형식'으로 넘긴다.
Jinja2 3.1모든 프롬프트를 .jinja 템플릿으로(총 10개). 기준표·시스템 메시지가 코드가 아니라 템플릿.
requests 2.32GitHub API 호출(GITHUB_TOKEN으로 레이트리밋 60→5000/시).
용어
Pydantic · JSON 스키마로 출력 강제
Pydantic은 파이썬에서 "이 데이터는 이런 모양이어야 한다"를 클래스로 적어 두는 라이브러리입니다. hiring-agent는 그 클래스에서 JSON 스키마를 뽑아 LLM에게 format=으로 넘겨, 반드시 그 모양의 JSON만 내놓도록 강제합니다. 받은 뒤엔 다시 그 클래스로 만들어 보며 한 번 더 검증합니다(생성 때 1번, 검증 때 1번 — 이중 안전장치). Field(ge=0, le=20)처럼 점수 상한 같은 업무 규칙도 스키마 안에 박아 둡니다.

4아키텍처 심화 분석 (이력서 PDF가 점수가 되기까지)

"곧게 흐르는 6단계 파이프라인. 그 한가운데 'GitHub로 검증하고 LLM이 고르는' 에이전트 매듭이 있다."

전체 흐름 한 장

이력서.pdf │ ① PDF → 마크다운 pymupdf_rag.to_markdown() │ (제목·표·링크 보존한 깔끔한 텍스트) ▼ ② 섹션별 LLM 추출 pdf.py — 6개 섹션을 따로따로 │ basics·work·education·skills·projects·awards │ 섹션마다 LLM 1번 + 전용 .jinja + Pydantic 스키마로 JSON 강제 ▼ → 하나의 JSON 이력서 객체 ③ GitHub 검증 (에이전트 매듭) github.py │ • 이력서에서 GitHub 아이디 추출 │ • /users, /repos, /contributors 호출 │ • 저장소마다 내 커밋 수·기여자 수 계산 │ • project_type = 기여자>1 ? "open_source" : "self_project" │ • ★LLM이 '평가용 상위 7개 저장소'를 직접 고름 │ (내 커밋 4개 이상이라는 하드 규칙) ▼ ④ 평가 입력 조립 이력서 + "=== GITHUB DATA ===" 합쳐 한 덩어리 ▼ ⑤ LLM 채점 evaluator.py │ 시스템: 공정성 규칙 / 유저: 채점 기준표(rubric) │ format = EvaluationData 스키마, 온도 0.1 ▼ → 항목 점수 + 근거 + 가점 - 감점 ⑥ 출력 score.py — 보고서 출력 + (개발모드) CSV 한 줄 append ▼ 최종 점수 (항목합 + 가점 − 감점, 코드로 −20~120에 가둠)

한 바퀴를 풀면 이렇습니다. PDF를 곧장 LLM에 던지지 않고 먼저 마크다운으로 바꿉니다(제목·표 구조가 살아 더 잘 읽힘). 한 번에 "이력서 전체 파싱해"라고 안 하고, 6개 섹션을 각각 좁은 프롬프트로 따로 뽑습니다(작은 로컬 모델에서 훨씬 안정적). 여기가 에이전트의 핵심 — 이력서 속 GitHub 주소를 따라가 실제 커밋·기여자 수를 확인하고, 그 정보를 보고 평가에 쓸 저장소 7개를 LLM이 직접 고릅니다. 마지막에 공정성 규칙(시스템) + 기준표(유저)로 채점하되, 점수는 코드가 다시 상한으로 자릅니다.

설계의 심장 — 4개 가중 항목 + 가점/감점

항목만점무엇을 보나
open_source35'남의' 프로젝트에 한 기여. 자기 저장소만이면 10점 이하로 제한.
self_projects30개인 프로젝트의 복잡도. 튜토리얼/CRUD 앱은 상한·감점.
production25실무·인턴·창업 경험. 창업자/초기 엔지니어 가점.
technical_skills10기술·언어의 폭.
bonus_points+20GSoC +5, 창업 +3~5, 포트폴리오 +2, 블로그 +1~3 등.
deductions링크 없는 프로젝트 −3~5, 전부 튜토리얼이면 −2 등.

설계 패턴 ① — 모델의 산수를 안 믿고 코드로 가둔다

LLM에게 상한을 알려 주지만, score.py다시 한 번 각 항목을 min()으로 자르고 총점도 120으로 클램프합니다. "알려 주되, 믿지 않고, 코드로 강제"하는 방어적 태도입니다.

# 각 항목 점수를 상한으로 자른다 — LLM이 넘겨도 무효
category_score = min(category_data["score"], category_data["max"])
...
max_possible_score = max_score + 20  # 100 + 가점 20
if total_score > max_possible_score:
    total_score = max_possible_score   # 총점도 가둔다

설계 패턴 ② — 공정성을 프롬프트에 명문화

편향을 막는 규칙이 기준표 프롬프트에 대문자로 못 박혀 있습니다. 그대로 베껴 쓸 만한 '편향 완화 프롬프트'의 예시입니다.

SCORES MUST NEVER DEPEND ON: 지원자의 이름·성별·인구통계 / 대학·학교 이름 / 학점(GPA) / 도시·지역. … 개인 GitHub 저장소를 가진 것만으로는 오픈소스 기여가 아니다 — 진짜 기여는 '남의' 프로젝트에 한 것.

설계 패턴 ③ — 제공사 추상화(Strategy 패턴)

LLMProvider라는 약속(Protocol)을 두고 OllamaProvider·GeminiProvider가 각각 .chat()을 구현합니다. Gemini 응답을 Ollama 모양({"message":{"content":…}})으로 바꿔 주어, 나머지 코드는 어떤 제공사든 똑같이 다룹니다.

5디렉토리 구조 해부

"폴더가 거의 없는 평평한 구조. 파일 이름이 곧 파이프라인 단계다."

hiring-agent/ ├── score.py (377줄) ⭐ 진입점·오케스트레이터: main(pdf)·보고서·CSV·캐시 ├── pdf.py (323줄) ⭐ PDF→텍스트 + 6개 섹션 LLM 추출 ├── pymupdf_rag.py (1377줄) PyMuPDF "to_markdown"(PDF→마크다운). 가장 큰 파일 ├── github.py (496줄) ⭐ GitHub API + 저장소 분류 + 상위7 LLM 선택 ├── evaluator.py (91줄) ⭐ 채점 LLM 호출(시스템 + 기준표 → EvaluationData) ├── transform.py (938줄) 느슨한 JSON→JSON이력서 정규화, JSON→텍스트, CSV 행 ├── models.py (391줄) Pydantic 스키마(이력서 + 점수) + 제공사 클래스 ├── llm_utils.py (62줄) 제공사 초기화 + 응답 JSON 청소 ├── prompt.py (67줄) 모델→제공사 매핑, 모델별 온도/top_p ├── config.py (6줄) DEVELOPMENT_MODE 플래그(캐시 + CSV) └── prompts/ ├── template_manager.py Jinja 로더/렌더러 └── templates/ (.jinja 10개) ├── basics·work·education·skills·projects·awards.jinja 추출(섹션별 1개) ├── github_project_selection.jinja "상위 7개 저장소 골라" 프롬프트 ├── resume_evaluation_criteria.jinja ★ 채점 기준표(유저 프롬프트) └── resume_evaluation_system_message.jinja 공정성 + 채점 시스템 메시지
파일역할
score.py전체를 지휘. PDF 경로를 받아 ①~⑥을 차례로 부르고, 점수를 코드로 가두고, 보고서를 찍는다.
evaluator.py + criteria.jinja채점의 두뇌. 91줄의 코드 + 기준표 프롬프트가 점수를 만든다.
github.py + selection.jinja에이전트 매듭. 실데이터 수집 + '상위 7개' 결정.
models.py스키마 + 제공사 Strategy. 업무 규칙이 Field 제약으로 들어 있다.

6학습 포인트 (기술별)

"'문서를 믿을 수 있는 JSON으로 바꾸는 법'과 '공정한 LLM 채점 설계'를 통째로 배울 수 있다."

학습 1 · 문서 → 구조화 JSON

PDF는 먼저 마크다운으로, 추출은 섹션별로

원시 PDF를 바로 LLM에 던지지 않고 마크다운으로 변환한 뒤(제목·표·링크 보존), 한 번에 다 파싱하지 않고 6개 섹션을 각각 좁은 프롬프트로 뽑습니다. 작은 모델에서 안정적인, 모든 '문서→JSON' 작업에 재사용 가능한 패턴입니다.

학습 2 · 출력을 절대 그대로 믿지 않기

스키마로 강제 + 인스턴스로 재검증 + 코드로 클램프

LLM 출력을 (a) JSON 스키마로 강제 생성하고, (b) Pydantic 인스턴스화로 재검증하고, (c) 점수는 코드로 상한 클램프합니다. 세 겹의 안전장치 — LLM을 쓰는 어떤 프로덕션 코드에도 적용할 교훈입니다.

# 채점: 스키마로 형식을 강제하고, 받은 뒤 다시 검증
kwargs = {"format": EvaluationData.model_json_schema()}
response = self.provider.chat(**chat_params, **kwargs)
evaluation_data = EvaluationData(**json.loads(response_text))
학습 3 · 공정성 프롬프트 설계

편향 완화를 자연어로 명문화

"이름·성별·학교·학점·지역으로 점수를 매기지 말라"를 시스템 + 유저 프롬프트 양쪽에 반복해 넣는, 구체적이고 베껴 쓸 수 있는 편향 완화 사례. "Google Summer of Code와 Girl Script Summer of Code를 구분하라"처럼 미묘한 함정까지 다룹니다. (비판적으로 볼 점: 기준표가 GSoC·창업·1000★ 저장소를 후하게 보고 튜토리얼/CRUD를 깎는 등 '가치 판단'을 담고 있어, GitHub 활동이 없는 지원자에게 불리할 수 있습니다 — 좋은 토론거리.)

학습 4 · 주장을 실데이터로 검증

이력서의 '말'을 GitHub의 '사실'로 대조

자기 보고한 프로젝트를 실제 커밋·기여자 수로 검증해, 자기 저장소(self_project)와 진짜 오픈소스 기여(open_source)를 가릅니다. "기여자가 2명 이상이면 오픈소스"라는 단순하지만 효과적인 휴리스틱과, "내 커밋 4개 미만 저장소는 절대 선택 금지"라는 하드 게이트가 좋은 예입니다.

7시스템 요구사항

"파이썬 3.11과 LLM 백엔드 하나면 끝. 도커도, 서버도 없다."

항목요구사항
언어Python 3.11+ (.python-version은 3.11.13 고정)
LLM (택1)Ollama(로컬·기본): ollama serve + ollama pull gemma3:4b(저사양 gemma3:1b) / 또는 Gemini(클라우드): GEMINI_API_KEY
환경변수LLM_PROVIDER(ollama/gemini), DEFAULT_MODEL, GEMINI_API_KEY(택1 시), GITHUB_TOKEN(선택 — 레이트리밋 상향)
설치pip install -r requirements.txt + cp .env.example .env. 도커·서버·uvicorn 없음.

8직접 해볼 수 있는 실습 과제

"내 이력서로 돌려 보기 → 기준표 바꿔 보기 → 검증 로직 뜯어보기 순으로."

실습 A 난이도 ★☆☆ 입문

로컬에서 내 이력서 채점해 보기

Ollama를 깔고 gemma3:4b를 받은 뒤 python score.py 내이력서.pdf를 실행합니다. 항목별 점수와 근거 문장이 어떻게 나오는지, 같은 이력서를 모델만 바꿔(1b↔4b↔12b) 돌리면 점수가 얼마나 흔들리는지 관찰하세요.

실습 B 난이도 ★★☆ 중급

채점 기준표(rubric)를 내 팀 기준으로 바꾸기

prompts/templates/resume_evaluation_criteria.jinja를 열어 가중치(예: 오픈소스 35 → 25)나 가점 항목을 바꾸고, 같은 이력서가 어떻게 다르게 채점되는지 비교합니다. '기준표=프롬프트'의 힘을 체감하는 실습입니다.

실습 C 난이도 ★★☆ 중급

GitHub 검증 단계만 떼어 실행

python github.py로 특정 사용자의 저장소 분류·커밋 수·상위 7 선택이 어떻게 동작하는지 봅니다. "기여자 수 > 1 → 오픈소스" 휴리스틱이 실제 계정에서 잘 맞는지, 어디서 어긋나는지 점검해 보세요.

실습 D 난이도 ★★★ 고급

편향 감사(audit) + 스키마 강제 직접 구현

이름·학교만 다르게 한 '쌍둥이 이력서'를 여러 개 만들어 점수가 흔들리는지 편향을 감사해 보고, 별도 작은 프로젝트에서 Pydantic .model_json_schema()로 LLM 출력을 강제하는 패턴을 직접 재현해 봅니다.

9관련 기술 심화 학습 로드맵

"문서 파싱 → 구조화 출력 → LLM 채점 설계 → 공정성/평가 순으로."

주차주제학습 자료 / 키워드
1주차PDF/문서 파싱PyMuPDF, pymupdf4llm, PDF→마크다운, 표/링크 보존
2주차구조화 LLM 출력Pydantic, JSON 스키마, function calling, format= 강제
3주차프롬프트 템플릿화Jinja2, 시스템/유저 메시지 분리, 기준표=프롬프트
4주차로컬 LLM 운영Ollama, 모델별 온도/컨텍스트, 제공사 추상화(Strategy)
5주차외부 데이터로 검증GitHub REST API, 레이트리밋, 휴리스틱 분류
6주차평가·공정성루브릭 설계, 편향 완화 프롬프트, 편향 감사(audit) 방법

10핵심 키워드 사전

"문서에 나온 용어를 한자리에."

용어의미
ATSApplicant Tracking System(지원자 추적 시스템). 채용에서 이력서를 모으고 거르는 소프트웨어. 보통 키워드 매칭에 의존.
루브릭(rubric)무엇을 보고 몇 점을 주는지 적은 채점 기준표. 여기선 프롬프트 파일.
Pydantic데이터 모양을 클래스로 정의·검증하는 파이썬 라이브러리. JSON 스키마를 뽑아 LLM에 출력 형식으로 넘긴다.
구조화 출력LLM이 자유 문장이 아니라 정해진 JSON 모양으로만 답하게 강제하는 것.
Ollama내 컴퓨터에서 오픈소스 LLM을 돌리는 도구. 무료·오프라인.
RAGRetrieval-Augmented Generation(검색 증강 생성). 외부 자료를 찾아 LLM에 보태는 기법. 이 프로젝트엔 실제로는 없음(파일명만 그렇게 보임).
편향(bias) 완화이름·성별·학교 등 무관한 속성이 점수에 영향을 못 주게 막는 설계.
클램프(clamp)값을 정해진 범위로 자르는 것(예: 점수를 0~상한으로).

11참고 링크