The plan targets PSLF-eligible, mission-driven employers (government,
tribal, nonprofit, FQHC/community health, academic, public hospital).
For-profit DSOs (Smile Doctors, Sonrava, Specialty Dental Brands) had
slipped in through site: queries and auto-discovered boards, and their
pages produced off-scope leads — including Pediatric Dentist titles
that were never orthodontist jobs.
- employer_scope: rule-based employer classifier (.gov/.mil/.edu/
.nsn.us hosts, configured public domains, government/tribal/
nonprofit/academic/FQHC terms); private/unknown employers are
FILTERED at verification (default-deny) and logged to
workspace/candidates/*.employers.jsonl. Board catalogs declare
employer_type; auto-discovered boards are written only when the
SERP evidence classifies as mission-driven.
- roles: new other_specialty class (pediatric dentist, endodontist,
oral surgeon, general dentist …) filtered before target matching;
hidden dentist-title lanes at public employers stay.
- verifier: normalize ATS page titles ("Job Application for X at Y"
-> "X") before storing; apply employer gate.
- discovery: scope filter + out-of-scope reporting; private auto
catalog cleared (University of Utah Health kept as academic).
- closed markers: drop bare "filled" — federal boilerplate ("until
the position is filled") marked open USAJOBS postings as CLOSED.
- urls: strip default :443/:80 ports so USAJOBS links dedupe.
|
3 weeks ago | |
|---|---|---|
| gimme_job | 3 weeks ago | |
| sites | 3 weeks ago | |
| tests | 3 weeks ago | |
| .env.example | 6 months ago | |
| .gitignore | 6 months ago | |
| CLAUDE.md | 6 months ago | |
| README.md | 3 weeks ago | |
| pyproject.toml | 3 weeks ago | |
| uv.lock | 6 months ago | |
README.md
gimme-job
로컬 구인공고 수집기(macOS). 여러 채용 사이트를 순회하며 채용 공고를 수집·정규화해 SQLite에 저장하고, 새 공고를 Telegram(또는 KakaoTalk/로컬 Markdown)으로 알려준다.
AI-assisted learning, non-AI runtime 크롤링 실행(runtime)은 순수 Python + Playwright만 사용한다. AI(Claude Code, Ollama)는 신규 사이트 학습과 깨진 어댑터 복구, 그리고 수집 결과 요약에만 사용된다.
기능
- 커스텀 어댑터 또는
sites/*.yaml매니페스트만으로 사이트 지원 - Chrome 영속 프로필(
JobAgent) 기반 세션 유지(로그인 불필요 반복) - 카드 수집 → 정규화 → 후처리 필터 → fingerprint 중복 제거 → DB 저장
- 이미 수집된 페이지 연속 2회 감지 시 조기 종료(불필요한 페이지네이션 방지)
- Ollama(
qwen3.5:9b)를 통한 일일 요약 - Telegram 알림 + 실패 시 로컬 Markdown 폴백
요구 사항
- Python >= 3.12
- uv (권장) —
pip사용 시pyproject.toml의 의존성 설치 - Playwright 브라우저:
chromium - (선택) Ollama — 요약 기능용
- (선택) Claude Code CLI —
learn/repair명령용 - (선택) Telegram 봇 토큰 — 알림용
설치
uv sync # 의존성 설치
uv run playwright install chromium # Chromium 설치
cp .env.example .env # 토큰 설정 (알림/LLM)
uv run gimme-job init # 디렉터리·DB 초기화 및 사전 점검
.env 주요 항목:
| 변수 | 설명 |
|---|---|
TELEGRAM_BOT_TOKEN |
Telegram 봇 토큰 |
TELEGRAM_CHAT_ID |
수신 chat ID (쉼표로 여러 개) |
KAKAO_* |
KakaoTalk self-memo 설정 (선택) |
OLLAMA_BASE_URL |
Ollama 주소 (기본 http://127.0.0.1:11434) |
GIMME_JOB_DB_PATH |
DB 경로 (기본 gimme_job.db) |
LOG_LEVEL |
로그 레벨 |
최초 로그인
사이트에 따라 로그인이 필요한 경우(LinkedIn, Indeed, Google 등) JobAgent 프로필에 한 번 로그인해 두면 이후 실행에 재사용된다.
uv run gimme-job login
브라우저가 열리면 필요한 사이트에 로그인한 뒤 터미널에서 Enter를 눌러 종료한다.
실행
1회 실행 (전체 사이트)
uv run gimme-job run
특정 사이트만
uv run gimme-job run --site usajobs
테스트 모드 (저장/알림 없이 추출만)
uv run gimme-job run --dry-run
정기 실행 (기본 1시간 간격)
uv run gimme-job auto --hour 2
능동 검색 (proactive)
수집 시스템(고정 사이트 긁기)과 별개로, 검색엔진 쿼리 매트릭스(전 50주 + 준주 + OCONUS)로
공고를 발견하고, 발견된 URL을 열어 ATS/지원 경로를 휴리스틱으로 검증해 원장에 저장한다.
런타임에 AI 호출 없음. 계획은 sites/proactive.yaml에서 관리.
일일 매트릭스는 두 개의 주(state) 레인으로 구성된다 — 일반 설정용어
({setting} orthodontist {state})와 hidden-title 조합("dentist II" orthodontics {state} 등,
문서 §3.2/§4)을 순환. 검색엔진에 인덱싱되지 않는 숨은 공고는 고용주 ATS 보드(Workday/ICIMS/
Greenhouse/Lever/SmartRecruiters/USAJOBS)를 직접 크롤링해 잡는다. 대상 고용주는
sites/employers.yaml(수동)과 sites/employers.auto.yaml(자동 발견)에서 병합되며,
미국 정부 직책은 USAJOBS_API_KEY/USAJOBS_API_EMAIL(.env)을 쓴다. (키 미승인 시 graceful skip.)
보드 카탈로그는 자동으로 성장한다 — site:{ats-domain} orthodontist SERP 쿼리로 후보를 찾고,
ATS API를 probe해 치과 키워드가 실제로 있는 보드만 등록한다. 주간 deep scan에서 자동 실행되며
(boards.discovery), 수동 실행도 가능하다.
검증 전에 role gate(제목 규칙)가 보조/비임상/타 전문과목 직무를 걸러낸다 — Dental Assistant·
Hygienist·Coordinator·Payable, 그리고 Pediatric Dentist·Endodontist 같은 비-교정 전문과목은
수집·검증 단계에서 FILTERED. 공공기관에서 교정의 공고가 일반 타이틀("Dentist II", "Staff Dentist",
"Chief Dental Officer")로 올라오는 hidden-title 레인은 유지한다. ortho 신호는 페이지 전체가 아니라
제목 + 공고 설명 영역에서만 확인하므로, 치과 고용주 회사소개 문구("... & Orthodontics")로 인한
오탐이 없다. 경계 타이틀은 런 후 오프라인으로 role-audit(gemma4)이 자동 검토한다 —
support/non_clinical 제안은 sites/role_terms.auto.yaml에 자동 반영되고, target 제안은 게이트
완화 위험이 있어 리포트(workspace/manifests/role-audit-*.md)에만 기록된다. 런타임 수집·검증은
여전히 non-AI이며, Ollama가 없으면 audit만 조용히 건너뛴다.
Employer scope gate — 대상은 mission-driven 고용주(정부·트라이벌·비영리·FQHC/커뮤니티헬스·
공공병원·대학)뿐이다. 영리 사기업(DSO 등)은 리드에서 제외하며, 공공/비영리 신호가 확인되지 않으면
기본 제외(default-deny)한다 — 신호가 없으면 workspace/candidates/*.employers.jsonl에 기록되어
나중에 검토할 수 있다. sites/employers.yaml의 보드는 employer_type을 선언하고
(예: USAJOBS → government, University of Utah Health → academic), 자동 발견 보드는 SERP 근거에서
mission-driven으로 분류된 것만 employers.auto.yaml에 기록된다.
uv run gimme-job proactive run # 1회 실행 (발견+검증+저장+보고)
uv run gimme-job proactive run --mode weekly # 주간 deep scan (ATS 도메인 site: 쿼리 + 보드 자동 발견)
uv run gimme-job proactive run --dry-run --limit 2 --max-verify 3 # 빠른 테스트
uv run gimme-job proactive discover-boards --write # ATS 보드 수동 발견 → employers.auto.yaml
uv run gimme-job proactive role-audit # 오프라인 LLM(gemma4) 경계 타이틀 검토 → 용어 제안
uv run gimme-job proactive role-audit --write # 제안 용어를 sites/role_terms.auto.yaml에 반영
uv run gimme-job proactive auto --at 06:00 # 매일 06:00 자동 (일요일 weekly 포함, Ctrl+C 종료)
uv run gimme-job proactive reverify # CLOSED/STALE 리드 재검증 → 재개방 감지
uv run gimme-job proactive outreach # 개인 클리닉 직접 연락 목록 (숨은 시장)
uv run gimme-job proactive google-unlock # Google 차단 1회 수동 해제 (아래 참고)
uv run gimme-job proactive report # 오늘 보고서 재생성·재알림 (--summary: Ollama 요약)
uv run gimme-job proactive list [--status NEW] # 리드 원장 조회
발견된 후보는 정규 URL로 1차 중복 제거 후 (제목, 고용주) 기준으로 교차 소스 병합(문서 §19)되어,
같은 공고가 여러 보드/검색엔진에 있어도 공식 ATS URL을 대표로 삼고 나머지는 secondary_urls에 보존한다.
Google 차단 해제: Google Jobs가 "unusual traffic"으로 차단되면
gimme-job proactive google-unlock을 실행해 실제 Chrome 창에서 CAPTCHA를 한 번 통과하세요. 쿠키가 JobAgent 프로필에 저장되어 이후 런에서 재사용됩니다. 여전히 차단되면sites/proactive.yaml의googlejobs.enabled: false로 DuckDuckGo 단독 모드로 전환할 수 있습니다.
새 고용주 ATS 보드 추가: 보통은 자동 발견에 맡기면 된다 —
proactive discover-boards --write(또는 주간 deep scan)가 mission-driven으로 분류된 보드만sites/employers.auto.yaml에 기록하고, 실행 시 두 카탈로그를 병합한다. 수동으로 확실히 추가하려면sites/employers.yaml의boards:에 항목과employer_type(government/tribal/nonprofit/academic/public_health/mission)을 추가한다 — 각 플랫폼 토큰(Greenhouse slug / SmartRecruiters company id / ICIMS tenant / Workday domain·tenant·org)은 실제로 응답하는지 확인 후 반영해야 한다 (파일 상단에 예시 curl 명령).smartrecruiters: {org: NATIVEHEALTH, employer_type: nonprofit}처럼 검증된 항목만 유지된다.
보고서는 매 런마다 workspace/reports/proactive-YYYY-MM-DD.html(스타일된 HTML 페이지)과
.md로 저장되고, 터미널 로그에 클릭 가능한 링크(OSC-8)가 함께 출력된다
(proactive run/report/outreach). 한 달(기본 30일)이 지난 리포트는 자동 삭제되며
sites/proactive.yaml의 report.retention_days로 조정한다. 알림은 설정된
프로바이더(sites/global.yaml의 notification.provider — Telegram 또는 KakaoTalk)로
전송된다(실패 시 markdown 폴백). 상태: NEW / REOPENED / STILL OPEN /
VERIFY(외부 보드 잔존분·리뷰 대상) / CLOSED / STALE.
알림만 다시 보내기
uv run gimme-job notify --today
조회 / 상태 확인
uv run gimme-job list # 수집된 공고 목록
uv run gimme-job list --site usajobs # 사이트별 필터
uv run gimme-job list --today # 오늘 새 공고만
uv run gimme-job status # 사이트별 활성/복구/실패 현황
신규 사이트 학습 / 깨진 어댑터 복구
# 새 사이트 학습 (Claude Code가 사이트를 분석해 어댑터+매니페스트 생성)
uv run gimme-job learn --site-id <site_id> --url "<검색결과 URL>"
# 깨진 어댑터 복구
uv run gimme-job repair <site_id> # 특정 사이트
uv run gimme-job repair --all # repair_needed 전체
어댑터 스모크 테스트
uv run gimme-job test <site_id>
# 또는
uv run pytest tests/adapters/test_<site_id>.py -v
프로젝트 구조
gimme_job/
cli.py # CLI 진입점
config.py # 전역/사이트 설정 로더
adapters/ # 사이트별 어댑터 (+ base.py, registry.py)
proactive/ # 능동 검색: plan(쿼리 매트릭스), engines(SERP), verifier(ATS 검증), roles(role gate), boards(ATS 피드), discovery(보드 자동 발견), role_audit(오프라인 LLM), outreach(직접 연락), engine(오케스트레이터), report, scheduler, deepscan
models/ # Pydantic(dto/manifest) + SQLAlchemy(db) 모델
runtime/ # orchestrator, browser, extractor, notifier 등
db/ # engine, repo, migrations
prompts/ # learn/repair용 프롬프트
templates/ # 알림 다이제스트 템플릿
sites/
global.yaml # 전역 설정(검색 키워드, 런타임 등)
proactive.yaml # 능동 검색 계획(용어·주·예산·denylist·검증 규칙)
employers.yaml # ATS 직접 크롤링 대상 고용주 카탈로그 (hidden jobs, 수동 큐레이션)
employers.auto.yaml # 자동 발견된 ATS 보드 (discover-boards --write가 생성/갱신)
role_terms.auto.yaml # role-audit이 제안한 role 용어 (자동, 기본값에 머지됨)
<site_id>.yaml # 사이트별 매니페스트
tests/adapters/ # 어댑터 스모크 테스트
tests/proactive/ # 능동 검색 단위 테스트
workspace/ # Chrome 프로필, 캡처, 학습 리포트 등 산출물
새 사이트를 추가하려면 다음 4개 파일이 필요하다:
sites/{site_id}.yaml— 매니페스트gimme_job/adapters/{site_id}.py— 어댑터 (BaseJobSiteAdapter프로토콜 구현)tests/adapters/test_{site_id}.py— 스모크 테스트workspace/manifests/{site_id}.learning-report.md— 학습 리포트