@reconcrap/boss-recommend-mcp 1.3.22 → 1.3.24
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
|
@@ -4959,30 +4959,18 @@ class RecommendScreenCli {
|
|
|
4959
4959
|
applyVisionEvidenceGate(result) {
|
|
4960
4960
|
const parsed = result && typeof result === "object" ? result : {};
|
|
4961
4961
|
const rawPassed = parsed?.rawPassed === true || parsed?.passed === true;
|
|
4962
|
-
const
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
const
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
? Number(parsed.evidenceRawCount)
|
|
4970
|
-
: parsedEvidence.length)
|
|
4971
|
-
: null;
|
|
4972
|
-
const evidenceMatchedCount = evidenceGateEligible
|
|
4973
|
-
? (Number.isFinite(Number(parsed?.evidenceMatchedCount))
|
|
4974
|
-
? Number(parsed.evidenceMatchedCount)
|
|
4975
|
-
: parsedEvidence.length)
|
|
4976
|
-
: null;
|
|
4977
|
-
const evidenceGateDemoted = parsed?.evidenceGateDemoted === true
|
|
4978
|
-
|| (evidenceGateEligible && rawPassed && evidenceMatchedCount <= 0);
|
|
4962
|
+
const parsedEvidence = toStringArray(parsed?.evidence);
|
|
4963
|
+
const evidenceRawCount = Number.isFinite(Number(parsed?.evidenceRawCount))
|
|
4964
|
+
? Number(parsed.evidenceRawCount)
|
|
4965
|
+
: parsedEvidence.length;
|
|
4966
|
+
const evidenceMatchedCount = Number.isFinite(Number(parsed?.evidenceMatchedCount))
|
|
4967
|
+
? Number(parsed.evidenceMatchedCount)
|
|
4968
|
+
: parsedEvidence.length;
|
|
4979
4969
|
const cot = normalizeText(parsed?.cot || parsed?.reason || "");
|
|
4980
4970
|
const summary = normalizeText(parsed?.summary || cot);
|
|
4981
|
-
const finalReason =
|
|
4982
|
-
? `模型未给出可在简历截图中引用的证据,按安全策略判为不通过。${cot ? ` 原始判断依据(CoT): ${cot}` : ""}`
|
|
4983
|
-
: (cot || (rawPassed ? "模型判定符合筛选标准。" : "模型判定不符合筛选标准。"));
|
|
4971
|
+
const finalReason = cot || (rawPassed ? "模型判定符合筛选标准。" : "模型判定不符合筛选标准。");
|
|
4984
4972
|
return {
|
|
4985
|
-
passed:
|
|
4973
|
+
passed: rawPassed,
|
|
4986
4974
|
rawPassed,
|
|
4987
4975
|
cot: finalReason,
|
|
4988
4976
|
reason: finalReason,
|
|
@@ -4990,7 +4978,7 @@ class RecommendScreenCli {
|
|
|
4990
4978
|
evidence: parsedEvidence,
|
|
4991
4979
|
evidenceRawCount,
|
|
4992
4980
|
evidenceMatchedCount,
|
|
4993
|
-
evidenceGateDemoted
|
|
4981
|
+
evidenceGateDemoted: false
|
|
4994
4982
|
};
|
|
4995
4983
|
}
|
|
4996
4984
|
|
|
@@ -5273,22 +5261,15 @@ class RecommendScreenCli {
|
|
|
5273
5261
|
const cot = normalizeText(extractCotFromChoice(choice, parsed));
|
|
5274
5262
|
const reason = cot || (rawPassed ? "模型判定符合筛选标准。" : "模型判定不符合筛选标准。");
|
|
5275
5263
|
const summary = reason;
|
|
5276
|
-
const
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
const
|
|
5281
|
-
?
|
|
5282
|
-
:
|
|
5283
|
-
const
|
|
5284
|
-
|
|
5285
|
-
: null;
|
|
5286
|
-
const evidenceGateDemoted = evidenceGateEligible && rawPassed && (evidenceMatchedCount ?? 0) <= 0;
|
|
5287
|
-
const finalReason = evidenceGateDemoted
|
|
5288
|
-
? `模型未给出可在简历截图中引用的证据,按安全策略判为不通过。${reason ? ` 原始判断依据(CoT): ${reason}` : ""}`
|
|
5289
|
-
: reason;
|
|
5290
|
-
const passed = evidenceGateDemoted ? false : rawPassed;
|
|
5291
|
-
const enrichedReason = enrichReasonWithEvidence(finalReason, summary || finalReason, parsedEvidence, passed);
|
|
5264
|
+
const parsedEvidence = toStringArray(parsed?.evidence);
|
|
5265
|
+
const evidenceRawCount = Number.isFinite(Number(parsed?.evidenceRawCount))
|
|
5266
|
+
? Number(parsed.evidenceRawCount)
|
|
5267
|
+
: parsedEvidence.length;
|
|
5268
|
+
const evidenceMatchedCount = Number.isFinite(Number(parsed?.evidenceMatchedCount))
|
|
5269
|
+
? Number(parsed.evidenceMatchedCount)
|
|
5270
|
+
: parsedEvidence.length;
|
|
5271
|
+
const passed = rawPassed;
|
|
5272
|
+
const enrichedReason = enrichReasonWithEvidence(reason, summary || reason, parsedEvidence, passed);
|
|
5292
5273
|
return {
|
|
5293
5274
|
passed,
|
|
5294
5275
|
rawPassed,
|
|
@@ -5298,8 +5279,8 @@ class RecommendScreenCli {
|
|
|
5298
5279
|
evidence: parsedEvidence,
|
|
5299
5280
|
evidenceRawCount,
|
|
5300
5281
|
evidenceMatchedCount,
|
|
5301
|
-
evidenceGateEligible,
|
|
5302
|
-
evidenceGateDemoted
|
|
5282
|
+
evidenceGateEligible: false,
|
|
5283
|
+
evidenceGateDemoted: false
|
|
5303
5284
|
};
|
|
5304
5285
|
}
|
|
5305
5286
|
|
|
@@ -5440,20 +5421,12 @@ class RecommendScreenCli {
|
|
|
5440
5421
|
const cot = normalizeText(extractCotFromChoice(choice, parsed));
|
|
5441
5422
|
const normalizedResume = normalizeText(safeResumeText);
|
|
5442
5423
|
const normalizedResumeLower = toLowerSafe(normalizedResume);
|
|
5443
|
-
const
|
|
5444
|
-
|| Number.isFinite(Number(parsed?.evidenceRawCount))
|
|
5445
|
-
|| Number.isFinite(Number(parsed?.evidenceMatchedCount));
|
|
5446
|
-
const parsedEvidence = evidenceGateEligible ? toStringArray(parsed.evidence) : [];
|
|
5424
|
+
const parsedEvidence = toStringArray(parsed?.evidence);
|
|
5447
5425
|
const evidence = [];
|
|
5448
|
-
const
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
if (matched.matched) {
|
|
5453
|
-
evidence.push(item);
|
|
5454
|
-
} else {
|
|
5455
|
-
unmatchedEvidence.push(item);
|
|
5456
|
-
}
|
|
5426
|
+
for (const item of parsedEvidence) {
|
|
5427
|
+
const matched = matchEvidenceAgainstResume(item, safeResumeText, normalizedResume, normalizedResumeLower);
|
|
5428
|
+
if (matched.matched) {
|
|
5429
|
+
evidence.push(item);
|
|
5457
5430
|
}
|
|
5458
5431
|
}
|
|
5459
5432
|
const parsedPassed = parsePassedDecision(parsed?.passed);
|
|
@@ -5465,19 +5438,8 @@ class RecommendScreenCli {
|
|
|
5465
5438
|
`Text model response missing boolean passed decision. content=${truncateText(content, 180)}`
|
|
5466
5439
|
);
|
|
5467
5440
|
}
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
const evidenceGateDemoted = evidenceGateEligible && rawPassed && evidence.length <= 0;
|
|
5471
|
-
if (evidenceGateDemoted) {
|
|
5472
|
-
passed = false;
|
|
5473
|
-
finalReason = `模型未给出可在简历原文中校验的证据,按安全策略判为不通过。${finalReason ? ` 原始判断依据(CoT): ${finalReason}` : ""}`;
|
|
5474
|
-
if (unmatchedEvidence.length > 0) {
|
|
5475
|
-
log(
|
|
5476
|
-
`[EVIDENCE_GATE] passed=true 但证据未命中简历原文,已降级为不通过;` +
|
|
5477
|
-
`chunk=${chunkIndex}/${chunkTotal}; unmatched=${unmatchedEvidence.slice(0, 3).join(" | ")}`
|
|
5478
|
-
);
|
|
5479
|
-
}
|
|
5480
|
-
}
|
|
5441
|
+
const passed = rawPassed;
|
|
5442
|
+
const finalReason = cot || (passed ? "模型判定符合筛选标准。" : "模型判定不符合筛选标准。");
|
|
5481
5443
|
const summary = finalReason;
|
|
5482
5444
|
const enrichedReason = enrichReasonWithEvidence(finalReason, summary || finalReason, evidence, passed);
|
|
5483
5445
|
return {
|
|
@@ -5487,9 +5449,9 @@ class RecommendScreenCli {
|
|
|
5487
5449
|
reason: enrichedReason,
|
|
5488
5450
|
summary: summary || enrichedReason,
|
|
5489
5451
|
evidence,
|
|
5490
|
-
evidenceRawCount:
|
|
5491
|
-
evidenceMatchedCount:
|
|
5492
|
-
evidenceGateDemoted,
|
|
5452
|
+
evidenceRawCount: parsedEvidence.length,
|
|
5453
|
+
evidenceMatchedCount: evidence.length,
|
|
5454
|
+
evidenceGateDemoted: false,
|
|
5493
5455
|
chunkIndex,
|
|
5494
5456
|
chunkTotal
|
|
5495
5457
|
};
|
|
@@ -1695,7 +1695,7 @@ async function testCloseDetailPageShouldContinueWhenListReady() {
|
|
|
1695
1695
|
assert.equal(closed, true);
|
|
1696
1696
|
}
|
|
1697
1697
|
|
|
1698
|
-
async function
|
|
1698
|
+
async function testVisionEvidenceGateShouldKeepRawPassWithoutExplicitEvidenceProtocol() {
|
|
1699
1699
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "boss-recommend-vision-evidence-gate-"));
|
|
1700
1700
|
const cli = new RecommendScreenCli(createArgs(tempDir));
|
|
1701
1701
|
cli.prepareVisionImageSegmentsForModel = async () => ({
|
|
@@ -1713,12 +1713,75 @@ async function testVisionEvidenceGateShouldDemoteImageFallbackWithoutEvidence()
|
|
|
1713
1713
|
});
|
|
1714
1714
|
const result = await cli.callVisionModel(path.join(tempDir, "fake.png"));
|
|
1715
1715
|
assert.equal(result.rawPassed, true);
|
|
1716
|
-
assert.equal(result.passed,
|
|
1717
|
-
assert.equal(result.evidenceGateDemoted,
|
|
1716
|
+
assert.equal(result.passed, true);
|
|
1717
|
+
assert.equal(result.evidenceGateDemoted, false);
|
|
1718
1718
|
assert.equal(result.evidenceRawCount, 0);
|
|
1719
1719
|
assert.equal(result.evidenceMatchedCount, 0);
|
|
1720
1720
|
}
|
|
1721
1721
|
|
|
1722
|
+
async function testVisionEvidenceGateShouldNotDemoteWhenExplicitlyArmedWithoutEvidence() {
|
|
1723
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "boss-recommend-vision-evidence-gate-explicit-"));
|
|
1724
|
+
const cli = new RecommendScreenCli(createArgs(tempDir));
|
|
1725
|
+
cli.prepareVisionImageSegmentsForModel = async () => ({
|
|
1726
|
+
imagePaths: ["segment-1"],
|
|
1727
|
+
source: "test",
|
|
1728
|
+
sourcePixels: 100,
|
|
1729
|
+
currentPixels: 100
|
|
1730
|
+
});
|
|
1731
|
+
cli.requestVisionModel = async () => ({
|
|
1732
|
+
passed: true,
|
|
1733
|
+
rawPassed: true,
|
|
1734
|
+
reason: "matched",
|
|
1735
|
+
summary: "matched",
|
|
1736
|
+
evidence: [],
|
|
1737
|
+
evidenceGateEligible: true,
|
|
1738
|
+
evidenceRawCount: 0,
|
|
1739
|
+
evidenceMatchedCount: 0
|
|
1740
|
+
});
|
|
1741
|
+
const result = await cli.callVisionModel(path.join(tempDir, "fake.png"));
|
|
1742
|
+
assert.equal(result.rawPassed, true);
|
|
1743
|
+
assert.equal(result.passed, true);
|
|
1744
|
+
assert.equal(result.evidenceGateDemoted, false);
|
|
1745
|
+
assert.equal(result.evidenceRawCount, 0);
|
|
1746
|
+
assert.equal(result.evidenceMatchedCount, 0);
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
async function testTextModelShouldNotDemoteRawPassWhenEvidenceDoesNotMatchResume() {
|
|
1750
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "boss-recommend-text-no-demote-"));
|
|
1751
|
+
const cli = new RecommendScreenCli(createArgs(tempDir));
|
|
1752
|
+
const originalFetch = global.fetch;
|
|
1753
|
+
global.fetch = async () => ({
|
|
1754
|
+
ok: true,
|
|
1755
|
+
status: 200,
|
|
1756
|
+
async json() {
|
|
1757
|
+
return {
|
|
1758
|
+
choices: [
|
|
1759
|
+
{
|
|
1760
|
+
message: {
|
|
1761
|
+
content: JSON.stringify({
|
|
1762
|
+
passed: true,
|
|
1763
|
+
reason: "matched",
|
|
1764
|
+
summary: "matched",
|
|
1765
|
+
evidence: ["完全不在简历里的证据"]
|
|
1766
|
+
})
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
]
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
});
|
|
1773
|
+
try {
|
|
1774
|
+
const result = await cli.callTextModel("这是简历原文,没有那条证据");
|
|
1775
|
+
assert.equal(result.rawPassed, true);
|
|
1776
|
+
assert.equal(result.passed, true);
|
|
1777
|
+
assert.equal(result.evidenceGateDemoted, false);
|
|
1778
|
+
assert.equal(result.evidenceRawCount, 1);
|
|
1779
|
+
assert.equal(result.evidenceMatchedCount, 0);
|
|
1780
|
+
} finally {
|
|
1781
|
+
global.fetch = originalFetch;
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1722
1785
|
async function testVisionModelShouldSendAllOrderedChunks() {
|
|
1723
1786
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "boss-recommend-vision-all-chunks-"));
|
|
1724
1787
|
const chunkPaths = [];
|
|
@@ -1815,7 +1878,9 @@ async function main() {
|
|
|
1815
1878
|
await testTextModelShouldDefaultThinkingLowForVolcengine();
|
|
1816
1879
|
await testTextModelShouldSupportLowThinkingForVolcengine();
|
|
1817
1880
|
await testPrepareVisionImageSegmentsShouldSplitLongImage();
|
|
1818
|
-
await
|
|
1881
|
+
await testVisionEvidenceGateShouldKeepRawPassWithoutExplicitEvidenceProtocol();
|
|
1882
|
+
await testVisionEvidenceGateShouldNotDemoteWhenExplicitlyArmedWithoutEvidence();
|
|
1883
|
+
await testTextModelShouldNotDemoteRawPassWhenEvidenceDoesNotMatchResume();
|
|
1819
1884
|
await testVisionModelShouldSendAllOrderedChunks();
|
|
1820
1885
|
testRecoverablePostActionErrorShouldTreatGreetContinueAndNoButtonAsRecoverable();
|
|
1821
1886
|
await testRecoverableGreetContinueButtonShouldNotAbortWhenDetailCloseFails();
|