개요
Hiring Agent는 이력서 PDF를 파싱하고, 대규모 언어 모델을 활용해 구조화된 데이터를 추출하며, GitHub 신호를 결합하여 공정하고 설명 가능한 점수를 출력합니다. 완전한 로컬 실행(Ollama) 또는 Google Gemini 사용을 지원합니다.
README 미리보기
# Hiring Agent\n\nResume-to-Score pipeline that extracts structured data from PDFs, enriches with GitHub signals, and outputs a fair, explainable evaluation.\n\n\n \n \n \n \n \n \n \n \n \n\n\n---\n\n## Contents\n\n- [Overview](#overview)\n- [Architecture](#architecture)\n- [Installation and Setup](#installation-and-setup)\n - [Prerequisites](#prerequisites)\n - [Quick setup with pip](#quick-setup-with-pip)\n - [Ollama models](#ollama-models)\n- [Configuration](#configuration)\n- [How it works](#how-it-works)\n- [CLI usage](#cli-usage)\n- [Directory layout](#directory-layout)\n- [Provider details](#provider-details)\n- [Contributing](#contributing)\n- [License](#license)\n\n---\n\n## Overview\n\nHiring Agent parses a resume PDF to Markdown, extracts sectioned JSON using a local or hosted LLM, augments the data with GitHub profile and repository signals, then produces an objective evaluation with category scores, evidence, bonus points, and deductions. You can run fully local with Ollama or use Google Gemini.\n\n---\n\n## Architecture\n\n\n\n\n\n**Flow**\n\n1. `pymupdf_rag.py` converts PDF pages to Markdown-like text.\n2. `pdf.py` calls the LLM per section using Jinja templates under `prompts/templates`.\n3. `github.py` fetches profile and repos, classifies projects, and asks the LLM to select the top 7.\n4. `evaluator.py` runs a strict-scored evaluation with fairness constraints.\n5. `score.py` orchestrates everything end to end and writes CSV when development mode is on.\n\n\n\n\n**Key modules**\n\n- `models.py`\n Pydantic schemas and LLM provider interfaces.\n\n- `llm_utils.py`\n Provider initialization and response cleanup.\n\n- `transform.py`\n Normalization from loose LLM JSON to JSON Resume style.\n\n- `prompts/`\n All Jinja templates for extraction and scoring.\n\n\n\n\n\n---\n\n## Installation and Setup\n\n### Prerequisites\n\n- **Python 3.11+**\n\n The repository pins `.python-version` to 3.11.13.\n\n- **One LLM backend** (either of
FAQ (5)
문제 해결GitHub API 오류 처리에서 "NameError: name 'response' is not defined"를 어떻게 수정하나요?
github.py의 fetch_all_github_repos에서 _fetch_github_api()는 (status_code, data)를 반환하므로 response.status_code나 response.text에 접근하지 마세요. 오류 처리 블록을 다음과 같이 교체하세요:
elif status_code == 404:
print(f"GitHub user not found: {username}")
return []
else:
print(f"GitHub API error: {status_code} - {repos_data}")
return []
이렇게 하면 GitHub가 404, rate-limit, 네트워크 또는 기타 API 오류를 반환할 때 애플리케이션이 NameError로 인해 충돌하는 것을 방지할 수 있습니다.
문제 해결Python에서 여러 PDF 파일을 처리할 때 PyMuPDF 메모리 누수를 어떻게 해결하나요?
각 PyMuPDF Document를 명시적으로 닫으세요. pdf.py에서 extract_text_from_pdf()를 컨텍스트 매니저를 사용하도록 업데이트하세요:
import fitz
def extract_text_from_pdf(path):
with fitz.open(path) as doc:
return "".join(page.get_text() for page in doc)
with 문은 텍스트 추출 후 PDF를 자동으로 닫아 메모리를 해제합니다. 컨텍스트 매니저를 사용할 수 없는 경우에는 finally 블록에서 doc.close()를 호출하세요. 이렇게 하면 Python 3.11 환경에서 PyMuPDF 1.24.x로 50개 이상의 PDF 배치를 처리할 때 메모리가 지속적으로 증가하는 것을 방지할 수 있습니다.
문제 해결Windows에서 Python으로 JSON 캐시 파일을 쓸 때 발생하는 "UnicodeEncodeError: 'charmap' codec can't encode" 오류는 어떻게 수정하나요?
캐시 파일을 작성하는 모든 Path.write_text() 호출에 UTF-8 인코딩을 명시적으로 설정하세요. Windows에서는 기본 인코딩이 cp1252일 수 있으며, 이 인코딩은 이모지, 악센트가 포함된 이름 또는 기타 유니코드 문자를 인코딩할 수 없습니다.
변경 전:
Path(github_cache_filename).write_text(
json.dumps(github_data, indent=2, ensure_ascii=False)
)
변경 후:
Path(github_cache_filename).write_text(
json.dumps(github_data, indent=2, ensure_ascii=False),
encoding="utf-8"
)
github.py와 score.py에서 캐시를 작성하는 모든 호출에 동일한 encoding="utf-8" 인수를 적용하세요. 여기에는 github.py 49행 부근의 GitHub API 캐시도 포함됩니다. 추가적인 견고성을 위해 캐시를 읽을 때 캐시를 다시 생성하여 비어 있거나 손상된 JSON을 처리할 수 있습니다. 이 수정은 Windows 11 및 Python 3.11 환경에서 검증되었습니다.
문제 해결score.py를 실행할 때 Windows 콘솔 UnicodeEncodeError와 Python 3.14 Protobuf TypeError를 어떻게 수정하나요?
Protobuf 또는 LLM 라이브러리를 임포트하기 전에 다음을 추가하세요:
import os
import sys
# Windows 콘솔 출력에 UTF-8 사용
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
if hasattr(sys.stderr, "reconfigure"):
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
# 순수 Python Protobuf 구현을 강제로 사용
os.environ.setdefault("PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION", "python")
환경 변수는 protobuf가 임포트되기 전에 설정해야 합니다. 또는 다음을 실행하세요:
$env:PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION="python"
python score.py
또한 set PYTHONUTF8=1로 UTF-8 모드를 활성화하거나, 스크립트를 시작하기 전에 chcp 65001을 실행할 수 있습니다. 수정 사항은 PR #340에서 제공됩니다.
문제 해결Gemini 429 오류 또는 Ollama 타임아웃 이후에 이력서 스코어링이 'List should have at least 1 item' 오류와 함께 실패하는 이유는 무엇인가요?
LLM 추출에 실패하면 빈 리스트나 null 필드와 같은 불완전한 데이터가 cache/resumecache_<resume>.json에 기록될 수 있습니다. 이후 실행 시 PDF를 다시 처리하는 대신 손상된 캐시가 로드되어 Pydantic ValidationError가 발생합니다.
즉시 해결 방법:
rm cache/resumecache_resume.json
python score.py data/resume.pdf
이름이 다른 경우 영향을 받는 PDF와 일치하는 캐시 파일을 삭제하세요. DEVELOPMENT_MODE = False로 임시 설정하여 캐시 로드를 우회할 수도 있습니다.
영구적인 해결 방법: 추출된 JSONResume 객체를 디스크에 쓰기 전에 검증하고, 실패한 추출은 절대 캐시하지 않으며, 로드 시 캐시된 데이터를 검증하세요. 캐시 검증에 실패하면 파일을 삭제하거나 무시하고 새로 PDF 추출을 실행하세요. 이 동작은 PR #198에서 해결되었으며, 해당 PR이 포함된 릴리스가 있다면 업데이트하세요.