АРЧ АП11 - Вынести meta и memory recap policy из route runtime и закрыть Phase 5 агентным прогоном

This commit is contained in:
dctouch 2026-04-17 11:39:45 +03:00
parent b3572d9d11
commit 15fa643fc8
17 changed files with 1342 additions and 142 deletions

View File

@ -0,0 +1,106 @@
{
"schema_version": "domain_truth_harness_spec_v1",
"scenario_id": "address_truth_harness_phase5_meta_memory_mix",
"domain": "address_phase5_meta_memory_mix",
"title": "Phase 5 meta and memory recap replay over interrupted address context",
"description": "Targeted replay for meta and memory recap policy extraction: inventory root, historical capability follow-up, data-scope meta interrupt, selected-object provenance, capability meta interrupt, and deterministic memory recap.",
"bindings": {},
"steps": [
{
"step_id": "step_01_inventory_root_march_2021",
"title": "Inventory root establishes grounded March 2021 context",
"question": "какие остатки на складе на март 2021",
"allowed_reply_types": [
"factual"
],
"expected_intents": [
"inventory_on_hand_as_of_date"
],
"required_filters": {
"as_of_date": "2021-03-31",
"period_from": "2021-03-01",
"period_to": "2021-03-31"
},
"required_direct_answer_patterns_any": [
"31\\.03\\.2021",
"(?i)на складе"
]
},
{
"step_id": "step_02_inventory_history_capability_followup",
"title": "Historical capability follow-up stays contextual and human",
"question": "а исторические остатки тоже можешь?",
"required_answer_patterns_any": [
"(?i)историческ",
"(?i)могу",
"(?i)март 2020|июнь 2016|2017"
],
"forbidden_answer_patterns": [
"(?i)^сейчас не дам прямой адресный ответ",
"(?i)^в текущем адресном контуре этот запрос лучше не закрывать в лоб"
]
},
{
"step_id": "step_03_data_scope_meta_interrupt",
"title": "Data-scope meta question stays deterministic and non-technical",
"question": "по какой компании мы сейчас работаем?",
"required_answer_patterns_any": [
"(?i)компан|организац|контур",
"(?i)работ",
"(?i)альтернатив|доступн|выбран"
],
"forbidden_answer_patterns": [
"(?i)tool_gate_reason",
"(?i)hard_meta_mode",
"(?i)living_router_reason"
]
},
{
"step_id": "step_04_selected_item_supplier",
"title": "Selected-object provenance survives the meta interrupt",
"question": "По выбранному объекту \"Столешница 600*3050*26 альмандин\": кто нам это поставил?",
"allowed_reply_types": [
"factual"
],
"expected_intents": [
"inventory_purchase_provenance_for_item"
],
"required_direct_answer_patterns_any": [
"(?i)столешница 600\\*3050\\*26 альмандин",
"(?i)поставщик|поставил|куплен",
"(?i)союз|торговый дом"
],
"forbidden_direct_answer_patterns": [
"(?i)^на 31\\.03\\.2021 на складе",
"(?i)^сейчас не дам прямой адресный ответ"
]
},
{
"step_id": "step_05_capability_meta_interrupt",
"title": "Capability meta question does not break the address context",
"question": "что ты умеешь?",
"required_answer_patterns_any": [
"(?i)могу|умею",
"(?i)остатк|документ|контрагент|ндс"
],
"forbidden_answer_patterns": [
"(?i)tool_gate_reason",
"(?i)address_mode"
]
},
{
"step_id": "step_06_memory_recap_after_interrupts",
"title": "Memory recap still remembers the selected object after meta interruptions",
"question": "а ты помнишь, что мы по этой позиции уже выяснили?",
"required_answer_patterns_any": [
"(?i)помню",
"(?i)столешница 600\\*3050\\*26 альмандин",
"(?i)позици"
],
"forbidden_answer_patterns": [
"(?i)^сейчас не дам прямой адресный ответ",
"(?i)^в текущем адресном контуре этот запрос лучше не закрывать в лоб"
]
}
]
}

View File

@ -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

View File

@ -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
};
}

View File

@ -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
};
}

View File

@ -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");

View File

@ -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,

View File

@ -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

View File

@ -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
};
}

View File

@ -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
};
}

View File

@ -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");

View File

@ -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,

View File

@ -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");
});
});

View File

@ -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);
});
});

View File

@ -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" })
});

View File

@ -1,4 +1,38 @@
[
{
"generation_id": "gen-ag04170830-5f771d",
"created_at": "2026-04-17T08:30:44+00:00",
"mode": "saved_user_sessions",
"title": "AGENT | Phase 5 meta and memory recap replay over interrupted address context",
"count": 6,
"domain": "address_phase5_meta_memory_mix",
"questions": [
"какие остатки на складе на март 2021",
"а исторические остатки тоже можешь?",
"по какой компании мы сейчас работаем?",
"По выбранному объекту \"Столешница 600*3050*26 альмандин\": кто нам это поставил?",
"что ты умеешь?",
"а ты помнишь, что мы по этой позиции уже выяснили?"
],
"generated_by": "codex_agent",
"saved_case_set_file": "assistant_autogen_saved_user_sessions_20260417083044_gen-ag04170830-5f771d.json",
"context": {
"llm_provider": null,
"model": null,
"assistant_prompt_version": null,
"decomposition_prompt_version": null,
"prompt_fingerprint": null,
"autogen_personality_id": null,
"autogen_personality_prompt": null,
"source_session_id": null,
"saved_session_file": "assistant_saved_session_20260417083044_gen-ag04170830-5f771d.json",
"saved_case_set_kind": "agent_semantic_scenario",
"agent_run": true,
"agent_focus": "meta and memory recap replay over interrupted address context",
"architecture_phase": "turnaround_11_phase5",
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\address_truth_harness_phase5_meta_memory_mix.json"
}
},
{
"generation_id": "gen-ag04170808-1907fa",
"created_at": "2026-04-17T08:08:08+00:00",

View File

@ -0,0 +1,93 @@
{
"saved_at": "2026-04-17T08:30:44+00:00",
"generation_id": "gen-ag04170830-5f771d",
"mode": "saved_user_sessions",
"title": "AGENT | Phase 5 meta and memory recap replay over interrupted address context",
"agent_run": true,
"questions": [
"какие остатки на складе на март 2021",
"а исторические остатки тоже можешь?",
"по какой компании мы сейчас работаем?",
"По выбранному объекту \"Столешница 600*3050*26 альмандин\": кто нам это поставил?",
"что ты умеешь?",
"а ты помнишь, что мы по этой позиции уже выяснили?"
],
"metadata": {
"assistant_prompt_version": null,
"decomposition_prompt_version": null,
"prompt_fingerprint": null,
"agent_focus": "meta and memory recap replay over interrupted address context",
"architecture_phase": "turnaround_11_phase5",
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\address_truth_harness_phase5_meta_memory_mix.json"
},
"source_session_id": null,
"session": {
"session_id": null,
"mode": "agent_semantic_run",
"items": [
{
"message_id": "agent-user-001",
"role": "user",
"text": "какие остатки на складе на март 2021",
"created_at": "2026-04-17T08:30:44+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-002",
"role": "user",
"text": "а исторические остатки тоже можешь?",
"created_at": "2026-04-17T08:30:44+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-003",
"role": "user",
"text": "по какой компании мы сейчас работаем?",
"created_at": "2026-04-17T08:30:44+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-004",
"role": "user",
"text": "По выбранному объекту \"Столешница 600*3050*26 альмандин\": кто нам это поставил?",
"created_at": "2026-04-17T08:30:44+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-005",
"role": "user",
"text": "что ты умеешь?",
"created_at": "2026-04-17T08:30:44+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-006",
"role": "user",
"text": "а ты помнишь, что мы по этой позиции уже выяснили?",
"created_at": "2026-04-17T08:30:44+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
}
],
"agent_run": true,
"metadata": {
"assistant_prompt_version": null,
"decomposition_prompt_version": null,
"prompt_fingerprint": null,
"agent_focus": "meta and memory recap replay over interrupted address context",
"architecture_phase": "turnaround_11_phase5",
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\address_truth_harness_phase5_meta_memory_mix.json"
}
}
}

View File

@ -0,0 +1,43 @@
{
"suite_id": "assistant_saved_session_gen-ag04170830-5f771d",
"suite_version": "0.1.0",
"schema_version": "assistant_saved_session_suite_v0_1",
"generated_at": "2026-04-17T08:30:44+00:00",
"generation_id": "gen-ag04170830-5f771d",
"mode": "saved_user_sessions",
"title": "AGENT | Phase 5 meta and memory recap replay over interrupted address context",
"domain": "address_phase5_meta_memory_mix",
"scenario_count": 1,
"case_ids": [
"SAVED-001"
],
"cases": [
{
"case_id": "SAVED-001",
"scenario_tag": "agent_saved_user_sessions",
"title": "AGENT | Phase 5 meta and memory recap replay over interrupted address context",
"question_type": "followup",
"broadness_level": "medium",
"turns": [
{
"user_message": "какие остатки на складе на март 2021"
},
{
"user_message": "а исторические остатки тоже можешь?"
},
{
"user_message": "по какой компании мы сейчас работаем?"
},
{
"user_message": "По выбранному объекту \"Столешница 600*3050*26 альмандин\": кто нам это поставил?"
},
{
"user_message": "что ты умеешь?"
},
{
"user_message": "а ты помнишь, что мы по этой позиции уже выяснили?"
}
]
}
]
}