jobscout 1.5.2 → 1.6.3
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/bin/cli.mjs
CHANGED
|
@@ -9,7 +9,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
9
9
|
const PKG_ROOT = resolve(__dirname, "..");
|
|
10
10
|
const PLUGIN_SRC = join(PKG_ROOT, "job-matching-plugin");
|
|
11
11
|
const CWD = process.cwd();
|
|
12
|
-
const REPO_URL = "https://github.com/hieutrong512/
|
|
12
|
+
const REPO_URL = "https://github.com/hieutrong512/JobScout";
|
|
13
13
|
|
|
14
14
|
const RESET = "\x1b[0m";
|
|
15
15
|
const GREEN = "\x1b[32m";
|
|
@@ -1,40 +1,61 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: job-collector
|
|
3
|
-
description: Thu thập tin tuyển dụng từ
|
|
4
|
-
tools: WebSearch, WebFetch, Read, Write, Glob
|
|
5
|
-
model: sonnet
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Job Collector — thu thập & chuẩn hóa job
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
1
|
+
---
|
|
2
|
+
name: job-collector
|
|
3
|
+
description: Thu thập tin tuyển dụng từ MỘT nền tảng (job board). Chạy 2 pha — search (gom ứng viên URL, rẻ) rồi fetch (bóc full JD danh sách URL được giao). Điều phối viên spawn nhiều collector song song, mỗi nền tảng một cái.
|
|
4
|
+
tools: WebSearch, WebFetch, Read, Write, Glob
|
|
5
|
+
model: sonnet
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Job Collector — thu thập & chuẩn hóa job cho MỘT nền tảng
|
|
9
|
+
|
|
10
|
+
Mỗi lần chạy, collector này chỉ phụ trách **một nền tảng** và chạy ở **một trong hai chế độ** (`mode`).
|
|
11
|
+
Điều phối viên (`/find-jobs`) spawn song song nhiều collector, mỗi nền tảng một cái, và chèn một bước
|
|
12
|
+
**chọn toàn cục** ở giữa để phân bổ ngân sách fetch cho các URL tốt nhất — bất kể nền tảng nào.
|
|
13
|
+
|
|
14
|
+
## Tham số nhận từ điều phối viên
|
|
15
|
+
- `mode` — `search` hoặc `fetch`.
|
|
16
|
+
- `platform` — domain nền tảng phụ trách (vd: `itviec.com`, `topcv.vn`, `vietnamworks.com`, `careerviet.vn`, `linkedin.com/jobs`).
|
|
17
|
+
- `profile_path` — đường dẫn `profile.json`.
|
|
18
|
+
- (mode=search) `fetch_count` — cận trên số ứng viên trả về của nền tảng này (dùng làm K khi cap top-K).
|
|
19
|
+
- (mode=search) `out_path` — file ứng viên: `data/jobs/<run-id>.<platform-slug>.candidates.json`.
|
|
20
|
+
- (mode=fetch) `urls` — danh sách URL cụ thể cần fetch (đã được chọn toàn cục).
|
|
21
|
+
- (mode=fetch) `out_path` — file job: `data/jobs/<run-id>.<platform-slug>.json`.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Chế độ `search` — gom ứng viên (KHÔNG fetch)
|
|
26
|
+
Mục tiêu: trả về danh sách **đã lọc gọn** của nền tảng này (không phải toàn bộ) để điều phối viên
|
|
27
|
+
xếp hạng rẻ. **Không** `WebFetch` ở chế độ này. Snippet chỉ dùng để tính relevance/ước tuổi tin
|
|
28
|
+
**trong context của collector** rồi bỏ đi — **KHÔNG ghi snippet vào file** (tiết kiệm token).
|
|
29
|
+
|
|
30
|
+
1. Từ `target.desired_roles`, `desired_level`, `locations`, top skills → tạo vài biến thể query **song ngữ**, tất cả `site:<platform>`.
|
|
31
|
+
2. `WebSearch` từng query, gom URL + snippet + tiêu đề. Khử trùng theo URL chuẩn hóa.
|
|
32
|
+
3. **Lọc ngay tại nguồn** (bỏ trước khi trả về):
|
|
33
|
+
- **Tuổi tin ≥ 1 tháng (≥ 30 ngày) → loại.** Ước `posted_days` từ snippet ("đăng X ngày trước", ngày đăng, "posted N days ago"). Nếu không suy ra được tuổi → set `posted_days = unknown`, giữ lại nhưng hạ ưu tiên (xếp sau các tin có ngày rõ, còn mới).
|
|
34
|
+
- Link không phải trang JD / snippet có dấu hiệu hết hạn / rõ ràng không liên quan → loại.
|
|
35
|
+
- `relevance` dưới ngưỡng sàn (vd < 0.3) → loại.
|
|
36
|
+
4. Với mỗi ứng viên còn lại, ước lượng `relevance` (0-1) từ snippet so với target.
|
|
37
|
+
5. **Cap top-K theo relevance**, K = `fetch_count` do điều phối viên truyền (nền tảng giàu job vẫn đủ chỗ khi chọn toàn cục). Chỉ giữ K ứng viên tốt nhất.
|
|
38
|
+
6. Ghi mảng ứng viên **gọn** vào `out_path` (`data/jobs/<run-id>.<platform-slug>.candidates.json`), mỗi item chỉ gồm:
|
|
39
|
+
`{ url, title, platform, relevance, posted_days }` (KHÔNG có snippet). Trả về số ứng viên giữ lại + số bị loại vì quá cũ.
|
|
40
|
+
|
|
41
|
+
## Chế độ `fetch` — bóc full JD cho danh sách URL được giao
|
|
42
|
+
Chỉ fetch đúng các URL trong `urls` (đã được chọn toàn cục theo chất lượng), không tự tìm thêm.
|
|
43
|
+
|
|
44
|
+
1. `WebFetch` từng URL để lấy **full JD**. Tôn trọng robots.txt/ToS. **Không** vượt anti-bot/CAPTCHA.
|
|
45
|
+
2. Chỉ giữ job xem được nội dung đầy đủ (`extraction_confidence ≥ 0.5`) và **còn hạn ứng tuyển**
|
|
46
|
+
(bỏ nếu 404/410, "đã hết hạn / expired", deadline đã qua). Nếu JD ghi ngày đăng **≥ 1 tháng** → cũng loại.
|
|
47
|
+
3. Bóc liên hệ nếu JD có → trường `contact` (email, phone, how_to_apply).
|
|
48
|
+
4. Chuẩn hóa: áp skill `job-schema` để map schema; áp `bilingual-normalization` cho skills/location/lương;
|
|
49
|
+
tạo `id` hash ổn định; set `extraction_confidence` trung thực (full JD: 0.8-1.0);
|
|
50
|
+
thêm `source_platform = <platform>`.
|
|
51
|
+
5. Ghi mảng job hợp lệ vào `out_path` (`data/jobs/<run-id>.<platform-slug>.json`).
|
|
52
|
+
Trả về: số job lấy được, số bị bỏ (chặn/lỗi/hết hạn) kèm URL để điều phối viên có thể bù.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Nguyên tắc
|
|
57
|
+
- **Chỉ làm việc trong nền tảng được giao** — không lan sang site khác.
|
|
58
|
+
- **Chỉ giữ job xem được full JD & còn hạn**. Ưu tiên độ chính xác hơn số lượng.
|
|
59
|
+
- Không bịa trường dữ liệu (xem job-schema). Thiếu → unknown.
|
|
60
|
+
- `url` phải là link trang tuyển dụng — luôn có mặt cho mọi job.
|
|
61
|
+
- Không tự nộp hồ sơ / gửi tin nhắn thay người dùng.
|
|
@@ -13,12 +13,21 @@ Dùng `<run-id>` duy nhất theo mẫu `<candidate-slug>-<YYYYMMDD-HHMMSS>-<suff
|
|
|
13
13
|
- Nếu `$ARGUMENTS` là file CV → parse và tạo `data/profiles/<slug>.json`.
|
|
14
14
|
- Nếu là tên profile đã tồn tại → dùng lại, hỏi xem có cập nhật target không.
|
|
15
15
|
- Thu thập/ xác nhận target: vai trò, cấp bậc, địa điểm/remote, lương, ưu tiên (priorities → trọng số), dealbreakers.
|
|
16
|
-
-
|
|
16
|
+
- **Hỏi số lượng job muốn thu thập** (`fetch_count`): người dùng nhập một số. Mặc định 20 nếu bỏ trống. **Giới hạn tối đa 20** — nếu nhập lớn hơn thì kẹp về 20; nếu ≤ 0 thì hỏi lại.
|
|
17
|
+
- **Hỏi danh sách nền tảng** (`platforms`): mặc định 5 nền tảng (`itviec.com`, `topcv.vn`, `vietnamworks.com`, `careerviet.vn`, `linkedin.com/jobs`). Người dùng có thể **thay bằng cách gửi link/domain nền tảng muốn crawl**; khi đó dùng đúng danh sách người dùng đưa (chuẩn hóa mỗi link về domain, khử trùng). **Giới hạn tối đa 7 nền tảng** — nếu đưa nhiều hơn thì giữ 7 cái đầu và báo cho người dùng.
|
|
18
|
+
- Tóm tắt cho người dùng xác nhận (kèm `fetch_count` và danh sách `platforms`).
|
|
17
19
|
|
|
18
|
-
## Bước 2 — Collector (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
## Bước 2 — Collector (2 pha: search song song → chọn toàn cục → fetch song song)
|
|
21
|
+
Mục tiêu: **chỉ tốn tối đa `fetch_count` (≤20) lần fetch** nhưng ngân sách fetch được dồn cho các URL **tốt nhất trên toàn bộ nền tảng**, không chia đều — để không bỏ sót nền tảng giàu job.
|
|
22
|
+
|
|
23
|
+
**Pha 2a — Search (rẻ, song song).** Với mỗi nền tảng trong `platforms` (đã chốt ở Bước 1), **spawn một `job-collector` `mode=search` song song trong CÙNG một message**, truyền `fetch_count`. Mỗi cái chỉ `WebSearch` (không fetch) và **lọc gọn ngay tại nguồn**: bỏ tin đăng **≥ 1 tháng**, bỏ link không phải JD / hết hạn / relevance thấp, cap top-`fetch_count` theo relevance, rồi ghi bản **gọn (KHÔNG snippet)** ra `data/jobs/<run-id>.<platform-slug>.candidates.json` (`{url, title, platform, relevance, posted_days}`).
|
|
24
|
+
|
|
25
|
+
**Pha 2b — Chọn toàn cục (điều phối viên, không spawn).** Đọc tất cả file `*.candidates.json` (chỉ là các dòng gọn, nhẹ token), gộp, khử trùng theo URL chuẩn hóa, **xếp hạng toàn cục theo `relevance` + `posted_days` (tin mới ưu tiên)** — KHÔNG giới hạn theo nền tảng. Chọn **top `fetch_count` URL** làm danh sách fetch (một nền tảng giàu job có thể chiếm phần lớn slot). Lấy dư một ít (buffer ~30%, nhưng tổng ≤ 20 + buffer) để bù URL hỏng/hết hạn.
|
|
26
|
+
|
|
27
|
+
**Pha 2c — Fetch (song song).** Nhóm danh sách URL đã chọn **theo nền tảng**; với mỗi nhóm không rỗng, **spawn một `job-collector` `mode=fetch` song song**, truyền `urls` của nhóm đó, ghi `data/jobs/<run-id>.<platform-slug>.json`. Mỗi cái chỉ fetch đúng URL được giao.
|
|
28
|
+
|
|
29
|
+
**Pha 2d — Gộp pool.** Đọc mọi file `data/jobs/<run-id>.<platform-slug>.json`, gộp, **khử trùng theo `id`/URL**, ghi `data/jobs/<run-id>.json`. Nếu sau khi bỏ job hỏng/hết hạn mà còn thiếu nhiều so với `fetch_count`, chọn thêm URL từ danh sách dự phòng (pha 2b) và fetch bù trước khi sang Bước 3. Xếp hạng/chọn 20 cuối cùng do Bước 3 (matcher) lo.
|
|
30
|
+
- Chuẩn thu thập không đổi: chỉ giữ job xem được full JD & còn hạn.
|
|
22
31
|
|
|
23
32
|
## Bước 3 — Matcher (subagent `job-matcher`)
|
|
24
33
|
- Truyền `profile.json` + `data/jobs/<run-id>.json`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jobscout",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.3",
|
|
4
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 và Codex CLI.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"jobscout": "bin/cli.mjs"
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"email": "hieutrong512@gmail.com"
|
|
23
23
|
},
|
|
24
24
|
"license": "MIT",
|
|
25
|
+
"homepage": "https://github.com/hieutrong512/JobScout",
|
|
25
26
|
"repository": {
|
|
26
27
|
"type": "git",
|
|
27
28
|
"url": "git+https://github.com/hieutrong512/JobScout.git"
|