@reconcrap/boss-recruit-mcp 1.0.18 → 1.0.20
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/package.json +1 -1
- package/skills/boss-recruit-pipeline/SKILL.md +3 -0
- package/src/parser.js +40 -4
- package/src/test-parser.js +35 -0
package/package.json
CHANGED
|
@@ -192,6 +192,9 @@ description: "Use when users ask to recruit candidates on Boss Zhipin via the bo
|
|
|
192
192
|
- 同时也要让用户确认其他已提取参数里是否有误;
|
|
193
193
|
- 必须单独确认筛选 `criteria` 是否准确(尤其学历/学校/论文等硬性条件不能丢失);
|
|
194
194
|
- 若 `required_confirmations` 或 `pending_questions` 里包含 `filter_recent_viewed`,必须明确补问:是否需要过滤近 14 天查看过的人选;
|
|
195
|
+
- 若用户回复中没有明确给出 `filter_recent_viewed` 的是/否,禁止直接再次调用流水线;必须先追问一个单点问题并拿到布尔值(需要过滤/不过滤);
|
|
196
|
+
- 对于多轮对话,必须维护一个“已确认参数快照”(city/degree/schools/keyword/filter_recent_viewed/target_count/criteria);后续每次重试都要把快照写入 `overrides` 与 `confirmation`,不能只重述自然语言 instruction;
|
|
197
|
+
- 当用户已用结构化格式(如“关键词:…,城市:…”)确认过参数后,后续调用不得丢弃这些字段;若缺字段,优先沿用快照值并提示用户可修改;
|
|
195
198
|
- 若确认,带 `confirmation.keyword_confirmed=true` 和 `keyword_value` 再次调用;
|
|
196
199
|
- 若用户修改关键词,传用户给的新词作为 `keyword_value` 再次调用;
|
|
197
200
|
- 对“是否过滤近 14 天查看”这个问题,需把用户选择写入 `overrides.filter_recent_viewed=true/false` 再次调用。
|
package/src/parser.js
CHANGED
|
@@ -31,6 +31,8 @@ const DEFAULT_PARAM_LABELS = {
|
|
|
31
31
|
};
|
|
32
32
|
const DEGREE_VALUES = new Set(["不限", "本科", "本科及以上", "硕士及以上", "博士"]);
|
|
33
33
|
const CITY_STOP_PATTERN = /(?:筛选|搜索|查找|找|做过|从事过|有过|相关|的人选|的人|并且|且|学历|学校|目标|必须|优先|,|。|;|;|,)/;
|
|
34
|
+
const SEARCH_INTENT_LEADING_PATTERN = /^(?:帮我|请)?(?:在boss上|在boss直聘上)?(?:找|筛选|搜索|查找)/i;
|
|
35
|
+
const SEARCH_INTENT_META_PATTERN = /(?:城市|地点|学历|学校|目标|人数|候选人|人选|岗位|职位|做过|有过|从事过|本科|硕士|博士)/i;
|
|
34
36
|
|
|
35
37
|
function normalizeText(input) {
|
|
36
38
|
return String(input || "").replace(/\s+/g, " ").trim();
|
|
@@ -91,6 +93,19 @@ function extractCity(text) {
|
|
|
91
93
|
}
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
const implicitPatterns = [
|
|
97
|
+
/(?:^|[,,。;;\n])(?:帮我|请)?(?:在boss上|在boss直聘上)?(?:找|筛选|搜索|查找)\s*([^\s,。;;、]{2,12}?)(?=(?:本科|硕士|博士|做过|有过|从事过|的人选|的人|候选人|工程师|研究员|岗位|职位|,|。|;|;|,|$))/i,
|
|
98
|
+
/(?:^|[,,。;;\n])在\s*([^\s,。;;、]{2,12}?)(?=(?:招|招聘|找|筛选|搜索|查找|本科|硕士|博士|做过|有过|从事过|的人选|的人|候选人|工程师|研究员|岗位|职位|,|。|;|;|,|$))/i
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
for (const pattern of implicitPatterns) {
|
|
102
|
+
const m = text.match(pattern);
|
|
103
|
+
if (m && m[1]) {
|
|
104
|
+
const city = sanitizeCityCandidate(m[1]);
|
|
105
|
+
if (city) return city;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
94
109
|
return null;
|
|
95
110
|
}
|
|
96
111
|
|
|
@@ -183,9 +198,9 @@ function extractKeywordExplicit(text) {
|
|
|
183
198
|
|
|
184
199
|
function extractKeywordAuto(text) {
|
|
185
200
|
const patterns = [
|
|
186
|
-
|
|
187
|
-
/有过\s*([
|
|
188
|
-
/从事过\s*([
|
|
201
|
+
/做(?:过)?\s*([\u4e00-\u9fa5A-Za-z0-9+#./\-\s]{2,40}?)(?:的人选|的人|相关|并且|且|,|。|,|$)/i,
|
|
202
|
+
/有过\s*([\u4e00-\u9fa5A-Za-z0-9+#./\-\s]{2,40}?)(?:经验|背景|的人选|并且|且|,|。|,|$)/i,
|
|
203
|
+
/从事过\s*([\u4e00-\u9fa5A-Za-z0-9+#./\-\s]{2,40}?)(?:相关|的人选|并且|且|,|。|,|$)/i
|
|
189
204
|
];
|
|
190
205
|
|
|
191
206
|
for (const pattern of patterns) {
|
|
@@ -216,6 +231,21 @@ function extractTargetCount(text) {
|
|
|
216
231
|
return null;
|
|
217
232
|
}
|
|
218
233
|
|
|
234
|
+
function extractExplicitCriteria(text) {
|
|
235
|
+
const patterns = [
|
|
236
|
+
/(?:筛选条件|筛选要求|硬性条件|筛选标准|要求)(?:为|是|:|:)\s*([^\n]+)/i
|
|
237
|
+
];
|
|
238
|
+
|
|
239
|
+
for (const pattern of patterns) {
|
|
240
|
+
const m = text.match(pattern);
|
|
241
|
+
if (m && m[1]) {
|
|
242
|
+
const criteria = m[1].trim();
|
|
243
|
+
if (criteria) return criteria;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
|
|
219
249
|
function sanitizeClause(clause) {
|
|
220
250
|
return clause
|
|
221
251
|
.replace(/^使用boss-recruit-pipeline skills/i, "")
|
|
@@ -232,6 +262,11 @@ function isCountPlanningClause(clause) {
|
|
|
232
262
|
}
|
|
233
263
|
|
|
234
264
|
function buildScreenCriteria(text, searchParams) {
|
|
265
|
+
const explicitCriteria = extractExplicitCriteria(text);
|
|
266
|
+
if (explicitCriteria) {
|
|
267
|
+
return explicitCriteria;
|
|
268
|
+
}
|
|
269
|
+
|
|
235
270
|
const clauses = text
|
|
236
271
|
.split(/[,,。;;\n]/)
|
|
237
272
|
.map((s) => sanitizeClause(s))
|
|
@@ -242,6 +277,7 @@ function buildScreenCriteria(text, searchParams) {
|
|
|
242
277
|
if (/地点|城市/.test(clause)) return false;
|
|
243
278
|
if (/近?14天(?:内)?查看(?:过)?|过滤近14天查看/.test(clause)) return false;
|
|
244
279
|
if (isCountPlanningClause(clause)) return false;
|
|
280
|
+
if (SEARCH_INTENT_LEADING_PATTERN.test(clause) && SEARCH_INTENT_META_PATTERN.test(clause)) return false;
|
|
245
281
|
return true;
|
|
246
282
|
});
|
|
247
283
|
|
|
@@ -443,7 +479,7 @@ export function parseRecruitInstruction({ instruction, confirmation, overrides }
|
|
|
443
479
|
if (!baseSearchParams.city) missingBeforeDefaults.push("city");
|
|
444
480
|
if (!baseSearchParams.degree) missingBeforeDefaults.push("degree");
|
|
445
481
|
if (!baseSearchParams.schools || baseSearchParams.schools.length === 0) missingBeforeDefaults.push("schools");
|
|
446
|
-
if (!baseSearchParams.keyword) missingBeforeDefaults.push("keyword");
|
|
482
|
+
if (!baseSearchParams.keyword && !keywordResolution.needsConfirmation) missingBeforeDefaults.push("keyword");
|
|
447
483
|
if (!baseScreenParams.target_count) missingBeforeDefaults.push("target_count");
|
|
448
484
|
|
|
449
485
|
const useDefaultForMissing = confirmation?.use_default_for_missing === true;
|
package/src/test-parser.js
CHANGED
|
@@ -302,6 +302,39 @@ function testCriteriaConfirmationPrompt() {
|
|
|
302
302
|
assert.equal(confirmedCriteria.needs_criteria_confirmation, false);
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
+
function testNaturalSentenceExtractsCityAndKeywordWithoutMissing() {
|
|
306
|
+
const r = parseRecruitInstruction({
|
|
307
|
+
instruction: "找杭州本科及以上做算法的人,本科学校必须是211或qs100院校,有CCF-A论文或会议成果,目标3人",
|
|
308
|
+
confirmation: null,
|
|
309
|
+
overrides: null
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
assert.equal(r.searchParams.city, "杭州");
|
|
313
|
+
assert.equal(r.proposed_keyword, "算法");
|
|
314
|
+
assert.deepEqual(r.missing_fields, []);
|
|
315
|
+
assert.equal(r.has_unresolved_missing_fields, false);
|
|
316
|
+
assert.equal(r.needs_keyword_confirmation, true);
|
|
317
|
+
assert.match(r.screenParams.criteria, /CCF-A论文/);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function testExplicitCriteriaTextIsPreserved() {
|
|
321
|
+
const r = parseRecruitInstruction({
|
|
322
|
+
instruction:
|
|
323
|
+
"关键词:算法,城市:杭州,学历:本科及以上,学校:985、211、qs100,目标人数:3人,筛选条件:必须有CCF-A论文或者会议成果,本科学校必须至少是211或者qs100院校",
|
|
324
|
+
confirmation: {
|
|
325
|
+
keyword_confirmed: true,
|
|
326
|
+
keyword_value: "算法",
|
|
327
|
+
search_params_confirmed: true
|
|
328
|
+
},
|
|
329
|
+
overrides: null
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
assert.equal(
|
|
333
|
+
r.screenParams.criteria,
|
|
334
|
+
"必须有CCF-A论文或者会议成果,本科学校必须至少是211或者qs100院校"
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
305
338
|
function main() {
|
|
306
339
|
testNeedInput();
|
|
307
340
|
testExampleExtraction();
|
|
@@ -314,6 +347,8 @@ function main() {
|
|
|
314
347
|
testDefaultsCanOnlyApplyWhenExplicitlyRequested();
|
|
315
348
|
testRecentViewedFilterPromptAndNegativeOverride();
|
|
316
349
|
testCriteriaConfirmationPrompt();
|
|
350
|
+
testNaturalSentenceExtractsCityAndKeywordWithoutMissing();
|
|
351
|
+
testExplicitCriteriaTextIsPreserved();
|
|
317
352
|
// eslint-disable-next-line no-console
|
|
318
353
|
console.log("parser tests passed");
|
|
319
354
|
}
|