@reconcrap/boss-recommend-mcp 1.3.13 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reconcrap/boss-recommend-mcp",
3
- "version": "1.3.13",
3
+ "version": "1.3.15",
4
4
  "description": "Unified MCP pipeline for recommend-page filtering and screening on Boss Zhipin",
5
5
  "keywords": [
6
6
  "boss",
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 FILTER_CLAUSE_PATTERNS = [
98
- /学校标签|院校标签|985|211|双一流|留学|国内外名校|公办本科/i,
99
- /学历|学位|教育|初中及以下|中专|中技|高中|大专|专科|本科|硕士|研究生|博士/i,
100
- /性别|男生|女生|男性|女性|男\b|女\b/i,
101
- /近?14天(?:内)?没有|近?14天(?:内)?没看过|近?14天(?:内)?未查看|过滤[^。;;\n]{0,12}14天|排除[^。;;\n]{0,12}14天/i,
102
- /目标(?:处理|筛选|通过)?(?:人数|数量)?|至少(?:处理|筛选|通过)|(?:处理|筛选|通过)\s*\d+\s*(?:位|人)/i,
103
- /最多(?:打招呼|沟通|联系)|(?:打招呼|沟通|联系)(?:上限|最多|不超过|至多)/i,
104
- /收藏|打招呼|直接沟通|什么也不做|不做任何操作|不操作|仅筛选|只筛选/i
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
- /推荐页|推荐页面|boss推荐/i,
108
- /帮我|请|运行|skill/i
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 buildCriteria(text, overrideCriteria) {
412
- const normalizedOverride = normalizeText(overrideCriteria);
413
- if (normalizedOverride) {
414
- return normalizedOverride;
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) => sanitizeClause(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
- const filtered = clauses.filter((clause) => {
423
- if (FILTER_CLAUSE_PATTERNS.some((pattern) => pattern.test(clause))) return false;
424
- if (META_CLAUSE_PATTERNS.some((pattern) => pattern.test(clause))) return false;
425
- return true;
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 result = uniqueList(filtered.map(normalizeText)).join(";");
429
- return result || null;
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 text = normalizeText(instruction);
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 jobSelectionHint = normalizeText(overrides?.job || confirmation?.job_value || "");
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: buildCriteria(text, overrideCriteria),
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: screenParams,
916
+ current_screen_params: {
917
+ ...screenParams,
918
+ criteria_normalized: criteriaResolution.normalized
919
+ },
787
920
  missing_fields,
788
921
  suspicious_fields,
789
922
  pending_questions
@@ -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
 
@@ -76,7 +76,7 @@ export class BossChatApp {
76
76
  logger = console,
77
77
  dryRun = false,
78
78
  artifactRootDir = '',
79
- resumeOpenCooldownMs = 16000,
79
+ resumeOpenCooldownMs = 3000,
80
80
  onProgress = null,
81
81
  }) {
82
82
  this.page = page;
@@ -93,7 +93,7 @@ export class BossChatApp {
93
93
  this.resumeOpenBlockedUntil = 0;
94
94
  this.resumeOpenCooldownMs = Number.isFinite(Number(resumeOpenCooldownMs))
95
95
  ? Math.max(0, Number(resumeOpenCooldownMs))
96
- : 16000;
96
+ : 3000;
97
97
  this.onProgress = typeof onProgress === 'function' ? onProgress : null;
98
98
  }
99
99
 
@@ -555,7 +555,7 @@ export class BossChatApp {
555
555
  let capture = null;
556
556
  let lastResumeError = null;
557
557
  let resumeProfile = null;
558
- await this.waitResumeOpenCooldown(this.resumeOpenCooldownMs + Math.floor(Math.random() * 1800));
558
+ await this.waitResumeOpenCooldown(this.resumeOpenCooldownMs + Math.floor(Math.random() * 200));
559
559
  await this.checkpoint();
560
560
  const openResult = await this.page.openOnlineResume();
561
561
  let openDetected = openResult ? Boolean(openResult?.detectedOpen) : true;
@@ -47,7 +47,7 @@ export class InteractionController {
47
47
  return 0;
48
48
  }
49
49
 
50
- const restMs = 4000 + Math.floor(Math.random() * 4000);
50
+ const restMs = 0;
51
51
  logger.log(`短暂休息 ${restMs}ms,保持处理节奏稳定...`);
52
52
  await this.wait(restMs);
53
53
  this.nextRestAt = processedCount + this.randomRestThreshold();
@@ -4931,12 +4931,12 @@ class RecommendScreenCli {
4931
4931
  async takeBreakIfNeeded() {
4932
4932
  this.restCounter += 1;
4933
4933
  if (Math.random() < 0.08) {
4934
- const pauseMs = 3000 + Math.floor(Math.random() * 4000);
4934
+ const pauseMs = 0;
4935
4935
  log(`[随机休息] 暂停 ${Math.round(pauseMs / 1000)} 秒`);
4936
4936
  await sleep(pauseMs);
4937
4937
  }
4938
4938
  if (this.restCounter >= this.restThreshold) {
4939
- const pauseMs = 15000 + Math.floor(Math.random() * 15000);
4939
+ const pauseMs = 0;
4940
4940
  log(`[批次休息] 已连续处理 ${this.restCounter} 人,暂停 ${Math.round(pauseMs / 1000)} 秒`);
4941
4941
  await sleep(pauseMs);
4942
4942
  this.restCounter = 0;