@chatman-media/kb 1.3.1 → 1.5.0
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/dist/answer-types.d.ts +19 -0
- package/dist/answer-types.d.ts.map +1 -1
- package/dist/answer.d.ts.map +1 -1
- package/dist/extract-user-facts.test.d.ts +2 -0
- package/dist/extract-user-facts.test.d.ts.map +1 -0
- package/dist/fact-checker.test.d.ts +2 -0
- package/dist/fact-checker.test.d.ts.map +1 -0
- package/dist/grade-skills.test.d.ts +2 -0
- package/dist/grade-skills.test.d.ts.map +1 -0
- package/dist/index.js +17 -5
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.test.d.ts +2 -0
- package/dist/prompt.test.d.ts.map +1 -0
- package/dist/reflect.test.d.ts +2 -0
- package/dist/reflect.test.d.ts.map +1 -0
- package/dist/rewrite-query.test.d.ts +2 -0
- package/dist/rewrite-query.test.d.ts.map +1 -0
- package/dist/styles.d.ts +20 -0
- package/dist/styles.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/answer-types.ts +16 -0
- package/src/answer.ts +6 -0
- package/src/extract-user-facts.test.ts +44 -0
- package/src/fact-checker.test.ts +66 -0
- package/src/grade-skills.test.ts +39 -0
- package/src/prompt.test.ts +71 -0
- package/src/prompt.ts +21 -4
- package/src/reflect.test.ts +44 -0
- package/src/rewrite-query.test.ts +56 -0
- package/src/styles.ts +17 -0
package/dist/answer-types.d.ts
CHANGED
|
@@ -54,6 +54,25 @@ export interface AnswerInput {
|
|
|
54
54
|
* uses a calm FAQ-support block instead — answering questions without selling.
|
|
55
55
|
*/
|
|
56
56
|
supportPhase?: "docs" | "submitted";
|
|
57
|
+
/**
|
|
58
|
+
* Per-stage override (Phase 2 C-2): the lead's current funnel-stage goal/
|
|
59
|
+
* guidance from stage_definitions. Takes precedence over the Style's
|
|
60
|
+
* per-sales-stage config when composing the prompt.
|
|
61
|
+
*/
|
|
62
|
+
stageOverride?: {
|
|
63
|
+
goal: string;
|
|
64
|
+
guidance?: string;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Dynamic per-request context (R4 / multi-request): the guest's current
|
|
68
|
+
* request_type + how many open requests. Injected as a separate prompt block.
|
|
69
|
+
*/
|
|
70
|
+
requestContext?: string;
|
|
71
|
+
/**
|
|
72
|
+
* The guest's current lead is on an `awaiting_operator` stage (R5): the bot
|
|
73
|
+
* holds and defers pricing/decisions to a human operator.
|
|
74
|
+
*/
|
|
75
|
+
awaitingOperator?: boolean;
|
|
57
76
|
/**
|
|
58
77
|
* Called after every `answerWithRag` or `answerWithRagStream` call with the
|
|
59
78
|
* final telemetry. Useful for logging, metrics, or A/B experiment recording
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"answer-types.d.ts","sourceRoot":"","sources":["../src/answer-types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"answer-types.d.ts","sourceRoot":"","sources":["../src/answer-types.ts"],"names":[],"mappings":"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"}
|
package/dist/answer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../src/answer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,YAAY,EAGjB,KAAK,OAAO,EACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AA8BzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,iBAAiB,EACjB,KAAK,OAAO,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,0BAA0B,EAC1B,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EACL,iBAAiB,EACjB,4BAA4B,EAC5B,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAI5B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oEAAoE;IACpE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CAgG/E;
|
|
1
|
+
{"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../src/answer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,YAAY,EAGjB,KAAK,OAAO,EACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AA8BzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,iBAAiB,EACjB,KAAK,OAAO,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,0BAA0B,EAC1B,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EACL,iBAAiB,EACjB,4BAA4B,EAC5B,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAI5B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oEAAoE;IACpE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CAgG/E;AA8OD;;;;;;;;GAQG;AACH,wBAAuB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,CA6HpF;AAED,wBAAsB,aAAa,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EACxD,KAAK,EAAE,WAAW,GAAG;IAAE,YAAY,EAAE,CAAC,CAAA;CAAE,GACvC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrC,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;AAiG/E;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CAAC,KAAK,EAAE;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;CACzB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8BlB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-user-facts.test.d.ts","sourceRoot":"","sources":["../src/extract-user-facts.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fact-checker.test.d.ts","sourceRoot":"","sources":["../src/fact-checker.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grade-skills.test.d.ts","sourceRoot":"","sources":["../src/grade-skills.test.ts"],"names":[],"mappings":""}
|
package/dist/index.js
CHANGED
|
@@ -45100,10 +45100,14 @@ ${triggerLine}${h.body}`;
|
|
|
45100
45100
|
const skillsBlock = skillsForStage.length ? `\u041F\u0420\u0418\u0401\u041C\u042B (\u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 \u0443\u043C\u0435\u0441\u0442\u043D\u044B\u0435, \u043D\u0435 \u0432\u0441\u0435 \u0441\u0440\u0430\u0437\u0443 \u2014 \u0432\u044B\u0431\u0438\u0440\u0430\u0439 \u043F\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0443):
|
|
45101
45101
|
` + skillsForStage.map((s) => `- ${s.displayName} \u2014 ${s.promptFragment}`).join(`
|
|
45102
45102
|
`) : "";
|
|
45103
|
-
const
|
|
45104
|
-
|
|
45105
|
-
\u041A\u0410\
|
|
45103
|
+
const effGoal = options.stageOverride?.goal ?? stageCfg?.goal;
|
|
45104
|
+
const effGuidance = options.stageOverride?.guidance ?? stageCfg?.guidance;
|
|
45105
|
+
const stageBlock = effGoal ? `\u0422\u0415\u041A\u0423\u0429\u0418\u0419 \u042D\u0422\u0410\u041F: ${stage.toUpperCase()}.
|
|
45106
|
+
` + `\u0426\u0415\u041B\u042C \u042D\u0422\u0410\u041F\u0410: ${effGoal}.` + (effGuidance ? `
|
|
45107
|
+
\u041A\u0410\u041A: ${effGuidance}` : "") + (stageCfg?.groundingRequired ? `
|
|
45106
45108
|
GROUNDING: \u043D\u0430 \u044D\u0442\u043E\u043C \u044D\u0442\u0430\u043F\u0435 \u0432\u0441\u0435 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u044B\u0435 \u0444\u0430\u043A\u0442\u044B (\u0446\u0438\u0444\u0440\u044B, \u0441\u0443\u043C\u043C\u044B, \u0441\u0440\u043E\u043A\u0438) \u0431\u0435\u0440\u0438 \u0422\u041E\u041B\u042C\u041A\u041E \u0438\u0437 \u0441\u0435\u043A\u0446\u0438\u0438 KB CONTEXT \u043D\u0438\u0436\u0435. \u0415\u0441\u043B\u0438 \u0435\u0451 \u043D\u0435\u0442 \u0438\u043B\u0438 \u043D\u0443\u0436\u043D\u043E\u0433\u043E \u0444\u0430\u043A\u0442\u0430 \u0432 \u043D\u0435\u0439 \u043D\u0435\u0442 \u2014 \u043D\u0435 \u0432\u044B\u0434\u0443\u043C\u044B\u0432\u0430\u0439, \u0441\u043A\u0430\u0436\u0438 \u0447\u0442\u043E \u0443\u0442\u043E\u0447\u043D\u0438\u0448\u044C.` : "") : `\u0422\u0415\u041A\u0423\u0429\u0418\u0419 \u042D\u0422\u0410\u041F: ${stage}. (\u0421\u043F\u0435\u0446\u0438\u0444\u0438\u0447\u0435\u0441\u043A\u0438\u0445 \u043F\u0440\u0430\u0432\u0438\u043B \u0434\u043B\u044F \u044D\u0442\u0430\u043F\u0430 \u043D\u0435\u0442 \u2014 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 \u043E\u0431\u0449\u0438\u0439 \u0441\u0442\u0438\u043B\u044C.)`;
|
|
45109
|
+
const requestBlock = options.requestContext ? `\u0417\u0410\u041F\u0420\u041E\u0421 \u0413\u041E\u0421\u0422\u042F: ${options.requestContext}` : "";
|
|
45110
|
+
const operatorBlock = options.awaitingOperator ? `\u041E\u0416\u0418\u0414\u0410\u041D\u0418\u0415 \u041E\u041F\u0415\u0420\u0410\u0422\u041E\u0420\u0410: \u043D\u0430 \u044D\u0442\u043E\u0439 \u0441\u0442\u0430\u0434\u0438\u0438 \u0443\u0441\u043B\u043E\u0432\u0438\u044F/\u0446\u0435\u043D\u0443/\u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u0433\u043E\u0442\u043E\u0432\u0438\u0442 \u043A\u043E\u043B\u043B\u0435\u0433\u0430-\u0447\u0435\u043B\u043E\u0432\u0435\u043A. \u041D\u0415 \u0432\u044B\u0434\u0443\u043C\u044B\u0432\u0430\u0439 \u0447\u0438\u0441\u043B\u0430 \u0438 \u0434\u0435\u0442\u0430\u043B\u0438. \u0421\u043A\u0430\u0436\u0438 \u0433\u043E\u0441\u0442\u044E \u043A\u043E\u0440\u043E\u0442\u043A\u043E, \u0447\u0442\u043E \u0443\u0442\u043E\u0447\u043D\u044F\u0435\u0448\u044C \u0438 \u0432\u0435\u0440\u043D\u0451\u0448\u044C\u0441\u044F \u0441 \u043E\u0442\u0432\u0435\u0442\u043E\u043C \u2014 \u0438 \u0436\u0434\u0438, \u043D\u0435 \u0434\u0430\u0432\u0438 \u0438 \u043D\u0435 \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u0439 \u0441\u0434\u0435\u043B\u043A\u0443.` : "";
|
|
45107
45111
|
const minorRule = guardrails.noMinors ? "- \u0415\u0441\u043B\u0438 prospect <18 \u043B\u0435\u0442 \u2014 \u0432\u0435\u0436\u043B\u0438\u0432\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0434\u0438\u0430\u043B\u043E\u0433." : "";
|
|
45108
45112
|
const topicsRule = guardrails.forbiddenTopics.length ? `- \u0417\u0430\u043F\u0440\u0435\u0449\u0451\u043D\u043D\u044B\u0435 \u0442\u0435\u043C\u044B: ${guardrails.forbiddenTopics.join(", ")}.` : "";
|
|
45109
45113
|
const brevityRule = persona.role === "human" ? `- \u041F\u0438\u0448\u0438 \u043A\u0430\u043A \u0432 \u0436\u0438\u0432\u043E\u043C \u0447\u0430\u0442\u0435: 2\u20136 \u043A\u043E\u0440\u043E\u0442\u043A\u0438\u0445 \u0444\u0440\u0430\u0437 \u043C\u043E\u0436\u043D\u043E, \u0435\u0441\u043B\u0438 \u043D\u0443\u0436\u043D\u043E \u043F\u0435\u0440\u0435\u0434\u0430\u0442\u044C \u0443\u0441\u043B\u043E\u0432\u0438\u044F. \u0411\u0435\u0437 markdown-\u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u043E\u0432. ` + `\u0421\u043F\u0438\u0441\u043A\u043E\u043C \u0441 \u043D\u043E\u043C\u0435\u0440\u0430\u043C\u0438 \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0441\u0430\u043C \u043F\u0440\u043E\u0441\u0438\u0442 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443.` : `- \u041F\u0438\u0448\u0438 \u043A\u043E\u0440\u043E\u0442\u043A\u043E: 1-3 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u044F. \u0411\u0435\u0437 markdown-\u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u043E\u0432 \u0438 \u043D\u0443\u043C\u0435\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0445 \u0441\u043F\u0438\u0441\u043A\u043E\u0432.`;
|
|
@@ -45130,6 +45134,8 @@ ${preFetchedKbContext}` : "";
|
|
|
45130
45134
|
support ? "" : directorHooksBlock,
|
|
45131
45135
|
support ? "" : skillsBlock,
|
|
45132
45136
|
support || stageBlock,
|
|
45137
|
+
operatorBlock,
|
|
45138
|
+
requestBlock,
|
|
45133
45139
|
summaryBlock,
|
|
45134
45140
|
userFactsBlock,
|
|
45135
45141
|
needsGroundingReminder ? kbGroundingReminder(persona.role) : "",
|
|
@@ -59905,7 +59911,10 @@ ${kbContextStr}` : vacBlock : kbContextStr;
|
|
|
59905
59911
|
...input.conversationSummary ? { conversationSummary: input.conversationSummary } : {},
|
|
59906
59912
|
...input.skills && input.skills.length > 0 ? { skills: input.skills } : {},
|
|
59907
59913
|
...input.directorHooks && input.directorHooks.length > 0 ? { directorHooks: input.directorHooks } : {},
|
|
59908
|
-
...input.supportPhase ? { supportPhase: input.supportPhase } : {}
|
|
59914
|
+
...input.supportPhase ? { supportPhase: input.supportPhase } : {},
|
|
59915
|
+
...input.stageOverride ? { stageOverride: input.stageOverride } : {},
|
|
59916
|
+
...input.requestContext ? { requestContext: input.requestContext } : {},
|
|
59917
|
+
...input.awaitingOperator ? { awaitingOperator: true } : {}
|
|
59909
59918
|
});
|
|
59910
59919
|
temperature = input.style.model.temperature;
|
|
59911
59920
|
} else {
|
|
@@ -60092,7 +60101,10 @@ ${kbContextStr}` : vacBlock : kbContextStr;
|
|
|
60092
60101
|
...input.conversationSummary ? { conversationSummary: input.conversationSummary } : {},
|
|
60093
60102
|
...input.skills && input.skills.length > 0 ? { skills: input.skills } : {},
|
|
60094
60103
|
...input.directorHooks && input.directorHooks.length > 0 ? { directorHooks: input.directorHooks } : {},
|
|
60095
|
-
...input.supportPhase ? { supportPhase: input.supportPhase } : {}
|
|
60104
|
+
...input.supportPhase ? { supportPhase: input.supportPhase } : {},
|
|
60105
|
+
...input.stageOverride ? { stageOverride: input.stageOverride } : {},
|
|
60106
|
+
...input.requestContext ? { requestContext: input.requestContext } : {},
|
|
60107
|
+
...input.awaitingOperator ? { awaitingOperator: true } : {}
|
|
60096
60108
|
});
|
|
60097
60109
|
temperature = input.style.model.temperature;
|
|
60098
60110
|
} else {
|
package/dist/prompt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAQ,KAAK,EAAE,MAAM,aAAa,CAAC;AA6C5E;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,WAAW,EAClB,mBAAmB,GAAE,MAAM,GAAG,IAAW,EACzC,OAAO,GAAE,cAAmB,GAC3B,MAAM,
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAQ,KAAK,EAAE,MAAM,aAAa,CAAC;AA6C5E;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,WAAW,EAClB,mBAAmB,GAAE,MAAM,GAAG,IAAW,EACzC,OAAO,GAAE,cAAmB,GAC3B,MAAM,CAsJR"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.test.d.ts","sourceRoot":"","sources":["../src/prompt.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reflect.test.d.ts","sourceRoot":"","sources":["../src/reflect.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rewrite-query.test.d.ts","sourceRoot":"","sources":["../src/rewrite-query.test.ts"],"names":[],"mappings":""}
|
package/dist/styles.d.ts
CHANGED
|
@@ -182,5 +182,25 @@ export interface ComposeOptions {
|
|
|
182
182
|
* block instead. `docs` = collecting their documents, `submitted` = filed.
|
|
183
183
|
*/
|
|
184
184
|
supportPhase?: "docs" | "submitted";
|
|
185
|
+
/**
|
|
186
|
+
* Per-stage override (Phase 2): goal/guidance from the lead's current funnel
|
|
187
|
+
* stage_definition. Takes precedence over `style.stages[stage]` for the stage
|
|
188
|
+
* block. Lets AI-built funnels carry per-stage instructions.
|
|
189
|
+
*/
|
|
190
|
+
stageOverride?: {
|
|
191
|
+
goal: string;
|
|
192
|
+
guidance?: string;
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Динамический контекст текущего запроса гостя (multi-request / concierge):
|
|
196
|
+
* какой тип запроса ведётся + сколько открыто. Инжектится блоком «ЗАПРОС
|
|
197
|
+
* ГОСТЯ». null/absent для линейных вертикалей.
|
|
198
|
+
*/
|
|
199
|
+
requestContext?: string;
|
|
200
|
+
/**
|
|
201
|
+
* Лид стоит на стадии awaiting_operator (R5): цену/условия даёт человек-оператор.
|
|
202
|
+
* Бот придерживает гостя и не выдумывает детали. Блок «ОЖИДАНИЕ ОПЕРАТОРА».
|
|
203
|
+
*/
|
|
204
|
+
awaitingOperator?: boolean;
|
|
185
205
|
}
|
|
186
206
|
//# sourceMappingURL=styles.d.ts.map
|
package/dist/styles.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,aAAa,+DAAgE,CAAC;AAC3F,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,eAAO,MAAM,gBAAgB,2DAA4D,CAAC;AAC1F,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D,eAAO,MAAM,UAAU,2FAOb,CAAC;AACX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,eAAO,MAAM,UAAU;;;;;;;;;;iBAGrB,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C,eAAO,MAAM,iBAAiB;;;;;iBAK5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,aAAa;;;;;;;;iBAKxB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEzD,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsCtB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,MAAM,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IACnC;;;;OAIG;IACH,aAAa,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACjD;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,aAAa,+DAAgE,CAAC;AAC3F,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,eAAO,MAAM,gBAAgB,2DAA4D,CAAC;AAC1F,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D,eAAO,MAAM,UAAU,2FAOb,CAAC;AACX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,eAAO,MAAM,UAAU;;;;;;;;;;iBAGrB,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C,eAAO,MAAM,iBAAiB;;;;;iBAK5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,aAAa;;;;;;;;iBAKxB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEzD,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsCtB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,MAAM,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IACnC;;;;OAIG;IACH,aAAa,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACjD;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IACpC;;;;OAIG;IACH,aAAa,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatman-media/kb",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Tenant-scoped Knowledge Base: hybrid retrieval (pgvector + BM25), ingest, answer pipeline, persona/skill composition. LLM I/O живёт в @chatman-media/llm-router.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"url": "https://github.com/chatman-media/lead-engine/issues"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@chatman-media/llm-router": "1.1.
|
|
59
|
+
"@chatman-media/llm-router": "1.1.1",
|
|
60
60
|
"unpdf": "^1.6.2",
|
|
61
61
|
"zod": "^4.4.1"
|
|
62
62
|
},
|
package/src/answer-types.ts
CHANGED
|
@@ -57,6 +57,22 @@ export interface AnswerInput {
|
|
|
57
57
|
* uses a calm FAQ-support block instead — answering questions without selling.
|
|
58
58
|
*/
|
|
59
59
|
supportPhase?: "docs" | "submitted";
|
|
60
|
+
/**
|
|
61
|
+
* Per-stage override (Phase 2 C-2): the lead's current funnel-stage goal/
|
|
62
|
+
* guidance from stage_definitions. Takes precedence over the Style's
|
|
63
|
+
* per-sales-stage config when composing the prompt.
|
|
64
|
+
*/
|
|
65
|
+
stageOverride?: { goal: string; guidance?: string };
|
|
66
|
+
/**
|
|
67
|
+
* Dynamic per-request context (R4 / multi-request): the guest's current
|
|
68
|
+
* request_type + how many open requests. Injected as a separate prompt block.
|
|
69
|
+
*/
|
|
70
|
+
requestContext?: string;
|
|
71
|
+
/**
|
|
72
|
+
* The guest's current lead is on an `awaiting_operator` stage (R5): the bot
|
|
73
|
+
* holds and defers pricing/decisions to a human operator.
|
|
74
|
+
*/
|
|
75
|
+
awaitingOperator?: boolean;
|
|
60
76
|
/**
|
|
61
77
|
* Called after every `answerWithRag` or `answerWithRagStream` call with the
|
|
62
78
|
* final telemetry. Useful for logging, metrics, or A/B experiment recording
|
package/src/answer.ts
CHANGED
|
@@ -234,6 +234,9 @@ async function answerFromHits(opts: {
|
|
|
234
234
|
? { directorHooks: input.directorHooks }
|
|
235
235
|
: {}),
|
|
236
236
|
...(input.supportPhase ? { supportPhase: input.supportPhase } : {}),
|
|
237
|
+
...(input.stageOverride ? { stageOverride: input.stageOverride } : {}),
|
|
238
|
+
...(input.requestContext ? { requestContext: input.requestContext } : {}),
|
|
239
|
+
...(input.awaitingOperator ? { awaitingOperator: true } : {}),
|
|
237
240
|
});
|
|
238
241
|
temperature = input.style.model.temperature;
|
|
239
242
|
} else {
|
|
@@ -503,6 +506,9 @@ export async function* answerWithRagStream(input: AnswerInput): AsyncIterable<st
|
|
|
503
506
|
? { directorHooks: input.directorHooks }
|
|
504
507
|
: {}),
|
|
505
508
|
...(input.supportPhase ? { supportPhase: input.supportPhase } : {}),
|
|
509
|
+
...(input.stageOverride ? { stageOverride: input.stageOverride } : {}),
|
|
510
|
+
...(input.requestContext ? { requestContext: input.requestContext } : {}),
|
|
511
|
+
...(input.awaitingOperator ? { awaitingOperator: true } : {}),
|
|
506
512
|
});
|
|
507
513
|
temperature = input.style.model.temperature;
|
|
508
514
|
} else {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ChatClient, ChatMessage } from "@chatman-media/llm-router";
|
|
2
|
+
import { describe, expect, it } from "bun:test";
|
|
3
|
+
import { extractUserFacts, parseFactsFromLlmOutput } from "./extract-user-facts.ts";
|
|
4
|
+
|
|
5
|
+
const chat = (text: string): ChatClient => ({ complete: async () => text }) as unknown as ChatClient;
|
|
6
|
+
const chatThrows = (): ChatClient =>
|
|
7
|
+
({ complete: async () => { throw new Error("x"); } }) as unknown as ChatClient;
|
|
8
|
+
|
|
9
|
+
describe("parseFactsFromLlmOutput", () => {
|
|
10
|
+
it("нет {} → {}", () => {
|
|
11
|
+
expect(parseFactsFromLlmOutput("no json")).toEqual({});
|
|
12
|
+
});
|
|
13
|
+
it("битый JSON → {}", () => {
|
|
14
|
+
expect(parseFactsFromLlmOutput("{broken")).toEqual({});
|
|
15
|
+
});
|
|
16
|
+
it("массив → {}", () => {
|
|
17
|
+
expect(parseFactsFromLlmOutput("[1,2]")).toEqual({});
|
|
18
|
+
});
|
|
19
|
+
it("валидный объект + coercion числовых значений", () => {
|
|
20
|
+
expect(parseFactsFromLlmOutput('{"name":"Аня","age":23}')).toEqual({ name: "Аня", age: "23" });
|
|
21
|
+
});
|
|
22
|
+
it("пустые / слишком длинные значения и длинные ключи отбрасываются", () => {
|
|
23
|
+
const longVal = "x".repeat(300);
|
|
24
|
+
const longKey = "k".repeat(50);
|
|
25
|
+
const out = parseFactsFromLlmOutput(`{"city":" ","name":"Аня","big":"${longVal}","${longKey}":"v"}`);
|
|
26
|
+
expect(out).toEqual({ name: "Аня" });
|
|
27
|
+
});
|
|
28
|
+
it("code-fence/think снимаются", () => {
|
|
29
|
+
expect(parseFactsFromLlmOutput('```json\n{"name":"Ира"}\n```')).toEqual({ name: "Ира" });
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("extractUserFacts", () => {
|
|
34
|
+
const msgs: ChatMessage[] = [{ role: "user", content: "я Аня, мне 23" }];
|
|
35
|
+
it("нет user-сообщений → {} без LLM", async () => {
|
|
36
|
+
expect(await extractUserFacts({ messages: [{ role: "assistant", content: "hi" }], chat: chatThrows() })).toEqual({});
|
|
37
|
+
});
|
|
38
|
+
it("парсит факты из LLM", async () => {
|
|
39
|
+
expect(await extractUserFacts({ messages: msgs, chat: chat('{"name":"Аня","age":"23"}') })).toEqual({ name: "Аня", age: "23" });
|
|
40
|
+
});
|
|
41
|
+
it("LLM упал → {}", async () => {
|
|
42
|
+
expect(await extractUserFacts({ messages: msgs, chat: chatThrows() })).toEqual({});
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ChatClient } from "@chatman-media/llm-router";
|
|
2
|
+
import { afterEach, describe, expect, it } from "bun:test";
|
|
3
|
+
import { checkFacts, parseFactCheckResult } from "./fact-checker.ts";
|
|
4
|
+
|
|
5
|
+
const chat = (text: string): ChatClient => ({ complete: async () => text }) as unknown as ChatClient;
|
|
6
|
+
const chatThrows = (): ChatClient =>
|
|
7
|
+
({ complete: async () => { throw new Error("down"); } }) as unknown as ChatClient;
|
|
8
|
+
|
|
9
|
+
const savedEnv = process.env.RAG_FACT_CHECKER_FAIL_OPEN;
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
if (savedEnv === undefined) delete process.env.RAG_FACT_CHECKER_FAIL_OPEN;
|
|
12
|
+
else process.env.RAG_FACT_CHECKER_FAIL_OPEN = savedEnv;
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe("parseFactCheckResult", () => {
|
|
16
|
+
it("чистый JSON", () => {
|
|
17
|
+
expect(parseFactCheckResult('{"grounded":false,"vacancyOk":true,"reason":"x"}')).toEqual({
|
|
18
|
+
grounded: false,
|
|
19
|
+
vacancyOk: true,
|
|
20
|
+
reason: "x",
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
it("code-fence + think убираются", () => {
|
|
24
|
+
expect(parseFactCheckResult('<think>..</think>```json\n{"grounded":true,"vacancyOk":false}\n```')).toMatchObject({
|
|
25
|
+
grounded: true,
|
|
26
|
+
vacancyOk: false,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
it("нет {} → OK (fail-open парсинга)", () => {
|
|
30
|
+
expect(parseFactCheckResult("no json")).toEqual({ grounded: true, vacancyOk: true });
|
|
31
|
+
});
|
|
32
|
+
it("битый JSON → OK", () => {
|
|
33
|
+
expect(parseFactCheckResult("{broken")).toEqual({ grounded: true, vacancyOk: true });
|
|
34
|
+
});
|
|
35
|
+
it("non-bool поля → дефолт true", () => {
|
|
36
|
+
expect(parseFactCheckResult('{"grounded":"yes"}')).toEqual({ grounded: true, vacancyOk: true });
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("checkFacts", () => {
|
|
41
|
+
const base = { question: "q", answer: "Зарплата 1500$", context: "контекст 1500$" };
|
|
42
|
+
it("пустой ответ → OK без вызова LLM", async () => {
|
|
43
|
+
expect(await checkFacts({ ...base, answer: " ", chat: chatThrows() })).toEqual({ grounded: true, vacancyOk: true });
|
|
44
|
+
});
|
|
45
|
+
it("нет контекста и вакансий → OK", async () => {
|
|
46
|
+
expect(await checkFacts({ ...base, context: "", chat: chatThrows() })).toEqual({ grounded: true, vacancyOk: true });
|
|
47
|
+
});
|
|
48
|
+
it("есть контекст → парсит вердикт LLM", async () => {
|
|
49
|
+
const r = await checkFacts({ ...base, chat: chat('{"grounded":false,"vacancyOk":true,"reason":"выдумал"}') });
|
|
50
|
+
expect(r.grounded).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
it("vacancies-ветка промпта", async () => {
|
|
53
|
+
const r = await checkFacts({ ...base, vacanciesBlock: "Вакансия: 2000$", chat: chat('{"grounded":true,"vacancyOk":false,"reason":"не совпало"}') });
|
|
54
|
+
expect(r.vacancyOk).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
it("LLM упал → fail-closed по умолчанию", async () => {
|
|
57
|
+
delete process.env.RAG_FACT_CHECKER_FAIL_OPEN;
|
|
58
|
+
const r = await checkFacts({ ...base, chat: chatThrows() });
|
|
59
|
+
expect(r.grounded).toBe(false);
|
|
60
|
+
expect(r.reason).toContain("checker_error");
|
|
61
|
+
});
|
|
62
|
+
it("LLM упал + FAIL_OPEN=1 → пропускаем", async () => {
|
|
63
|
+
process.env.RAG_FACT_CHECKER_FAIL_OPEN = "1";
|
|
64
|
+
expect(await checkFacts({ ...base, chat: chatThrows() })).toEqual({ grounded: true, vacancyOk: true });
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ChatClient } from "@chatman-media/llm-router";
|
|
2
|
+
import { describe, expect, it } from "bun:test";
|
|
3
|
+
import { gradeSkills, parseSlugList } from "./grade-skills.ts";
|
|
4
|
+
|
|
5
|
+
const chat = (text: string): ChatClient => ({ complete: async () => text }) as unknown as ChatClient;
|
|
6
|
+
const chatThrows = (): ChatClient =>
|
|
7
|
+
({ complete: async () => { throw new Error("x"); } }) as unknown as ChatClient;
|
|
8
|
+
const ALLOWED = ["mirroring", "scarcity-spots-left", "tactical-empathy"] as const;
|
|
9
|
+
|
|
10
|
+
describe("parseSlugList", () => {
|
|
11
|
+
it("пусто → []", () => {
|
|
12
|
+
expect(parseSlugList("", ALLOWED)).toEqual([]);
|
|
13
|
+
});
|
|
14
|
+
it("JSON-массив, фильтр по allowed", () => {
|
|
15
|
+
expect(parseSlugList('["mirroring","unknown-skill","tactical-empathy"]', ALLOWED)).toEqual(["mirroring", "tactical-empathy"]);
|
|
16
|
+
});
|
|
17
|
+
it("code-fenced JSON", () => {
|
|
18
|
+
expect(parseSlugList('```json\n["scarcity-spots-left"]\n```', ALLOWED)).toEqual(["scarcity-spots-left"]);
|
|
19
|
+
});
|
|
20
|
+
it("comma/newline текст → fallback-парсинг", () => {
|
|
21
|
+
expect(parseSlugList("mirroring, tactical-empathy", ALLOWED)).toEqual(["mirroring", "tactical-empathy"]);
|
|
22
|
+
});
|
|
23
|
+
it("всё неразрешённое → []", () => {
|
|
24
|
+
expect(parseSlugList('["foo","bar"]', ALLOWED)).toEqual([]);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("gradeSkills", () => {
|
|
29
|
+
const base = { question: "q", reply: "r", availableSlugs: ALLOWED };
|
|
30
|
+
it("пустой availableSlugs → [] без LLM", async () => {
|
|
31
|
+
expect(await gradeSkills({ ...base, availableSlugs: [], chat: chatThrows() })).toEqual([]);
|
|
32
|
+
});
|
|
33
|
+
it("парсит и фильтрует ответ LLM", async () => {
|
|
34
|
+
expect(await gradeSkills({ ...base, chat: chat('["mirroring","x"]') })).toEqual(["mirroring"]);
|
|
35
|
+
});
|
|
36
|
+
it("LLM упал → [] (failure-soft)", async () => {
|
|
37
|
+
expect(await gradeSkills({ ...base, chat: chatThrows() })).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Unit tests for kb composeSystemPrompt — the prompt builder used by the reply
|
|
2
|
+
// pipeline (answer.ts). Focus: Phase 2 C-2 `stageOverride` precedence (the
|
|
3
|
+
// lead's funnel-stage goal/guidance wins over the Style's per-sales-stage cfg).
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from "bun:test";
|
|
6
|
+
import { composeSystemPrompt } from "./prompt.ts";
|
|
7
|
+
import type { Style } from "./styles.ts";
|
|
8
|
+
|
|
9
|
+
const baseStyle: Style = {
|
|
10
|
+
slug: "t",
|
|
11
|
+
displayName: "T",
|
|
12
|
+
persona: { name: "Алекс", role: "human" },
|
|
13
|
+
voice: { tone: "friendly", language: "ru", forbid: [] },
|
|
14
|
+
framework: "SPIN",
|
|
15
|
+
hooks: [],
|
|
16
|
+
stages: { qualify: { goal: "STYLE_GOAL", groundingRequired: false } },
|
|
17
|
+
fewShot: [],
|
|
18
|
+
guardrails: { noMinors: true, botDisclosureOnDirectQuestion: true, forbiddenTopics: [] },
|
|
19
|
+
model: { id: "x", temperature: 0.5, maxTokens: 100 },
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe("composeSystemPrompt — stageOverride (Phase 2 C-2)", () => {
|
|
23
|
+
it("без override → goal берётся из Style", () => {
|
|
24
|
+
const p = composeSystemPrompt(baseStyle, "qualify");
|
|
25
|
+
expect(p).toContain("STYLE_GOAL");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("stageOverride имеет приоритет над Style для goal и guidance", () => {
|
|
29
|
+
const p = composeSystemPrompt(baseStyle, "qualify", null, {
|
|
30
|
+
stageOverride: { goal: "OVERRIDE_GOAL", guidance: "OVERRIDE_GUIDE" },
|
|
31
|
+
});
|
|
32
|
+
expect(p).toContain("OVERRIDE_GOAL");
|
|
33
|
+
expect(p).toContain("OVERRIDE_GUIDE");
|
|
34
|
+
expect(p).not.toContain("STYLE_GOAL");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("stageOverride работает для стадии без конфига в Style", () => {
|
|
38
|
+
// "close" нет в baseStyle.stages → раньше был бы generic-блок; теперь override.
|
|
39
|
+
const p = composeSystemPrompt(baseStyle, "close", null, {
|
|
40
|
+
stageOverride: { goal: "CLOSE_GOAL" },
|
|
41
|
+
});
|
|
42
|
+
expect(p).toContain("CLOSE_GOAL");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("composeSystemPrompt — requestContext (R4 multi-request)", () => {
|
|
47
|
+
it("requestContext → блок «ЗАПРОС ГОСТЯ» в промпте", () => {
|
|
48
|
+
const p = composeSystemPrompt(baseStyle, "qualify", null, {
|
|
49
|
+
requestContext: "гость сейчас ведёт запрос «Трансфер».",
|
|
50
|
+
});
|
|
51
|
+
expect(p).toContain("ЗАПРОС ГОСТЯ:");
|
|
52
|
+
expect(p).toContain("«Трансфер»");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("без requestContext → блока нет", () => {
|
|
56
|
+
const p = composeSystemPrompt(baseStyle, "qualify");
|
|
57
|
+
expect(p).not.toContain("ЗАПРОС ГОСТЯ:");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("composeSystemPrompt — awaitingOperator (R5)", () => {
|
|
62
|
+
it("awaitingOperator → блок «ОЖИДАНИЕ ОПЕРАТОРА»", () => {
|
|
63
|
+
const p = composeSystemPrompt(baseStyle, "close", null, { awaitingOperator: true });
|
|
64
|
+
expect(p).toContain("ОЖИДАНИЕ ОПЕРАТОРА");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("без флага → блока нет", () => {
|
|
68
|
+
const p = composeSystemPrompt(baseStyle, "close");
|
|
69
|
+
expect(p).not.toContain("ОЖИДАНИЕ ОПЕРАТОРА");
|
|
70
|
+
});
|
|
71
|
+
});
|
package/src/prompt.ts
CHANGED
|
@@ -121,15 +121,30 @@ export function composeSystemPrompt(
|
|
|
121
121
|
skillsForStage.map((s) => `- ${s.displayName} — ${s.promptFragment}`).join("\n")
|
|
122
122
|
: "";
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
// Per-stage instructions: the lead's funnel-stage override (Phase 2) takes
|
|
125
|
+
// precedence over the Style's per-sales-stage config; grounding stays from style.
|
|
126
|
+
const effGoal = options.stageOverride?.goal ?? stageCfg?.goal;
|
|
127
|
+
const effGuidance = options.stageOverride?.guidance ?? stageCfg?.guidance;
|
|
128
|
+
const stageBlock = effGoal
|
|
125
129
|
? `ТЕКУЩИЙ ЭТАП: ${stage.toUpperCase()}.\n` +
|
|
126
|
-
`ЦЕЛЬ ЭТАПА: ${
|
|
127
|
-
(
|
|
128
|
-
(stageCfg
|
|
130
|
+
`ЦЕЛЬ ЭТАПА: ${effGoal}.` +
|
|
131
|
+
(effGuidance ? `\nКАК: ${effGuidance}` : "") +
|
|
132
|
+
(stageCfg?.groundingRequired
|
|
129
133
|
? `\nGROUNDING: на этом этапе все конкретные факты (цифры, суммы, сроки) бери ТОЛЬКО из секции KB CONTEXT ниже. Если её нет или нужного факта в ней нет — не выдумывай, скажи что уточнишь.`
|
|
130
134
|
: "")
|
|
131
135
|
: `ТЕКУЩИЙ ЭТАП: ${stage}. (Специфических правил для этапа нет — используй общий стиль.)`;
|
|
132
136
|
|
|
137
|
+
// Динамический контекст текущего запроса гостя (multi-request): тип запроса +
|
|
138
|
+
// сколько открыто — помогает боту не путать параллельные заявки.
|
|
139
|
+
const requestBlock = options.requestContext
|
|
140
|
+
? `ЗАПРОС ГОСТЯ: ${options.requestContext}`
|
|
141
|
+
: "";
|
|
142
|
+
|
|
143
|
+
// Лид ждёт оператора (awaiting_operator): бот держит, не выдумывает цену/условия.
|
|
144
|
+
const operatorBlock = options.awaitingOperator
|
|
145
|
+
? `ОЖИДАНИЕ ОПЕРАТОРА: на этой стадии условия/цену/решение готовит коллега-человек. НЕ выдумывай числа и детали. Скажи гостю коротко, что уточняешь и вернёшься с ответом — и жди, не дави и не закрывай сделку.`
|
|
146
|
+
: "";
|
|
147
|
+
|
|
133
148
|
const minorRule = guardrails.noMinors ? "- Если prospect <18 лет — вежливо заверши диалог." : "";
|
|
134
149
|
const topicsRule = guardrails.forbiddenTopics.length
|
|
135
150
|
? `- Запрещённые темы: ${guardrails.forbiddenTopics.join(", ")}.`
|
|
@@ -178,6 +193,8 @@ export function composeSystemPrompt(
|
|
|
178
193
|
support ? "" : directorHooksBlock,
|
|
179
194
|
support ? "" : skillsBlock,
|
|
180
195
|
support || stageBlock,
|
|
196
|
+
operatorBlock,
|
|
197
|
+
requestBlock,
|
|
181
198
|
summaryBlock,
|
|
182
199
|
userFactsBlock,
|
|
183
200
|
needsGroundingReminder ? kbGroundingReminder(persona.role) : "",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ChatClient } from "@chatman-media/llm-router";
|
|
2
|
+
import { describe, expect, it } from "bun:test";
|
|
3
|
+
import { parseReflection, verifyAnswer } from "./reflect.ts";
|
|
4
|
+
|
|
5
|
+
const chat = (text: string): ChatClient => ({ complete: async () => text }) as unknown as ChatClient;
|
|
6
|
+
const chatThrows = (): ChatClient =>
|
|
7
|
+
({ complete: async () => { throw new Error("x"); } }) as unknown as ChatClient;
|
|
8
|
+
|
|
9
|
+
describe("parseReflection", () => {
|
|
10
|
+
it("нет {} → grounded:true", () => {
|
|
11
|
+
expect(parseReflection("plain")).toEqual({ grounded: true });
|
|
12
|
+
});
|
|
13
|
+
it("битый JSON → true", () => {
|
|
14
|
+
expect(parseReflection("{nope")).toEqual({ grounded: true });
|
|
15
|
+
});
|
|
16
|
+
it("grounded non-bool → true", () => {
|
|
17
|
+
expect(parseReflection('{"grounded":1}')).toEqual({ grounded: true });
|
|
18
|
+
});
|
|
19
|
+
it("grounded:true → {true}", () => {
|
|
20
|
+
expect(parseReflection('{"grounded":true}')).toEqual({ grounded: true });
|
|
21
|
+
});
|
|
22
|
+
it("grounded:false → {false, reason}", () => {
|
|
23
|
+
expect(parseReflection('{"grounded":false,"reason":"выдумал город"}')).toEqual({ grounded: false, reason: "выдумал город" });
|
|
24
|
+
});
|
|
25
|
+
it("grounded:false без reason → unknown", () => {
|
|
26
|
+
expect(parseReflection('{"grounded":false}')).toEqual({ grounded: false, reason: "unknown" });
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("verifyAnswer", () => {
|
|
31
|
+
const base = { question: "q", answer: "Дубай 1500$", context: "Дубай 1500$" };
|
|
32
|
+
it("пустой ответ → grounded:true", async () => {
|
|
33
|
+
expect(await verifyAnswer({ ...base, answer: " ", chat: chatThrows() })).toEqual({ grounded: true });
|
|
34
|
+
});
|
|
35
|
+
it("нет контекста → grounded:true", async () => {
|
|
36
|
+
expect(await verifyAnswer({ ...base, context: "", chat: chatThrows() })).toEqual({ grounded: true });
|
|
37
|
+
});
|
|
38
|
+
it("LLM-вердикт false → {false,reason}", async () => {
|
|
39
|
+
expect(await verifyAnswer({ ...base, chat: chat('{"grounded":false,"reason":"r"}') })).toMatchObject({ grounded: false });
|
|
40
|
+
});
|
|
41
|
+
it("LLM упал → grounded:true (fail-open)", async () => {
|
|
42
|
+
expect(await verifyAnswer({ ...base, chat: chatThrows() })).toEqual({ grounded: true });
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ChatClient, ChatMessage } from "@chatman-media/llm-router";
|
|
2
|
+
import { describe, expect, it } from "bun:test";
|
|
3
|
+
import { questionNeedsRewrite, rewriteQuery, sanitizeRewritten } from "./rewrite-query.ts";
|
|
4
|
+
|
|
5
|
+
const chat = (text: string): ChatClient => ({ complete: async () => text }) as unknown as ChatClient;
|
|
6
|
+
const chatThrows = (): ChatClient =>
|
|
7
|
+
({ complete: async () => { throw new Error("x"); } }) as unknown as ChatClient;
|
|
8
|
+
const hist: ChatMessage[] = [{ role: "assistant", content: "в Дубае платят 1500" }];
|
|
9
|
+
|
|
10
|
+
describe("questionNeedsRewrite", () => {
|
|
11
|
+
it("пустой → false", () => {
|
|
12
|
+
expect(questionNeedsRewrite(" ", hist)).toBe(false);
|
|
13
|
+
});
|
|
14
|
+
it("нет истории → false", () => {
|
|
15
|
+
expect(questionNeedsRewrite("а там как?", [])).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
it("короткий (<=4 слова) с историей → true", () => {
|
|
18
|
+
expect(questionNeedsRewrite("а виза как?", hist)).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
it("дейктик-маркер → true", () => {
|
|
21
|
+
expect(questionNeedsRewrite("сколько стоит это оформление документов в итоге", hist)).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it("follow-up союз в начале → true", () => {
|
|
24
|
+
expect(questionNeedsRewrite("или есть другие варианты работы там сейчас", hist)).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
it("длинный самостоятельный → false", () => {
|
|
27
|
+
expect(questionNeedsRewrite("сколько платят моделям в Дубае за месяц работы по контракту", hist)).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe("sanitizeRewritten", () => {
|
|
32
|
+
it("снимает think/fence/«ответ:» и берёт первую строку", () => {
|
|
33
|
+
expect(sanitizeRewritten('<think>..</think>ответ: какие условия в Дубае\nещё текст', "fb", 200)).toBe("какие условия в Дубае");
|
|
34
|
+
});
|
|
35
|
+
it("пусто/мусор → fallback", () => {
|
|
36
|
+
expect(sanitizeRewritten(" \n ", "ОРИГИНАЛ", 200)).toBe("ОРИГИНАЛ");
|
|
37
|
+
});
|
|
38
|
+
it("обрезка по maxLength", () => {
|
|
39
|
+
expect(sanitizeRewritten("абвгде", "fb", 3)).toBe("абв");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("rewriteQuery", () => {
|
|
44
|
+
it("пустой вопрос → как есть", async () => {
|
|
45
|
+
expect(await rewriteQuery({ question: " ", chat: chatThrows() })).toBe("");
|
|
46
|
+
});
|
|
47
|
+
it("не нужен рерайт (нет истории) → оригинал без вызова LLM", async () => {
|
|
48
|
+
expect(await rewriteQuery({ question: "а там?", chat: chatThrows() })).toBe("а там?");
|
|
49
|
+
});
|
|
50
|
+
it("нужен рерайт → sanitized из LLM", async () => {
|
|
51
|
+
expect(await rewriteQuery({ question: "а виза?", history: hist, chat: chat("как оформляется виза") })).toBe("как оформляется виза");
|
|
52
|
+
});
|
|
53
|
+
it("LLM упал → оригинал", async () => {
|
|
54
|
+
expect(await rewriteQuery({ question: "а виза?", history: hist, chat: chatThrows() })).toBe("а виза?");
|
|
55
|
+
});
|
|
56
|
+
});
|
package/src/styles.ts
CHANGED
|
@@ -135,4 +135,21 @@ export interface ComposeOptions {
|
|
|
135
135
|
* block instead. `docs` = collecting their documents, `submitted` = filed.
|
|
136
136
|
*/
|
|
137
137
|
supportPhase?: "docs" | "submitted";
|
|
138
|
+
/**
|
|
139
|
+
* Per-stage override (Phase 2): goal/guidance from the lead's current funnel
|
|
140
|
+
* stage_definition. Takes precedence over `style.stages[stage]` for the stage
|
|
141
|
+
* block. Lets AI-built funnels carry per-stage instructions.
|
|
142
|
+
*/
|
|
143
|
+
stageOverride?: { goal: string; guidance?: string };
|
|
144
|
+
/**
|
|
145
|
+
* Динамический контекст текущего запроса гостя (multi-request / concierge):
|
|
146
|
+
* какой тип запроса ведётся + сколько открыто. Инжектится блоком «ЗАПРОС
|
|
147
|
+
* ГОСТЯ». null/absent для линейных вертикалей.
|
|
148
|
+
*/
|
|
149
|
+
requestContext?: string;
|
|
150
|
+
/**
|
|
151
|
+
* Лид стоит на стадии awaiting_operator (R5): цену/условия даёт человек-оператор.
|
|
152
|
+
* Бот придерживает гостя и не выдумывает детали. Блок «ОЖИДАНИЕ ОПЕРАТОРА».
|
|
153
|
+
*/
|
|
154
|
+
awaitingOperator?: boolean;
|
|
138
155
|
}
|