АРЧ АП11 - Добавить builder смешанных AGENT-прогонов и починить meta-memory recall в turnaround 11

This commit is contained in:
2026-04-17 12:54:47 +03:00
parent f7edf6aacb
commit 9f0f7f3e79
22 changed files with 5765 additions and 65 deletions
@@ -107,9 +107,14 @@ function createAssistantLivingModePolicy(deps) {
}
const hasMemoryCue = samples.some((sample) => /(?:помни(?:шь|те|м)?|remember|recall)/iu.test(sample));
const hasDiscussionCue = samples.some((sample) => /(?:обсуждал[аи]?|говорил[аи]?|смотрел[аи]?|разбирал[аи]?|спрашивал[аи]?)/iu.test(sample));
if (!hasMemoryCue || !hasDiscussionCue) {
const hasExplicitRecapPrompt = samples.some((sample) => /(?:что\s+мы\s+.*(?:обсуждали|выяснили)|что\s+уже\s+выяснили|напомни\s+что\s+мы|what\s+we\s+already\s+(?:discussed|figured\s+out))/iu.test(sample));
if (!hasMemoryCue || !(hasDiscussionCue || hasExplicitRecapPrompt)) {
return false;
}
if (hasExplicitRecapPrompt) {
return !samples.some((sample) => hasAssistantDataScopeMetaQuestionSignal(sample) ||
shouldHandleAsAssistantCapabilityMetaQuery(sample));
}
return !samples.some((sample) => hasAssistantDataScopeMetaQuestionSignal(sample) ||
shouldHandleAsAssistantCapabilityMetaQuery(sample) ||
hasDataRetrievalRequestSignal(sample) ||
@@ -27,6 +27,9 @@ function collectMessageSamples(input) {
function hasSignalAcrossSamples(samples, detector) {
return samples.some((sample) => detector(sample));
}
function hasExplicitRecapPromptSignal(samples) {
return samples.some((sample) => /(?:что\s+мы\s+.*(?:обсуждали|выяснили)|что\s+уже\s+выяснили|что\s+уже\s+поняли|напомни\s+что\s+мы)/iu.test(sample));
}
function findLastGroundedInventoryAddressDebug(items) {
if (!Array.isArray(items)) {
return null;
@@ -182,6 +185,7 @@ function createAssistantMemoryRecapPolicy(deps) {
const samples = collectMessageSamples(input);
const historicalCapabilitySignal = hasSignalAcrossSamples(samples, deps.hasHistoricalCapabilityFollowupSignal);
const memoryRecapSignal = hasSignalAcrossSamples(samples, deps.hasConversationMemoryRecallFollowupSignal);
const explicitRecapPromptSignal = hasExplicitRecapPromptSignal(samples);
return {
contextualHistoricalCapabilityFollowupDetected: Boolean(input.capabilityMetaQuery &&
!input.dataScopeMetaQuery &&
@@ -190,10 +194,9 @@ function createAssistantMemoryRecapPolicy(deps) {
deps.isGroundedInventoryContextDebug(input.lastGroundedAddressDebug)),
contextualMemoryRecapFollowupDetected: Boolean(!input.dataScopeMetaQuery &&
!input.capabilityMetaQuery &&
!input.dataRetrievalSignal &&
!input.strongDataSignal &&
!input.aggregateBusinessAnalyticsSignal &&
memoryRecapSignal &&
(explicitRecapPromptSignal || (!input.dataRetrievalSignal && !input.strongDataSignal)) &&
input.hasPriorAddressDebug)
};
}
+29 -29
View File
@@ -304,36 +304,36 @@ function createAssistantRoutePolicy(deps) {
}
};
}
if (nonDomainQueryIndexed) {
if (contextualMemoryRecapFollowupDetected) {
return {
runAddressLane: false,
toolGateDecision: "skip_address_lane",
toolGateReason: "memory_recap_followup_detected",
livingMode: "chat",
livingReason: "memory_recap_followup_detected",
orchestrationContract: {
schema_version: "assistant_orchestration_contract_v1",
hard_meta_mode: "non_domain",
provider_execution: providerExecution,
address_mode: resolvedModeDetection.mode,
address_mode_confidence: resolvedModeDetection.confidence,
address_intent: resolvedIntentResolution.intent,
address_intent_confidence: resolvedIntentResolution.confidence,
strong_data_signal_detected: strongDataSignal,
data_retrieval_signal_detected: dataRetrievalSignal,
followup_context_detected: Boolean(followupContext || lastGroundedAddressDebug),
unsupported_address_intent_fallback_to_deep: false,
final_decision: {
run_address_lane: false,
tool_gate_decision: "skip_address_lane",
tool_gate_reason: "memory_recap_followup_detected",
living_mode: "chat",
living_reason: "memory_recap_followup_detected"
}
if (contextualMemoryRecapFollowupDetected) {
return {
runAddressLane: false,
toolGateDecision: "skip_address_lane",
toolGateReason: "memory_recap_followup_detected",
livingMode: "chat",
livingReason: "memory_recap_followup_detected",
orchestrationContract: {
schema_version: "assistant_orchestration_contract_v1",
hard_meta_mode: nonDomainQueryIndexed ? "non_domain" : null,
provider_execution: providerExecution,
address_mode: resolvedModeDetection.mode,
address_mode_confidence: resolvedModeDetection.confidence,
address_intent: resolvedIntentResolution.intent,
address_intent_confidence: resolvedIntentResolution.confidence,
strong_data_signal_detected: strongDataSignal,
data_retrieval_signal_detected: dataRetrievalSignal,
followup_context_detected: Boolean(followupContext || lastGroundedAddressDebug || lastAddressAssistantDebug),
unsupported_address_intent_fallback_to_deep: false,
final_decision: {
run_address_lane: false,
tool_gate_decision: "skip_address_lane",
tool_gate_reason: "memory_recap_followup_detected",
living_mode: "chat",
living_reason: "memory_recap_followup_detected"
}
};
}
}
};
}
if (nonDomainQueryIndexed) {
return {
runAddressLane: false,
toolGateDecision: "skip_address_lane",
@@ -171,9 +171,14 @@ export function createAssistantLivingModePolicy(deps: AssistantLivingModePolicyD
}
const hasMemoryCue = samples.some((sample) => /(?:помни(?:шь|те|м)?|remember|recall)/iu.test(sample));
const hasDiscussionCue = samples.some((sample) => /(?:обсуждал[аи]?|говорил[аи]?|смотрел[аи]?|разбирал[аи]?|спрашивал[аи]?)/iu.test(sample));
if (!hasMemoryCue || !hasDiscussionCue) {
const hasExplicitRecapPrompt = samples.some((sample) => /(?:что\s+мы\s+.*(?:обсуждали|выяснили)|что\s+уже\s+выяснили|напомни\s+что\s+мы|what\s+we\s+already\s+(?:discussed|figured\s+out))/iu.test(sample));
if (!hasMemoryCue || !(hasDiscussionCue || hasExplicitRecapPrompt)) {
return false;
}
if (hasExplicitRecapPrompt) {
return !samples.some((sample) => hasAssistantDataScopeMetaQuestionSignal(sample) ||
shouldHandleAsAssistantCapabilityMetaQuery(sample));
}
return !samples.some((sample) => hasAssistantDataScopeMetaQuestionSignal(sample) ||
shouldHandleAsAssistantCapabilityMetaQuery(sample) ||
hasDataRetrievalRequestSignal(sample) ||
@@ -69,6 +69,14 @@ function hasSignalAcrossSamples(
return samples.some((sample) => detector(sample));
}
function hasExplicitRecapPromptSignal(samples: string[]): boolean {
return samples.some((sample) =>
/(?:что\s+мы\s+.*(?:обсуждали|выяснили)|что\s+уже\s+выяснили|что\s+уже\s+поняли|напомни\s+что\s+мы)/iu.test(
sample
)
);
}
function findLastGroundedInventoryAddressDebug(items: unknown[]): Record<string, unknown> | null {
if (!Array.isArray(items)) {
return null;
@@ -269,6 +277,7 @@ export function createAssistantMemoryRecapPolicy(
samples,
deps.hasConversationMemoryRecallFollowupSignal
);
const explicitRecapPromptSignal = hasExplicitRecapPromptSignal(samples);
return {
contextualHistoricalCapabilityFollowupDetected: Boolean(
input.capabilityMetaQuery &&
@@ -280,10 +289,9 @@ export function createAssistantMemoryRecapPolicy(
contextualMemoryRecapFollowupDetected: Boolean(
!input.dataScopeMetaQuery &&
!input.capabilityMetaQuery &&
!input.dataRetrievalSignal &&
!input.strongDataSignal &&
!input.aggregateBusinessAnalyticsSignal &&
memoryRecapSignal &&
(explicitRecapPromptSignal || (!input.dataRetrievalSignal && !input.strongDataSignal)) &&
input.hasPriorAddressDebug
)
};
@@ -338,36 +338,36 @@ export function createAssistantRoutePolicy(deps) {
}
};
}
if (nonDomainQueryIndexed) {
if (contextualMemoryRecapFollowupDetected) {
return {
runAddressLane: false,
toolGateDecision: "skip_address_lane",
toolGateReason: "memory_recap_followup_detected",
livingMode: "chat",
livingReason: "memory_recap_followup_detected",
orchestrationContract: {
schema_version: "assistant_orchestration_contract_v1",
hard_meta_mode: "non_domain",
provider_execution: providerExecution,
address_mode: resolvedModeDetection.mode,
address_mode_confidence: resolvedModeDetection.confidence,
address_intent: resolvedIntentResolution.intent,
address_intent_confidence: resolvedIntentResolution.confidence,
strong_data_signal_detected: strongDataSignal,
data_retrieval_signal_detected: dataRetrievalSignal,
followup_context_detected: Boolean(followupContext || lastGroundedAddressDebug),
unsupported_address_intent_fallback_to_deep: false,
final_decision: {
run_address_lane: false,
tool_gate_decision: "skip_address_lane",
tool_gate_reason: "memory_recap_followup_detected",
living_mode: "chat",
living_reason: "memory_recap_followup_detected"
}
if (contextualMemoryRecapFollowupDetected) {
return {
runAddressLane: false,
toolGateDecision: "skip_address_lane",
toolGateReason: "memory_recap_followup_detected",
livingMode: "chat",
livingReason: "memory_recap_followup_detected",
orchestrationContract: {
schema_version: "assistant_orchestration_contract_v1",
hard_meta_mode: nonDomainQueryIndexed ? "non_domain" : null,
provider_execution: providerExecution,
address_mode: resolvedModeDetection.mode,
address_mode_confidence: resolvedModeDetection.confidence,
address_intent: resolvedIntentResolution.intent,
address_intent_confidence: resolvedIntentResolution.confidence,
strong_data_signal_detected: strongDataSignal,
data_retrieval_signal_detected: dataRetrievalSignal,
followup_context_detected: Boolean(followupContext || lastGroundedAddressDebug || lastAddressAssistantDebug),
unsupported_address_intent_fallback_to_deep: false,
final_decision: {
run_address_lane: false,
tool_gate_decision: "skip_address_lane",
tool_gate_reason: "memory_recap_followup_detected",
living_mode: "chat",
living_reason: "memory_recap_followup_detected"
}
};
}
}
};
}
if (nonDomainQueryIndexed) {
return {
runAddressLane: false,
toolGateDecision: "skip_address_lane",
@@ -34,6 +34,14 @@ function buildPolicy() {
}
describe("assistantLivingModePolicy", () => {
it("detects explicit recap wording as memory signal even when selected-object words are present", () => {
const policy = buildPolicy();
expect(
policy.hasConversationMemoryRecallFollowupSignal("а ты помнишь, что мы по этой позиции уже выяснили?")
).toBe(true);
});
it("routes casual small-talk to chat mode", () => {
const policy = buildPolicy();
@@ -55,6 +55,25 @@ describe("assistantMemoryRecapPolicy", () => {
expect(signals.contextualMemoryRecapFollowupDetected).toBe(true);
});
it("treats explicit recap wording over selected-object phrasing as memory follow-up even when data cues are present", () => {
const signals = policy.resolveRouteMemorySignals({
rawUserMessage: "а ты помнишь, что мы по этой позиции уже выяснили?",
repairedRawUserMessage: "",
effectiveAddressUserMessage: "",
repairedEffectiveAddressUserMessage: "",
dataScopeMetaQuery: false,
capabilityMetaQuery: false,
dataRetrievalSignal: true,
strongDataSignal: true,
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",
@@ -187,6 +187,43 @@ describe("assistantRoutePolicy", () => {
expect(decision.livingReason).toBe("memory_recap_followup_detected");
});
it("routes explicit recap wording with selected-object phrasing to chat even when address-like cues exist", () => {
const policy = buildPolicy({
hasStrongDataIntentSignal: () => true,
hasDataRetrievalRequestSignal: () => true,
resolveRouteMemorySignals: () => ({
contextualHistoricalCapabilityFollowupDetected: false,
contextualMemoryRecapFollowupDetected: true
}),
findLastGroundedAddressAnswerDebug: () => ({
execution_lane: "address_query",
detected_intent: "inventory_purchase_provenance_for_item"
})
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "а ты помнишь, что мы по этой позиции уже выяснили?",
effectiveAddressUserMessage: "а ты помнишь, что мы по этой позиции уже выяснили?",
followupContext: null,
llmPreDecomposeMeta: {
applied: false,
reason: "normalized_fragment_rejected_semantic_guard",
predecomposeContract: {
mode: "unsupported",
mode_confidence: "low",
intent: "unknown",
intent_confidence: "low"
}
},
useMock: false
});
expect(decision.runAddressLane).toBe(false);
expect(decision.toolGateReason).toBe("memory_recap_followup_detected");
expect(decision.livingMode).toBe("chat");
expect(decision.livingReason).toBe("memory_recap_followup_detected");
});
it("does not force unsupported-intent fallback when predecompose runtime is unavailable", () => {
const policy = buildPolicy({
hasStrongDataIntentSignal: () => true,