АРЧ АП11 - Добавить builder смешанных AGENT-прогонов и починить meta-memory recall в turnaround 11
This commit is contained in:
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user