jobscout 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -0
- package/bin/cli.mjs +190 -0
- package/chatgpt/GPT-INSTRUCTIONS.md +103 -0
- package/chatgpt/README.md +72 -0
- package/chatgpt/knowledge/job.schema.json +58 -0
- package/chatgpt/knowledge/match.schema.json +48 -0
- package/chatgpt/knowledge/profile.schema.json +121 -0
- package/chatgpt/knowledge/skill-application-assistant.md +38 -0
- package/chatgpt/knowledge/skill-bilingual-normalization.md +46 -0
- package/chatgpt/knowledge/skill-candidate-intake.md +43 -0
- package/chatgpt/knowledge/skill-find-jobs.md +50 -0
- package/chatgpt/knowledge/skill-fit-analyzer.md +37 -0
- package/chatgpt/knowledge/skill-job-collector.md +21 -0
- package/chatgpt/knowledge/skill-job-matcher.md +16 -0
- package/chatgpt/knowledge/skill-job-schema.md +62 -0
- package/chatgpt/knowledge/skill-scoring-rubric.md +108 -0
- package/job-matching-plugin/.claude-plugin/plugin.json +17 -0
- package/job-matching-plugin/.codex-plugin/plugin.json +18 -0
- package/job-matching-plugin/AGENTS.md +55 -0
- package/job-matching-plugin/README.md +84 -0
- package/job-matching-plugin/agents/job-collector.md +40 -0
- package/job-matching-plugin/agents/job-matcher.md +37 -0
- package/job-matching-plugin/commands/find-jobs.md +41 -0
- package/job-matching-plugin/docs/claude-runtime-notes.md +69 -0
- package/job-matching-plugin/schemas/job.schema.json +58 -0
- package/job-matching-plugin/schemas/match.schema.json +48 -0
- package/job-matching-plugin/schemas/profile.schema.json +121 -0
- package/job-matching-plugin/skills/application-assistant/SKILL.md +38 -0
- package/job-matching-plugin/skills/bilingual-normalization/SKILL.md +46 -0
- package/job-matching-plugin/skills/candidate-intake/SKILL.md +43 -0
- package/job-matching-plugin/skills/find-jobs/SKILL.md +50 -0
- package/job-matching-plugin/skills/fit-analyzer/SKILL.md +37 -0
- package/job-matching-plugin/skills/job-collector/SKILL.md +21 -0
- package/job-matching-plugin/skills/job-matcher/SKILL.md +16 -0
- package/job-matching-plugin/skills/job-schema/SKILL.md +62 -0
- package/job-matching-plugin/skills/scoring-rubric/SKILL.md +108 -0
- package/package.json +46 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: candidate-intake
|
|
3
|
+
description: Parse CV của ứng viên (PDF/DOCX/text) và thu thập target/mong muốn để tạo profile.json chuẩn. Dùng khi bắt đầu một phiên tìm việc, khi người dùng cung cấp CV mới, hoặc muốn cập nhật mục tiêu nghề nghiệp.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Candidate Intake — tạo profile ứng viên
|
|
7
|
+
|
|
8
|
+
Đầu ra: một file `data/profiles/<slug>.json` tuân theo `./schemas/profile.schema.json`.
|
|
9
|
+
|
|
10
|
+
## Bước 1 — Đọc CV
|
|
11
|
+
|
|
12
|
+
- Trích text từ CV:
|
|
13
|
+
- **PDF** → Claude Code: dùng skill `pdf` (anthropic-skills). Codex/CLI: `pdftotext cv.pdf -` (poppler) hoặc `python -c "import pypdf; ..."`. Không có tool → yêu cầu người dùng dán nội dung CV.
|
|
14
|
+
- **DOCX** → Claude Code: skill `docx`. Codex/CLI: `python -c "import docx; ..."` (python-docx) hoặc unzip đọc `word/document.xml`.
|
|
15
|
+
- **Text/Markdown** → đọc trực tiếp.
|
|
16
|
+
- Trích: thông tin cá nhân tối thiểu (tên, headline, location), kỹ năng, kinh nghiệm (title/công ty/thời gian/highlights), học vấn, ngôn ngữ.
|
|
17
|
+
- Suy `total_years` và `seniority` từ tổng thời gian + phạm vi trách nhiệm.
|
|
18
|
+
|
|
19
|
+
## Bước 2 — Chuẩn hóa (song ngữ)
|
|
20
|
+
|
|
21
|
+
- Áp skill `bilingual-normalization`: đưa skills & chức danh về canonical name, chuẩn hóa location.
|
|
22
|
+
- Với mỗi skill, cố gắng gắn `years`/`level`/`evidence` từ CV (không bịa — thiếu thì để trống).
|
|
23
|
+
|
|
24
|
+
## Bước 3 — Thu thập target
|
|
25
|
+
|
|
26
|
+
Hỏi ứng viên (gộp câu hỏi, đừng hỏi lẻ tẻ). Nếu người dùng đã nêu sẵn trong hội thoại thì dùng luôn, chỉ hỏi phần còn thiếu:
|
|
27
|
+
|
|
28
|
+
1. **Vai trò mong muốn** (desired_roles) + cấp bậc (desired_level).
|
|
29
|
+
2. **Địa điểm** + hình thức (onsite/hybrid/remote/any).
|
|
30
|
+
3. **Lương kỳ vọng** (min & target, VND hay USD, tháng/năm).
|
|
31
|
+
4. **Ngành** ưu tiên + **quy mô công ty**.
|
|
32
|
+
5. **Dealbreakers** — điều tuyệt đối không chấp nhận.
|
|
33
|
+
6. **Ưu tiên** (priorities): cái gì quan trọng nhất? (lương / kỹ năng phù hợp / địa điểm / ngành / văn hóa). Chuyển thành trọng số 0–1 cho `target.priorities` (tổng ~1) — đây là dữ liệu quan trọng cho `scoring-rubric`.
|
|
34
|
+
|
|
35
|
+
## Bước 4 — Ghi file & Xác nhận
|
|
36
|
+
- Ghi `profile.json` đúng schema. Đặt tên slug từ tên ứng viên (không dấu, kebab-case).
|
|
37
|
+
- Tóm tắt lại cho người dùng xác nhận: seniority suy ra, top skills, target, và trọng số priorities. Cho phép chỉnh trước khi sang bước collector.
|
|
38
|
+
|
|
39
|
+
## Nguyên tắc
|
|
40
|
+
|
|
41
|
+
- Không bịa kỹ năng/kinh nghiệm không có trong CV.
|
|
42
|
+
- Nếu CV thiếu năm tháng cụ thể, ước lượng và ghi rõ là ước lượng.
|
|
43
|
+
- Tôn trọng riêng tư: chỉ lưu thông tin cần cho matching; không đưa dữ liệu cá nhân ra ngoài.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: find-jobs
|
|
3
|
+
description: Chạy full pipeline tìm việc song ngữ Việt–Anh — parse CV, thu thập job (web search), chấm điểm, xuất báo cáo fit/gap. Dùng khi người dùng muốn "tìm job cho tôi" từ một CV hoặc profile đã có.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# find-jobs — điều phối full pipeline tìm việc
|
|
7
|
+
|
|
8
|
+
Skill điều phối, chạy tuần tự 4 bước. Đối số là đường dẫn CV (PDF/DOCX/text) hoặc tên
|
|
9
|
+
profile đã có trong `data/profiles/`.
|
|
10
|
+
|
|
11
|
+
Dùng `<run-id>` duy nhất theo mẫu `<candidate-slug>-<YYYYMMDD-HHMMSS>-<suffix>` và giữ nguyên
|
|
12
|
+
ID này xuyên suốt pipeline. Tạo `data/profiles`, `data/jobs`, `data/results` trong thư mục làm việc
|
|
13
|
+
nếu chưa có.
|
|
14
|
+
|
|
15
|
+
> **Yêu cầu (bước 2 — thu thập job):**
|
|
16
|
+
> - **Claude Code**: `WebSearch`/`WebFetch` có sẵn — không cần cấu hình.
|
|
17
|
+
> - **Codex**: cần bật `web_search` bằng `codex --search` hoặc `tools.web_search = true` trong `~/.codex/config.toml`. Nếu chưa bật, dừng ở bước 2 và báo người dùng.
|
|
18
|
+
|
|
19
|
+
## Bước 1 — Intake → profile.json
|
|
20
|
+
Áp skill **`candidate-intake`**.
|
|
21
|
+
- Nếu đối số là file CV → parse và tạo `data/profiles/<slug>.json` theo `schemas/profile.schema.json`.
|
|
22
|
+
- Nếu là tên profile đã tồn tại → dùng lại, hỏi có cập nhật target không.
|
|
23
|
+
- Thu thập/xác nhận target: vai trò, cấp bậc, địa điểm/remote, lương, priorities (→ trọng số), dealbreakers.
|
|
24
|
+
- Tóm tắt cho người dùng xác nhận.
|
|
25
|
+
|
|
26
|
+
## Bước 2 — Collect → data/jobs/<run-id>.json
|
|
27
|
+
Spawn một subagent context riêng và yêu cầu nó áp skill **`job-collector`**; không phụ thuộc custom
|
|
28
|
+
agent cài ngoài plugin.
|
|
29
|
+
- Truyền đường dẫn `profile.json`, `<run-id>` và output `data/jobs/<run-id>.json`.
|
|
30
|
+
- **Nguồn Job Boards**: Quét từ ITviec, TopCV, VietnamWorks, LinkedIn...
|
|
31
|
+
- Giữ job thỏa mãn tiêu chuẩn: chỉ giữ job xem được full JD & còn hạn. Tối đa 20 job hợp lệ.
|
|
32
|
+
|
|
33
|
+
## Bước 3 — Match → data/results/<run-id>.shortlist.json
|
|
34
|
+
Spawn một subagent context riêng và yêu cầu nó áp skill **`job-matcher`** (chấm điểm bulk).
|
|
35
|
+
- Truyền `profile.json` + `data/jobs/<run-id>.json`.
|
|
36
|
+
- Chấm điểm theo skill **`scoring-rubric`**, dùng trọng số từ `target.priorities`.
|
|
37
|
+
|
|
38
|
+
## Bước 4 — Fit report → data/results/<run-id>.fit_report.md
|
|
39
|
+
Áp skill **`fit-analyzer`**.
|
|
40
|
+
- Bảng xếp hạng: **mọi job đều có link nộp CV (url) bấm được**; phân tích chi tiết top 20 (hoặc toàn bộ).
|
|
41
|
+
- Báo đường dẫn file báo cáo cho người dùng.
|
|
42
|
+
|
|
43
|
+
## Sau khi xong
|
|
44
|
+
Đề xuất bước tiếp: tailor hồ sơ cho job top (skill `application-assistant`), hoặc mở rộng
|
|
45
|
+
nguồn (remote quốc tế / thành phố khác).
|
|
46
|
+
|
|
47
|
+
## Nguyên tắc
|
|
48
|
+
- Không bịa dữ liệu job/CV. Thiếu → unknown, hạ confidence.
|
|
49
|
+
- Tôn trọng robots/ToS khi scrape; không vượt anti-bot/CAPTCHA.
|
|
50
|
+
- Không tự nộp hồ sơ thay người dùng.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fit-analyzer
|
|
3
|
+
description: Với các job trong shortlist, giải thích mức độ phù hợp, khoảng cách kỹ năng/kinh nghiệm, rủi ro, và gợi ý cải thiện — dạng báo cáo dễ đọc cho ứng viên. Dùng sau khi job-matcher đã tạo shortlist.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Fit Analyzer — báo cáo phù hợp & khoảng cách
|
|
7
|
+
|
|
8
|
+
Đầu vào: `profile.json` + `data/results/<run-id>.shortlist.json` (mảng MatchResult + job tương ứng).
|
|
9
|
+
Đầu ra: `data/results/<run-id>.fit_report.md`.
|
|
10
|
+
|
|
11
|
+
## Phạm vi báo cáo (QUAN TRỌNG)
|
|
12
|
+
|
|
13
|
+
Phân tích chi tiết **TẤT CẢ job trong shortlist**, không rút gọn còn top 3. Nếu shortlist có nhiều hơn **20** job thì phân tích chi tiết 20 job điểm cao nhất và vẫn liệt kê đủ phần còn lại trong bảng xếp hạng. Có thể thêm mục "🏆 Ưu tiên apply ngay" (2–3 job) ở đầu như phần tóm tắt, nhưng **phần thân báo cáo vẫn phải có block chi tiết cho từng job** (không bỏ job nào).
|
|
14
|
+
|
|
15
|
+
## Nội dung báo cáo
|
|
16
|
+
|
|
17
|
+
Với **mỗi** job (theo thứ tự điểm giảm dần), trình bày:
|
|
18
|
+
|
|
19
|
+
1. **Header**: `#N. Title @ Company` — overall_score, recommendation, **link nộp CV (url)**, lương, location/remote, hạn nộp (nếu có).
|
|
20
|
+
- Nếu JD có kênh liên hệ trực tiếp (email, phone, Zalo, form): hiển thị rõ.
|
|
21
|
+
2. **Vì sao hợp** (2–3 gạch đầu dòng): điểm mạnh khớp — matched_skills chính, domain, seniority, lương.
|
|
22
|
+
3. **Khoảng cách** (gaps): missing_must_have + gaps[] kèm severity. Nói rõ cái nào là "học nhanh được" vs "cần thời gian".
|
|
23
|
+
4. **Rủi ro / cần xác minh**:
|
|
24
|
+
- Nếu `confidence` thấp → khuyến nghị đọc kỹ JD gốc; overqualified/underqualified; lương unknown.
|
|
25
|
+
5. **Hành động đề xuất**: Apply qua link / Chuẩn bị hồ sơ. Nếu muốn chủ động liên hệ HR (email/LinkedIn/Zalo), dùng skill `application-assistant` để lấy tin nhắn mẫu.
|
|
26
|
+
|
|
27
|
+
## Tổng quan đầu báo cáo
|
|
28
|
+
|
|
29
|
+
- Bảng xếp hạng: rank | title | company | score | recommendation | lương | location | **link nộp CV**. **MỌI job trong bảng đều phải có link (url) bấm được** — không chỉ các job top. Dùng markdown link (vd `[Nộp CV](url)`).
|
|
30
|
+
- Nhận xét chung: xu hướng thị trường quan sát được, gap lặp lại nhiều lần (gợi ý kỹ năng nên học), có nên nới target không (vd lương kỳ vọng quá cao so với mặt bằng).
|
|
31
|
+
|
|
32
|
+
## Nguyên tắc
|
|
33
|
+
|
|
34
|
+
- Trung thực: nếu match yếu, nói thẳng; đừng thổi phồng.
|
|
35
|
+
- Ưu tiên hành động cụ thể hơn nhận xét chung chung.
|
|
36
|
+
- Song ngữ khi cần (thuật ngữ giữ tiếng Anh, giải thích tiếng Việt).
|
|
37
|
+
- Không tạo kỳ vọng sai về khả năng trúng tuyển.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: job-collector
|
|
3
|
+
description: Thu thập và chuẩn hóa việc làm từ web search. Dùng trong bước collect của pipeline find-jobs sau khi đã có profile ứng viên.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Job Collector
|
|
7
|
+
|
|
8
|
+
Nhận đường dẫn `profile.json`, `run_id` duy nhất và đường dẫn output. Thu thập tối đa 20 job hợp lệ, chuẩn hóa theo `job-schema`, rồi ghi vào `data/jobs/<run_id>.json`.
|
|
9
|
+
|
|
10
|
+
## Nguồn dữ liệu
|
|
11
|
+
|
|
12
|
+
- Job boards: dùng web search/fetch sẵn có của host. Chỉ giữ job đọc được JD và chưa có bằng chứng hết hạn.
|
|
13
|
+
|
|
14
|
+
## Contract
|
|
15
|
+
|
|
16
|
+
1. Đọc profile và sinh query song ngữ từ role, seniority, location và skills.
|
|
17
|
+
2. Khử trùng theo URL chuẩn hóa; ưu tiên nguồn gốc và dữ liệu mới hơn.
|
|
18
|
+
3. Ghi object theo `schemas/job.schema.json`, đặt `extraction_confidence` trung thực.
|
|
19
|
+
4. Trả số lượng thu được, số bị loại và cảnh báo dữ liệu thiếu.
|
|
20
|
+
|
|
21
|
+
Không tự nộp hồ sơ, không vượt CAPTCHA/anti-bot và không ghi đè output của run khác.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: job-matcher
|
|
3
|
+
description: Chấm điểm và xếp hạng danh sách job so với profile ứng viên. Dùng trong bước match của pipeline find-jobs sau khi collector đã tạo jobs JSON.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Job Matcher
|
|
7
|
+
|
|
8
|
+
Nhận `profile.json`, `data/jobs/<run_id>.json` và output `data/results/<run_id>.shortlist.json`.
|
|
9
|
+
|
|
10
|
+
1. Khử trùng job theo `id`, giữ bản có `extraction_confidence` cao hơn.
|
|
11
|
+
2. Áp `scoring-rubric` và `bilingual-normalization` cho từng job.
|
|
12
|
+
3. Chuẩn hóa mọi bộ trọng số, kể cả mặc định, để tổng bằng 1 trước khi tính điểm.
|
|
13
|
+
4. Hard-filter dealbreakers chỉ khi có đủ bằng chứng; thiếu dữ liệu thì hạ confidence.
|
|
14
|
+
5. Ghi mảng `{match, job_ref}` theo `schemas/match.schema.json`, sắp xếp giảm dần và để excluded cuối.
|
|
15
|
+
|
|
16
|
+
Luôn ghi `weights_used`, rationale và các gap; không thổi phồng recommendation.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: job-schema
|
|
3
|
+
description: Schema chuẩn cho một tin tuyển dụng (job) và cách map dữ liệu thô từ search/scrape vào schema đó. Dùng bởi job-collector khi chuẩn hóa JD, và job-matcher khi đọc job.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Job Schema — chuẩn hóa tin tuyển dụng
|
|
7
|
+
|
|
8
|
+
Schema chính: `./schemas/job.schema.json`. Skill này hướng dẫn cách điền cho nhất quán.
|
|
9
|
+
|
|
10
|
+
## Quy tắc bắt buộc
|
|
11
|
+
|
|
12
|
+
- `id`: tạo hash ổn định. Ưu tiên chuẩn hóa URL (bỏ query tracking) rồi hash; nếu không có URL sạch, hash chuỗi `company|title|location` (lowercase, bỏ dấu). Dùng để **khử trùng lặp** giữa các nguồn.
|
|
13
|
+
- `source`: định danh nguồn ngắn gọn: `itviec`, `topcv`, `vietnamworks`, `linkedin`, `careerviet`, `google`, ...
|
|
14
|
+
- `collected_at`: ISO timestamp.
|
|
15
|
+
- `extraction_confidence` (0–1):
|
|
16
|
+
- **Cao** (0.8–1.0): đọc được full JD từ trang tuyển dụng chính thống hoặc link JD mở.
|
|
17
|
+
- **Thấp** (≤0.4): chỉ có snippet rời rạc từ kết quả tìm kiếm không đủ thông tin.
|
|
18
|
+
|
|
19
|
+
## Chuẩn hóa các trường
|
|
20
|
+
|
|
21
|
+
- **skills** (`must_have_skills`, `nice_to_have_skills`): đưa về canonical name qua skill `bilingual-normalization` (vd "ReactJS", "React.js" → "React").
|
|
22
|
+
- **salary**: parse cả tiếng Việt ("15-20 triệu", "thỏa thuận", "Up to $2000", "range 30-50m") → {min, max, currency, period, negotiable}. "Thỏa thuận"/"Negotiable" → currency=unknown, negotiable=true.
|
|
23
|
+
- **remote**: nhận diện "remote", "làm việc từ xa", "hybrid", "onsite", "tại văn phòng".
|
|
24
|
+
- **seniority**: suy từ title/JD ("Junior", "Senior", "Trưởng nhóm"→lead, "Fresher"→junior).
|
|
25
|
+
- **company**: Nếu JD không nêu rõ tên cty (stealth startup, headhunter tuyển hộ) → ghi `Confidential (<lĩnh vực>)` hoặc tên agency (vd `Confidential (Fintech HCM)`).
|
|
26
|
+
- **contact**: Trích xuất kênh liên hệ ứng tuyển nếu JD có:
|
|
27
|
+
- `email`: email nhận CV (vd `hr@company.com`).
|
|
28
|
+
- `phone`: số điện thoại liên hệ.
|
|
29
|
+
- `form_url`: link Google Form / Typeform nộp hồ sơ.
|
|
30
|
+
- `how_to_apply`: tóm tắt ngắn (vd `"Gửi CV qua email hr@..."`, `"Apply qua link"`).
|
|
31
|
+
- **language**: `vi`/`en`/`mixed` tùy JD.
|
|
32
|
+
- **min_years**: parse "2+ năm kinh nghiệm", "at least 3 years".
|
|
33
|
+
|
|
34
|
+
## Nguyên tắc chống bịa
|
|
35
|
+
|
|
36
|
+
- Chỉ điền trường khi có bằng chứng trong dữ liệu nguồn. Không suy đoán lương/quy mô công ty nếu JD không nói → để `unknown`.
|
|
37
|
+
- `url` luôn là link trang tuyển dụng gốc để người dùng bấm vào apply.
|
|
38
|
+
- Rút gọn `description` nếu quá dài (giữ phần requirements + trách nhiệm chính).
|
|
39
|
+
|
|
40
|
+
## Ví dụ tin tuyển dụng chuẩn (Full JD)
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"id": "a1b2c3",
|
|
45
|
+
"title": "Senior Frontend Developer (ReactJS)",
|
|
46
|
+
"company": "ABC Tech",
|
|
47
|
+
"location": "Ho Chi Minh City",
|
|
48
|
+
"remote": "hybrid",
|
|
49
|
+
"url": "https://itviec.com/it-jobs/senior-frontend-abc",
|
|
50
|
+
"source": "itviec",
|
|
51
|
+
"language": "mixed",
|
|
52
|
+
"requirements": {
|
|
53
|
+
"must_have_skills": ["React", "TypeScript"],
|
|
54
|
+
"nice_to_have_skills": ["Next.js"],
|
|
55
|
+
"min_years": 3,
|
|
56
|
+
"seniority": "senior"
|
|
57
|
+
},
|
|
58
|
+
"salary": { "min": 30000000, "max": 45000000, "currency": "VND", "period": "month" },
|
|
59
|
+
"collected_at": "2026-08-25T10:00:00Z",
|
|
60
|
+
"extraction_confidence": 0.9
|
|
61
|
+
}
|
|
62
|
+
```
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scoring-rubric
|
|
3
|
+
description: Công thức chấm điểm mức độ khớp giữa 1 job và profile ứng viên. Dùng bởi job-matcher và bất kỳ nơi nào cần đánh giá độ phù hợp job/CV. Định nghĩa trọng số, cách tính từng chiều, hard filter (dealbreakers), và cách xử lý dữ liệu thiếu.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Scoring Rubric — chấm điểm khớp job ↔ profile
|
|
7
|
+
|
|
8
|
+
Đây là "bộ não" quyết định chất lượng match. Kết quả tuân theo `./schemas/match.schema.json`.
|
|
9
|
+
|
|
10
|
+
## Nguyên tắc
|
|
11
|
+
|
|
12
|
+
1. Điểm mỗi chiều thang **0–100**, độc lập.
|
|
13
|
+
2. `overall_score` = tổng có trọng số của các chiều, **trừ** khi có dealbreaker (→ excluded).
|
|
14
|
+
3. Minh bạch: luôn ghi `weights_used`, `matched_skills`, `missing_must_have`, `rationale`.
|
|
15
|
+
4. Dữ liệu thiếu KHÔNG bị chấm 0 một cách mù quáng — xem mục "Xử lý dữ liệu thiếu".
|
|
16
|
+
|
|
17
|
+
## Bước 0 — Hard filter (dealbreakers)
|
|
18
|
+
|
|
19
|
+
Trước khi chấm điểm, kiểm tra `profile.target.dealbreakers` và các ràng buộc cứng:
|
|
20
|
+
|
|
21
|
+
- Vi phạm bất kỳ dealbreaker → `recommendation = "excluded"`, `overall_score = 0`, ghi vào `dealbreaker_violations`. **Không** tính tiếp.
|
|
22
|
+
- Ví dụ dealbreaker cứng thường gặp: location ngoài vùng chấp nhận (khi remote=onsite), loại hình công ty bị loại (outsource), lương max < salary.min của ứng viên (khi có đủ dữ liệu).
|
|
23
|
+
- Nếu dữ liệu không đủ để khẳng định vi phạm (vd lương "unknown") → KHÔNG loại, hạ `confidence` thay vì exclude.
|
|
24
|
+
|
|
25
|
+
## Trọng số mặc định
|
|
26
|
+
|
|
27
|
+
| Chiều | Mặc định | Ý nghĩa |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| skills | 0.35 | Khớp kỹ năng must-have / nice-to-have |
|
|
30
|
+
| seniority | 0.20 | Cấp bậc & số năm kinh nghiệm |
|
|
31
|
+
| domain | 0.15 | Ngành/lĩnh vực |
|
|
32
|
+
| location | 0.05 | Địa điểm & hình thức remote |
|
|
33
|
+
| compensation | 0.15 | Lương so với kỳ vọng |
|
|
34
|
+
| culture | 0.05 | Quy mô/loại hình công ty, tín hiệu văn hóa |
|
|
35
|
+
|
|
36
|
+
**Bắt buộc chuẩn hóa:** cả bộ trọng số mặc định và bộ `profile.target.priorities` override đều phải
|
|
37
|
+
được chia cho tổng thực tế trước khi tính điểm, để tổng `weights_used` luôn bằng 1. Bộ số mặc định
|
|
38
|
+
trên chủ ý thể hiện tỷ lệ tương đối; không được dùng trực tiếp khi tổng chưa bằng 1. Luôn ghi bộ đã
|
|
39
|
+
chuẩn hóa vào `weights_used`.
|
|
40
|
+
|
|
41
|
+
## Cách tính từng chiều
|
|
42
|
+
|
|
43
|
+
### skills (0–100)
|
|
44
|
+
- Chuẩn hóa mọi skill về canonical name (dùng skill `bilingual-normalization`) trước khi so khớp.
|
|
45
|
+
- `must_cover` = tỉ lệ must-have của job mà profile đáp ứng.
|
|
46
|
+
- `nice_cover` = tỉ lệ nice-to-have đáp ứng.
|
|
47
|
+
- `skills_score = 100 * (0.75 * must_cover + 0.25 * nice_cover)`.
|
|
48
|
+
- Cộng thưởng nhẹ nếu ứng viên có skill level `advanced/expert` cho must-have chính (tối đa +10, cap 100).
|
|
49
|
+
- Nếu job không nêu must-have (`must_have_skills` rỗng) → suy từ title + description; hạ `confidence`.
|
|
50
|
+
- Ghi `matched_skills` và `missing_must_have`.
|
|
51
|
+
|
|
52
|
+
### seniority (0–100)
|
|
53
|
+
- So `profile.seniority` với `job.requirements.seniority` (map thang: intern<junior<mid<senior<lead<manager<director).
|
|
54
|
+
- Khoảng cách 0 bậc → 100; 1 bậc → 75; 2 bậc → 45; ≥3 bậc → 15.
|
|
55
|
+
- **Overqualified** (ứng viên cao hơn job ≥2 bậc) cũng bị trừ (dễ bị loại/chán việc): áp cùng thang khoảng cách.
|
|
56
|
+
- Điều chỉnh bằng `min_years`: nếu `total_years < min_years` → nhân 0.6; nếu thừa nhiều năm → không thưởng.
|
|
57
|
+
- Nếu job seniority = unknown → suy từ min_years/title; hạ confidence.
|
|
58
|
+
|
|
59
|
+
### domain (0–100)
|
|
60
|
+
- Trùng ngành trong `profile.domains`/`target.industries` với `job.industry` → 100.
|
|
61
|
+
- Ngành liền kề/chuyển giao được (adjacent) → 60.
|
|
62
|
+
- Không liên quan → 30 (không phải 0, vì kỹ năng có thể chuyển ngành).
|
|
63
|
+
|
|
64
|
+
### location (0–100)
|
|
65
|
+
- remote=remote và ứng viên chấp nhận remote/any → 100.
|
|
66
|
+
- Job location nằm trong `target.locations` → 100.
|
|
67
|
+
- hybrid + cùng thành phố → 85.
|
|
68
|
+
- onsite khác thành phố nhưng ứng viên để "any" → 60.
|
|
69
|
+
- onsite ngoài vùng chấp nhận (chưa tới mức dealbreaker) → 25.
|
|
70
|
+
|
|
71
|
+
### compensation (0–100)
|
|
72
|
+
- Cần đưa về cùng đơn vị (VND/tháng) — dùng `bilingual-normalization` để quy đổi USD↔VND & year↔month.
|
|
73
|
+
- job_mid = trung bình (min,max). So với `target.salary.target` (hoặc min).
|
|
74
|
+
- job_mid ≥ target → 100; trong khoảng [min, target] → nội suy 70–100; dưới min nhưng ≥ 90% min → 50; thấp hơn → 20.
|
|
75
|
+
- Lương unknown → điểm neutral 60, hạ confidence (KHÔNG chấm 0).
|
|
76
|
+
|
|
77
|
+
### culture (0–100)
|
|
78
|
+
- Khớp `target.company_size` → +40 baseline lên; tín hiệu tích cực trong JD (đãi ngộ, lộ trình, tech stack hiện đại) cộng thêm.
|
|
79
|
+
- Ít dữ liệu → 60 neutral.
|
|
80
|
+
|
|
81
|
+
## Xử lý dữ liệu thiếu
|
|
82
|
+
|
|
83
|
+
- Không bao giờ chấm 0 vì "thiếu thông tin" — dùng điểm **neutral** (55–60) và **giảm `confidence`**.
|
|
84
|
+
- `confidence` tổng của match ≈ trung bình có trọng số của `job.extraction_confidence` và độ đầy đủ dữ liệu các chiều.
|
|
85
|
+
- Match có confidence < 0.4 nên được đánh dấu "cần xác minh JD" trong rationale.
|
|
86
|
+
|
|
87
|
+
## Tổng hợp & khuyến nghị
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
normalized_weight[d] = weight[d] / Σ weight
|
|
91
|
+
overall = Σ (dimension_score[d] * normalized_weight[d]) với d ∈ 6 chiều
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Map sang `recommendation`:
|
|
95
|
+
|
|
96
|
+
| overall | recommendation |
|
|
97
|
+
|---|---|
|
|
98
|
+
| ≥ 80 | strong |
|
|
99
|
+
| 65–79 | good |
|
|
100
|
+
| 50–64 | maybe |
|
|
101
|
+
| < 50 | weak |
|
|
102
|
+
| (dealbreaker) | excluded |
|
|
103
|
+
|
|
104
|
+
## Đầu ra
|
|
105
|
+
|
|
106
|
+
Trả về đúng `./schemas/match.schema.json`: overall_score, recommendation, dimension_scores, weights_used, matched_skills, missing_must_have, gaps[], dealbreaker_violations[], rationale, confidence.
|
|
107
|
+
|
|
108
|
+
`rationale` viết ngắn, cụ thể, song ngữ nếu cần — nêu 1–2 lý do mạnh nhất và 1 gap lớn nhất.
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jobscout",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "AI job matching — tìm và xếp hạng việc làm phù hợp CV, song ngữ Việt–Anh. Chạy trên Claude Code, Codex CLI và ChatGPT.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"jobscout": "bin/cli.mjs"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"job-matching",
|
|
11
|
+
"jobscout",
|
|
12
|
+
"cv",
|
|
13
|
+
"career",
|
|
14
|
+
"recruitment",
|
|
15
|
+
"claude",
|
|
16
|
+
"codex",
|
|
17
|
+
"chatgpt",
|
|
18
|
+
"ai-agents",
|
|
19
|
+
"vietnam"
|
|
20
|
+
],
|
|
21
|
+
"author": {
|
|
22
|
+
"name": "Nguyen Trong Hieu",
|
|
23
|
+
"email": "hieutrong512@gmail.com"
|
|
24
|
+
},
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/hieutrong512/JobMatching.git"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"bin/",
|
|
32
|
+
"job-matching-plugin/.claude-plugin/",
|
|
33
|
+
"job-matching-plugin/.codex-plugin/",
|
|
34
|
+
"job-matching-plugin/skills/",
|
|
35
|
+
"job-matching-plugin/schemas/",
|
|
36
|
+
"job-matching-plugin/agents/",
|
|
37
|
+
"job-matching-plugin/commands/",
|
|
38
|
+
"job-matching-plugin/docs/",
|
|
39
|
+
"job-matching-plugin/AGENTS.md",
|
|
40
|
+
"job-matching-plugin/README.md",
|
|
41
|
+
"chatgpt/"
|
|
42
|
+
],
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=18"
|
|
45
|
+
}
|
|
46
|
+
}
|