Stop domain-wheel collecting and age-band years in prompts. Ask until the training gate, then discriminate until convergence, then deliver a range plus a concrete follow-up. Reserve holdout only with four dated events. Co-authored-by: Cursor <cursoragent@cursor.com>
104 lines
3.4 KiB
TypeScript
104 lines
3.4 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
COLLECT_FLOW_BANNED_PHRASES,
|
|
anchoredFollowups,
|
|
collectionQuestionPool,
|
|
inviteDeclined,
|
|
moreCollectHint,
|
|
preciseGapNarration,
|
|
} from "../src/lib/rectification-agentic/v9/collection-question-pool.ts";
|
|
|
|
const educationStart = {
|
|
status: "confirmed",
|
|
domain: "education",
|
|
datePrecision: "month",
|
|
occurredFrom: "2016-09-01",
|
|
occurredTo: "2016-09-30",
|
|
eventKind: "education_start",
|
|
summary: "上大学",
|
|
} as const;
|
|
|
|
const educationEnd = {
|
|
status: "confirmed",
|
|
domain: "education",
|
|
datePrecision: "month",
|
|
occurredFrom: "2020-06-01",
|
|
occurredTo: "2020-06-30",
|
|
eventKind: "education_completion",
|
|
summary: "毕业",
|
|
} as const;
|
|
|
|
const inviteDeclinedTopic = {
|
|
target_domain: "other",
|
|
status: "declined",
|
|
intent: "collect_method_evidence",
|
|
questionId: "collect:invite:more",
|
|
target_kind: "invite_more",
|
|
};
|
|
|
|
function assertNoBanned(text: string) {
|
|
for (const phrase of COLLECT_FLOW_BANNED_PHRASES) {
|
|
assert.equal(text.includes(phrase), false, phrase);
|
|
}
|
|
}
|
|
|
|
test("invite stays first while it is producing, then drops after a decline", () => {
|
|
const first = collectionQuestionPool([educationStart]);
|
|
assert.equal(first[0]?.kind, "invite");
|
|
assert.equal(first[0]?.key, "collect:invite:more");
|
|
assert.match(first[0]?.prompt ?? "", /^还有吗?/);
|
|
const still = collectionQuestionPool([educationStart, educationEnd]);
|
|
assert.equal(still[0]?.kind, "invite");
|
|
assert.equal(inviteDeclined([inviteDeclinedTopic]), true);
|
|
const afterDecline = collectionQuestionPool(
|
|
[educationStart, educationEnd],
|
|
[inviteDeclinedTopic],
|
|
);
|
|
assert.notEqual(afterDecline[0]?.kind, "invite");
|
|
assert.equal(afterDecline.some((item) => item.kind === "invite"), false);
|
|
});
|
|
|
|
test("education completion anchors to the graduation job question from the user year", () => {
|
|
const asked = new Set<string>();
|
|
const anchors = anchoredFollowups([educationStart, educationEnd], new Set(), asked);
|
|
assert.match(anchors[0]?.prompt ?? "", /2020 年毕业后第一份工作/);
|
|
assert.doesNotMatch(anchors.map((item) => item.prompt).join("\n"), /年前后/);
|
|
const declinedAnchor = collectionQuestionPool(
|
|
[educationStart, educationEnd],
|
|
[
|
|
inviteDeclinedTopic,
|
|
{
|
|
target_domain: "career",
|
|
status: "declined",
|
|
intent: "collect_method_evidence",
|
|
questionId: "collect:anchor:education_completion:2020",
|
|
target_kind: "anchor:education_completion:2020",
|
|
},
|
|
],
|
|
);
|
|
assert.equal(
|
|
declinedAnchor.some((item) => item.key === "collect:anchor:education_completion:2020"),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test("precise gap names the recorded events and at least two uncovered kinds", () => {
|
|
const gap = preciseGapNarration([educationStart, educationEnd], [inviteDeclinedTopic]);
|
|
assert.match(gap, /现在记下的是/);
|
|
assert.match(gap, /2016 年 9 月上大学/);
|
|
assert.match(gap, /2020 年 6 月毕业/);
|
|
assert.match(gap, /都是上学的事/);
|
|
assert.match(gap, /就能开始筛/);
|
|
const examples = (gap.match(/第一份工作|搬到别的城市|谈恋爱|家里添丁|生病受伤/g) ?? []).length;
|
|
assert.ok(examples >= 2, gap);
|
|
assertNoBanned(gap);
|
|
});
|
|
|
|
test("more-collect hint after delivery never uses banned collect-flow phrases", () => {
|
|
const hint = moreCollectHint([educationStart, educationEnd], [inviteDeclinedTopic]);
|
|
assert.match(hint, /范围还能再收一截/);
|
|
assertNoBanned(hint);
|
|
});
|