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
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# JobMatching
|
|
2
|
+
|
|
3
|
+
AI tìm và xếp hạng việc làm phù hợp nhất với CV của bạn — song ngữ Việt–Anh, không cần API key.
|
|
4
|
+
|
|
5
|
+
Thu thập job thật từ ITviec, TopCV, VietnamWorks, LinkedIn… qua web search, chấm điểm 6 chiều
|
|
6
|
+
(skills · seniority · domain · compensation · location · culture), xuất báo cáo fit/gap chi tiết
|
|
7
|
+
với link apply cho từng job.
|
|
8
|
+
|
|
9
|
+
**Chạy được trên Claude Code, Codex CLI và ChatGPT** — cùng một logic, một lệnh cài đặt.
|
|
10
|
+
|
|
11
|
+
## Cài đặt
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx jobscout setup
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Tự phát hiện nền tảng và setup. Hoặc chỉ định:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx jobscout setup claude # Claude Code
|
|
21
|
+
npx jobscout setup codex # Codex CLI
|
|
22
|
+
npx jobscout setup chatgpt # ChatGPT (Custom GPT)
|
|
23
|
+
npx jobscout setup all # Tất cả
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Sau khi setup
|
|
27
|
+
|
|
28
|
+
### Claude Code
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
/plugin marketplace add .
|
|
32
|
+
/plugin install job-matching
|
|
33
|
+
/find-jobs path/to/CV.pdf
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Codex CLI
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
codex --search
|
|
40
|
+
# Gọi skill find-jobs với đường dẫn CV
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### ChatGPT
|
|
44
|
+
|
|
45
|
+
CLI tạo thư mục `job-matching-chatgpt/` chứa sẵn mọi thứ:
|
|
46
|
+
|
|
47
|
+
1. Vào [chatgpt.com/gpts/editor](https://chatgpt.com/gpts/editor) → **Create**
|
|
48
|
+
2. **Instructions**: dán nội dung `job-matching-chatgpt/GPT-INSTRUCTIONS.md`
|
|
49
|
+
3. **Knowledge**: kéo-thả tất cả file trong `job-matching-chatgpt/knowledge/`
|
|
50
|
+
4. Bật **Web Search** → Save → Publish
|
|
51
|
+
5. Mở GPT, dán CV là chạy
|
|
52
|
+
|
|
53
|
+
## Pipeline
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
CV + Target
|
|
57
|
+
▼ [1] candidate-intake Parse CV → profile.json
|
|
58
|
+
▼ [2] job-collector Web search → tối đa 20 job hợp lệ
|
|
59
|
+
▼ [3] job-matcher Chấm điểm 6 chiều → shortlist xếp hạng
|
|
60
|
+
▼ [4] fit-analyzer Báo cáo fit/gap + link apply từng job
|
|
61
|
+
▼ [5] application-assistant (tùy chọn) Tailor CV / cover letter / tin nhắn HR
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Trọng số chấm điểm
|
|
65
|
+
|
|
66
|
+
| Chiều | Mặc định | Ý nghĩa |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| skills | 35% | Khớp kỹ năng must-have / nice-to-have |
|
|
69
|
+
| seniority | 20% | Cấp bậc & năm kinh nghiệm |
|
|
70
|
+
| domain | 15% | Ngành / lĩnh vực |
|
|
71
|
+
| compensation | 15% | Lương so với kỳ vọng |
|
|
72
|
+
| location | 5% | Địa điểm & remote |
|
|
73
|
+
| culture | 5% | Quy mô / loại hình công ty |
|
|
74
|
+
|
|
75
|
+
Override bằng cách khai `priorities` khi được hỏi target (tổng tự chuẩn hóa về 1).
|
|
76
|
+
|
|
77
|
+
## Thành phần
|
|
78
|
+
|
|
79
|
+
| Thành phần | Loại | Vai trò |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `candidate-intake` | Skill | Parse CV + hỏi target → `profile.json` |
|
|
82
|
+
| `job-collector` | Subagent | Search + scrape JD từ job boards → `jobs.json` |
|
|
83
|
+
| `job-matcher` | Subagent | Chấm điểm & xếp hạng → `shortlist.json` |
|
|
84
|
+
| `fit-analyzer` | Skill | Báo cáo fit/gap chi tiết → `fit_report.md` |
|
|
85
|
+
| `application-assistant` | Skill | Tailor CV / cover letter / tin nhắn tiếp cận HR |
|
|
86
|
+
| `scoring-rubric` | Skill | Công thức tính điểm 6 chiều |
|
|
87
|
+
| `job-schema` | Skill | Schema chuẩn cho job + cách map dữ liệu thô |
|
|
88
|
+
| `bilingual-normalization` | Skill | Chuẩn hóa skill/chức danh/lương Việt–Anh |
|
|
89
|
+
|
|
90
|
+
## Data contracts
|
|
91
|
+
|
|
92
|
+
JSON schema trong `job-matching-plugin/schemas/`:
|
|
93
|
+
- **`profile.schema.json`** — hồ sơ ứng viên (CV + target + priorities)
|
|
94
|
+
- **`job.schema.json`** — tin tuyển dụng chuẩn hóa (kèm contact nếu JD có)
|
|
95
|
+
- **`match.schema.json`** — kết quả chấm điểm (6 chiều + rationale)
|
|
96
|
+
|
|
97
|
+
## Cấu trúc repo
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
├── bin/cli.mjs ← CLI (npx job-matching setup)
|
|
101
|
+
├── job-matching-plugin/ ← nguồn chân lý duy nhất
|
|
102
|
+
│ ├── .claude-plugin/ Claude Code manifest
|
|
103
|
+
│ ├── .codex-plugin/ Codex CLI manifest
|
|
104
|
+
│ ├── skills/*/SKILL.md Logic pipeline + rubric
|
|
105
|
+
│ ├── schemas/*.json Data contracts
|
|
106
|
+
│ ├── agents/*.md Subagent definitions
|
|
107
|
+
│ └── commands/find-jobs.md Slash command /find-jobs
|
|
108
|
+
├── chatgpt/ ← adapter ChatGPT
|
|
109
|
+
│ ├── GPT-INSTRUCTIONS.md Orchestrator cho Custom GPT
|
|
110
|
+
│ └── knowledge/ Skills + schemas flatten sẵn
|
|
111
|
+
└── package.json
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Lưu ý
|
|
115
|
+
|
|
116
|
+
- Quét từ các trang tuyển dụng chính thống. Chỉ giữ job xem được full JD và còn hạn.
|
|
117
|
+
- Tôn trọng robots.txt và ToS; không vượt qua anti-bot/CAPTCHA.
|
|
118
|
+
- Không bịa dữ liệu job/CV. Thiếu thông tin → `unknown` + hạ confidence.
|
|
119
|
+
- **Không tự nộp hồ sơ / gửi tin nhắn thay người dùng.**
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
MIT
|
package/bin/cli.mjs
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync, mkdirSync, cpSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { resolve, join, dirname } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const PKG_ROOT = resolve(__dirname, "..");
|
|
9
|
+
const PLUGIN_SRC = join(PKG_ROOT, "job-matching-plugin");
|
|
10
|
+
const CHATGPT_SRC = join(PKG_ROOT, "chatgpt");
|
|
11
|
+
const CWD = process.cwd();
|
|
12
|
+
|
|
13
|
+
const RESET = "\x1b[0m";
|
|
14
|
+
const GREEN = "\x1b[32m";
|
|
15
|
+
const CYAN = "\x1b[36m";
|
|
16
|
+
const YELLOW = "\x1b[33m";
|
|
17
|
+
const BOLD = "\x1b[1m";
|
|
18
|
+
const DIM = "\x1b[2m";
|
|
19
|
+
|
|
20
|
+
function log(msg) { console.log(msg); }
|
|
21
|
+
function ok(msg) { log(`${GREEN}✓${RESET} ${msg}`); }
|
|
22
|
+
function info(msg) { log(`${CYAN}→${RESET} ${msg}`); }
|
|
23
|
+
function warn(msg) { log(`${YELLOW}!${RESET} ${msg}`); }
|
|
24
|
+
function heading(msg) { log(`\n${BOLD}${msg}${RESET}`); }
|
|
25
|
+
|
|
26
|
+
const command = process.argv[2];
|
|
27
|
+
|
|
28
|
+
const HELP = `
|
|
29
|
+
${BOLD}jobscout${RESET} — AI job matching setup CLI
|
|
30
|
+
|
|
31
|
+
${BOLD}Usage:${RESET}
|
|
32
|
+
npx jobscout ${CYAN}<command>${RESET}
|
|
33
|
+
|
|
34
|
+
${BOLD}Commands:${RESET}
|
|
35
|
+
${CYAN}setup${RESET} Auto-detect platform & install (recommended)
|
|
36
|
+
${CYAN}setup claude${RESET} Setup for Claude Code
|
|
37
|
+
${CYAN}setup codex${RESET} Setup for Codex CLI
|
|
38
|
+
${CYAN}setup chatgpt${RESET} Setup for ChatGPT (copy files to configure Custom GPT)
|
|
39
|
+
${CYAN}setup all${RESET} Setup for all platforms
|
|
40
|
+
|
|
41
|
+
${BOLD}Examples:${RESET}
|
|
42
|
+
${DIM}npx jobscout setup ${RESET}# auto-detect
|
|
43
|
+
${DIM}npx jobscout setup claude ${RESET}# Claude Code only
|
|
44
|
+
${DIM}npx jobscout setup chatgpt ${RESET}# ChatGPT only
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
function copyDir(src, dest) {
|
|
48
|
+
cpSync(src, dest, { recursive: true });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function ensureDir(dir) {
|
|
52
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function detectPlatforms() {
|
|
56
|
+
const platforms = [];
|
|
57
|
+
if (existsSync(join(CWD, ".claude")) || process.env.CLAUDE_CODE) {
|
|
58
|
+
platforms.push("claude");
|
|
59
|
+
}
|
|
60
|
+
if (existsSync(join(CWD, ".codex")) || process.env.CODEX_HOME) {
|
|
61
|
+
platforms.push("codex");
|
|
62
|
+
}
|
|
63
|
+
return platforms;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function setupClaude() {
|
|
67
|
+
heading("Claude Code");
|
|
68
|
+
|
|
69
|
+
const marketplaceDest = join(CWD, ".claude-plugin");
|
|
70
|
+
const pluginDest = join(CWD, "job-matching-plugin");
|
|
71
|
+
|
|
72
|
+
ensureDir(marketplaceDest);
|
|
73
|
+
|
|
74
|
+
const marketplace = {
|
|
75
|
+
name: "jobmatching-marketplace",
|
|
76
|
+
owner: { name: "JobMatching" },
|
|
77
|
+
metadata: {
|
|
78
|
+
description: "Marketplace chứa plugin Job Matching — tìm job phù hợp với target + CV.",
|
|
79
|
+
version: "1.2.0"
|
|
80
|
+
},
|
|
81
|
+
plugins: [
|
|
82
|
+
{
|
|
83
|
+
name: "job-matching",
|
|
84
|
+
source: "./job-matching-plugin",
|
|
85
|
+
description: "Tìm và xếp hạng job phù hợp nhất với target + CV. Song ngữ Việt–Anh.",
|
|
86
|
+
version: "1.2.0"
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
writeFileSync(
|
|
92
|
+
join(marketplaceDest, "marketplace.json"),
|
|
93
|
+
JSON.stringify(marketplace, null, 2) + "\n"
|
|
94
|
+
);
|
|
95
|
+
ok("Tạo .claude-plugin/marketplace.json");
|
|
96
|
+
|
|
97
|
+
copyDir(PLUGIN_SRC, pluginDest);
|
|
98
|
+
ok("Copy job-matching-plugin/ (skills, schemas, agents, commands)");
|
|
99
|
+
|
|
100
|
+
log("");
|
|
101
|
+
info("Mở Claude Code terminal rồi chạy:");
|
|
102
|
+
log(` ${CYAN}/plugin marketplace add .${RESET}`);
|
|
103
|
+
log(` ${CYAN}/plugin install job-matching${RESET}`);
|
|
104
|
+
log(` ${CYAN}/find-jobs path/to/CV.pdf${RESET}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function setupCodex() {
|
|
108
|
+
heading("Codex CLI");
|
|
109
|
+
|
|
110
|
+
const pluginDest = join(CWD, "job-matching-plugin");
|
|
111
|
+
copyDir(PLUGIN_SRC, pluginDest);
|
|
112
|
+
ok("Copy job-matching-plugin/ (skills, schemas, .codex-plugin)");
|
|
113
|
+
|
|
114
|
+
log("");
|
|
115
|
+
info("Chạy Codex:");
|
|
116
|
+
log(` ${CYAN}codex --search${RESET} ${DIM}# bật web search${RESET}`);
|
|
117
|
+
log(` Gọi skill ${CYAN}find-jobs${RESET} với đường dẫn CV.`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function setupChatgpt() {
|
|
121
|
+
heading("ChatGPT");
|
|
122
|
+
|
|
123
|
+
const dest = join(CWD, "job-matching-chatgpt");
|
|
124
|
+
ensureDir(dest);
|
|
125
|
+
|
|
126
|
+
const instrSrc = join(CHATGPT_SRC, "GPT-INSTRUCTIONS.md");
|
|
127
|
+
const instrDest = join(dest, "GPT-INSTRUCTIONS.md");
|
|
128
|
+
cpSync(instrSrc, instrDest);
|
|
129
|
+
ok("Copy GPT-INSTRUCTIONS.md");
|
|
130
|
+
|
|
131
|
+
const knowledgeSrc = join(CHATGPT_SRC, "knowledge");
|
|
132
|
+
if (existsSync(knowledgeSrc)) {
|
|
133
|
+
const knowledgeDest = join(dest, "knowledge");
|
|
134
|
+
copyDir(knowledgeSrc, knowledgeDest);
|
|
135
|
+
ok("Copy knowledge/ (skills + schemas)");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
log("");
|
|
139
|
+
info("Tạo Custom GPT:");
|
|
140
|
+
log(` 1. Vào ${CYAN}chatgpt.com/gpts/editor${RESET} → Create`);
|
|
141
|
+
log(` 2. Instructions: dán nội dung ${CYAN}${instrDest}${RESET}`);
|
|
142
|
+
log(` 3. Knowledge: kéo-thả tất cả file trong ${CYAN}${join(dest, "knowledge")}${RESET}`);
|
|
143
|
+
log(` 4. Bật ${CYAN}Web Search${RESET} → Save → Publish`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function runSetup(platform) {
|
|
147
|
+
log(`${BOLD}🔧 JobMatching Setup${RESET}`);
|
|
148
|
+
|
|
149
|
+
if (!platform) {
|
|
150
|
+
const detected = detectPlatforms();
|
|
151
|
+
if (detected.length > 0) {
|
|
152
|
+
info(`Phát hiện: ${detected.join(", ")}`);
|
|
153
|
+
detected.forEach((p) => {
|
|
154
|
+
if (p === "claude") setupClaude();
|
|
155
|
+
if (p === "codex") setupCodex();
|
|
156
|
+
});
|
|
157
|
+
} else {
|
|
158
|
+
info("Không phát hiện Claude Code / Codex. Setup cho tất cả nền tảng.");
|
|
159
|
+
setupClaude();
|
|
160
|
+
setupCodex();
|
|
161
|
+
setupChatgpt();
|
|
162
|
+
}
|
|
163
|
+
} else if (platform === "claude") {
|
|
164
|
+
setupClaude();
|
|
165
|
+
} else if (platform === "codex") {
|
|
166
|
+
setupCodex();
|
|
167
|
+
} else if (platform === "chatgpt") {
|
|
168
|
+
setupChatgpt();
|
|
169
|
+
} else if (platform === "all") {
|
|
170
|
+
setupClaude();
|
|
171
|
+
setupCodex();
|
|
172
|
+
setupChatgpt();
|
|
173
|
+
} else {
|
|
174
|
+
warn(`Platform "${platform}" không hợp lệ. Dùng: claude, codex, chatgpt, all`);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
heading("Xong!");
|
|
179
|
+
log(`Repo: ${DIM}https://github.com/hieutrong512/JobMatching${RESET}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
183
|
+
log(HELP);
|
|
184
|
+
} else if (command === "setup") {
|
|
185
|
+
runSetup(process.argv[3]);
|
|
186
|
+
} else {
|
|
187
|
+
warn(`Lệnh "${command}" không tồn tại.`);
|
|
188
|
+
log(HELP);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# JobMatching — hướng dẫn cho ChatGPT (Custom GPT / Project)
|
|
2
|
+
|
|
3
|
+
Bạn là **JobMatching**, trợ lý tìm và xếp hạng việc làm phù hợp nhất với **target** và **CV**
|
|
4
|
+
của ứng viên, song ngữ Việt–Anh. Bạn thu thập job qua **web browsing** (Search) — không cần API key.
|
|
5
|
+
|
|
6
|
+
> Dán toàn bộ file này vào ô **Instructions** của một Custom GPT (bật **Web Search**), hoặc dùng làm
|
|
7
|
+
> chỉ dẫn cho một ChatGPT Project. Nếu có upload các file knowledge (`*.md` skill + `*.schema.json`),
|
|
8
|
+
> hãy ưu tiên đọc chúng để lấy chi tiết; nếu không có, làm theo bản rút gọn dưới đây là đủ.
|
|
9
|
+
|
|
10
|
+
## Môi trường ChatGPT
|
|
11
|
+
|
|
12
|
+
- **Web**: dùng công cụ browsing/Search sẵn có để tìm và mở JD. Không bịa job hay URL.
|
|
13
|
+
- **File**: ChatGPT không có ổ đĩa lâu dài. Giữ dữ liệu trung gian (profile/jobs/shortlist) **dạng JSON
|
|
14
|
+
ngay trong hội thoại** (khối ```json). Nếu bật Code Interpreter, có thể ghi file tạm để tải về, nhưng
|
|
15
|
+
không bắt buộc.
|
|
16
|
+
- Không tự nộp hồ sơ / gửi email / gửi tin nhắn thay người dùng.
|
|
17
|
+
|
|
18
|
+
## Pipeline (chạy tuần tự trong 1 hội thoại)
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
CV + Target → [1] Intake → profile(JSON)
|
|
22
|
+
→ [2] Collect → jobs(JSON, tối đa 20)
|
|
23
|
+
→ [3] Match → shortlist(JSON đã xếp hạng)
|
|
24
|
+
→ [4] Fit report (Markdown)
|
|
25
|
+
→ [5] (tùy chọn) Application assistant
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### [1] Intake — tạo `profile`
|
|
29
|
+
- Nhận CV (dán text, hoặc upload PDF/DOCX → trích text). Trích: thông tin cá nhân tối thiểu, skills,
|
|
30
|
+
kinh nghiệm (title/công ty/thời gian/highlights), học vấn, ngôn ngữ. Suy `total_years` + `seniority`.
|
|
31
|
+
- Hỏi target **gộp 1 lần** (bỏ qua phần người dùng đã nêu): (1) vai trò + cấp bậc; (2) địa điểm + remote;
|
|
32
|
+
(3) lương min & target (VND/USD, tháng/năm); (4) ngành + quy mô công ty; (5) dealbreakers;
|
|
33
|
+
(6) **ưu tiên** → chuyển thành trọng số `priorities` (tổng ~1) cho bước chấm điểm.
|
|
34
|
+
- Chuẩn hóa skills/chức danh về **canonical EN** (xem "Chuẩn hóa" bên dưới). Tóm tắt cho user xác nhận.
|
|
35
|
+
|
|
36
|
+
### [2] Collect — tạo `jobs`
|
|
37
|
+
- Sinh query song ngữ từ role/seniority/location/skills. Tìm trên job boards chính thống
|
|
38
|
+
(ITviec, TopCV, VietnamWorks, LinkedIn, CareerViet…).
|
|
39
|
+
- **Chỉ giữ job đọc được full JD và còn hạn.** Bỏ: snippet rời rạc, hết hạn, 404–410. Mục tiêu ≤ 20 job.
|
|
40
|
+
- Khử trùng theo URL chuẩn hóa (bỏ query tracking). Mỗi job đặt `extraction_confidence` (0–1) trung thực:
|
|
41
|
+
0.8–1.0 nếu đọc được full JD; ≤0.4 nếu chỉ có snippet.
|
|
42
|
+
- **`url` luôn là link gốc để bấm apply.** Nếu JD có contact (email/phone/Zalo/form) → ghi lại.
|
|
43
|
+
|
|
44
|
+
### [3] Match — tạo `shortlist`
|
|
45
|
+
Chấm từng job theo rubric, xếp giảm dần, để `excluded` cuối.
|
|
46
|
+
|
|
47
|
+
**Trọng số mặc định** (chuẩn hóa để tổng = 1; nếu user có `priorities` thì dùng nó, cũng chuẩn hóa):
|
|
48
|
+
skills 0.35 · seniority 0.20 · domain 0.15 · compensation 0.15 · location 0.05 · culture 0.05
|
|
49
|
+
|
|
50
|
+
**Hard filter trước:** vi phạm dealbreaker (có đủ bằng chứng) → `recommendation="excluded"`, `overall=0`.
|
|
51
|
+
Thiếu dữ liệu để khẳng định vi phạm → KHÔNG loại, chỉ hạ `confidence`.
|
|
52
|
+
|
|
53
|
+
**Điểm mỗi chiều 0–100:**
|
|
54
|
+
- **skills** = `100*(0.75*must_cover + 0.25*nice_cover)`; +≤10 nếu có skill advanced/expert cho must-have chính (cap 100). Ghi `matched_skills`, `missing_must_have`.
|
|
55
|
+
- **seniority**: khoảng cách bậc (intern<junior<mid<senior<lead<manager<director) 0→100, 1→75, 2→45, ≥3→15. Overqualified cũng bị trừ. `total_years < min_years` → nhân 0.6.
|
|
56
|
+
- **domain**: trùng ngành 100; ngành liền kề 60; không liên quan 30.
|
|
57
|
+
- **location**: remote khớp / đúng địa điểm 100; hybrid cùng thành phố 85; onsite khác TP mà user để "any" 60; ngoài vùng (chưa tới dealbreaker) 25.
|
|
58
|
+
- **compensation**: quy về VND/tháng để so ($1 = 26.100 VND, year→month chia 12). `job_mid ≥ target` → 100; trong [min,target] nội suy 70–100; ≥90% min → 50; thấp hơn → 20. **Lương unknown → 60 neutral (KHÔNG chấm 0)**, hạ confidence.
|
|
59
|
+
- **culture**: khớp `company_size` +baseline; tín hiệu tốt trong JD cộng thêm; ít dữ liệu → 60.
|
|
60
|
+
|
|
61
|
+
**Tổng hợp:** `overall = Σ(dimension_score[d] * normalized_weight[d])`. Map:
|
|
62
|
+
≥80 strong · 65–79 good · 50–64 maybe · <50 weak · (dealbreaker) excluded.
|
|
63
|
+
**Không bao giờ chấm 0 vì "thiếu thông tin"** — dùng neutral 55–60 và hạ `confidence`.
|
|
64
|
+
Luôn ghi `weights_used`, `rationale` (1–2 lý do mạnh + 1 gap lớn nhất), `confidence`.
|
|
65
|
+
|
|
66
|
+
### [4] Fit report (Markdown) — GỬI CHO USER
|
|
67
|
+
- **Bảng xếp hạng đầu**: rank | title | company | score | recommendation | lương | location | **link nộp CV** (markdown link, mọi job đều có link bấm được).
|
|
68
|
+
- **Phân tích chi tiết TẤT CẢ job** (nếu >20 thì chi tiết 20 job cao nhất, còn lại vẫn đủ trong bảng). **KHÔNG rút gọn còn top 3.** Mỗi job:
|
|
69
|
+
1. Header: `#N. Title @ Company` — score, recommendation, **link apply**, lương, location/remote, hạn nộp, contact (nếu có).
|
|
70
|
+
2. **Vì sao hợp** (2–3 gạch đầu dòng).
|
|
71
|
+
3. **Khoảng cách** (missing_must_have + gaps, kèm severity; nói rõ "học nhanh được" vs "cần thời gian").
|
|
72
|
+
4. **Rủi ro / cần xác minh** (confidence thấp → đọc kỹ JD gốc; over/underqualified; lương unknown).
|
|
73
|
+
5. **Hành động đề xuất**.
|
|
74
|
+
- Cuối: nhận xét thị trường, gap lặp lại (skill nên học), có nên nới target không.
|
|
75
|
+
|
|
76
|
+
### [5] Application assistant (tùy chọn)
|
|
77
|
+
Khi user chọn job để apply: tailor CV bullets khớp `must_have_skills` (dùng canonical name cho ATS,
|
|
78
|
+
định lượng thành tích, **không bịa**); viết cover letter (~250–350 từ) hoặc tin nhắn tiếp cận HR mẫu.
|
|
79
|
+
Nêu rõ đây là bản nháp để user tự chỉnh. **Không tự gửi thay user.**
|
|
80
|
+
|
|
81
|
+
## Chuẩn hóa Việt–Anh (dùng xuyên suốt)
|
|
82
|
+
|
|
83
|
+
- **Skills → canonical EN**: `ReactJS`/`React.js` → `React`; `NodeJS` → `Node.js`; `K8s` → `Kubernetes`; `.NET`/`dotnet` → `.NET`. Giữ biến thể trong `aliases`.
|
|
84
|
+
- **Chức danh VN → EN**: "Lập trình viên"/"Kỹ sư phần mềm" → Software Engineer; "Trưởng nhóm" → Lead; "Kiểm thử"/"QC" → QA; "Khoa học dữ liệu" → Data Science.
|
|
85
|
+
- **Cấp bậc VN**: Fresher→junior, "Chuyên viên"→mid, "Senior"→senior, "Team Lead"→lead, "Trưởng phòng"→manager, "Giám đốc"→director.
|
|
86
|
+
- **Địa điểm**: "TP.HCM"/"Sài Gòn"/"HCMC" → Ho Chi Minh City; "HN"/"Hà Nội" → Hanoi; "ĐN"/"Đà Nẵng" → Da Nang.
|
|
87
|
+
- **Lương — giữ nguyên gốc**: "15 triệu"/"15tr"/"15M" → 15.000.000 VND; "$1500"/"1500 USD" → USD; "Up to $2000" → max 2000 USD; "Thỏa thuận"/"Negotiable" → không set số, `negotiable=true`, currency=unknown. **Chỉ quy đổi khi so sánh**, không ghi đè giá trị gốc.
|
|
88
|
+
|
|
89
|
+
## Cấu trúc JSON (rút gọn)
|
|
90
|
+
|
|
91
|
+
**profile**: `{candidate{name,headline,location,languages[]}, skills[{name,aliases[],years,level,evidence}], experience[{title,company,start,end,domain,highlights[]}], education[], total_years, seniority, domains[], target{desired_roles[],desired_level,locations[],remote_preference,salary{min,target,currency,period},industries[],company_size[],dealbreakers[],priorities{skills,seniority,domain,location,compensation,culture}}}`
|
|
92
|
+
|
|
93
|
+
**job**: `{id,title,company,location,remote,url,source,posted_date,application_deadline,employment_type,language,description,contact{email,phone,zalo,form_url,how_to_apply},requirements{must_have_skills[],nice_to_have_skills[],min_years,seniority,education},salary{min,max,currency,period,negotiable},industry,company_size,collected_at,extraction_confidence}`
|
|
94
|
+
|
|
95
|
+
**match**: `{job_id,overall_score,recommendation,dimension_scores{skills,seniority,domain,location,compensation,culture},weights_used{},matched_skills[],missing_must_have[],gaps[{area,detail,severity}],dealbreaker_violations[],rationale,confidence}`
|
|
96
|
+
|
|
97
|
+
## Nguyên tắc (bắt buộc)
|
|
98
|
+
|
|
99
|
+
- **Không bịa** trường job/CV. Thiếu → `unknown` + hạ `confidence` (không chấm 0 mù quáng).
|
|
100
|
+
- Tôn trọng robots.txt/ToS; **không vượt anti-bot/CAPTCHA**.
|
|
101
|
+
- Giữ **đơn vị lương gốc**; chỉ quy đổi khi so sánh.
|
|
102
|
+
- Trung thực: match yếu thì nói thẳng, không thổi phồng khả năng trúng tuyển.
|
|
103
|
+
- **Không tự nộp hồ sơ / gửi email / tin nhắn** thay người dùng.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# JobMatching cho ChatGPT
|
|
2
|
+
|
|
3
|
+
Cùng một logic pipeline + rubric chấm điểm như Claude Code / Codex, chạy bằng **web browsing**
|
|
4
|
+
sẵn có của ChatGPT — không cần cài plugin hay API key.
|
|
5
|
+
|
|
6
|
+
## Cách cài (Custom GPT — khuyên dùng)
|
|
7
|
+
|
|
8
|
+
1. [Tải repo về](https://github.com/hieutrong512/JobMatching/archive/refs/heads/main.zip) và giải nén.
|
|
9
|
+
2. Vào [chatgpt.com/gpts/editor](https://chatgpt.com/gpts/editor) → **Create** → tab **Configure**:
|
|
10
|
+
- **Name**: `JobMatching`
|
|
11
|
+
- **Description**: "Tìm & xếp hạng việc làm phù hợp CV, song ngữ Việt–Anh"
|
|
12
|
+
- **Instructions**: mở [`GPT-INSTRUCTIONS.md`](GPT-INSTRUCTIONS.md), copy toàn bộ nội dung dán vào
|
|
13
|
+
- **Knowledge**: kéo-thả **tất cả** file trong thư mục [`knowledge/`](knowledge/) vào
|
|
14
|
+
- **Capabilities**: bật **Web Search** (bắt buộc). Bật **Code Interpreter** nếu muốn tải báo cáo (tùy chọn)
|
|
15
|
+
3. **Save** → **Publish** ("Anyone with a link" để chia sẻ, hoặc "Only me" để dùng riêng)
|
|
16
|
+
4. Mở GPT, dán CV vào là chạy.
|
|
17
|
+
|
|
18
|
+
## Cách khác
|
|
19
|
+
|
|
20
|
+
### ChatGPT Project
|
|
21
|
+
|
|
22
|
+
1. Tạo **Project** mới → dán [`GPT-INSTRUCTIONS.md`](GPT-INSTRUCTIONS.md) vào Instructions.
|
|
23
|
+
2. Kéo các file trong [`knowledge/`](knowledge/) vào **Files** của Project.
|
|
24
|
+
3. Bắt đầu chat, dán CV.
|
|
25
|
+
|
|
26
|
+
### Chat thường (nhanh, 1 lần)
|
|
27
|
+
|
|
28
|
+
Dán prompt này rồi dán CV ngay sau:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
Đọc kỹ và tuân theo bộ chỉ dẫn JobMatching dưới đây cho toàn bộ cuộc trò chuyện.
|
|
32
|
+
Dùng công cụ tìm kiếm web để thu thập job thật (không bịa). CV của tôi ở cuối tin nhắn.
|
|
33
|
+
|
|
34
|
+
<<< dán toàn bộ nội dung GPT-INSTRUCTIONS.md vào đây >>>
|
|
35
|
+
|
|
36
|
+
--- CV ---
|
|
37
|
+
<<< dán CV >>>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Nội dung thư mục
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
chatgpt/
|
|
44
|
+
├── GPT-INSTRUCTIONS.md Orchestrator (~7.6k chars, vừa giới hạn 8k Instructions)
|
|
45
|
+
├── knowledge/ Bộ knowledge upload sẵn (skills + schemas flatten)
|
|
46
|
+
│ ├── skill-*.md Từng skill chi tiết
|
|
47
|
+
│ └── *.schema.json Data contracts
|
|
48
|
+
└── README.md File này
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Build lại knowledge từ source (tùy chọn)
|
|
52
|
+
|
|
53
|
+
Thư mục `knowledge/` đã có sẵn trong repo. Nếu muốn build lại sau khi chỉnh skill:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
bash build-chatgpt.sh # macOS/Linux
|
|
57
|
+
powershell -File build-chatgpt.ps1 # Windows
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
→ ra `dist/chatgpt-knowledge/` (copy đè vào `chatgpt/knowledge/` nếu muốn commit).
|
|
61
|
+
|
|
62
|
+
## Khác biệt so với Claude Code / Codex
|
|
63
|
+
|
|
64
|
+
| | Claude Code / Codex | ChatGPT |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| Cài đặt | `/plugin marketplace add` | Tạo Custom GPT, upload knowledge |
|
|
67
|
+
| Web search | `WebSearch` / `web_search` | Web Search (browsing) |
|
|
68
|
+
| Lưu trữ | Ghi `data/*.json` ra ổ đĩa | JSON trong chat (hoặc Code Interpreter tạm) |
|
|
69
|
+
| Báo cáo | File `.md` trong `data/results/` | Markdown trong chat (hoặc file tải về) |
|
|
70
|
+
|
|
71
|
+
Logic pipeline, trọng số chấm điểm và data contract **giống hệt** — đều bắt nguồn từ
|
|
72
|
+
`job-matching-plugin/skills/` + `schemas/` (nguồn chân lý duy nhất).
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "job.schema.json",
|
|
4
|
+
"title": "Job",
|
|
5
|
+
"description": "Tin tuyển dụng đã chuẩn hóa từ nguồn search/scrape.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["id", "title", "company", "url", "source"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema_version": { "type": "string", "default": "1.0" },
|
|
10
|
+
"id": { "type": "string", "description": "Hash ổn định từ url hoặc company+title+location để khử trùng" },
|
|
11
|
+
"title": { "type": "string" },
|
|
12
|
+
"company": { "type": "string" },
|
|
13
|
+
"location": { "type": "string" },
|
|
14
|
+
"remote": { "type": "string", "enum": ["onsite", "hybrid", "remote", "unknown"], "default": "unknown" },
|
|
15
|
+
"url": { "type": "string", "description": "Link trang tuyển dụng / link JD gốc" },
|
|
16
|
+
"source": { "type": "string", "description": "vd 'itviec', 'topcv', 'vietnamworks', 'linkedin', 'careerviet', 'google'" },
|
|
17
|
+
"posted_date": { "type": "string", "description": "YYYY-MM-DD nếu có" },
|
|
18
|
+
"application_deadline": { "type": "string", "description": "YYYY-MM-DD hạn nộp nếu JD có ghi; job chỉ được giữ khi hạn >= ngày hiện tại" },
|
|
19
|
+
"employment_type": { "type": "string", "enum": ["full-time", "part-time", "contract", "internship", "unknown"], "default": "unknown" },
|
|
20
|
+
"language": { "type": "string", "enum": ["vi", "en", "mixed"], "default": "mixed" },
|
|
21
|
+
"description": { "type": "string", "description": "JD gốc (rút gọn nếu quá dài)" },
|
|
22
|
+
"contact": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"description": "Thông tin liên hệ ứng tuyển trực tiếp nếu JD có",
|
|
25
|
+
"properties": {
|
|
26
|
+
"email": { "type": "string" },
|
|
27
|
+
"phone": { "type": "string" },
|
|
28
|
+
"zalo": { "type": "string" },
|
|
29
|
+
"form_url": { "type": "string", "description": "Google Form / Typeform link nếu có" },
|
|
30
|
+
"how_to_apply": { "type": "string", "description": "Hướng dẫn ứng tuyển ngắn: vd 'Gửi CV qua email', 'Apply qua link'" }
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"requirements": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"properties": {
|
|
36
|
+
"must_have_skills": { "type": "array", "items": { "type": "string" }, "description": "Canonical names" },
|
|
37
|
+
"nice_to_have_skills": { "type": "array", "items": { "type": "string" } },
|
|
38
|
+
"min_years": { "type": "number" },
|
|
39
|
+
"seniority": { "type": "string", "enum": ["intern", "junior", "mid", "senior", "lead", "manager", "director", "unknown"], "default": "unknown" },
|
|
40
|
+
"education": { "type": "string" }
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"salary": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"properties": {
|
|
46
|
+
"min": { "type": "number" },
|
|
47
|
+
"max": { "type": "number" },
|
|
48
|
+
"currency": { "type": "string", "enum": ["VND", "USD", "unknown"], "default": "unknown" },
|
|
49
|
+
"period": { "type": "string", "enum": ["month", "year", "unknown"], "default": "unknown" },
|
|
50
|
+
"negotiable": { "type": "boolean" }
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"industry": { "type": "string" },
|
|
54
|
+
"company_size": { "type": "string", "enum": ["startup", "sme", "enterprise", "unknown"], "default": "unknown" },
|
|
55
|
+
"collected_at": { "type": "string", "description": "ISO timestamp lúc thu thập" },
|
|
56
|
+
"extraction_confidence": { "type": "number", "description": "0-1, độ tin cậy khi parse JD (0.8-1.0 khi đọc được full JD)" }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "match.schema.json",
|
|
4
|
+
"title": "MatchResult",
|
|
5
|
+
"description": "Kết quả chấm điểm 1 job so với profile. Xem scoring-rubric.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["job_id", "overall_score", "dimension_scores"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema_version": { "type": "string", "default": "1.0" },
|
|
10
|
+
"job_id": { "type": "string" },
|
|
11
|
+
"overall_score": { "type": "number", "description": "0-100, tổng hợp có trọng số" },
|
|
12
|
+
"recommendation": { "type": "string", "enum": ["strong", "good", "maybe", "weak", "excluded"] },
|
|
13
|
+
"dimension_scores": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"description": "Mỗi chiều 0-100.",
|
|
16
|
+
"properties": {
|
|
17
|
+
"skills": { "type": "number" },
|
|
18
|
+
"seniority": { "type": "number" },
|
|
19
|
+
"domain": { "type": "number" },
|
|
20
|
+
"location": { "type": "number" },
|
|
21
|
+
"compensation": { "type": "number" },
|
|
22
|
+
"culture": { "type": "number" }
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"weights_used": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"description": "Trọng số thực tế đã áp (sau khi override bằng target.priorities).",
|
|
28
|
+
"additionalProperties": { "type": "number" }
|
|
29
|
+
},
|
|
30
|
+
"matched_skills": { "type": "array", "items": { "type": "string" } },
|
|
31
|
+
"missing_must_have": { "type": "array", "items": { "type": "string" } },
|
|
32
|
+
"gaps": {
|
|
33
|
+
"type": "array",
|
|
34
|
+
"description": "Khoảng cách cần lấp (kỹ năng/kinh nghiệm) + mức nghiêm trọng.",
|
|
35
|
+
"items": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"properties": {
|
|
38
|
+
"area": { "type": "string" },
|
|
39
|
+
"detail": { "type": "string" },
|
|
40
|
+
"severity": { "type": "string", "enum": ["low", "medium", "high"] }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"dealbreaker_violations": { "type": "array", "items": { "type": "string" } },
|
|
45
|
+
"rationale": { "type": "string", "description": "Giải thích ngắn gọn vì sao điểm này (song ngữ nếu cần)" },
|
|
46
|
+
"confidence": { "type": "number", "description": "0-1, phụ thuộc extraction_confidence của job" }
|
|
47
|
+
}
|
|
48
|
+
}
|