You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
I Luk Kim 3b6e5cdbd4 proactive: mission-driven employer scope + non-ortho specialty gate
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 proactive: mission-driven employer scope + non-ortho specialty gate 3 weeks ago
sites proactive: mission-driven employer scope + non-ortho specialty gate 3 weeks ago
tests proactive: mission-driven employer scope + non-ortho specialty gate 3 weeks ago
.env.example Add Google Jobs & HospitalRecruiting adapters; LinkedIn Volunteer filter; aaoinfo fixes 6 months ago
.gitignore Initial implementation of gimme-job CLI 6 months ago
CLAUDE.md Initial implementation of gimme-job CLI 6 months ago
README.md proactive: mission-driven employer scope + non-ortho specialty gate 3 weeks ago
pyproject.toml build: scope sdist to package, sites, tests, and docs 3 weeks ago
uv.lock Add 8 site adapters, early stop, Telegram notifications, and list command 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개 파일이 필요하다:

  1. sites/{site_id}.yaml — 매니페스트
  2. gimme_job/adapters/{site_id}.py — 어댑터 (BaseJobSiteAdapter 프로토콜 구현)
  3. tests/adapters/test_{site_id}.py — 스모크 테스트
  4. workspace/manifests/{site_id}.learning-report.md — 학습 리포트