项目简介
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
常见问题 (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、速率限制、网络或其他 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,它无法编码表情符号、带重音符号的姓名或其他 Unicode 字符。
将:
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"
)
将相同的 encoding="utf-8" 参数应用于 github.py 和 score.py 中的所有缓存写入调用,包括 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 的版本,请升级到该版本。