АРЧ АП11 - Архитектура после регресса: Архитектура: протянуть continuity snapshot в route arbitration и убрать non-domain drift при выборе организации
This commit is contained in:
parent
cd734f568c
commit
21738f8662
|
|
@ -234,6 +234,15 @@ Still open after the accepted phase11 replay:
|
||||||
- answer shaping on some long exact list answers is still heavier than the target human product feel, even though the truth path and routing are now correct;
|
- answer shaping on some long exact list answers is still heavier than the target human product feel, even though the truth path and routing are now correct;
|
||||||
- the next architecture slice should move to wider saved-session acceptance coverage and humanized exact-answer presentation, not back to isolated prompt-level repairs.
|
- the next architecture slice should move to wider saved-session acceptance coverage and humanized exact-answer presentation, not back to isolated prompt-level repairs.
|
||||||
|
|
||||||
|
Latest continuity-authority convergence evidence after the current route pass:
|
||||||
|
|
||||||
|
- the route hot path now consumes the shared continuity snapshot directly instead of relying only on local `findLastGrounded...` helpers:
|
||||||
|
- grounded address context can now survive into route arbitration even when the legacy local helper returns nothing for the current turn shape;
|
||||||
|
- active organization continuity is now allowed to participate in organization-selection arbitration, instead of forcing route policy to reconstruct that context only from immediate clarification payloads;
|
||||||
|
- a bare organization-selection turn after grounded bookkeeping continuity is no longer automatically classified as `non_domain_query_indexed` noise when the session still carries valid grounded business context;
|
||||||
|
- session organization recovery inside the data-scope layer now has a final fallback to the same continuity snapshot, reducing one more duplicate path that used to rescan assistant history independently;
|
||||||
|
- this pass does not yet finish full single-owner continuity, but it narrows one of the remaining seams where route arbitration and scope memory could disagree about whether the session was still grounded.
|
||||||
|
|
||||||
## Next Execution Slice (2026-04-18)
|
## Next Execution Slice (2026-04-18)
|
||||||
|
|
||||||
The project is now moving from:
|
The project is now moving from:
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,11 @@ function createAssistantDataScopePolicy(deps) {
|
||||||
return (0, assistantOrganizationMatcher_1.mergeKnownOrganizations)(collected, 20);
|
return (0, assistantOrganizationMatcher_1.mergeKnownOrganizations)(collected, 20);
|
||||||
}
|
}
|
||||||
function findLastAssistantActiveOrganization(items) {
|
function findLastAssistantActiveOrganization(items) {
|
||||||
|
const continuitySnapshot = (0, assistantContinuityPolicy_1.resolveAssistantContinuitySnapshot)({
|
||||||
|
sessionItems: items,
|
||||||
|
toNonEmptyString: assistantOrganizationMatcher_1.normalizeOrganizationScopeValue
|
||||||
|
});
|
||||||
|
const continuityOrganization = (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)(continuitySnapshot.activeOrganization);
|
||||||
for (let index = (Array.isArray(items) ? items.length : 0) - 1; index >= 0; index -= 1) {
|
for (let index = (Array.isArray(items) ? items.length : 0) - 1; index >= 0; index -= 1) {
|
||||||
const item = Array.isArray(items) ? items[index] : null;
|
const item = Array.isArray(items) ? items[index] : null;
|
||||||
if (!item || typeof item !== "object" || item.role !== "assistant") {
|
if (!item || typeof item !== "object" || item.role !== "assistant") {
|
||||||
|
|
@ -243,7 +248,7 @@ function createAssistantDataScopePolicy(deps) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return continuityOrganization;
|
||||||
}
|
}
|
||||||
function extractOrganizationFactsFromRows(rows) {
|
function extractOrganizationFactsFromRows(rows) {
|
||||||
const names = [];
|
const names = [];
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.createAssistantRoutePolicy = createAssistantRoutePolicy;
|
exports.createAssistantRoutePolicy = createAssistantRoutePolicy;
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
const assistantContinuityPolicy_1 = require("./assistantContinuityPolicy");
|
||||||
const ADDRESS_INTENTS_KEEP_ADDRESS_LANE = new Set([
|
const ADDRESS_INTENTS_KEEP_ADDRESS_LANE = new Set([
|
||||||
"period_coverage_profile",
|
"period_coverage_profile",
|
||||||
"document_type_and_account_section_profile",
|
"document_type_and_account_section_profile",
|
||||||
|
|
@ -71,17 +72,26 @@ function createAssistantRoutePolicy(deps) {
|
||||||
const sessionOrganizationScope = input?.sessionOrganizationScope && typeof input.sessionOrganizationScope === "object"
|
const sessionOrganizationScope = input?.sessionOrganizationScope && typeof input.sessionOrganizationScope === "object"
|
||||||
? input.sessionOrganizationScope
|
? input.sessionOrganizationScope
|
||||||
: null;
|
: null;
|
||||||
const lastGroundedAddressDebug = findLastGroundedAddressAnswerDebug(sessionItems);
|
const continuitySnapshot = (0, assistantContinuityPolicy_1.resolveAssistantContinuitySnapshot)({
|
||||||
|
sessionItems,
|
||||||
|
toNonEmptyString
|
||||||
|
});
|
||||||
|
const continuityActiveOrganization = normalizeOrganizationScopeValue(sessionOrganizationScope?.activeOrganization) ??
|
||||||
|
normalizeOrganizationScopeValue(continuitySnapshot.activeOrganization);
|
||||||
|
const lastGroundedAddressDebug = findLastGroundedAddressAnswerDebug(sessionItems) ??
|
||||||
|
continuitySnapshot.lastGroundedAddressDebug;
|
||||||
const lastOrganizationClarificationDebug = findLastOrganizationClarificationAddressDebug(sessionItems);
|
const lastOrganizationClarificationDebug = findLastOrganizationClarificationAddressDebug(sessionItems);
|
||||||
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
|
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
|
||||||
? mergeKnownOrganizations([
|
? mergeKnownOrganizations([
|
||||||
...lastOrganizationClarificationDebug.organization_candidates,
|
...lastOrganizationClarificationDebug.organization_candidates,
|
||||||
...((Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
...((Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
||||||
? sessionOrganizationScope.knownOrganizations
|
? sessionOrganizationScope.knownOrganizations
|
||||||
: []))
|
: [])),
|
||||||
|
continuityActiveOrganization
|
||||||
])
|
])
|
||||||
: [];
|
: [];
|
||||||
const organizationClarificationSelectionFromScope = normalizeOrganizationScopeValue(sessionOrganizationScope?.selectedOrganization);
|
const organizationClarificationSelectionFromScope = normalizeOrganizationScopeValue(sessionOrganizationScope?.selectedOrganization) ??
|
||||||
|
continuityActiveOrganization;
|
||||||
const organizationClarificationSelection = resolveOrganizationSelectionFromMessage(rawUserMessage, organizationClarificationCandidates) ??
|
const organizationClarificationSelection = resolveOrganizationSelectionFromMessage(rawUserMessage, organizationClarificationCandidates) ??
|
||||||
resolveOrganizationSelectionFromMessage(repairedRawUserMessage, organizationClarificationCandidates) ??
|
resolveOrganizationSelectionFromMessage(repairedRawUserMessage, organizationClarificationCandidates) ??
|
||||||
resolveOrganizationSelectionFromMessage(effectiveAddressUserMessage, organizationClarificationCandidates) ??
|
resolveOrganizationSelectionFromMessage(effectiveAddressUserMessage, organizationClarificationCandidates) ??
|
||||||
|
|
@ -194,7 +204,7 @@ function createAssistantRoutePolicy(deps) {
|
||||||
hasShortInventoryObjectFollowupSignal(repairedRawUserMessage) ||
|
hasShortInventoryObjectFollowupSignal(repairedRawUserMessage) ||
|
||||||
hasShortInventoryObjectFollowupSignal(effectiveAddressUserMessage) ||
|
hasShortInventoryObjectFollowupSignal(effectiveAddressUserMessage) ||
|
||||||
hasShortInventoryObjectFollowupSignal(repairedEffectiveAddressUserMessage)));
|
hasShortInventoryObjectFollowupSignal(repairedEffectiveAddressUserMessage)));
|
||||||
const organizationClarificationContinuationDetected = Boolean(followupContext &&
|
const organizationClarificationContinuationDetected = Boolean((followupContext || continuitySnapshot.hasGroundedAddressContext) &&
|
||||||
lastOrganizationClarificationDebug &&
|
lastOrganizationClarificationDebug &&
|
||||||
organizationClarificationSelection &&
|
organizationClarificationSelection &&
|
||||||
!dataScopeMetaQuery &&
|
!dataScopeMetaQuery &&
|
||||||
|
|
@ -691,7 +701,9 @@ function createAssistantRoutePolicy(deps) {
|
||||||
repairedEffectiveAddressUserMessage,
|
repairedEffectiveAddressUserMessage,
|
||||||
sessionItems
|
sessionItems
|
||||||
}));
|
}));
|
||||||
const hasPriorAddressAnswerContext = Boolean(lastGroundedAddressDebug || toNonEmptyString(followupContext?.previous_intent));
|
const hasPriorAddressAnswerContext = Boolean(lastGroundedAddressDebug ||
|
||||||
|
continuitySnapshot.hasGroundedAddressContext ||
|
||||||
|
toNonEmptyString(followupContext?.previous_intent));
|
||||||
const metaFollowupOverGroundedAnswer = isMetaFollowupOverGroundedAnswer({
|
const metaFollowupOverGroundedAnswer = isMetaFollowupOverGroundedAnswer({
|
||||||
followupContext,
|
followupContext,
|
||||||
hasPriorAddressAnswerContext,
|
hasPriorAddressAnswerContext,
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import {
|
||||||
normalizeOrganizationScopeValue
|
normalizeOrganizationScopeValue
|
||||||
} from "./assistantOrganizationMatcher";
|
} from "./assistantOrganizationMatcher";
|
||||||
import {
|
import {
|
||||||
|
resolveAssistantContinuitySnapshot,
|
||||||
isGroundedAddressDebug,
|
isGroundedAddressDebug,
|
||||||
readAddressDebugOrganization
|
readAddressDebugOrganization
|
||||||
} from "./assistantContinuityPolicy";
|
} from "./assistantContinuityPolicy";
|
||||||
|
|
@ -296,6 +297,12 @@ export function createAssistantDataScopePolicy(deps: AssistantDataScopePolicyDep
|
||||||
}
|
}
|
||||||
|
|
||||||
function findLastAssistantActiveOrganization(items: unknown[]): string | null {
|
function findLastAssistantActiveOrganization(items: unknown[]): string | null {
|
||||||
|
const continuitySnapshot = resolveAssistantContinuitySnapshot({
|
||||||
|
sessionItems: items,
|
||||||
|
toNonEmptyString: normalizeOrganizationScopeValue
|
||||||
|
});
|
||||||
|
const continuityOrganization = normalizeOrganizationScopeValue(continuitySnapshot.activeOrganization);
|
||||||
|
|
||||||
for (let index = (Array.isArray(items) ? items.length : 0) - 1; index >= 0; index -= 1) {
|
for (let index = (Array.isArray(items) ? items.length : 0) - 1; index >= 0; index -= 1) {
|
||||||
const item = Array.isArray(items) ? items[index] : null;
|
const item = Array.isArray(items) ? items[index] : null;
|
||||||
if (!item || typeof item !== "object" || (item as { role?: string }).role !== "assistant") {
|
if (!item || typeof item !== "object" || (item as { role?: string }).role !== "assistant") {
|
||||||
|
|
@ -323,7 +330,7 @@ export function createAssistantDataScopePolicy(deps: AssistantDataScopePolicyDep
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return continuityOrganization;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractOrganizationFactsFromRows(rows: unknown[]): AssistantOrganizationFacts {
|
function extractOrganizationFactsFromRows(rows: unknown[]): AssistantOrganizationFacts {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
import { resolveAssistantContinuitySnapshot } from "./assistantContinuityPolicy";
|
||||||
|
|
||||||
const ADDRESS_INTENTS_KEEP_ADDRESS_LANE = new Set([
|
const ADDRESS_INTENTS_KEEP_ADDRESS_LANE = new Set([
|
||||||
"period_coverage_profile",
|
"period_coverage_profile",
|
||||||
"document_type_and_account_section_profile",
|
"document_type_and_account_section_profile",
|
||||||
|
|
@ -107,17 +109,26 @@ export function createAssistantRoutePolicy(deps) {
|
||||||
const sessionOrganizationScope = input?.sessionOrganizationScope && typeof input.sessionOrganizationScope === "object"
|
const sessionOrganizationScope = input?.sessionOrganizationScope && typeof input.sessionOrganizationScope === "object"
|
||||||
? input.sessionOrganizationScope
|
? input.sessionOrganizationScope
|
||||||
: null;
|
: null;
|
||||||
const lastGroundedAddressDebug = findLastGroundedAddressAnswerDebug(sessionItems);
|
const continuitySnapshot = resolveAssistantContinuitySnapshot({
|
||||||
|
sessionItems,
|
||||||
|
toNonEmptyString
|
||||||
|
});
|
||||||
|
const continuityActiveOrganization = normalizeOrganizationScopeValue(sessionOrganizationScope?.activeOrganization) ??
|
||||||
|
normalizeOrganizationScopeValue(continuitySnapshot.activeOrganization);
|
||||||
|
const lastGroundedAddressDebug = findLastGroundedAddressAnswerDebug(sessionItems) ??
|
||||||
|
continuitySnapshot.lastGroundedAddressDebug;
|
||||||
const lastOrganizationClarificationDebug = findLastOrganizationClarificationAddressDebug(sessionItems);
|
const lastOrganizationClarificationDebug = findLastOrganizationClarificationAddressDebug(sessionItems);
|
||||||
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
|
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
|
||||||
? mergeKnownOrganizations([
|
? mergeKnownOrganizations([
|
||||||
...lastOrganizationClarificationDebug.organization_candidates,
|
...lastOrganizationClarificationDebug.organization_candidates,
|
||||||
...((Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
...((Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
||||||
? sessionOrganizationScope.knownOrganizations
|
? sessionOrganizationScope.knownOrganizations
|
||||||
: []))
|
: [])),
|
||||||
|
continuityActiveOrganization
|
||||||
])
|
])
|
||||||
: [];
|
: [];
|
||||||
const organizationClarificationSelectionFromScope = normalizeOrganizationScopeValue(sessionOrganizationScope?.selectedOrganization);
|
const organizationClarificationSelectionFromScope = normalizeOrganizationScopeValue(sessionOrganizationScope?.selectedOrganization) ??
|
||||||
|
continuityActiveOrganization;
|
||||||
const organizationClarificationSelection = resolveOrganizationSelectionFromMessage(rawUserMessage, organizationClarificationCandidates) ??
|
const organizationClarificationSelection = resolveOrganizationSelectionFromMessage(rawUserMessage, organizationClarificationCandidates) ??
|
||||||
resolveOrganizationSelectionFromMessage(repairedRawUserMessage, organizationClarificationCandidates) ??
|
resolveOrganizationSelectionFromMessage(repairedRawUserMessage, organizationClarificationCandidates) ??
|
||||||
resolveOrganizationSelectionFromMessage(effectiveAddressUserMessage, organizationClarificationCandidates) ??
|
resolveOrganizationSelectionFromMessage(effectiveAddressUserMessage, organizationClarificationCandidates) ??
|
||||||
|
|
@ -230,7 +241,7 @@ export function createAssistantRoutePolicy(deps) {
|
||||||
hasShortInventoryObjectFollowupSignal(repairedRawUserMessage) ||
|
hasShortInventoryObjectFollowupSignal(repairedRawUserMessage) ||
|
||||||
hasShortInventoryObjectFollowupSignal(effectiveAddressUserMessage) ||
|
hasShortInventoryObjectFollowupSignal(effectiveAddressUserMessage) ||
|
||||||
hasShortInventoryObjectFollowupSignal(repairedEffectiveAddressUserMessage)));
|
hasShortInventoryObjectFollowupSignal(repairedEffectiveAddressUserMessage)));
|
||||||
const organizationClarificationContinuationDetected = Boolean(followupContext &&
|
const organizationClarificationContinuationDetected = Boolean((followupContext || continuitySnapshot.hasGroundedAddressContext) &&
|
||||||
lastOrganizationClarificationDebug &&
|
lastOrganizationClarificationDebug &&
|
||||||
organizationClarificationSelection &&
|
organizationClarificationSelection &&
|
||||||
!dataScopeMetaQuery &&
|
!dataScopeMetaQuery &&
|
||||||
|
|
@ -727,7 +738,9 @@ export function createAssistantRoutePolicy(deps) {
|
||||||
repairedEffectiveAddressUserMessage,
|
repairedEffectiveAddressUserMessage,
|
||||||
sessionItems
|
sessionItems
|
||||||
}));
|
}));
|
||||||
const hasPriorAddressAnswerContext = Boolean(lastGroundedAddressDebug || toNonEmptyString(followupContext?.previous_intent));
|
const hasPriorAddressAnswerContext = Boolean(lastGroundedAddressDebug ||
|
||||||
|
continuitySnapshot.hasGroundedAddressContext ||
|
||||||
|
toNonEmptyString(followupContext?.previous_intent));
|
||||||
const metaFollowupOverGroundedAnswer = isMetaFollowupOverGroundedAnswer({
|
const metaFollowupOverGroundedAnswer = isMetaFollowupOverGroundedAnswer({
|
||||||
followupContext,
|
followupContext,
|
||||||
hasPriorAddressAnswerContext,
|
hasPriorAddressAnswerContext,
|
||||||
|
|
|
||||||
|
|
@ -364,4 +364,46 @@ describe("assistantRoutePolicy", () => {
|
||||||
expect(decision.toolGateReason).toBe("address_signal_detected");
|
expect(decision.toolGateReason).toBe("address_signal_detected");
|
||||||
expect(decision.livingMode).toBe("address_data");
|
expect(decision.livingMode).toBe("address_data");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not mark organization selection after grounded continuity as non-domain noise", () => {
|
||||||
|
const policy = buildPolicy({
|
||||||
|
findLastGroundedAddressAnswerDebug: () => null,
|
||||||
|
findLastOrganizationClarificationAddressDebug: () => ({
|
||||||
|
organization_candidates: ["Org A", "Org B"]
|
||||||
|
}),
|
||||||
|
resolveOrganizationSelectionFromMessage: (userMessage: unknown, knownOrganizations: unknown) => {
|
||||||
|
const normalized = String(userMessage ?? "").trim().toLowerCase();
|
||||||
|
const candidates = Array.isArray(knownOrganizations) ? knownOrganizations.map((item) => String(item)) : [];
|
||||||
|
return candidates.find((candidate) => candidate.toLowerCase() === normalized) ?? null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const decision = policy.resolveAssistantOrchestrationDecision({
|
||||||
|
rawUserMessage: "Org A",
|
||||||
|
effectiveAddressUserMessage: "Org A",
|
||||||
|
followupContext: null,
|
||||||
|
llmPreDecomposeMeta: null,
|
||||||
|
sessionItems: [
|
||||||
|
{
|
||||||
|
role: "assistant",
|
||||||
|
debug: {
|
||||||
|
execution_lane: "address_query",
|
||||||
|
answer_grounding_check: { status: "grounded" },
|
||||||
|
extracted_filters: {
|
||||||
|
organization: "Org A",
|
||||||
|
as_of_date: "2021-03-31"
|
||||||
|
},
|
||||||
|
detected_intent: "receivables_confirmed_as_of_date"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
useMock: false
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(decision.runAddressLane).toBe(false);
|
||||||
|
expect(decision.toolGateReason).toBe("no_address_signal_after_l0");
|
||||||
|
expect(decision.livingMode).toBe("chat");
|
||||||
|
expect(decision.orchestrationContract?.hard_meta_mode).toBeNull();
|
||||||
|
expect(decision.orchestrationContract?.followup_context_detected).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue