АРЧ АП11 - Вынести meta и memory recap policy из route runtime и закрыть Phase 5 агентным прогоном
This commit is contained in:
+11
-10
@@ -1,6 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.runAssistantLivingChatRuntime = runAssistantLivingChatRuntime;
|
||||
const assistantMemoryRecapPolicy_1 = require("./assistantMemoryRecapPolicy");
|
||||
function formatIsoDateForReply(value) {
|
||||
const source = String(value ?? "").trim();
|
||||
const match = source.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
||||
@@ -159,14 +160,14 @@ async function runAssistantLivingChatRuntime(input) {
|
||||
let knownOrganizations = input.mergeKnownOrganizations(input.sessionScope.knownOrganizations ?? []);
|
||||
let selectedOrganization = input.toNonEmptyString(input.sessionScope.selectedOrganization);
|
||||
let activeOrganization = input.toNonEmptyString(input.sessionScope.activeOrganization);
|
||||
const contextualInventoryHistoryCapabilityFollowup = input.modeDecision?.reason === "inventory_history_capability_followup_detected";
|
||||
const contextualMemoryRecapFollowup = input.modeDecision?.reason === "memory_recap_followup_detected";
|
||||
const lastGroundedInventoryAddressDebug = contextualInventoryHistoryCapabilityFollowup
|
||||
? findLastGroundedInventoryAddressDebug(input.sessionItems)
|
||||
: null;
|
||||
const lastMemoryAddressDebug = contextualMemoryRecapFollowup
|
||||
? findLastAddressDebugWithItem(input.sessionItems) ?? findLastAddressDebug(input.sessionItems)
|
||||
: null;
|
||||
const memoryRecapContext = (0, assistantMemoryRecapPolicy_1.resolveAssistantLivingChatMemoryContext)({
|
||||
modeDecisionReason: input.modeDecision?.reason ?? null,
|
||||
sessionItems: input.sessionItems
|
||||
});
|
||||
const contextualInventoryHistoryCapabilityFollowup = memoryRecapContext.contextualInventoryHistoryCapabilityFollowup;
|
||||
const contextualMemoryRecapFollowup = memoryRecapContext.contextualMemoryRecapFollowup;
|
||||
const lastGroundedInventoryAddressDebug = memoryRecapContext.lastGroundedInventoryAddressDebug;
|
||||
const lastMemoryAddressDebug = memoryRecapContext.lastMemoryAddressDebug;
|
||||
if (capabilityMetaQuery && (destructiveSignal || dangerSignal)) {
|
||||
chatText = input.buildAssistantSafetyRefusalReply();
|
||||
livingChatSource = "deterministic_safety_refusal";
|
||||
@@ -212,7 +213,7 @@ async function runAssistantLivingChatRuntime(input) {
|
||||
}
|
||||
else if (contextualInventoryHistoryCapabilityFollowup) {
|
||||
const scopedOrganization = selectedOrganization ?? activeOrganization ?? null;
|
||||
chatText = buildInventoryHistoryCapabilityFollowupReply({
|
||||
chatText = (0, assistantMemoryRecapPolicy_1.buildInventoryHistoryCapabilityFollowupReply)({
|
||||
organization: scopedOrganization,
|
||||
addressDebug: lastGroundedInventoryAddressDebug,
|
||||
toNonEmptyString: input.toNonEmptyString
|
||||
@@ -222,7 +223,7 @@ async function runAssistantLivingChatRuntime(input) {
|
||||
}
|
||||
else if (contextualMemoryRecapFollowup) {
|
||||
const scopedOrganization = selectedOrganization ?? activeOrganization ?? null;
|
||||
chatText = buildAddressMemoryRecapReply({
|
||||
chatText = (0, assistantMemoryRecapPolicy_1.buildAddressMemoryRecapReply)({
|
||||
organization: scopedOrganization,
|
||||
addressDebug: lastMemoryAddressDebug,
|
||||
toNonEmptyString: input.toNonEmptyString
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
"use strict";
|
||||
// @ts-nocheck
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.buildInventoryHistoryCapabilityFollowupReply = buildInventoryHistoryCapabilityFollowupReply;
|
||||
exports.buildAddressMemoryRecapReply = buildAddressMemoryRecapReply;
|
||||
exports.resolveAssistantLivingChatMemoryContext = resolveAssistantLivingChatMemoryContext;
|
||||
exports.createAssistantMemoryRecapPolicy = createAssistantMemoryRecapPolicy;
|
||||
function formatIsoDateForReply(value) {
|
||||
const source = String(value ?? "").trim();
|
||||
const match = source.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
return `${match[3]}.${match[2]}.${match[1]}`;
|
||||
}
|
||||
function collectMessageSamples(input) {
|
||||
const values = [
|
||||
input.rawUserMessage,
|
||||
input.repairedRawUserMessage,
|
||||
input.effectiveAddressUserMessage,
|
||||
input.repairedEffectiveAddressUserMessage
|
||||
];
|
||||
return Array.from(new Set(values
|
||||
.map((item) => String(item ?? "").trim())
|
||||
.filter((item) => item.length > 0)));
|
||||
}
|
||||
function hasSignalAcrossSamples(samples, detector) {
|
||||
return samples.some((sample) => detector(sample));
|
||||
}
|
||||
function findLastGroundedInventoryAddressDebug(items) {
|
||||
if (!Array.isArray(items)) {
|
||||
return null;
|
||||
}
|
||||
for (let index = items.length - 1; index >= 0; index -= 1) {
|
||||
const item = items[index];
|
||||
if (!item || item.role !== "assistant" || !item.debug || typeof item.debug !== "object") {
|
||||
continue;
|
||||
}
|
||||
const debug = item.debug;
|
||||
const answerGroundingCheck = debug.answer_grounding_check && typeof debug.answer_grounding_check === "object"
|
||||
? debug.answer_grounding_check
|
||||
: null;
|
||||
const groundingStatus = String(answerGroundingCheck?.status ?? "");
|
||||
const detectedIntent = String(debug.detected_intent ?? "");
|
||||
const capabilityId = String(debug.capability_id ?? "");
|
||||
const rootFrameContext = debug.address_root_frame_context && typeof debug.address_root_frame_context === "object"
|
||||
? debug.address_root_frame_context
|
||||
: null;
|
||||
const rootIntent = String(rootFrameContext?.root_intent ?? "");
|
||||
const isInventoryContext = detectedIntent === "inventory_on_hand_as_of_date" ||
|
||||
capabilityId === "confirmed_inventory_on_hand_as_of_date" ||
|
||||
rootIntent === "inventory_on_hand_as_of_date";
|
||||
if (groundingStatus === "grounded" && isInventoryContext) {
|
||||
return debug;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function findLastAddressDebugWithItem(items) {
|
||||
if (!Array.isArray(items)) {
|
||||
return null;
|
||||
}
|
||||
for (let index = items.length - 1; index >= 0; index -= 1) {
|
||||
const item = items[index];
|
||||
if (!item || item.role !== "assistant" || !item.debug || typeof item.debug !== "object") {
|
||||
continue;
|
||||
}
|
||||
const debug = item.debug;
|
||||
if (String(debug.execution_lane ?? "") !== "address_query") {
|
||||
continue;
|
||||
}
|
||||
const extractedFilters = debug.extracted_filters && typeof debug.extracted_filters === "object"
|
||||
? debug.extracted_filters
|
||||
: null;
|
||||
const itemLabel = String(extractedFilters?.item ?? "").trim() ||
|
||||
(String(debug.anchor_type ?? "") === "item"
|
||||
? String(debug.anchor_value_resolved ?? debug.anchor_value_raw ?? "").trim()
|
||||
: "");
|
||||
if (itemLabel) {
|
||||
return debug;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function findLastAddressDebug(items) {
|
||||
if (!Array.isArray(items)) {
|
||||
return null;
|
||||
}
|
||||
for (let index = items.length - 1; index >= 0; index -= 1) {
|
||||
const item = items[index];
|
||||
if (!item || item.role !== "assistant" || !item.debug || typeof item.debug !== "object") {
|
||||
continue;
|
||||
}
|
||||
if (String(item.debug.execution_lane ?? "") === "address_query") {
|
||||
return item.debug;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function buildInventoryHistoryCapabilityFollowupReply(input) {
|
||||
const rootFrameContext = input.addressDebug?.address_root_frame_context &&
|
||||
typeof input.addressDebug.address_root_frame_context === "object"
|
||||
? input.addressDebug.address_root_frame_context
|
||||
: null;
|
||||
const extractedFilters = input.addressDebug?.extracted_filters && typeof input.addressDebug.extracted_filters === "object"
|
||||
? input.addressDebug.extracted_filters
|
||||
: null;
|
||||
const organization = input.organization ??
|
||||
input.toNonEmptyString(rootFrameContext?.organization) ??
|
||||
input.toNonEmptyString(extractedFilters?.organization);
|
||||
const lastAsOfDate = formatIsoDateForReply(rootFrameContext?.as_of_date) ??
|
||||
formatIsoDateForReply(extractedFilters?.as_of_date);
|
||||
const organizationPart = organization ? ` по компании «${organization}»` : "";
|
||||
const referenceLine = lastAsOfDate
|
||||
? `Да, могу. Сейчас мы уже смотрели складской срез${organizationPart} на ${lastAsOfDate}.`
|
||||
: `Да, могу показать исторические данные${organizationPart} в этом же складском контуре.`;
|
||||
return [
|
||||
referenceLine,
|
||||
`Могу показать исторические остатки${organizationPart} за нужный месяц, дату или год.`,
|
||||
"Например:",
|
||||
"- `на март 2020`",
|
||||
"- `на июнь 2016`",
|
||||
"- `за 2017 год`",
|
||||
"- `сравни июнь 2016 с текущим срезом`",
|
||||
"Если хочешь, сразу покажу нужный исторический период."
|
||||
].join("\n");
|
||||
}
|
||||
function buildAddressMemoryRecapReply(input) {
|
||||
const extractedFilters = input.addressDebug?.extracted_filters && typeof input.addressDebug.extracted_filters === "object"
|
||||
? input.addressDebug.extracted_filters
|
||||
: null;
|
||||
const rootFrameContext = input.addressDebug?.address_root_frame_context &&
|
||||
typeof input.addressDebug.address_root_frame_context === "object"
|
||||
? input.addressDebug.address_root_frame_context
|
||||
: null;
|
||||
const item = input.toNonEmptyString(extractedFilters?.item) ??
|
||||
(String(input.addressDebug?.anchor_type ?? "") === "item"
|
||||
? input.toNonEmptyString(input.addressDebug?.anchor_value_resolved) ??
|
||||
input.toNonEmptyString(input.addressDebug?.anchor_value_raw)
|
||||
: null);
|
||||
const organization = input.organization ??
|
||||
input.toNonEmptyString(extractedFilters?.organization) ??
|
||||
input.toNonEmptyString(rootFrameContext?.organization);
|
||||
const scopedDate = formatIsoDateForReply(extractedFilters?.as_of_date) ??
|
||||
formatIsoDateForReply(rootFrameContext?.as_of_date) ??
|
||||
formatIsoDateForReply(extractedFilters?.period_to);
|
||||
if (item) {
|
||||
const datePart = scopedDate ? ` в срезе на ${scopedDate}` : "";
|
||||
const organizationPart = organization ? ` по компании «${organization}»` : "";
|
||||
return [
|
||||
`Да, помню. Мы обсуждали позицию «${item}»${organizationPart}${datePart}.`,
|
||||
"Могу продолжить по ней без переписывания сущности: кто поставил, когда купили, по каким документам или кому продали."
|
||||
].join(" ");
|
||||
}
|
||||
if (organization || scopedDate) {
|
||||
const organizationPart = organization ? ` по компании «${organization}»` : "";
|
||||
const datePart = scopedDate ? ` на ${scopedDate}` : "";
|
||||
return [
|
||||
`Да, помню. Мы уже смотрели адресный контур${organizationPart}${datePart}.`,
|
||||
"Могу кратко напомнить контекст или сразу продолжить следующий шаг по этому же сценарию."
|
||||
].join(" ");
|
||||
}
|
||||
return "Да, помню предыдущий адресный контур. Могу кратко напомнить, что мы уже подтвердили, или сразу продолжить следующий шаг.";
|
||||
}
|
||||
function resolveAssistantLivingChatMemoryContext(input) {
|
||||
const contextualInventoryHistoryCapabilityFollowup = String(input.modeDecisionReason ?? "") === "inventory_history_capability_followup_detected";
|
||||
const contextualMemoryRecapFollowup = String(input.modeDecisionReason ?? "") === "memory_recap_followup_detected";
|
||||
const sessionItems = Array.isArray(input.sessionItems) ? input.sessionItems : [];
|
||||
return {
|
||||
contextualInventoryHistoryCapabilityFollowup,
|
||||
contextualMemoryRecapFollowup,
|
||||
lastGroundedInventoryAddressDebug: contextualInventoryHistoryCapabilityFollowup
|
||||
? findLastGroundedInventoryAddressDebug(sessionItems)
|
||||
: null,
|
||||
lastMemoryAddressDebug: contextualMemoryRecapFollowup
|
||||
? findLastAddressDebugWithItem(sessionItems) ?? findLastAddressDebug(sessionItems)
|
||||
: null
|
||||
};
|
||||
}
|
||||
function createAssistantMemoryRecapPolicy(deps) {
|
||||
function resolveRouteMemorySignals(input) {
|
||||
const samples = collectMessageSamples(input);
|
||||
const historicalCapabilitySignal = hasSignalAcrossSamples(samples, deps.hasHistoricalCapabilityFollowupSignal);
|
||||
const memoryRecapSignal = hasSignalAcrossSamples(samples, deps.hasConversationMemoryRecallFollowupSignal);
|
||||
return {
|
||||
contextualHistoricalCapabilityFollowupDetected: Boolean(input.capabilityMetaQuery &&
|
||||
!input.dataScopeMetaQuery &&
|
||||
!input.dataRetrievalSignal &&
|
||||
historicalCapabilitySignal &&
|
||||
deps.isGroundedInventoryContextDebug(input.lastGroundedAddressDebug)),
|
||||
contextualMemoryRecapFollowupDetected: Boolean(!input.dataScopeMetaQuery &&
|
||||
!input.capabilityMetaQuery &&
|
||||
!input.dataRetrievalSignal &&
|
||||
!input.strongDataSignal &&
|
||||
!input.aggregateBusinessAnalyticsSignal &&
|
||||
memoryRecapSignal &&
|
||||
input.hasPriorAddressDebug)
|
||||
};
|
||||
}
|
||||
return {
|
||||
resolveRouteMemorySignals
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
"use strict";
|
||||
// @ts-nocheck
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createAssistantMetaFollowupPolicy = createAssistantMetaFollowupPolicy;
|
||||
function collectMessageSamples(input) {
|
||||
const values = [
|
||||
input.rawUserMessage,
|
||||
input.repairedRawUserMessage,
|
||||
input.effectiveAddressUserMessage,
|
||||
input.repairedEffectiveAddressUserMessage
|
||||
];
|
||||
return Array.from(new Set(values
|
||||
.map((item) => String(item ?? "").trim())
|
||||
.filter((item) => item.length > 0)));
|
||||
}
|
||||
function hasSignalAcrossSamples(samples, detector) {
|
||||
return samples.some((sample) => detector(sample));
|
||||
}
|
||||
function hasImplicitHistoricalCapabilityMetaSignal(samples) {
|
||||
return samples.some((sample) => /(?:историческ|история|архив|раньше|ретро|старые\s+данные)/iu.test(sample) &&
|
||||
/(?:мож(?:ешь|ем|но)|уме(?:ешь|ете))/iu.test(sample));
|
||||
}
|
||||
function createAssistantMetaFollowupPolicy(deps) {
|
||||
function resolveMetaSignalSet(input) {
|
||||
const samples = collectMessageSamples(input);
|
||||
if (samples.length === 0) {
|
||||
return {
|
||||
dataScopeMetaQuery: false,
|
||||
capabilityMetaQuery: false,
|
||||
metaAnswerFollowupSignal: false
|
||||
};
|
||||
}
|
||||
return {
|
||||
dataScopeMetaQuery: hasSignalAcrossSamples(samples, deps.hasAssistantDataScopeMetaQuestionSignal),
|
||||
capabilityMetaQuery: hasSignalAcrossSamples(samples, deps.shouldHandleAsAssistantCapabilityMetaQuery) ||
|
||||
hasImplicitHistoricalCapabilityMetaSignal(samples),
|
||||
metaAnswerFollowupSignal: hasSignalAcrossSamples(samples, deps.hasMetaAnswerFollowupSignal)
|
||||
};
|
||||
}
|
||||
function resolveHardMetaMode(input) {
|
||||
if (Boolean(input.dataScopeMetaQuery)) {
|
||||
return "data_scope";
|
||||
}
|
||||
if (Boolean(input.capabilityMetaQuery) && !Boolean(input.dataRetrievalSignal)) {
|
||||
return "capability";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function isMetaFollowupOverGroundedAnswer(input) {
|
||||
return Boolean(input.followupContext &&
|
||||
input.hasPriorAddressAnswerContext &&
|
||||
(input.metaAnswerFollowupSignal || input.vatEvaluativeFollowupSignal) &&
|
||||
!input.dataScopeMetaQuery &&
|
||||
!input.capabilityMetaQuery &&
|
||||
!input.aggregateBusinessAnalyticsSignal &&
|
||||
!input.dataRetrievalSignal &&
|
||||
!input.strongDataSignal &&
|
||||
String(input.resolvedMode ?? "") !== "address_query" &&
|
||||
String(input.resolvedIntent ?? "") === "unknown" &&
|
||||
(!input.llmContractIntent || String(input.llmContractIntent) === "unknown") &&
|
||||
String(input.llmContractMode ?? "") !== "address_query");
|
||||
}
|
||||
return {
|
||||
resolveMetaSignalSet,
|
||||
resolveHardMetaMode,
|
||||
isMetaFollowupOverGroundedAnswer
|
||||
};
|
||||
}
|
||||
+48
-49
@@ -46,7 +46,7 @@ function shouldBypassStrictDeepInvestigationCueForAddressIntent(intent) {
|
||||
return Boolean(intent && ADDRESS_INTENTS_ALLOW_STRICT_DEEP_INVESTIGATION_BYPASS.has(intent));
|
||||
}
|
||||
function createAssistantRoutePolicy(deps) {
|
||||
const { repairAddressMojibake, findLastGroundedAddressAnswerDebug, findLastOrganizationClarificationAddressDebug, mergeKnownOrganizations, normalizeOrganizationScopeValue, resolveOrganizationSelectionFromMessage, hasAssistantDataScopeMetaQuestionSignal, shouldHandleAsAssistantCapabilityMetaQuery, hasDataRetrievalRequestSignal, hasAggregateBusinessAnalyticsSignal, hasStandaloneAddressTopicSignal, hasOpenContractsAddressSignal, detectAddressQuestionMode, resolveAddressIntent, toNonEmptyString, hasStrictDeepInvestigationCue, hasStrongDataIntentSignal, hasAccountingSignal, hasDangerOrCoercionSignal, hasAddressFollowupContextSignal, hasShortDebtMirrorFollowupSignal, isInventorySelectedObjectIntent, hasShortInventoryObjectFollowupSignal, hasHistoricalCapabilityFollowupSignal, isGroundedInventoryContextDebug, hasConversationMemoryRecallFollowupSignal, findLastAddressAssistantItem, hasMetaAnswerFollowupSignal, resolveAddressToolGateDecision, hasSameDateAccountFollowupSignalForPredecompose, hasLooseAllTimeAddressLookupSignal, hasDeepAnalysisPreferenceSignal, hasDirectDeepAnalysisSignal, compactWhitespace, hasDeepSessionContinuationSignal, resolveLivingAssistantModeDecision } = deps;
|
||||
const { repairAddressMojibake, findLastGroundedAddressAnswerDebug, findLastOrganizationClarificationAddressDebug, mergeKnownOrganizations, normalizeOrganizationScopeValue, resolveOrganizationSelectionFromMessage, resolveMetaSignalSet, resolveHardMetaMode, isMetaFollowupOverGroundedAnswer, hasDataRetrievalRequestSignal, hasAggregateBusinessAnalyticsSignal, hasStandaloneAddressTopicSignal, hasOpenContractsAddressSignal, detectAddressQuestionMode, resolveAddressIntent, toNonEmptyString, hasStrictDeepInvestigationCue, hasStrongDataIntentSignal, hasAccountingSignal, hasDangerOrCoercionSignal, hasAddressFollowupContextSignal, hasShortDebtMirrorFollowupSignal, isInventorySelectedObjectIntent, hasShortInventoryObjectFollowupSignal, isGroundedInventoryContextDebug, resolveRouteMemorySignals, findLastAddressAssistantItem, resolveAddressToolGateDecision, hasSameDateAccountFollowupSignalForPredecompose, hasLooseAllTimeAddressLookupSignal, hasDeepAnalysisPreferenceSignal, hasDirectDeepAnalysisSignal, compactWhitespace, hasDeepSessionContinuationSignal, resolveLivingAssistantModeDecision } = deps;
|
||||
function resolveAssistantOrchestrationDecision(input) {
|
||||
const rawUserMessage = String(input?.rawUserMessage ?? input?.userMessage ?? "");
|
||||
const effectiveAddressUserMessage = String(input?.effectiveAddressUserMessage ?? rawUserMessage);
|
||||
@@ -78,14 +78,14 @@ function createAssistantRoutePolicy(deps) {
|
||||
organizationClarificationCandidates.some((candidate) => normalizeOrganizationScopeValue(candidate) === organizationClarificationSelectionFromScope)
|
||||
? organizationClarificationSelectionFromScope
|
||||
: null);
|
||||
const dataScopeMetaQuery = hasAssistantDataScopeMetaQuestionSignal(rawUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(repairedRawUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(effectiveAddressUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(repairedEffectiveAddressUserMessage);
|
||||
const capabilityMetaQuery = shouldHandleAsAssistantCapabilityMetaQuery(rawUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(repairedRawUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(effectiveAddressUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(repairedEffectiveAddressUserMessage);
|
||||
const metaSignals = resolveMetaSignalSet({
|
||||
rawUserMessage,
|
||||
repairedRawUserMessage,
|
||||
effectiveAddressUserMessage,
|
||||
repairedEffectiveAddressUserMessage
|
||||
});
|
||||
const dataScopeMetaQuery = metaSignals.dataScopeMetaQuery;
|
||||
const capabilityMetaQuery = metaSignals.capabilityMetaQuery;
|
||||
const dataRetrievalSignal = hasDataRetrievalRequestSignal(rawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(repairedRawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(effectiveAddressUserMessage) ||
|
||||
@@ -191,30 +191,29 @@ function createAssistantRoutePolicy(deps) {
|
||||
(llmFirstUnsupportedCandidate || llmContractMode === null) &&
|
||||
!protectedInventoryShortFollowup &&
|
||||
!organizationClarificationContinuationDetected);
|
||||
const contextualHistoricalCapabilityFollowupDetected = Boolean(capabilityMetaQuery &&
|
||||
!dataScopeMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
(hasHistoricalCapabilityFollowupSignal(rawUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(repairedRawUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(repairedEffectiveAddressUserMessage)) &&
|
||||
isGroundedInventoryContextDebug(lastGroundedAddressDebug));
|
||||
const contextualMemoryRecapFollowupDetected = Boolean(!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
!strongDataSignal &&
|
||||
!aggregateBusinessAnalyticsSignal &&
|
||||
(hasConversationMemoryRecallFollowupSignal(rawUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(repairedRawUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(repairedEffectiveAddressUserMessage)) &&
|
||||
(lastGroundedAddressDebug ||
|
||||
findLastAddressAssistantItem(sessionItems)?.debug));
|
||||
const hardMetaMode = dataScopeMetaQuery
|
||||
? "data_scope"
|
||||
: capabilityMetaQuery && !dataRetrievalSignal
|
||||
? "capability"
|
||||
: null;
|
||||
const lastAddressAssistantDebug = sessionItems
|
||||
? findLastAddressAssistantItem(sessionItems)?.debug ?? null
|
||||
: null;
|
||||
const memorySignals = resolveRouteMemorySignals({
|
||||
rawUserMessage,
|
||||
repairedRawUserMessage,
|
||||
effectiveAddressUserMessage,
|
||||
repairedEffectiveAddressUserMessage,
|
||||
dataScopeMetaQuery,
|
||||
capabilityMetaQuery,
|
||||
dataRetrievalSignal,
|
||||
strongDataSignal,
|
||||
aggregateBusinessAnalyticsSignal,
|
||||
lastGroundedAddressDebug,
|
||||
hasPriorAddressDebug: Boolean(lastGroundedAddressDebug || lastAddressAssistantDebug)
|
||||
});
|
||||
const contextualHistoricalCapabilityFollowupDetected = memorySignals.contextualHistoricalCapabilityFollowupDetected;
|
||||
const contextualMemoryRecapFollowupDetected = memorySignals.contextualMemoryRecapFollowupDetected;
|
||||
const hardMetaMode = resolveHardMetaMode({
|
||||
dataScopeMetaQuery,
|
||||
capabilityMetaQuery,
|
||||
dataRetrievalSignal
|
||||
});
|
||||
if (hardMetaMode === "data_scope") {
|
||||
return {
|
||||
runAddressLane: false,
|
||||
@@ -355,10 +354,7 @@ function createAssistantRoutePolicy(deps) {
|
||||
}
|
||||
};
|
||||
}
|
||||
const metaAnswerFollowupSignal = hasMetaAnswerFollowupSignal(rawUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(repairedRawUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(repairedEffectiveAddressUserMessage);
|
||||
const metaAnswerFollowupSignal = metaSignals.metaAnswerFollowupSignal;
|
||||
const baseToolGate = resolveAddressToolGateDecision(effectiveAddressUserMessage, followupContext, llmPreDecomposeMeta, rawUserMessage);
|
||||
const preserveAddressLaneSignal = Boolean((llmPreDecomposeMeta?.llmCanonicalCandidateDetected &&
|
||||
llmPreDecomposeMeta?.applied &&
|
||||
@@ -463,18 +459,21 @@ function createAssistantRoutePolicy(deps) {
|
||||
sessionItems
|
||||
}));
|
||||
const hasPriorAddressAnswerContext = Boolean(lastGroundedAddressDebug || toNonEmptyString(followupContext?.previous_intent));
|
||||
const metaFollowupOverGroundedAnswer = Boolean(followupContext &&
|
||||
hasPriorAddressAnswerContext &&
|
||||
(metaAnswerFollowupSignal || vatEvaluativeFollowupSignal) &&
|
||||
!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!aggregateBusinessAnalyticsSignal &&
|
||||
!dataRetrievalSignal &&
|
||||
!strongDataSignal &&
|
||||
resolvedModeDetection.mode !== "address_query" &&
|
||||
resolvedIntentResolution.intent === "unknown" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown") &&
|
||||
llmContractMode !== "address_query");
|
||||
const metaFollowupOverGroundedAnswer = isMetaFollowupOverGroundedAnswer({
|
||||
followupContext,
|
||||
hasPriorAddressAnswerContext,
|
||||
metaAnswerFollowupSignal,
|
||||
vatEvaluativeFollowupSignal,
|
||||
dataScopeMetaQuery,
|
||||
capabilityMetaQuery,
|
||||
aggregateBusinessAnalyticsSignal,
|
||||
dataRetrievalSignal,
|
||||
strongDataSignal,
|
||||
resolvedMode: resolvedModeDetection.mode,
|
||||
resolvedIntent: resolvedIntentResolution.intent,
|
||||
llmContractIntent,
|
||||
llmContractMode
|
||||
});
|
||||
let runAddressLane = Boolean(baseToolGate?.runAddressLane);
|
||||
let toolGateDecision = String(baseToolGate?.decision ?? "skip_address_lane");
|
||||
let toolGateReason = String(baseToolGate?.reason ?? "no_address_signal_after_l0");
|
||||
|
||||
+16
-6
@@ -68,6 +68,8 @@ const assistantCoverageGrounding_1 = __importStar(require("./assistantCoverageGr
|
||||
const assistantDeepTurnAttemptRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnAttemptRuntimeAdapter"));
|
||||
const assistantBoundaryPolicy_1 = __importStar(require("./assistantBoundaryPolicy"));
|
||||
const assistantLivingModePolicy_1 = __importStar(require("./assistantLivingModePolicy"));
|
||||
const assistantMetaFollowupPolicy_1 = __importStar(require("./assistantMetaFollowupPolicy"));
|
||||
const assistantMemoryRecapPolicy_1 = __importStar(require("./assistantMemoryRecapPolicy"));
|
||||
const assistantRoutePolicy_1 = __importStar(require("./assistantRoutePolicy"));
|
||||
const assistantTransitionPolicy_1 = __importStar(require("./assistantTransitionPolicy"));
|
||||
const assistantOrganizationScopeRuntimeAdapter_1 = __importStar(require("./assistantOrganizationScopeRuntimeAdapter"));
|
||||
@@ -4753,6 +4755,16 @@ const assistantLivingModePolicy = (0, assistantLivingModePolicy_1.createAssistan
|
||||
hasAssistantCapabilityQuestionSignal,
|
||||
hasOperationalAdminActionRequestSignal
|
||||
});
|
||||
const assistantMetaFollowupPolicy = (0, assistantMetaFollowupPolicy_1.createAssistantMetaFollowupPolicy)({
|
||||
hasAssistantDataScopeMetaQuestionSignal: assistantLivingModePolicy.hasAssistantDataScopeMetaQuestionSignal,
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: assistantLivingModePolicy.shouldHandleAsAssistantCapabilityMetaQuery,
|
||||
hasMetaAnswerFollowupSignal: assistantLivingModePolicy.hasMetaAnswerFollowupSignal
|
||||
});
|
||||
const assistantMemoryRecapPolicy = (0, assistantMemoryRecapPolicy_1.createAssistantMemoryRecapPolicy)({
|
||||
hasHistoricalCapabilityFollowupSignal: assistantLivingModePolicy.hasHistoricalCapabilityFollowupSignal,
|
||||
hasConversationMemoryRecallFollowupSignal: assistantLivingModePolicy.hasConversationMemoryRecallFollowupSignal,
|
||||
isGroundedInventoryContextDebug
|
||||
});
|
||||
const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePolicy)({
|
||||
repairAddressMojibake,
|
||||
findLastGroundedAddressAnswerDebug,
|
||||
@@ -4760,8 +4772,9 @@ const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePoli
|
||||
mergeKnownOrganizations,
|
||||
normalizeOrganizationScopeValue,
|
||||
resolveOrganizationSelectionFromMessage,
|
||||
hasAssistantDataScopeMetaQuestionSignal: assistantLivingModePolicy.hasAssistantDataScopeMetaQuestionSignal,
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: assistantLivingModePolicy.shouldHandleAsAssistantCapabilityMetaQuery,
|
||||
resolveMetaSignalSet: assistantMetaFollowupPolicy.resolveMetaSignalSet,
|
||||
resolveHardMetaMode: assistantMetaFollowupPolicy.resolveHardMetaMode,
|
||||
isMetaFollowupOverGroundedAnswer: assistantMetaFollowupPolicy.isMetaFollowupOverGroundedAnswer,
|
||||
hasDataRetrievalRequestSignal: assistantLivingModePolicy.hasDataRetrievalRequestSignal,
|
||||
hasAggregateBusinessAnalyticsSignal,
|
||||
hasStandaloneAddressTopicSignal,
|
||||
@@ -4777,11 +4790,8 @@ const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePoli
|
||||
hasShortDebtMirrorFollowupSignal,
|
||||
isInventorySelectedObjectIntent,
|
||||
hasShortInventoryObjectFollowupSignal,
|
||||
hasHistoricalCapabilityFollowupSignal: assistantLivingModePolicy.hasHistoricalCapabilityFollowupSignal,
|
||||
isGroundedInventoryContextDebug,
|
||||
hasConversationMemoryRecallFollowupSignal: assistantLivingModePolicy.hasConversationMemoryRecallFollowupSignal,
|
||||
resolveRouteMemorySignals: assistantMemoryRecapPolicy.resolveRouteMemorySignals,
|
||||
findLastAddressAssistantItem,
|
||||
hasMetaAnswerFollowupSignal: assistantLivingModePolicy.hasMetaAnswerFollowupSignal,
|
||||
resolveAddressToolGateDecision,
|
||||
hasSameDateAccountFollowupSignalForPredecompose,
|
||||
hasLooseAllTimeAddressLookupSignal,
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import {
|
||||
buildAddressMemoryRecapReply as buildAddressMemoryRecapReplyFromPolicy,
|
||||
buildInventoryHistoryCapabilityFollowupReply as buildInventoryHistoryCapabilityFollowupReplyFromPolicy,
|
||||
resolveAssistantLivingChatMemoryContext
|
||||
} from "./assistantMemoryRecapPolicy";
|
||||
|
||||
export interface AssistantLivingChatSessionScopeInput {
|
||||
knownOrganizations?: unknown[];
|
||||
selectedOrganization?: unknown;
|
||||
@@ -249,16 +255,15 @@ export async function runAssistantLivingChatRuntime(
|
||||
let knownOrganizations = input.mergeKnownOrganizations(input.sessionScope.knownOrganizations ?? []);
|
||||
let selectedOrganization = input.toNonEmptyString(input.sessionScope.selectedOrganization);
|
||||
let activeOrganization = input.toNonEmptyString(input.sessionScope.activeOrganization);
|
||||
const memoryRecapContext = resolveAssistantLivingChatMemoryContext({
|
||||
modeDecisionReason: input.modeDecision?.reason ?? null,
|
||||
sessionItems: input.sessionItems
|
||||
});
|
||||
const contextualInventoryHistoryCapabilityFollowup =
|
||||
input.modeDecision?.reason === "inventory_history_capability_followup_detected";
|
||||
const contextualMemoryRecapFollowup =
|
||||
input.modeDecision?.reason === "memory_recap_followup_detected";
|
||||
const lastGroundedInventoryAddressDebug = contextualInventoryHistoryCapabilityFollowup
|
||||
? findLastGroundedInventoryAddressDebug(input.sessionItems)
|
||||
: null;
|
||||
const lastMemoryAddressDebug = contextualMemoryRecapFollowup
|
||||
? findLastAddressDebugWithItem(input.sessionItems) ?? findLastAddressDebug(input.sessionItems)
|
||||
: null;
|
||||
memoryRecapContext.contextualInventoryHistoryCapabilityFollowup;
|
||||
const contextualMemoryRecapFollowup = memoryRecapContext.contextualMemoryRecapFollowup;
|
||||
const lastGroundedInventoryAddressDebug = memoryRecapContext.lastGroundedInventoryAddressDebug;
|
||||
const lastMemoryAddressDebug = memoryRecapContext.lastMemoryAddressDebug;
|
||||
|
||||
if (capabilityMetaQuery && (destructiveSignal || dangerSignal)) {
|
||||
chatText = input.buildAssistantSafetyRefusalReply();
|
||||
@@ -303,7 +308,7 @@ export async function runAssistantLivingChatRuntime(
|
||||
livingChatSource = "deterministic_operational_boundary";
|
||||
} else if (contextualInventoryHistoryCapabilityFollowup) {
|
||||
const scopedOrganization = selectedOrganization ?? activeOrganization ?? null;
|
||||
chatText = buildInventoryHistoryCapabilityFollowupReply({
|
||||
chatText = buildInventoryHistoryCapabilityFollowupReplyFromPolicy({
|
||||
organization: scopedOrganization,
|
||||
addressDebug: lastGroundedInventoryAddressDebug,
|
||||
toNonEmptyString: input.toNonEmptyString
|
||||
@@ -312,7 +317,7 @@ export async function runAssistantLivingChatRuntime(
|
||||
livingChatSource = "deterministic_inventory_history_capability_contract";
|
||||
} else if (contextualMemoryRecapFollowup) {
|
||||
const scopedOrganization = selectedOrganization ?? activeOrganization ?? null;
|
||||
chatText = buildAddressMemoryRecapReply({
|
||||
chatText = buildAddressMemoryRecapReplyFromPolicy({
|
||||
organization: scopedOrganization,
|
||||
addressDebug: lastMemoryAddressDebug,
|
||||
toNonEmptyString: input.toNonEmptyString
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
// @ts-nocheck
|
||||
|
||||
export interface ResolveAssistantRouteMemorySignalsInput {
|
||||
rawUserMessage?: unknown;
|
||||
repairedRawUserMessage?: unknown;
|
||||
effectiveAddressUserMessage?: unknown;
|
||||
repairedEffectiveAddressUserMessage?: unknown;
|
||||
dataScopeMetaQuery?: boolean;
|
||||
capabilityMetaQuery?: boolean;
|
||||
dataRetrievalSignal?: boolean;
|
||||
strongDataSignal?: boolean;
|
||||
aggregateBusinessAnalyticsSignal?: boolean;
|
||||
lastGroundedAddressDebug?: unknown;
|
||||
hasPriorAddressDebug?: boolean;
|
||||
}
|
||||
|
||||
export interface AssistantRouteMemorySignals {
|
||||
contextualHistoricalCapabilityFollowupDetected: boolean;
|
||||
contextualMemoryRecapFollowupDetected: boolean;
|
||||
}
|
||||
|
||||
export interface ResolveAssistantLivingChatMemoryContextInput {
|
||||
modeDecisionReason?: unknown;
|
||||
sessionItems?: unknown[];
|
||||
}
|
||||
|
||||
export interface AssistantLivingChatMemoryContext {
|
||||
contextualInventoryHistoryCapabilityFollowup: boolean;
|
||||
contextualMemoryRecapFollowup: boolean;
|
||||
lastGroundedInventoryAddressDebug: Record<string, unknown> | null;
|
||||
lastMemoryAddressDebug: Record<string, unknown> | null;
|
||||
}
|
||||
|
||||
export interface AssistantMemoryRecapPolicyDeps {
|
||||
hasHistoricalCapabilityFollowupSignal: (text: unknown) => boolean;
|
||||
hasConversationMemoryRecallFollowupSignal: (text: unknown) => boolean;
|
||||
isGroundedInventoryContextDebug: (debug: unknown) => boolean;
|
||||
}
|
||||
|
||||
function formatIsoDateForReply(value: unknown): string | null {
|
||||
const source = String(value ?? "").trim();
|
||||
const match = source.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
return `${match[3]}.${match[2]}.${match[1]}`;
|
||||
}
|
||||
|
||||
function collectMessageSamples(input: ResolveAssistantRouteMemorySignalsInput): string[] {
|
||||
const values = [
|
||||
input.rawUserMessage,
|
||||
input.repairedRawUserMessage,
|
||||
input.effectiveAddressUserMessage,
|
||||
input.repairedEffectiveAddressUserMessage
|
||||
];
|
||||
return Array.from(
|
||||
new Set(
|
||||
values
|
||||
.map((item) => String(item ?? "").trim())
|
||||
.filter((item) => item.length > 0)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function hasSignalAcrossSamples(
|
||||
samples: string[],
|
||||
detector: (text: unknown) => boolean
|
||||
): boolean {
|
||||
return samples.some((sample) => detector(sample));
|
||||
}
|
||||
|
||||
function findLastGroundedInventoryAddressDebug(items: unknown[]): Record<string, unknown> | null {
|
||||
if (!Array.isArray(items)) {
|
||||
return null;
|
||||
}
|
||||
for (let index = items.length - 1; index >= 0; index -= 1) {
|
||||
const item = items[index] as { role?: string; debug?: Record<string, unknown> } | null;
|
||||
if (!item || item.role !== "assistant" || !item.debug || typeof item.debug !== "object") {
|
||||
continue;
|
||||
}
|
||||
const debug = item.debug;
|
||||
const answerGroundingCheck =
|
||||
debug.answer_grounding_check && typeof debug.answer_grounding_check === "object"
|
||||
? (debug.answer_grounding_check as Record<string, unknown>)
|
||||
: null;
|
||||
const groundingStatus = String(answerGroundingCheck?.status ?? "");
|
||||
const detectedIntent = String(debug.detected_intent ?? "");
|
||||
const capabilityId = String(debug.capability_id ?? "");
|
||||
const rootFrameContext =
|
||||
debug.address_root_frame_context && typeof debug.address_root_frame_context === "object"
|
||||
? (debug.address_root_frame_context as Record<string, unknown>)
|
||||
: null;
|
||||
const rootIntent = String(rootFrameContext?.root_intent ?? "");
|
||||
const isInventoryContext =
|
||||
detectedIntent === "inventory_on_hand_as_of_date" ||
|
||||
capabilityId === "confirmed_inventory_on_hand_as_of_date" ||
|
||||
rootIntent === "inventory_on_hand_as_of_date";
|
||||
if (groundingStatus === "grounded" && isInventoryContext) {
|
||||
return debug;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findLastAddressDebugWithItem(items: unknown[]): Record<string, unknown> | null {
|
||||
if (!Array.isArray(items)) {
|
||||
return null;
|
||||
}
|
||||
for (let index = items.length - 1; index >= 0; index -= 1) {
|
||||
const item = items[index] as { role?: string; debug?: Record<string, unknown> } | null;
|
||||
if (!item || item.role !== "assistant" || !item.debug || typeof item.debug !== "object") {
|
||||
continue;
|
||||
}
|
||||
const debug = item.debug;
|
||||
if (String(debug.execution_lane ?? "") !== "address_query") {
|
||||
continue;
|
||||
}
|
||||
const extractedFilters =
|
||||
debug.extracted_filters && typeof debug.extracted_filters === "object"
|
||||
? (debug.extracted_filters as Record<string, unknown>)
|
||||
: null;
|
||||
const itemLabel =
|
||||
String(extractedFilters?.item ?? "").trim() ||
|
||||
(String(debug.anchor_type ?? "") === "item"
|
||||
? String(debug.anchor_value_resolved ?? debug.anchor_value_raw ?? "").trim()
|
||||
: "");
|
||||
if (itemLabel) {
|
||||
return debug;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findLastAddressDebug(items: unknown[]): Record<string, unknown> | null {
|
||||
if (!Array.isArray(items)) {
|
||||
return null;
|
||||
}
|
||||
for (let index = items.length - 1; index >= 0; index -= 1) {
|
||||
const item = items[index] as { role?: string; debug?: Record<string, unknown> } | null;
|
||||
if (!item || item.role !== "assistant" || !item.debug || typeof item.debug !== "object") {
|
||||
continue;
|
||||
}
|
||||
if (String(item.debug.execution_lane ?? "") === "address_query") {
|
||||
return item.debug;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function buildInventoryHistoryCapabilityFollowupReply(input: {
|
||||
organization: string | null;
|
||||
addressDebug: Record<string, unknown> | null;
|
||||
toNonEmptyString: (value: unknown) => string | null;
|
||||
}): string {
|
||||
const rootFrameContext =
|
||||
input.addressDebug?.address_root_frame_context &&
|
||||
typeof input.addressDebug.address_root_frame_context === "object"
|
||||
? (input.addressDebug.address_root_frame_context as Record<string, unknown>)
|
||||
: null;
|
||||
const extractedFilters =
|
||||
input.addressDebug?.extracted_filters && typeof input.addressDebug.extracted_filters === "object"
|
||||
? (input.addressDebug.extracted_filters as Record<string, unknown>)
|
||||
: null;
|
||||
const organization =
|
||||
input.organization ??
|
||||
input.toNonEmptyString(rootFrameContext?.organization) ??
|
||||
input.toNonEmptyString(extractedFilters?.organization);
|
||||
const lastAsOfDate =
|
||||
formatIsoDateForReply(rootFrameContext?.as_of_date) ??
|
||||
formatIsoDateForReply(extractedFilters?.as_of_date);
|
||||
const organizationPart = organization ? ` по компании «${organization}»` : "";
|
||||
const referenceLine = lastAsOfDate
|
||||
? `Да, могу. Сейчас мы уже смотрели складской срез${organizationPart} на ${lastAsOfDate}.`
|
||||
: `Да, могу показать исторические данные${organizationPart} в этом же складском контуре.`;
|
||||
return [
|
||||
referenceLine,
|
||||
`Могу показать исторические остатки${organizationPart} за нужный месяц, дату или год.`,
|
||||
"Например:",
|
||||
"- `на март 2020`",
|
||||
"- `на июнь 2016`",
|
||||
"- `за 2017 год`",
|
||||
"- `сравни июнь 2016 с текущим срезом`",
|
||||
"Если хочешь, сразу покажу нужный исторический период."
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
export function buildAddressMemoryRecapReply(input: {
|
||||
organization: string | null;
|
||||
addressDebug: Record<string, unknown> | null;
|
||||
toNonEmptyString: (value: unknown) => string | null;
|
||||
}): string {
|
||||
const extractedFilters =
|
||||
input.addressDebug?.extracted_filters && typeof input.addressDebug.extracted_filters === "object"
|
||||
? (input.addressDebug.extracted_filters as Record<string, unknown>)
|
||||
: null;
|
||||
const rootFrameContext =
|
||||
input.addressDebug?.address_root_frame_context &&
|
||||
typeof input.addressDebug.address_root_frame_context === "object"
|
||||
? (input.addressDebug.address_root_frame_context as Record<string, unknown>)
|
||||
: null;
|
||||
const item =
|
||||
input.toNonEmptyString(extractedFilters?.item) ??
|
||||
(String(input.addressDebug?.anchor_type ?? "") === "item"
|
||||
? input.toNonEmptyString(input.addressDebug?.anchor_value_resolved) ??
|
||||
input.toNonEmptyString(input.addressDebug?.anchor_value_raw)
|
||||
: null);
|
||||
const organization =
|
||||
input.organization ??
|
||||
input.toNonEmptyString(extractedFilters?.organization) ??
|
||||
input.toNonEmptyString(rootFrameContext?.organization);
|
||||
const scopedDate =
|
||||
formatIsoDateForReply(extractedFilters?.as_of_date) ??
|
||||
formatIsoDateForReply(rootFrameContext?.as_of_date) ??
|
||||
formatIsoDateForReply(extractedFilters?.period_to);
|
||||
|
||||
if (item) {
|
||||
const datePart = scopedDate ? ` в срезе на ${scopedDate}` : "";
|
||||
const organizationPart = organization ? ` по компании «${organization}»` : "";
|
||||
return [
|
||||
`Да, помню. Мы обсуждали позицию «${item}»${organizationPart}${datePart}.`,
|
||||
"Могу продолжить по ней без переписывания сущности: кто поставил, когда купили, по каким документам или кому продали."
|
||||
].join(" ");
|
||||
}
|
||||
|
||||
if (organization || scopedDate) {
|
||||
const organizationPart = organization ? ` по компании «${organization}»` : "";
|
||||
const datePart = scopedDate ? ` на ${scopedDate}` : "";
|
||||
return [
|
||||
`Да, помню. Мы уже смотрели адресный контур${organizationPart}${datePart}.`,
|
||||
"Могу кратко напомнить контекст или сразу продолжить следующий шаг по этому же сценарию."
|
||||
].join(" ");
|
||||
}
|
||||
|
||||
return "Да, помню предыдущий адресный контур. Могу кратко напомнить, что мы уже подтвердили, или сразу продолжить следующий шаг.";
|
||||
}
|
||||
|
||||
export function resolveAssistantLivingChatMemoryContext(
|
||||
input: ResolveAssistantLivingChatMemoryContextInput
|
||||
): AssistantLivingChatMemoryContext {
|
||||
const contextualInventoryHistoryCapabilityFollowup =
|
||||
String(input.modeDecisionReason ?? "") === "inventory_history_capability_followup_detected";
|
||||
const contextualMemoryRecapFollowup =
|
||||
String(input.modeDecisionReason ?? "") === "memory_recap_followup_detected";
|
||||
const sessionItems = Array.isArray(input.sessionItems) ? input.sessionItems : [];
|
||||
return {
|
||||
contextualInventoryHistoryCapabilityFollowup,
|
||||
contextualMemoryRecapFollowup,
|
||||
lastGroundedInventoryAddressDebug: contextualInventoryHistoryCapabilityFollowup
|
||||
? findLastGroundedInventoryAddressDebug(sessionItems)
|
||||
: null,
|
||||
lastMemoryAddressDebug: contextualMemoryRecapFollowup
|
||||
? findLastAddressDebugWithItem(sessionItems) ?? findLastAddressDebug(sessionItems)
|
||||
: null
|
||||
};
|
||||
}
|
||||
|
||||
export function createAssistantMemoryRecapPolicy(
|
||||
deps: AssistantMemoryRecapPolicyDeps
|
||||
) {
|
||||
function resolveRouteMemorySignals(
|
||||
input: ResolveAssistantRouteMemorySignalsInput
|
||||
): AssistantRouteMemorySignals {
|
||||
const samples = collectMessageSamples(input);
|
||||
const historicalCapabilitySignal = hasSignalAcrossSamples(
|
||||
samples,
|
||||
deps.hasHistoricalCapabilityFollowupSignal
|
||||
);
|
||||
const memoryRecapSignal = hasSignalAcrossSamples(
|
||||
samples,
|
||||
deps.hasConversationMemoryRecallFollowupSignal
|
||||
);
|
||||
return {
|
||||
contextualHistoricalCapabilityFollowupDetected: Boolean(
|
||||
input.capabilityMetaQuery &&
|
||||
!input.dataScopeMetaQuery &&
|
||||
!input.dataRetrievalSignal &&
|
||||
historicalCapabilitySignal &&
|
||||
deps.isGroundedInventoryContextDebug(input.lastGroundedAddressDebug)
|
||||
),
|
||||
contextualMemoryRecapFollowupDetected: Boolean(
|
||||
!input.dataScopeMetaQuery &&
|
||||
!input.capabilityMetaQuery &&
|
||||
!input.dataRetrievalSignal &&
|
||||
!input.strongDataSignal &&
|
||||
!input.aggregateBusinessAnalyticsSignal &&
|
||||
memoryRecapSignal &&
|
||||
input.hasPriorAddressDebug
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
resolveRouteMemorySignals
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
// @ts-nocheck
|
||||
|
||||
export interface ResolveAssistantMetaSignalSetInput {
|
||||
rawUserMessage?: unknown;
|
||||
repairedRawUserMessage?: unknown;
|
||||
effectiveAddressUserMessage?: unknown;
|
||||
repairedEffectiveAddressUserMessage?: unknown;
|
||||
}
|
||||
|
||||
export interface ResolveAssistantMetaFollowupOverGroundedAnswerInput {
|
||||
followupContext?: unknown;
|
||||
hasPriorAddressAnswerContext?: boolean;
|
||||
metaAnswerFollowupSignal?: boolean;
|
||||
vatEvaluativeFollowupSignal?: boolean;
|
||||
dataScopeMetaQuery?: boolean;
|
||||
capabilityMetaQuery?: boolean;
|
||||
aggregateBusinessAnalyticsSignal?: boolean;
|
||||
dataRetrievalSignal?: boolean;
|
||||
strongDataSignal?: boolean;
|
||||
resolvedMode?: unknown;
|
||||
resolvedIntent?: unknown;
|
||||
llmContractIntent?: unknown;
|
||||
llmContractMode?: unknown;
|
||||
}
|
||||
|
||||
export interface ResolveAssistantHardMetaModeInput {
|
||||
dataScopeMetaQuery?: boolean;
|
||||
capabilityMetaQuery?: boolean;
|
||||
dataRetrievalSignal?: boolean;
|
||||
}
|
||||
|
||||
export interface AssistantMetaSignalSet {
|
||||
dataScopeMetaQuery: boolean;
|
||||
capabilityMetaQuery: boolean;
|
||||
metaAnswerFollowupSignal: boolean;
|
||||
}
|
||||
|
||||
export interface AssistantMetaFollowupPolicyDeps {
|
||||
hasAssistantDataScopeMetaQuestionSignal: (text: unknown) => boolean;
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: (text: unknown) => boolean;
|
||||
hasMetaAnswerFollowupSignal: (text: unknown) => boolean;
|
||||
}
|
||||
|
||||
function collectMessageSamples(input: ResolveAssistantMetaSignalSetInput): string[] {
|
||||
const values = [
|
||||
input.rawUserMessage,
|
||||
input.repairedRawUserMessage,
|
||||
input.effectiveAddressUserMessage,
|
||||
input.repairedEffectiveAddressUserMessage
|
||||
];
|
||||
return Array.from(
|
||||
new Set(
|
||||
values
|
||||
.map((item) => String(item ?? "").trim())
|
||||
.filter((item) => item.length > 0)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function hasSignalAcrossSamples(
|
||||
samples: string[],
|
||||
detector: (text: unknown) => boolean
|
||||
): boolean {
|
||||
return samples.some((sample) => detector(sample));
|
||||
}
|
||||
|
||||
function hasImplicitHistoricalCapabilityMetaSignal(samples: string[]): boolean {
|
||||
return samples.some(
|
||||
(sample) =>
|
||||
/(?:историческ|история|архив|раньше|ретро|старые\s+данные)/iu.test(sample) &&
|
||||
/(?:мож(?:ешь|ем|но)|уме(?:ешь|ете))/iu.test(sample)
|
||||
);
|
||||
}
|
||||
|
||||
export function createAssistantMetaFollowupPolicy(
|
||||
deps: AssistantMetaFollowupPolicyDeps
|
||||
) {
|
||||
function resolveMetaSignalSet(
|
||||
input: ResolveAssistantMetaSignalSetInput
|
||||
): AssistantMetaSignalSet {
|
||||
const samples = collectMessageSamples(input);
|
||||
if (samples.length === 0) {
|
||||
return {
|
||||
dataScopeMetaQuery: false,
|
||||
capabilityMetaQuery: false,
|
||||
metaAnswerFollowupSignal: false
|
||||
};
|
||||
}
|
||||
return {
|
||||
dataScopeMetaQuery: hasSignalAcrossSamples(
|
||||
samples,
|
||||
deps.hasAssistantDataScopeMetaQuestionSignal
|
||||
),
|
||||
capabilityMetaQuery:
|
||||
hasSignalAcrossSamples(samples, deps.shouldHandleAsAssistantCapabilityMetaQuery) ||
|
||||
hasImplicitHistoricalCapabilityMetaSignal(samples),
|
||||
metaAnswerFollowupSignal: hasSignalAcrossSamples(
|
||||
samples,
|
||||
deps.hasMetaAnswerFollowupSignal
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
function resolveHardMetaMode(
|
||||
input: ResolveAssistantHardMetaModeInput
|
||||
): "data_scope" | "capability" | null {
|
||||
if (Boolean(input.dataScopeMetaQuery)) {
|
||||
return "data_scope";
|
||||
}
|
||||
if (Boolean(input.capabilityMetaQuery) && !Boolean(input.dataRetrievalSignal)) {
|
||||
return "capability";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isMetaFollowupOverGroundedAnswer(
|
||||
input: ResolveAssistantMetaFollowupOverGroundedAnswerInput
|
||||
): boolean {
|
||||
return Boolean(
|
||||
input.followupContext &&
|
||||
input.hasPriorAddressAnswerContext &&
|
||||
(input.metaAnswerFollowupSignal || input.vatEvaluativeFollowupSignal) &&
|
||||
!input.dataScopeMetaQuery &&
|
||||
!input.capabilityMetaQuery &&
|
||||
!input.aggregateBusinessAnalyticsSignal &&
|
||||
!input.dataRetrievalSignal &&
|
||||
!input.strongDataSignal &&
|
||||
String(input.resolvedMode ?? "") !== "address_query" &&
|
||||
String(input.resolvedIntent ?? "") === "unknown" &&
|
||||
(!input.llmContractIntent || String(input.llmContractIntent) === "unknown") &&
|
||||
String(input.llmContractMode ?? "") !== "address_query"
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
resolveMetaSignalSet,
|
||||
resolveHardMetaMode,
|
||||
isMetaFollowupOverGroundedAnswer
|
||||
};
|
||||
}
|
||||
@@ -50,8 +50,9 @@ export function createAssistantRoutePolicy(deps) {
|
||||
mergeKnownOrganizations,
|
||||
normalizeOrganizationScopeValue,
|
||||
resolveOrganizationSelectionFromMessage,
|
||||
hasAssistantDataScopeMetaQuestionSignal,
|
||||
shouldHandleAsAssistantCapabilityMetaQuery,
|
||||
resolveMetaSignalSet,
|
||||
resolveHardMetaMode,
|
||||
isMetaFollowupOverGroundedAnswer,
|
||||
hasDataRetrievalRequestSignal,
|
||||
hasAggregateBusinessAnalyticsSignal,
|
||||
hasStandaloneAddressTopicSignal,
|
||||
@@ -67,11 +68,9 @@ export function createAssistantRoutePolicy(deps) {
|
||||
hasShortDebtMirrorFollowupSignal,
|
||||
isInventorySelectedObjectIntent,
|
||||
hasShortInventoryObjectFollowupSignal,
|
||||
hasHistoricalCapabilityFollowupSignal,
|
||||
isGroundedInventoryContextDebug,
|
||||
hasConversationMemoryRecallFollowupSignal,
|
||||
resolveRouteMemorySignals,
|
||||
findLastAddressAssistantItem,
|
||||
hasMetaAnswerFollowupSignal,
|
||||
resolveAddressToolGateDecision,
|
||||
hasSameDateAccountFollowupSignalForPredecompose,
|
||||
hasLooseAllTimeAddressLookupSignal,
|
||||
@@ -112,14 +111,14 @@ export function createAssistantRoutePolicy(deps) {
|
||||
organizationClarificationCandidates.some((candidate) => normalizeOrganizationScopeValue(candidate) === organizationClarificationSelectionFromScope)
|
||||
? organizationClarificationSelectionFromScope
|
||||
: null);
|
||||
const dataScopeMetaQuery = hasAssistantDataScopeMetaQuestionSignal(rawUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(repairedRawUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(effectiveAddressUserMessage) ||
|
||||
hasAssistantDataScopeMetaQuestionSignal(repairedEffectiveAddressUserMessage);
|
||||
const capabilityMetaQuery = shouldHandleAsAssistantCapabilityMetaQuery(rawUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(repairedRawUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(effectiveAddressUserMessage) ||
|
||||
shouldHandleAsAssistantCapabilityMetaQuery(repairedEffectiveAddressUserMessage);
|
||||
const metaSignals = resolveMetaSignalSet({
|
||||
rawUserMessage,
|
||||
repairedRawUserMessage,
|
||||
effectiveAddressUserMessage,
|
||||
repairedEffectiveAddressUserMessage
|
||||
});
|
||||
const dataScopeMetaQuery = metaSignals.dataScopeMetaQuery;
|
||||
const capabilityMetaQuery = metaSignals.capabilityMetaQuery;
|
||||
const dataRetrievalSignal = hasDataRetrievalRequestSignal(rawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(repairedRawUserMessage) ||
|
||||
hasDataRetrievalRequestSignal(effectiveAddressUserMessage) ||
|
||||
@@ -225,30 +224,29 @@ export function createAssistantRoutePolicy(deps) {
|
||||
(llmFirstUnsupportedCandidate || llmContractMode === null) &&
|
||||
!protectedInventoryShortFollowup &&
|
||||
!organizationClarificationContinuationDetected);
|
||||
const contextualHistoricalCapabilityFollowupDetected = Boolean(capabilityMetaQuery &&
|
||||
!dataScopeMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
(hasHistoricalCapabilityFollowupSignal(rawUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(repairedRawUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasHistoricalCapabilityFollowupSignal(repairedEffectiveAddressUserMessage)) &&
|
||||
isGroundedInventoryContextDebug(lastGroundedAddressDebug));
|
||||
const contextualMemoryRecapFollowupDetected = Boolean(!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dataRetrievalSignal &&
|
||||
!strongDataSignal &&
|
||||
!aggregateBusinessAnalyticsSignal &&
|
||||
(hasConversationMemoryRecallFollowupSignal(rawUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(repairedRawUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasConversationMemoryRecallFollowupSignal(repairedEffectiveAddressUserMessage)) &&
|
||||
(lastGroundedAddressDebug ||
|
||||
findLastAddressAssistantItem(sessionItems)?.debug));
|
||||
const hardMetaMode = dataScopeMetaQuery
|
||||
? "data_scope"
|
||||
: capabilityMetaQuery && !dataRetrievalSignal
|
||||
? "capability"
|
||||
: null;
|
||||
const lastAddressAssistantDebug = sessionItems
|
||||
? findLastAddressAssistantItem(sessionItems)?.debug ?? null
|
||||
: null;
|
||||
const memorySignals = resolveRouteMemorySignals({
|
||||
rawUserMessage,
|
||||
repairedRawUserMessage,
|
||||
effectiveAddressUserMessage,
|
||||
repairedEffectiveAddressUserMessage,
|
||||
dataScopeMetaQuery,
|
||||
capabilityMetaQuery,
|
||||
dataRetrievalSignal,
|
||||
strongDataSignal,
|
||||
aggregateBusinessAnalyticsSignal,
|
||||
lastGroundedAddressDebug,
|
||||
hasPriorAddressDebug: Boolean(lastGroundedAddressDebug || lastAddressAssistantDebug)
|
||||
});
|
||||
const contextualHistoricalCapabilityFollowupDetected = memorySignals.contextualHistoricalCapabilityFollowupDetected;
|
||||
const contextualMemoryRecapFollowupDetected = memorySignals.contextualMemoryRecapFollowupDetected;
|
||||
const hardMetaMode = resolveHardMetaMode({
|
||||
dataScopeMetaQuery,
|
||||
capabilityMetaQuery,
|
||||
dataRetrievalSignal
|
||||
});
|
||||
if (hardMetaMode === "data_scope") {
|
||||
return {
|
||||
runAddressLane: false,
|
||||
@@ -389,10 +387,7 @@ export function createAssistantRoutePolicy(deps) {
|
||||
}
|
||||
};
|
||||
}
|
||||
const metaAnswerFollowupSignal = hasMetaAnswerFollowupSignal(rawUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(repairedRawUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(effectiveAddressUserMessage) ||
|
||||
hasMetaAnswerFollowupSignal(repairedEffectiveAddressUserMessage);
|
||||
const metaAnswerFollowupSignal = metaSignals.metaAnswerFollowupSignal;
|
||||
const baseToolGate = resolveAddressToolGateDecision(effectiveAddressUserMessage, followupContext, llmPreDecomposeMeta, rawUserMessage);
|
||||
const preserveAddressLaneSignal = Boolean((llmPreDecomposeMeta?.llmCanonicalCandidateDetected &&
|
||||
llmPreDecomposeMeta?.applied &&
|
||||
@@ -497,18 +492,21 @@ export function createAssistantRoutePolicy(deps) {
|
||||
sessionItems
|
||||
}));
|
||||
const hasPriorAddressAnswerContext = Boolean(lastGroundedAddressDebug || toNonEmptyString(followupContext?.previous_intent));
|
||||
const metaFollowupOverGroundedAnswer = Boolean(followupContext &&
|
||||
hasPriorAddressAnswerContext &&
|
||||
(metaAnswerFollowupSignal || vatEvaluativeFollowupSignal) &&
|
||||
!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!aggregateBusinessAnalyticsSignal &&
|
||||
!dataRetrievalSignal &&
|
||||
!strongDataSignal &&
|
||||
resolvedModeDetection.mode !== "address_query" &&
|
||||
resolvedIntentResolution.intent === "unknown" &&
|
||||
(!llmContractIntent || llmContractIntent === "unknown") &&
|
||||
llmContractMode !== "address_query");
|
||||
const metaFollowupOverGroundedAnswer = isMetaFollowupOverGroundedAnswer({
|
||||
followupContext,
|
||||
hasPriorAddressAnswerContext,
|
||||
metaAnswerFollowupSignal,
|
||||
vatEvaluativeFollowupSignal,
|
||||
dataScopeMetaQuery,
|
||||
capabilityMetaQuery,
|
||||
aggregateBusinessAnalyticsSignal,
|
||||
dataRetrievalSignal,
|
||||
strongDataSignal,
|
||||
resolvedMode: resolvedModeDetection.mode,
|
||||
resolvedIntent: resolvedIntentResolution.intent,
|
||||
llmContractIntent,
|
||||
llmContractMode
|
||||
});
|
||||
let runAddressLane = Boolean(baseToolGate?.runAddressLane);
|
||||
let toolGateDecision = String(baseToolGate?.decision ?? "skip_address_lane");
|
||||
let toolGateReason = String(baseToolGate?.reason ?? "no_address_signal_after_l0");
|
||||
|
||||
@@ -22,6 +22,8 @@ import * as assistantCoverageGrounding_1 from "./assistantCoverageGrounding";
|
||||
import * as assistantDeepTurnAttemptRuntimeAdapter_1 from "./assistantDeepTurnAttemptRuntimeAdapter";
|
||||
import * as assistantBoundaryPolicy_1 from "./assistantBoundaryPolicy";
|
||||
import * as assistantLivingModePolicy_1 from "./assistantLivingModePolicy";
|
||||
import * as assistantMetaFollowupPolicy_1 from "./assistantMetaFollowupPolicy";
|
||||
import * as assistantMemoryRecapPolicy_1 from "./assistantMemoryRecapPolicy";
|
||||
import * as assistantRoutePolicy_1 from "./assistantRoutePolicy";
|
||||
import * as assistantTransitionPolicy_1 from "./assistantTransitionPolicy";
|
||||
import * as assistantOrganizationScopeRuntimeAdapter_1 from "./assistantOrganizationScopeRuntimeAdapter";
|
||||
@@ -4714,6 +4716,16 @@ const assistantLivingModePolicy = (0, assistantLivingModePolicy_1.createAssistan
|
||||
hasAssistantCapabilityQuestionSignal,
|
||||
hasOperationalAdminActionRequestSignal
|
||||
});
|
||||
const assistantMetaFollowupPolicy = (0, assistantMetaFollowupPolicy_1.createAssistantMetaFollowupPolicy)({
|
||||
hasAssistantDataScopeMetaQuestionSignal: assistantLivingModePolicy.hasAssistantDataScopeMetaQuestionSignal,
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: assistantLivingModePolicy.shouldHandleAsAssistantCapabilityMetaQuery,
|
||||
hasMetaAnswerFollowupSignal: assistantLivingModePolicy.hasMetaAnswerFollowupSignal
|
||||
});
|
||||
const assistantMemoryRecapPolicy = (0, assistantMemoryRecapPolicy_1.createAssistantMemoryRecapPolicy)({
|
||||
hasHistoricalCapabilityFollowupSignal: assistantLivingModePolicy.hasHistoricalCapabilityFollowupSignal,
|
||||
hasConversationMemoryRecallFollowupSignal: assistantLivingModePolicy.hasConversationMemoryRecallFollowupSignal,
|
||||
isGroundedInventoryContextDebug
|
||||
});
|
||||
const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePolicy)({
|
||||
repairAddressMojibake,
|
||||
findLastGroundedAddressAnswerDebug,
|
||||
@@ -4721,8 +4733,9 @@ const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePoli
|
||||
mergeKnownOrganizations,
|
||||
normalizeOrganizationScopeValue,
|
||||
resolveOrganizationSelectionFromMessage,
|
||||
hasAssistantDataScopeMetaQuestionSignal: assistantLivingModePolicy.hasAssistantDataScopeMetaQuestionSignal,
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: assistantLivingModePolicy.shouldHandleAsAssistantCapabilityMetaQuery,
|
||||
resolveMetaSignalSet: assistantMetaFollowupPolicy.resolveMetaSignalSet,
|
||||
resolveHardMetaMode: assistantMetaFollowupPolicy.resolveHardMetaMode,
|
||||
isMetaFollowupOverGroundedAnswer: assistantMetaFollowupPolicy.isMetaFollowupOverGroundedAnswer,
|
||||
hasDataRetrievalRequestSignal: assistantLivingModePolicy.hasDataRetrievalRequestSignal,
|
||||
hasAggregateBusinessAnalyticsSignal,
|
||||
hasStandaloneAddressTopicSignal,
|
||||
@@ -4738,11 +4751,8 @@ const assistantRoutePolicy = (0, assistantRoutePolicy_1.createAssistantRoutePoli
|
||||
hasShortDebtMirrorFollowupSignal,
|
||||
isInventorySelectedObjectIntent,
|
||||
hasShortInventoryObjectFollowupSignal,
|
||||
hasHistoricalCapabilityFollowupSignal: assistantLivingModePolicy.hasHistoricalCapabilityFollowupSignal,
|
||||
isGroundedInventoryContextDebug,
|
||||
hasConversationMemoryRecallFollowupSignal: assistantLivingModePolicy.hasConversationMemoryRecallFollowupSignal,
|
||||
resolveRouteMemorySignals: assistantMemoryRecapPolicy.resolveRouteMemorySignals,
|
||||
findLastAddressAssistantItem,
|
||||
hasMetaAnswerFollowupSignal: assistantLivingModePolicy.hasMetaAnswerFollowupSignal,
|
||||
resolveAddressToolGateDecision,
|
||||
hasSameDateAccountFollowupSignalForPredecompose,
|
||||
hasLooseAllTimeAddressLookupSignal,
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildAddressMemoryRecapReply,
|
||||
createAssistantMemoryRecapPolicy,
|
||||
resolveAssistantLivingChatMemoryContext
|
||||
} from "../src/services/assistantMemoryRecapPolicy";
|
||||
|
||||
const policy = createAssistantMemoryRecapPolicy({
|
||||
hasHistoricalCapabilityFollowupSignal: (text: unknown) =>
|
||||
/историческ|архив|раньше/i.test(String(text ?? "")),
|
||||
hasConversationMemoryRecallFollowupSignal: (text: unknown) =>
|
||||
/помнишь|remember/i.test(String(text ?? "")),
|
||||
isGroundedInventoryContextDebug: (debug: unknown) =>
|
||||
String((debug as Record<string, unknown> | null)?.detected_intent ?? "") === "inventory_on_hand_as_of_date"
|
||||
});
|
||||
|
||||
describe("assistantMemoryRecapPolicy", () => {
|
||||
it("detects contextual historical capability follow-up", () => {
|
||||
const signals = policy.resolveRouteMemorySignals({
|
||||
rawUserMessage: "а исторические остатки тоже можешь?",
|
||||
repairedRawUserMessage: "",
|
||||
effectiveAddressUserMessage: "",
|
||||
repairedEffectiveAddressUserMessage: "",
|
||||
dataScopeMetaQuery: false,
|
||||
capabilityMetaQuery: true,
|
||||
dataRetrievalSignal: false,
|
||||
strongDataSignal: false,
|
||||
aggregateBusinessAnalyticsSignal: false,
|
||||
lastGroundedAddressDebug: {
|
||||
detected_intent: "inventory_on_hand_as_of_date"
|
||||
},
|
||||
hasPriorAddressDebug: true
|
||||
});
|
||||
|
||||
expect(signals.contextualHistoricalCapabilityFollowupDetected).toBe(true);
|
||||
expect(signals.contextualMemoryRecapFollowupDetected).toBe(false);
|
||||
});
|
||||
|
||||
it("detects contextual memory recap over prior address debug", () => {
|
||||
const signals = policy.resolveRouteMemorySignals({
|
||||
rawUserMessage: "а ты помнишь что мы обсуждали?",
|
||||
repairedRawUserMessage: "",
|
||||
effectiveAddressUserMessage: "",
|
||||
repairedEffectiveAddressUserMessage: "",
|
||||
dataScopeMetaQuery: false,
|
||||
capabilityMetaQuery: false,
|
||||
dataRetrievalSignal: false,
|
||||
strongDataSignal: false,
|
||||
aggregateBusinessAnalyticsSignal: false,
|
||||
lastGroundedAddressDebug: null,
|
||||
hasPriorAddressDebug: true
|
||||
});
|
||||
|
||||
expect(signals.contextualHistoricalCapabilityFollowupDetected).toBe(false);
|
||||
expect(signals.contextualMemoryRecapFollowupDetected).toBe(true);
|
||||
});
|
||||
|
||||
it("builds deterministic recap from prior selected object context", () => {
|
||||
const context = resolveAssistantLivingChatMemoryContext({
|
||||
modeDecisionReason: "memory_recap_followup_detected",
|
||||
sessionItems: [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
anchor_type: "item",
|
||||
anchor_value_resolved: "Рабочая станция",
|
||||
extracted_filters: {
|
||||
item: "Рабочая станция",
|
||||
as_of_date: "2022-02-28"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const reply = buildAddressMemoryRecapReply({
|
||||
organization: null,
|
||||
addressDebug: context.lastMemoryAddressDebug,
|
||||
toNonEmptyString: (value: unknown) => {
|
||||
const text = String(value ?? "").trim();
|
||||
return text.length > 0 ? text : null;
|
||||
}
|
||||
});
|
||||
|
||||
expect(context.contextualMemoryRecapFollowup).toBe(true);
|
||||
expect(reply).toContain("Рабочая станция");
|
||||
expect(reply).toContain("28.02.2022");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createAssistantMetaFollowupPolicy } from "../src/services/assistantMetaFollowupPolicy";
|
||||
|
||||
const policy = createAssistantMetaFollowupPolicy({
|
||||
hasAssistantDataScopeMetaQuestionSignal: (text: unknown) =>
|
||||
/по какой компании|какая база/i.test(String(text ?? "")),
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: (text: unknown) =>
|
||||
/что ты можешь|что ты умеешь/i.test(String(text ?? "")),
|
||||
hasMetaAnswerFollowupSignal: (text: unknown) =>
|
||||
/это норм|что думаешь/i.test(String(text ?? ""))
|
||||
});
|
||||
|
||||
describe("assistantMetaFollowupPolicy", () => {
|
||||
it("collects meta signals across message variants", () => {
|
||||
const signals = policy.resolveMetaSignalSet({
|
||||
rawUserMessage: "",
|
||||
repairedRawUserMessage: "",
|
||||
effectiveAddressUserMessage: "по какой компании мы можем работать?",
|
||||
repairedEffectiveAddressUserMessage: ""
|
||||
});
|
||||
|
||||
expect(signals.dataScopeMetaQuery).toBe(true);
|
||||
expect(signals.capabilityMetaQuery).toBe(false);
|
||||
expect(signals.metaAnswerFollowupSignal).toBe(false);
|
||||
});
|
||||
|
||||
it("treats historical capability phrasing as capability meta follow-up", () => {
|
||||
const signals = policy.resolveMetaSignalSet({
|
||||
rawUserMessage: "а исторические остатки тоже можешь?",
|
||||
repairedRawUserMessage: "",
|
||||
effectiveAddressUserMessage: "",
|
||||
repairedEffectiveAddressUserMessage: ""
|
||||
});
|
||||
|
||||
expect(signals.capabilityMetaQuery).toBe(true);
|
||||
});
|
||||
|
||||
it("resolves hard meta mode with data retrieval guard", () => {
|
||||
expect(
|
||||
policy.resolveHardMetaMode({
|
||||
dataScopeMetaQuery: true,
|
||||
capabilityMetaQuery: false,
|
||||
dataRetrievalSignal: false
|
||||
})
|
||||
).toBe("data_scope");
|
||||
|
||||
expect(
|
||||
policy.resolveHardMetaMode({
|
||||
dataScopeMetaQuery: false,
|
||||
capabilityMetaQuery: true,
|
||||
dataRetrievalSignal: true
|
||||
})
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("detects evaluative meta follow-up over grounded answer", () => {
|
||||
const detected = policy.isMetaFollowupOverGroundedAnswer({
|
||||
followupContext: { previous_intent: "vat_payable_forecast" },
|
||||
hasPriorAddressAnswerContext: true,
|
||||
metaAnswerFollowupSignal: true,
|
||||
vatEvaluativeFollowupSignal: false,
|
||||
dataScopeMetaQuery: false,
|
||||
capabilityMetaQuery: false,
|
||||
aggregateBusinessAnalyticsSignal: false,
|
||||
dataRetrievalSignal: false,
|
||||
strongDataSignal: false,
|
||||
resolvedMode: "unsupported",
|
||||
resolvedIntent: "unknown",
|
||||
llmContractIntent: "unknown",
|
||||
llmContractMode: "unsupported"
|
||||
});
|
||||
|
||||
expect(detected).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -32,8 +32,35 @@ function buildPolicy(overrides: Record<string, unknown> = {}) {
|
||||
),
|
||||
normalizeOrganizationScopeValue,
|
||||
resolveOrganizationSelectionFromMessage: () => null,
|
||||
hasAssistantDataScopeMetaQuestionSignal: (text: string) => /по какой компании|какая база|по каким конторам/i.test(text),
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: (text: string) => /что ты можешь|что ты умеешь/i.test(text),
|
||||
resolveMetaSignalSet: (input: {
|
||||
rawUserMessage?: string;
|
||||
repairedRawUserMessage?: string;
|
||||
effectiveAddressUserMessage?: string;
|
||||
repairedEffectiveAddressUserMessage?: string;
|
||||
}) => {
|
||||
const samples = [
|
||||
input.rawUserMessage,
|
||||
input.repairedRawUserMessage,
|
||||
input.effectiveAddressUserMessage,
|
||||
input.repairedEffectiveAddressUserMessage
|
||||
].join(" ");
|
||||
return {
|
||||
dataScopeMetaQuery: /по какой компании|какая база|по каким конторам/i.test(samples),
|
||||
capabilityMetaQuery: /что ты можешь|что ты умеешь/i.test(samples),
|
||||
metaAnswerFollowupSignal: /это норм|что думаешь/i.test(samples)
|
||||
};
|
||||
},
|
||||
resolveHardMetaMode: (input: {
|
||||
dataScopeMetaQuery?: boolean;
|
||||
capabilityMetaQuery?: boolean;
|
||||
dataRetrievalSignal?: boolean;
|
||||
}) =>
|
||||
input.dataScopeMetaQuery
|
||||
? "data_scope"
|
||||
: input.capabilityMetaQuery && !input.dataRetrievalSignal
|
||||
? "capability"
|
||||
: null,
|
||||
isMetaFollowupOverGroundedAnswer: () => false,
|
||||
hasDataRetrievalRequestSignal: () => false,
|
||||
hasAggregateBusinessAnalyticsSignal: () => false,
|
||||
hasStandaloneAddressTopicSignal: () => false,
|
||||
@@ -49,11 +76,11 @@ function buildPolicy(overrides: Record<string, unknown> = {}) {
|
||||
hasShortDebtMirrorFollowupSignal: () => false,
|
||||
isInventorySelectedObjectIntent: (intent: unknown) => /inventory/i.test(String(intent ?? "")),
|
||||
hasShortInventoryObjectFollowupSignal: () => false,
|
||||
hasHistoricalCapabilityFollowupSignal: () => false,
|
||||
isGroundedInventoryContextDebug: (debug: unknown) => Boolean(debug),
|
||||
hasConversationMemoryRecallFollowupSignal: () => false,
|
||||
resolveRouteMemorySignals: () => ({
|
||||
contextualHistoricalCapabilityFollowupDetected: false,
|
||||
contextualMemoryRecapFollowupDetected: false
|
||||
}),
|
||||
findLastAddressAssistantItem: () => null,
|
||||
hasMetaAnswerFollowupSignal: () => false,
|
||||
resolveAddressToolGateDecision: () => ({
|
||||
runAddressLane: false,
|
||||
decision: "skip_address_lane",
|
||||
@@ -118,7 +145,10 @@ describe("assistantRoutePolicy", () => {
|
||||
|
||||
it("routes memory recap follow-up over grounded answer to chat", () => {
|
||||
const policy = buildPolicy({
|
||||
hasConversationMemoryRecallFollowupSignal: () => true,
|
||||
resolveRouteMemorySignals: () => ({
|
||||
contextualHistoricalCapabilityFollowupDetected: false,
|
||||
contextualMemoryRecapFollowupDetected: true
|
||||
}),
|
||||
findLastGroundedAddressAnswerDebug: () => ({ execution_lane: "address_query" })
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user