@reconcrap/boss-recommend-mcp 1.3.14 → 1.3.15
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/src/parser.js +159 -26
- package/src/test-parser.js +97 -0
package/package.json
CHANGED
package/src/parser.js
CHANGED
|
@@ -86,26 +86,33 @@ const RECENT_NOT_VIEW_NEGATIVE_PATTERNS = [
|
|
|
86
86
|
/保留[^。;;\n]{0,12}14天/i
|
|
87
87
|
];
|
|
88
88
|
const TARGET_COUNT_PATTERNS = [
|
|
89
|
+
/目标筛选数(?:量)?(?:为|是|:|:)?\s*(\d+)/i,
|
|
90
|
+
/目标通过数(?:量)?(?:为|是|:|:)?\s*(\d+)/i,
|
|
89
91
|
/目标(?:处理|筛选|通过)?(?:人数|数量)?(?:为|是|:|:)?\s*(\d+)/i,
|
|
90
92
|
/至少(?:处理|筛选|通过)\s*(\d+)\s*(?:位|人)/i,
|
|
91
93
|
/(?:处理|筛选|通过)\s*(\d+)\s*(?:位|人)/i
|
|
92
94
|
];
|
|
93
95
|
const MAX_GREET_COUNT_PATTERNS = [
|
|
96
|
+
/最大招呼数(?:量)?(?:为|是|:|:)?\s*(\d+)/i,
|
|
97
|
+
/最大(?:打招呼|招呼|沟通|联系)(?:人数|数量|数)?(?:为|是|:|:)?\s*(\d+)/i,
|
|
94
98
|
/最多(?:打招呼|沟通|联系)\s*(\d+)\s*(?:位|人|个)?/i,
|
|
95
99
|
/(?:打招呼|沟通|联系)(?:上限|最多|不超过|至多)(?:为|是|:|:)?\s*(\d+)/i
|
|
96
100
|
];
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
101
|
+
const CRITERIA_EXPLICIT_MARKER_PATTERN = /筛选条件\s*[::]/i;
|
|
102
|
+
const CRITERIA_EXPLICIT_STOP_PATTERN = /(?:^|[\n;;])\s*(?:页面选择|学校标签|院校标签|学历|学位|性别|是否过滤近14天看过|目标筛选数|目标通过人数|通过筛选后动作|最大招呼数|最大打招呼数|岗位)\s*[::]/i;
|
|
103
|
+
const CRITERIA_META_FIELD_PREFIX_PATTERNS = [
|
|
104
|
+
/^(?:页面选择|学校标签|院校标签|学历|学位|性别|是否过滤近14天看过|目标筛选数|目标通过人数|通过筛选后动作|最大招呼数|最大打招呼数|岗位)\s*(?:[::]|$)/i,
|
|
105
|
+
/^(?:近?14天(?:内)?(?:没有|没看过|未查看)|(?:不过滤|保留|过滤|排除)[^。;;\n]{0,12}14天)\s*(?:[::]|$)?/i,
|
|
106
|
+
/^(?:目标(?:处理|筛选|通过)?(?:人数|数量)?|至少(?:处理|筛选|通过)|(?:处理|筛选|通过)\s*\d+\s*(?:位|人))(?:[::\s]|$)/i,
|
|
107
|
+
/^(?:最多(?:打招呼|沟通|联系)|(?:打招呼|沟通|联系)(?:上限|最多|不超过|至多))(?:[::\s]|$)/i,
|
|
108
|
+
/^(?:(?:通过筛选后)?动作|post[_\s-]?action|max[_\s-]?greet[_\s-]?count|target[_\s-]?count)\s*(?:[::]|$)/i
|
|
105
109
|
];
|
|
106
110
|
const META_CLAUSE_PATTERNS = [
|
|
107
|
-
|
|
108
|
-
|
|
111
|
+
/^推荐页|^推荐页面|^boss推荐/i,
|
|
112
|
+
/^帮我|^请|^运行|^使用.*skill/i,
|
|
113
|
+
/^启动boss推荐任务/i,
|
|
114
|
+
/^条件如下(?:[::]|$)/i,
|
|
115
|
+
/^(?:符合标准(?:的人选)?(?:都)?(?:的)?(?:动作)?[::]?\s*)?(?:收藏|打招呼|直接沟通|什么也不做|不做任何操作|不操作|仅筛选|只筛选)(?:[::]|$)/i
|
|
109
116
|
];
|
|
110
117
|
const FEATURED_SCOPE_PATTERN = /(?:精选牛人|精选页|精选页面|精选tab|精选标签|tab[^。;;\n]{0,6}精选|精选)/i;
|
|
111
118
|
const LATEST_SCOPE_PATTERN = /(?:最新页|最新页面|最新tab|最新标签|tab[^。;;\n]{0,6}最新|最新)/i;
|
|
@@ -394,6 +401,13 @@ function extractMaxGreetCount(text) {
|
|
|
394
401
|
return null;
|
|
395
402
|
}
|
|
396
403
|
|
|
404
|
+
function extractJobSelectionHint(text) {
|
|
405
|
+
const normalized = String(text || "").replace(/\r\n/g, "\n");
|
|
406
|
+
const match = normalized.match(/(?:^|[\n;;])\s*(?:岗位|职位|job)\s*[::]\s*([^\n;;]+)/i);
|
|
407
|
+
if (!match?.[1]) return null;
|
|
408
|
+
return normalizeText(String(match[1] || "").replace(/[。;;]+$/, "").trim());
|
|
409
|
+
}
|
|
410
|
+
|
|
397
411
|
function sanitizeClause(clause) {
|
|
398
412
|
let current = normalizeText(clause);
|
|
399
413
|
for (const pattern of LEADING_NOISE_PATTERNS) {
|
|
@@ -408,25 +422,128 @@ function sanitizeClause(clause) {
|
|
|
408
422
|
return current;
|
|
409
423
|
}
|
|
410
424
|
|
|
411
|
-
function
|
|
412
|
-
const
|
|
413
|
-
if (
|
|
414
|
-
|
|
425
|
+
function isMetaClause(clause) {
|
|
426
|
+
const normalized = sanitizeClause(clause);
|
|
427
|
+
if (!normalized) return true;
|
|
428
|
+
const withoutNumbering = normalized.replace(/^\d+\s*[))]\s*/, "").trim();
|
|
429
|
+
if (!withoutNumbering) return true;
|
|
430
|
+
if (CRITERIA_META_FIELD_PREFIX_PATTERNS.some((pattern) => pattern.test(withoutNumbering))) return true;
|
|
431
|
+
if (META_CLAUSE_PATTERNS.some((pattern) => pattern.test(withoutNumbering))) return true;
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function splitRawCriteriaClauses(text) {
|
|
436
|
+
const normalized = String(text || "").replace(/\r\n/g, "\n").trim();
|
|
437
|
+
if (!normalized) return [];
|
|
438
|
+
const firstNumberedIndex = normalized.search(/\d+\s*[))]/);
|
|
439
|
+
if (firstNumberedIndex === -1) {
|
|
440
|
+
return normalized
|
|
441
|
+
.split(/[;;\n]+/)
|
|
442
|
+
.map((item) => String(item || "").trim())
|
|
443
|
+
.filter(Boolean);
|
|
415
444
|
}
|
|
416
445
|
|
|
446
|
+
const prefix = normalized
|
|
447
|
+
.slice(0, firstNumberedIndex)
|
|
448
|
+
.replace(/[;;,,。]+$/, "")
|
|
449
|
+
.trim();
|
|
450
|
+
const numberedClauses = normalized
|
|
451
|
+
.slice(firstNumberedIndex)
|
|
452
|
+
.split(/(?=\d+\s*[))])/)
|
|
453
|
+
.map((item) => String(item || "").trim())
|
|
454
|
+
.filter(Boolean);
|
|
455
|
+
|
|
456
|
+
return prefix ? [prefix, ...numberedClauses] : numberedClauses;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function normalizeRawCriteriaClauses(clauses = []) {
|
|
460
|
+
const filtered = clauses
|
|
461
|
+
.map((item) => String(item || "").replace(/^[;;,,。]+/, "").replace(/[;;,,。]+$/, "").trim())
|
|
462
|
+
.filter(Boolean)
|
|
463
|
+
.filter((item) => !isMetaClause(item));
|
|
464
|
+
const unique = uniqueList(filtered);
|
|
465
|
+
if (!unique.length) return null;
|
|
466
|
+
return unique.reduce((acc, clause) => {
|
|
467
|
+
if (!acc) return clause;
|
|
468
|
+
if (/[::]$/.test(acc) && /^\d+\s*[))]/.test(clause)) {
|
|
469
|
+
return `${acc}${clause}`;
|
|
470
|
+
}
|
|
471
|
+
return `${acc};${clause}`;
|
|
472
|
+
}, "");
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function normalizeCriteriaClauses(clauses = []) {
|
|
476
|
+
const filtered = clauses
|
|
477
|
+
.map((item) => sanitizeClause(item))
|
|
478
|
+
.map((item) => item.replace(/^[;;,,。]+/, "").replace(/[;;,,。]+$/, "").trim())
|
|
479
|
+
.filter(Boolean)
|
|
480
|
+
.filter((item) => !isMetaClause(item));
|
|
481
|
+
const unique = uniqueList(filtered.map((item) => normalizeText(item)));
|
|
482
|
+
if (!unique.length) return null;
|
|
483
|
+
return unique.reduce((acc, clause) => {
|
|
484
|
+
if (!acc) return clause;
|
|
485
|
+
if (/[::]$/.test(acc) && /^\d+\s*[))]/.test(clause)) {
|
|
486
|
+
return `${acc}${clause}`;
|
|
487
|
+
}
|
|
488
|
+
return `${acc};${clause}`;
|
|
489
|
+
}, "");
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function extractExplicitCriteriaBlock(text) {
|
|
493
|
+
const normalizedText = String(text || "").replace(/\r\n/g, "\n");
|
|
494
|
+
const markerMatch = normalizedText.match(CRITERIA_EXPLICIT_MARKER_PATTERN);
|
|
495
|
+
if (!markerMatch) return {
|
|
496
|
+
raw: null,
|
|
497
|
+
normalized: null
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
let block = normalizedText.slice(markerMatch.index + markerMatch[0].length);
|
|
501
|
+
const stopMatch = block.match(CRITERIA_EXPLICIT_STOP_PATTERN);
|
|
502
|
+
if (stopMatch && stopMatch.index > 0) {
|
|
503
|
+
block = block.slice(0, stopMatch.index);
|
|
504
|
+
}
|
|
505
|
+
const rawClauses = splitRawCriteriaClauses(block);
|
|
506
|
+
return {
|
|
507
|
+
raw: normalizeRawCriteriaClauses(rawClauses),
|
|
508
|
+
normalized: normalizeCriteriaClauses(rawClauses)
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function buildFallbackCriteria(text) {
|
|
417
513
|
const clauses = sanitizeInstruction(text)
|
|
418
514
|
.split(/[,,。;;\n]/)
|
|
419
|
-
.map((item) =>
|
|
515
|
+
.map((item) => String(item || "").trim())
|
|
420
516
|
.filter(Boolean);
|
|
517
|
+
return {
|
|
518
|
+
raw: normalizeRawCriteriaClauses(clauses),
|
|
519
|
+
normalized: normalizeCriteriaClauses(clauses)
|
|
520
|
+
};
|
|
521
|
+
}
|
|
421
522
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
523
|
+
function buildCriteria({ instruction, rawInstruction, overrideCriteria }) {
|
|
524
|
+
const rawOverride = String(overrideCriteria || "").trim();
|
|
525
|
+
const normalizedOverride = normalizeText(rawOverride);
|
|
526
|
+
if (normalizedOverride) {
|
|
527
|
+
return {
|
|
528
|
+
raw: rawOverride || normalizedOverride,
|
|
529
|
+
normalized: normalizedOverride,
|
|
530
|
+
source: "override"
|
|
531
|
+
};
|
|
532
|
+
}
|
|
427
533
|
|
|
428
|
-
const
|
|
429
|
-
|
|
534
|
+
const explicitCriteria = extractExplicitCriteriaBlock(rawInstruction || instruction);
|
|
535
|
+
if (explicitCriteria.raw) {
|
|
536
|
+
return {
|
|
537
|
+
...explicitCriteria,
|
|
538
|
+
source: "explicit"
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const fallbackCriteria = buildFallbackCriteria(rawInstruction || instruction);
|
|
543
|
+
return {
|
|
544
|
+
...fallbackCriteria,
|
|
545
|
+
source: fallbackCriteria.raw ? "fallback" : null
|
|
546
|
+
};
|
|
430
547
|
}
|
|
431
548
|
|
|
432
549
|
function resolvePostAction({ instruction, confirmation, overrides }) {
|
|
@@ -550,7 +667,8 @@ function collectSuspiciousFields({ invalidOverrideSchoolTags, maxGreetCountResol
|
|
|
550
667
|
}
|
|
551
668
|
|
|
552
669
|
export function parseRecommendInstruction({ instruction, confirmation, overrides }) {
|
|
553
|
-
const
|
|
670
|
+
const rawInstruction = String(instruction || "");
|
|
671
|
+
const text = normalizeText(rawInstruction);
|
|
554
672
|
const detectedSchoolTags = extractSchoolTags(text);
|
|
555
673
|
const detectedDegrees = extractDegrees(text);
|
|
556
674
|
const schoolTagAudit = auditSchoolTagSelections(overrides?.school_tag);
|
|
@@ -563,7 +681,17 @@ export function parseRecommendInstruction({ instruction, confirmation, overrides
|
|
|
563
681
|
const overrideRecentNotView = normalizeRecentNotView(overrides?.recent_not_view);
|
|
564
682
|
const confirmationRecentNotView = normalizeRecentNotView(confirmation?.recent_not_view_value);
|
|
565
683
|
const overrideCriteria = overrides?.criteria;
|
|
566
|
-
const
|
|
684
|
+
const criteriaResolution = buildCriteria({
|
|
685
|
+
instruction: text,
|
|
686
|
+
rawInstruction,
|
|
687
|
+
overrideCriteria
|
|
688
|
+
});
|
|
689
|
+
const jobSelectionHint = normalizeText(
|
|
690
|
+
overrides?.job
|
|
691
|
+
|| confirmation?.job_value
|
|
692
|
+
|| extractJobSelectionHint(rawInstruction)
|
|
693
|
+
|| ""
|
|
694
|
+
);
|
|
567
695
|
const pageScopeResolution = resolvePageScope({ instruction: text, confirmation, overrides });
|
|
568
696
|
|
|
569
697
|
const inferredSchoolTag = detectedSchoolTags.length > 0
|
|
@@ -584,7 +712,7 @@ export function parseRecommendInstruction({ instruction, confirmation, overrides
|
|
|
584
712
|
recent_not_view: overrideRecentNotView || confirmationRecentNotView || extractRecentNotView(text) || "不限"
|
|
585
713
|
};
|
|
586
714
|
const screenParams = {
|
|
587
|
-
criteria:
|
|
715
|
+
criteria: criteriaResolution.raw || criteriaResolution.normalized || null,
|
|
588
716
|
target_count: null,
|
|
589
717
|
post_action: null,
|
|
590
718
|
max_greet_count: null
|
|
@@ -765,6 +893,7 @@ export function parseRecommendInstruction({ instruction, confirmation, overrides
|
|
|
765
893
|
needs_post_action_confirmation,
|
|
766
894
|
needs_max_greet_count_confirmation,
|
|
767
895
|
needs_page_confirmation,
|
|
896
|
+
criteria_normalized: criteriaResolution.normalized,
|
|
768
897
|
proposed_target_count: targetCountResolution.proposed_target_count,
|
|
769
898
|
proposed_post_action: postActionResolution.proposed_post_action,
|
|
770
899
|
proposed_max_greet_count: maxGreetCountResolution.proposed_max_greet_count,
|
|
@@ -777,13 +906,17 @@ export function parseRecommendInstruction({ instruction, confirmation, overrides
|
|
|
777
906
|
extracted_search_params: searchParams,
|
|
778
907
|
extracted_screen_params: {
|
|
779
908
|
criteria: screenParams.criteria,
|
|
909
|
+
criteria_normalized: criteriaResolution.normalized,
|
|
780
910
|
target_count: targetCountResolution.proposed_target_count,
|
|
781
911
|
post_action: postActionResolution.proposed_post_action,
|
|
782
912
|
max_greet_count: maxGreetCountResolution.proposed_max_greet_count
|
|
783
913
|
},
|
|
784
914
|
current_page_scope: pageScopeResolution.page_scope,
|
|
785
915
|
current_search_params: searchParams,
|
|
786
|
-
current_screen_params:
|
|
916
|
+
current_screen_params: {
|
|
917
|
+
...screenParams,
|
|
918
|
+
criteria_normalized: criteriaResolution.normalized
|
|
919
|
+
},
|
|
787
920
|
missing_fields,
|
|
788
921
|
suspicious_fields,
|
|
789
922
|
pending_questions
|
package/src/test-parser.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { parseRecommendInstruction } from "./parser.js";
|
|
3
3
|
|
|
4
|
+
const REPRODUCTION_INSTRUCTION = `启动boss推荐任务。条件如下:
|
|
5
|
+
|
|
6
|
+
页面选择:推荐;
|
|
7
|
+
学校标签:985/211/国内外名校;
|
|
8
|
+
学历:本科及以上;
|
|
9
|
+
性别:不限;
|
|
10
|
+
是否过滤近14天看过:近14天没有;
|
|
11
|
+
目标筛选数:152;
|
|
12
|
+
通过筛选后动作:打招呼;
|
|
13
|
+
最大招呼数:152;
|
|
14
|
+
岗位:研发工程师(AI应用方向)-2026届校招 _ 杭州;
|
|
15
|
+
筛选条件:需同时满足全部条件:1)如果有本科学历,本科学历必须为 211 及以上或 QS 前 500 海外院校;2)至少一段学历为 985、QS 前 100 海外院校或中科院;3)具备大模型 / AI / 图形学 / 计算机视觉 / 3D相关的算法或工程经验(实习、项目、科研均可)。学校是否是985、211、qs排名等判断如果简历内没有明确标明,需要通过学校名称来判断;4)必须是25年应届生或者26年应届生或者27年应届生,除了标签以外需要通过人选最高学历的求学年份判断(比如:本科简历里写了2021 - 2025,应该理解为25年毕业,属于25年应届生);5)年龄必须35岁以内。`;
|
|
16
|
+
|
|
4
17
|
function testNeedConfirmationIncludesPostAction() {
|
|
5
18
|
const result = parseRecommendInstruction({
|
|
6
19
|
instruction: "推荐页上筛选985男生,近14天没有,有大模型平台经验,符合标准的收藏",
|
|
@@ -606,6 +619,85 @@ function testPageScopeOverrideShouldNotBypassConfirmation() {
|
|
|
606
619
|
assert.equal(result.pending_questions.some((item) => item.field === "page_scope"), true);
|
|
607
620
|
}
|
|
608
621
|
|
|
622
|
+
function testExplicitCriteriaBlockShouldKeepAllCoreRulesAndExcludeMetaFields() {
|
|
623
|
+
const result = parseRecommendInstruction({
|
|
624
|
+
instruction: REPRODUCTION_INSTRUCTION,
|
|
625
|
+
confirmation: null,
|
|
626
|
+
overrides: null
|
|
627
|
+
});
|
|
628
|
+
const criteria = result.screenParams.criteria || "";
|
|
629
|
+
|
|
630
|
+
assert.equal(criteria.includes("需同时满足全部条件"), true);
|
|
631
|
+
assert.equal(criteria.includes("1)如果有本科学历,本科学历必须为 211 及以上或 QS 前 500 海外院校"), true);
|
|
632
|
+
assert.equal(criteria.includes("2)至少一段学历为 985、QS 前 100 海外院校或中科院"), true);
|
|
633
|
+
assert.equal(criteria.includes("3)具备大模型 / AI / 图形学 / 计算机视觉 / 3D相关的算法或工程经验"), true);
|
|
634
|
+
assert.equal(criteria.includes("4)必须是25年应届生或者26年应届生或者27年应届生"), true);
|
|
635
|
+
assert.equal(criteria.includes("5)年龄必须35岁以内"), true);
|
|
636
|
+
|
|
637
|
+
assert.equal(criteria.includes("页面选择"), false);
|
|
638
|
+
assert.equal(criteria.includes("目标筛选数"), false);
|
|
639
|
+
assert.equal(criteria.includes("通过筛选后动作"), false);
|
|
640
|
+
assert.equal(criteria.includes("最大招呼数"), false);
|
|
641
|
+
assert.equal(criteria.includes("岗位"), false);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function testFallbackCriteriaShouldStillWorkWithoutExplicitMarker() {
|
|
645
|
+
const result = parseRecommendInstruction({
|
|
646
|
+
instruction: "页面选择:推荐;学校标签:985/211;岗位:算法工程师;候选人需满足至少两段 AI 项目经验;最大招呼数:20;",
|
|
647
|
+
confirmation: null,
|
|
648
|
+
overrides: null
|
|
649
|
+
});
|
|
650
|
+
const criteria = result.screenParams.criteria || "";
|
|
651
|
+
|
|
652
|
+
assert.equal(criteria.includes("至少两段 AI 项目经验"), true);
|
|
653
|
+
assert.equal(criteria.includes("页面选择"), false);
|
|
654
|
+
assert.equal(criteria.includes("岗位"), false);
|
|
655
|
+
assert.equal(criteria.includes("最大招呼数"), false);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function testOverrideCriteriaShouldHaveHighestPriorityOverExplicitCriteriaBlock() {
|
|
659
|
+
const result = parseRecommendInstruction({
|
|
660
|
+
instruction: REPRODUCTION_INSTRUCTION,
|
|
661
|
+
confirmation: null,
|
|
662
|
+
overrides: {
|
|
663
|
+
criteria: "只看有开源 Agent 项目经验的人选"
|
|
664
|
+
}
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
assert.equal(result.screenParams.criteria, "只看有开源 Agent 项目经验的人选");
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function testFallbackCriteriaShouldNotDropReal985211QsRules() {
|
|
671
|
+
const result = parseRecommendInstruction({
|
|
672
|
+
instruction: "页面选择:推荐;目标筛选数:10;至少一段学历为985或QS前100海外院校;如果有本科学历,本科必须211或QS前500;岗位:算法工程师;",
|
|
673
|
+
confirmation: null,
|
|
674
|
+
overrides: null
|
|
675
|
+
});
|
|
676
|
+
const criteria = result.screenParams.criteria || "";
|
|
677
|
+
|
|
678
|
+
assert.equal(criteria.includes("985"), true);
|
|
679
|
+
assert.equal(criteria.includes("QS前100"), true);
|
|
680
|
+
assert.equal(criteria.includes("211"), true);
|
|
681
|
+
assert.equal(criteria.includes("QS前500"), true);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function testMetaHintsShouldBeProposedFromInstruction() {
|
|
685
|
+
const result = parseRecommendInstruction({
|
|
686
|
+
instruction: `页面选择:推荐;目标筛选数:5;通过筛选后动作:打招呼;最大招呼数:3;岗位:研发工程师(AI应用方向)-2026届校招 _ 杭州;筛选条件:需同时满足全部条件:1)具备AI经验;`,
|
|
687
|
+
confirmation: null,
|
|
688
|
+
overrides: null
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
assert.equal(result.proposed_page_scope, "recommend");
|
|
692
|
+
assert.equal(result.proposed_target_count, 5);
|
|
693
|
+
assert.equal(result.proposed_post_action, "greet");
|
|
694
|
+
assert.equal(result.proposed_max_greet_count, 3);
|
|
695
|
+
assert.equal(result.job_selection_hint, "研发工程师(AI应用方向)-2026届校招 _ 杭州");
|
|
696
|
+
assert.equal(result.screenParams.target_count, null);
|
|
697
|
+
assert.equal(result.screenParams.post_action, null);
|
|
698
|
+
assert.equal(result.screenParams.max_greet_count, null);
|
|
699
|
+
}
|
|
700
|
+
|
|
609
701
|
function main() {
|
|
610
702
|
testNeedConfirmationIncludesPostAction();
|
|
611
703
|
testConfirmedPostActionAndOverrides();
|
|
@@ -639,6 +731,11 @@ function main() {
|
|
|
639
731
|
testLatestKeywordShouldProposeLatestPageScope();
|
|
640
732
|
testConfirmedPageScopeShouldBeResolved();
|
|
641
733
|
testPageScopeOverrideShouldNotBypassConfirmation();
|
|
734
|
+
testExplicitCriteriaBlockShouldKeepAllCoreRulesAndExcludeMetaFields();
|
|
735
|
+
testFallbackCriteriaShouldStillWorkWithoutExplicitMarker();
|
|
736
|
+
testOverrideCriteriaShouldHaveHighestPriorityOverExplicitCriteriaBlock();
|
|
737
|
+
testFallbackCriteriaShouldNotDropReal985211QsRules();
|
|
738
|
+
testMetaHintsShouldBeProposedFromInstruction();
|
|
642
739
|
console.log("parser tests passed");
|
|
643
740
|
}
|
|
644
741
|
|