АРЧ АП11 - Архитектура после регресса: Архитектура: централизовать organization authority в continuity policy для route, living-chat и data-scope
This commit is contained in:
parent
53e9151d5a
commit
9710d81676
|
|
@ -245,6 +245,12 @@ Latest continuity-authority convergence evidence after the current route pass:
|
|||
- deterministic organization-fact boundary replies can now trigger from grounded continuity even when `sessionScope.selectedOrganization` and `sessionScope.activeOrganization` are both empty at runtime entry;
|
||||
- the chat layer now records whether it entered with grounded continuity and which organization came from that continuity snapshot, making future saved-session review less blind;
|
||||
- proactive organization offer logic is now explicitly blocked when grounded address continuity already exists, so the chat layer does not re-offer company selection on top of an already grounded business session;
|
||||
- the next continuity-authority pass now centralizes `organization authority` in the shared continuity policy instead of letting route, living-chat, and data-scope reconstruct it independently:
|
||||
- a single resolver now merges grounded address continuity, assistant-side living-chat scope memory, session scope, and clarification candidates into one authority object;
|
||||
- route arbitration now consumes that shared authority for organization clarification continuation instead of rebuilding candidates and fallback selection locally;
|
||||
- living-chat runtime now boots with the same authority object, so `knownOrganizations`, `selectedOrganization`, and `activeOrganization` no longer start from a different merge order than the route layer;
|
||||
- data-scope active-organization recovery now also reads the same authority object, reducing one more history-scan seam that used to drift from route and chat;
|
||||
- targeted tests now protect this precedence layer directly, including the case where assistant-side living-chat selection must outrank older grounded continuity without losing the grounded organization in the merged known set;
|
||||
- the first human-answer-shaping cleanup pass is now applied to heavy profile/aggregate exact answers:
|
||||
- `period_coverage_profile` and `document_type_and_account_section_profile` now start with a direct business-first lead (`Коротко: ...`) instead of service-flavored intros like `профиль собран` / `строк агрегата`;
|
||||
- the top block now states the business conclusion first and leaves ranked detail blocks below, which reduces the catalog-like feel without hiding the actual data;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ exports.readAddressDebugScopedDate = readAddressDebugScopedDate;
|
|||
exports.buildInventoryRootFrameFromAddressDebug = buildInventoryRootFrameFromAddressDebug;
|
||||
exports.isGroundedAddressDebug = isGroundedAddressDebug;
|
||||
exports.resolveAssistantContinuitySnapshot = resolveAssistantContinuitySnapshot;
|
||||
exports.resolveAssistantOrganizationAuthority = resolveAssistantOrganizationAuthority;
|
||||
const assistantOrganizationMatcher_1 = require("./assistantOrganizationMatcher");
|
||||
function fallbackToNonEmptyString(value) {
|
||||
if (value === null || value === undefined) {
|
||||
return null;
|
||||
|
|
@ -21,6 +23,12 @@ function toRecordObject(value) {
|
|||
}
|
||||
return value;
|
||||
}
|
||||
function mergeKnownOrganizationsDefault(values) {
|
||||
return (0, assistantOrganizationMatcher_1.mergeKnownOrganizations)(values);
|
||||
}
|
||||
function normalizeOrganizationScopeDefault(value) {
|
||||
return (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)(value);
|
||||
}
|
||||
function formatIsoDateForReply(value) {
|
||||
const source = String(value ?? "").trim();
|
||||
const match = source.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
||||
|
|
@ -166,3 +174,87 @@ function resolveAssistantContinuitySnapshot(input) {
|
|||
hasGroundedInventoryContext: Boolean(lastGroundedInventoryAddressDebug)
|
||||
};
|
||||
}
|
||||
function collectAssistantOrganizationSignals(sessionItems, normalizeOrganizationScopeValue) {
|
||||
let lastAssistantSelectedOrganization = null;
|
||||
let lastAssistantActiveOrganization = null;
|
||||
const knownOrganizationsFromAssistantHistory = [];
|
||||
for (let index = sessionItems.length - 1; index >= 0; index -= 1) {
|
||||
const item = sessionItems[index];
|
||||
if (!item || item.role !== "assistant" || !item.debug || typeof item.debug !== "object") {
|
||||
continue;
|
||||
}
|
||||
const debug = item.debug;
|
||||
if (!lastAssistantActiveOrganization) {
|
||||
lastAssistantActiveOrganization = normalizeOrganizationScopeValue(debug.assistant_active_organization);
|
||||
}
|
||||
if (!lastAssistantSelectedOrganization) {
|
||||
lastAssistantSelectedOrganization = normalizeOrganizationScopeValue(debug.living_chat_selected_organization);
|
||||
}
|
||||
const directKnownOrganizations = Array.isArray(debug.assistant_known_organizations)
|
||||
? debug.assistant_known_organizations
|
||||
: [];
|
||||
const probeOrganizations = Array.isArray(debug.living_chat_data_scope_probe_organizations)
|
||||
? debug.living_chat_data_scope_probe_organizations
|
||||
: [];
|
||||
const organizationCandidates = Array.isArray(debug.organization_candidates) ? debug.organization_candidates : [];
|
||||
const directResolvedOrganizations = [
|
||||
debug.assistant_active_organization,
|
||||
debug.living_chat_selected_organization,
|
||||
readAddressDebugOrganization(debug, normalizeOrganizationScopeValue)
|
||||
];
|
||||
knownOrganizationsFromAssistantHistory.push(...directKnownOrganizations, ...probeOrganizations, ...organizationCandidates, ...directResolvedOrganizations);
|
||||
}
|
||||
return {
|
||||
lastAssistantSelectedOrganization,
|
||||
lastAssistantActiveOrganization,
|
||||
knownOrganizationsFromAssistantHistory: mergeKnownOrganizationsDefault(knownOrganizationsFromAssistantHistory)
|
||||
};
|
||||
}
|
||||
function resolveAssistantOrganizationAuthority(input) {
|
||||
const normalizeOrganizationScopeValue = input.normalizeOrganizationScopeValue ?? normalizeOrganizationScopeDefault;
|
||||
const mergeKnownOrganizations = input.mergeKnownOrganizations ?? mergeKnownOrganizationsDefault;
|
||||
const sessionItems = Array.isArray(input.sessionItems) ? input.sessionItems : [];
|
||||
const continuitySnapshot = resolveAssistantContinuitySnapshot({
|
||||
sessionItems,
|
||||
toNonEmptyString: input.toNonEmptyString ?? normalizeOrganizationScopeValue
|
||||
});
|
||||
const assistantSignals = collectAssistantOrganizationSignals(sessionItems, normalizeOrganizationScopeValue);
|
||||
const continuityActiveOrganization = normalizeOrganizationScopeValue(input.sessionActiveOrganization) ??
|
||||
assistantSignals.lastAssistantActiveOrganization ??
|
||||
normalizeOrganizationScopeValue(continuitySnapshot.activeOrganization);
|
||||
const knownOrganizations = mergeKnownOrganizations([
|
||||
...(Array.isArray(input.sessionKnownOrganizations) ? input.sessionKnownOrganizations : []),
|
||||
...assistantSignals.knownOrganizationsFromAssistantHistory,
|
||||
input.sessionSelectedOrganization,
|
||||
input.sessionActiveOrganization,
|
||||
continuityActiveOrganization
|
||||
]);
|
||||
const selectedOrganization = normalizeOrganizationScopeValue(input.sessionSelectedOrganization) ??
|
||||
assistantSignals.lastAssistantSelectedOrganization;
|
||||
const activeOrganization = normalizeOrganizationScopeValue(input.sessionActiveOrganization) ??
|
||||
selectedOrganization ??
|
||||
assistantSignals.lastAssistantActiveOrganization ??
|
||||
continuityActiveOrganization ??
|
||||
(knownOrganizations.length === 1 ? knownOrganizations[0] : null);
|
||||
const organizationClarificationCandidates = Array.isArray(input.lastOrganizationClarificationDebug?.organization_candidates)
|
||||
? mergeKnownOrganizations([
|
||||
...input.lastOrganizationClarificationDebug.organization_candidates,
|
||||
...knownOrganizations,
|
||||
selectedOrganization,
|
||||
activeOrganization,
|
||||
continuityActiveOrganization
|
||||
])
|
||||
: [];
|
||||
const organizationClarificationSelectionFromScope = selectedOrganization ?? activeOrganization;
|
||||
return {
|
||||
continuitySnapshot,
|
||||
continuityActiveOrganization,
|
||||
lastAssistantSelectedOrganization: assistantSignals.lastAssistantSelectedOrganization,
|
||||
lastAssistantActiveOrganization: assistantSignals.lastAssistantActiveOrganization,
|
||||
knownOrganizations,
|
||||
selectedOrganization,
|
||||
activeOrganization,
|
||||
organizationClarificationCandidates,
|
||||
organizationClarificationSelectionFromScope
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -219,36 +219,13 @@ function createAssistantDataScopePolicy(deps) {
|
|||
return (0, assistantOrganizationMatcher_1.mergeKnownOrganizations)(collected, 20);
|
||||
}
|
||||
function findLastAssistantActiveOrganization(items) {
|
||||
const continuitySnapshot = (0, assistantContinuityPolicy_1.resolveAssistantContinuitySnapshot)({
|
||||
const authority = (0, assistantContinuityPolicy_1.resolveAssistantOrganizationAuthority)({
|
||||
sessionItems: items,
|
||||
toNonEmptyString: assistantOrganizationMatcher_1.normalizeOrganizationScopeValue
|
||||
toNonEmptyString: assistantOrganizationMatcher_1.normalizeOrganizationScopeValue,
|
||||
normalizeOrganizationScopeValue: assistantOrganizationMatcher_1.normalizeOrganizationScopeValue,
|
||||
mergeKnownOrganizations: assistantOrganizationMatcher_1.mergeKnownOrganizations
|
||||
});
|
||||
const continuityOrganization = (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)(continuitySnapshot.activeOrganization);
|
||||
for (let index = (Array.isArray(items) ? items.length : 0) - 1; index >= 0; index -= 1) {
|
||||
const item = Array.isArray(items) ? items[index] : null;
|
||||
if (!item || typeof item !== "object" || item.role !== "assistant") {
|
||||
continue;
|
||||
}
|
||||
const debug = item.debug;
|
||||
if (!debug || typeof debug !== "object") {
|
||||
continue;
|
||||
}
|
||||
const direct = (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)(debug.assistant_active_organization);
|
||||
if (direct) {
|
||||
return direct;
|
||||
}
|
||||
const selected = (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)(debug.living_chat_selected_organization);
|
||||
if (selected) {
|
||||
return selected;
|
||||
}
|
||||
if ((0, assistantContinuityPolicy_1.isGroundedAddressDebug)(debug)) {
|
||||
const groundedOrganization = (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)((0, assistantContinuityPolicy_1.readAddressDebugOrganization)(debug));
|
||||
if (groundedOrganization) {
|
||||
return groundedOrganization;
|
||||
}
|
||||
}
|
||||
}
|
||||
return continuityOrganization;
|
||||
return (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)(authority.activeOrganization);
|
||||
}
|
||||
function extractOrganizationFactsFromRows(rows) {
|
||||
const names = [];
|
||||
|
|
|
|||
|
|
@ -152,10 +152,18 @@ function buildAddressMemoryRecapReply(input) {
|
|||
}
|
||||
async function runAssistantLivingChatRuntime(input) {
|
||||
const userMessage = String(input.userMessage ?? "");
|
||||
const continuitySnapshot = (0, assistantContinuityPolicy_1.resolveAssistantContinuitySnapshot)({
|
||||
const organizationAuthority = (0, assistantContinuityPolicy_1.resolveAssistantOrganizationAuthority)({
|
||||
sessionItems: input.sessionItems,
|
||||
toNonEmptyString: input.toNonEmptyString
|
||||
sessionKnownOrganizations: Array.isArray(input.sessionScope.knownOrganizations)
|
||||
? input.sessionScope.knownOrganizations
|
||||
: [],
|
||||
sessionSelectedOrganization: input.sessionScope.selectedOrganization,
|
||||
sessionActiveOrganization: input.sessionScope.activeOrganization,
|
||||
toNonEmptyString: input.toNonEmptyString,
|
||||
normalizeOrganizationScopeValue: input.toNonEmptyString,
|
||||
mergeKnownOrganizations: input.mergeKnownOrganizations
|
||||
});
|
||||
const continuitySnapshot = organizationAuthority.continuitySnapshot;
|
||||
const dataScopeMetaQuery = input.hasAssistantDataScopeMetaQuestionSignal(userMessage);
|
||||
const capabilityMetaQuery = input.shouldHandleAsAssistantCapabilityMetaQuery(userMessage);
|
||||
const destructiveSignal = input.hasDestructiveDataActionSignal(userMessage);
|
||||
|
|
@ -169,13 +177,10 @@ async function runAssistantLivingChatRuntime(input) {
|
|||
let livingChatGroundingGuardApplied = false;
|
||||
let livingChatGroundingGuardReason = null;
|
||||
let livingChatProactiveScopeOfferApplied = false;
|
||||
const continuityActiveOrganization = input.toNonEmptyString(continuitySnapshot.activeOrganization);
|
||||
let knownOrganizations = input.mergeKnownOrganizations([
|
||||
...(Array.isArray(input.sessionScope.knownOrganizations) ? input.sessionScope.knownOrganizations : []),
|
||||
continuityActiveOrganization
|
||||
]);
|
||||
let selectedOrganization = input.toNonEmptyString(input.sessionScope.selectedOrganization);
|
||||
let activeOrganization = input.toNonEmptyString(input.sessionScope.activeOrganization) ?? continuityActiveOrganization;
|
||||
const continuityActiveOrganization = organizationAuthority.continuityActiveOrganization;
|
||||
let knownOrganizations = [...organizationAuthority.knownOrganizations];
|
||||
let selectedOrganization = organizationAuthority.selectedOrganization;
|
||||
let activeOrganization = organizationAuthority.activeOrganization;
|
||||
const memoryRecapContext = (0, assistantMemoryRecapPolicy_1.resolveAssistantLivingChatMemoryContext)({
|
||||
modeDecisionReason: input.modeDecision?.reason ?? null,
|
||||
sessionItems: input.sessionItems
|
||||
|
|
|
|||
|
|
@ -72,26 +72,25 @@ function createAssistantRoutePolicy(deps) {
|
|||
const sessionOrganizationScope = input?.sessionOrganizationScope && typeof input.sessionOrganizationScope === "object"
|
||||
? input.sessionOrganizationScope
|
||||
: null;
|
||||
const continuitySnapshot = (0, assistantContinuityPolicy_1.resolveAssistantContinuitySnapshot)({
|
||||
const organizationAuthority = (0, assistantContinuityPolicy_1.resolveAssistantOrganizationAuthority)({
|
||||
sessionItems,
|
||||
toNonEmptyString
|
||||
sessionKnownOrganizations: Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
||||
? sessionOrganizationScope.knownOrganizations
|
||||
: [],
|
||||
sessionSelectedOrganization: sessionOrganizationScope?.selectedOrganization,
|
||||
sessionActiveOrganization: sessionOrganizationScope?.activeOrganization,
|
||||
lastOrganizationClarificationDebug: findLastOrganizationClarificationAddressDebug(sessionItems),
|
||||
toNonEmptyString,
|
||||
normalizeOrganizationScopeValue,
|
||||
mergeKnownOrganizations
|
||||
});
|
||||
const continuityActiveOrganization = normalizeOrganizationScopeValue(sessionOrganizationScope?.activeOrganization) ??
|
||||
normalizeOrganizationScopeValue(continuitySnapshot.activeOrganization);
|
||||
const continuitySnapshot = organizationAuthority.continuitySnapshot;
|
||||
const continuityActiveOrganization = organizationAuthority.continuityActiveOrganization;
|
||||
const lastGroundedAddressDebug = findLastGroundedAddressAnswerDebug(sessionItems) ??
|
||||
continuitySnapshot.lastGroundedAddressDebug;
|
||||
const lastOrganizationClarificationDebug = findLastOrganizationClarificationAddressDebug(sessionItems);
|
||||
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
|
||||
? mergeKnownOrganizations([
|
||||
...lastOrganizationClarificationDebug.organization_candidates,
|
||||
...((Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
||||
? sessionOrganizationScope.knownOrganizations
|
||||
: [])),
|
||||
continuityActiveOrganization
|
||||
])
|
||||
: [];
|
||||
const organizationClarificationSelectionFromScope = normalizeOrganizationScopeValue(sessionOrganizationScope?.selectedOrganization) ??
|
||||
continuityActiveOrganization;
|
||||
const organizationClarificationCandidates = organizationAuthority.organizationClarificationCandidates;
|
||||
const organizationClarificationSelectionFromScope = organizationAuthority.organizationClarificationSelectionFromScope;
|
||||
const organizationClarificationSelection = resolveOrganizationSelectionFromMessage(rawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(repairedRawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(effectiveAddressUserMessage, organizationClarificationCandidates) ??
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
import {
|
||||
mergeKnownOrganizations as mergeKnownOrganizationsFromMatcher,
|
||||
normalizeOrganizationScopeValue as normalizeOrganizationScopeValueFromMatcher
|
||||
} from "./assistantOrganizationMatcher";
|
||||
|
||||
export interface AssistantContinuitySnapshotInput {
|
||||
sessionItems?: unknown[];
|
||||
toNonEmptyString?: (value: unknown) => string | null;
|
||||
|
|
@ -22,6 +27,29 @@ export interface AssistantContinuitySnapshot {
|
|||
hasGroundedInventoryContext: boolean;
|
||||
}
|
||||
|
||||
export interface AssistantOrganizationAuthorityInput {
|
||||
sessionItems?: unknown[];
|
||||
sessionKnownOrganizations?: unknown[];
|
||||
sessionSelectedOrganization?: unknown;
|
||||
sessionActiveOrganization?: unknown;
|
||||
lastOrganizationClarificationDebug?: Record<string, unknown> | null;
|
||||
toNonEmptyString?: (value: unknown) => string | null;
|
||||
normalizeOrganizationScopeValue?: (value: unknown) => string | null;
|
||||
mergeKnownOrganizations?: (values: unknown[]) => string[];
|
||||
}
|
||||
|
||||
export interface AssistantOrganizationAuthority {
|
||||
continuitySnapshot: AssistantContinuitySnapshot;
|
||||
continuityActiveOrganization: string | null;
|
||||
lastAssistantSelectedOrganization: string | null;
|
||||
lastAssistantActiveOrganization: string | null;
|
||||
knownOrganizations: string[];
|
||||
selectedOrganization: string | null;
|
||||
activeOrganization: string | null;
|
||||
organizationClarificationCandidates: string[];
|
||||
organizationClarificationSelectionFromScope: string | null;
|
||||
}
|
||||
|
||||
function fallbackToNonEmptyString(value: unknown): string | null {
|
||||
if (value === null || value === undefined) {
|
||||
return null;
|
||||
|
|
@ -37,6 +65,14 @@ function toRecordObject(value: unknown): Record<string, unknown> | null {
|
|||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function mergeKnownOrganizationsDefault(values: unknown[]): string[] {
|
||||
return mergeKnownOrganizationsFromMatcher(values);
|
||||
}
|
||||
|
||||
function normalizeOrganizationScopeDefault(value: unknown): string | null {
|
||||
return normalizeOrganizationScopeValueFromMatcher(value);
|
||||
}
|
||||
|
||||
export function formatIsoDateForReply(value: unknown): string | null {
|
||||
const source = String(value ?? "").trim();
|
||||
const match = source.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
||||
|
|
@ -228,3 +264,113 @@ export function resolveAssistantContinuitySnapshot(
|
|||
hasGroundedInventoryContext: Boolean(lastGroundedInventoryAddressDebug)
|
||||
};
|
||||
}
|
||||
|
||||
function collectAssistantOrganizationSignals(
|
||||
sessionItems: unknown[],
|
||||
normalizeOrganizationScopeValue: (value: unknown) => string | null
|
||||
): {
|
||||
lastAssistantSelectedOrganization: string | null;
|
||||
lastAssistantActiveOrganization: string | null;
|
||||
knownOrganizationsFromAssistantHistory: string[];
|
||||
} {
|
||||
let lastAssistantSelectedOrganization: string | null = null;
|
||||
let lastAssistantActiveOrganization: string | null = null;
|
||||
const knownOrganizationsFromAssistantHistory: unknown[] = [];
|
||||
|
||||
for (let index = sessionItems.length - 1; index >= 0; index -= 1) {
|
||||
const item = sessionItems[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 (!lastAssistantActiveOrganization) {
|
||||
lastAssistantActiveOrganization = normalizeOrganizationScopeValue(debug.assistant_active_organization);
|
||||
}
|
||||
if (!lastAssistantSelectedOrganization) {
|
||||
lastAssistantSelectedOrganization = normalizeOrganizationScopeValue(debug.living_chat_selected_organization);
|
||||
}
|
||||
|
||||
const directKnownOrganizations = Array.isArray(debug.assistant_known_organizations)
|
||||
? debug.assistant_known_organizations
|
||||
: [];
|
||||
const probeOrganizations = Array.isArray(debug.living_chat_data_scope_probe_organizations)
|
||||
? debug.living_chat_data_scope_probe_organizations
|
||||
: [];
|
||||
const organizationCandidates = Array.isArray(debug.organization_candidates) ? debug.organization_candidates : [];
|
||||
const directResolvedOrganizations = [
|
||||
debug.assistant_active_organization,
|
||||
debug.living_chat_selected_organization,
|
||||
readAddressDebugOrganization(debug, normalizeOrganizationScopeValue)
|
||||
];
|
||||
|
||||
knownOrganizationsFromAssistantHistory.push(
|
||||
...directKnownOrganizations,
|
||||
...probeOrganizations,
|
||||
...organizationCandidates,
|
||||
...directResolvedOrganizations
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
lastAssistantSelectedOrganization,
|
||||
lastAssistantActiveOrganization,
|
||||
knownOrganizationsFromAssistantHistory: mergeKnownOrganizationsDefault(knownOrganizationsFromAssistantHistory)
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveAssistantOrganizationAuthority(
|
||||
input: AssistantOrganizationAuthorityInput
|
||||
): AssistantOrganizationAuthority {
|
||||
const normalizeOrganizationScopeValue =
|
||||
input.normalizeOrganizationScopeValue ?? normalizeOrganizationScopeDefault;
|
||||
const mergeKnownOrganizations = input.mergeKnownOrganizations ?? mergeKnownOrganizationsDefault;
|
||||
const sessionItems = Array.isArray(input.sessionItems) ? input.sessionItems : [];
|
||||
const continuitySnapshot = resolveAssistantContinuitySnapshot({
|
||||
sessionItems,
|
||||
toNonEmptyString: input.toNonEmptyString ?? normalizeOrganizationScopeValue
|
||||
});
|
||||
const assistantSignals = collectAssistantOrganizationSignals(sessionItems, normalizeOrganizationScopeValue);
|
||||
const continuityActiveOrganization =
|
||||
normalizeOrganizationScopeValue(input.sessionActiveOrganization) ??
|
||||
assistantSignals.lastAssistantActiveOrganization ??
|
||||
normalizeOrganizationScopeValue(continuitySnapshot.activeOrganization);
|
||||
const knownOrganizations = mergeKnownOrganizations([
|
||||
...(Array.isArray(input.sessionKnownOrganizations) ? input.sessionKnownOrganizations : []),
|
||||
...assistantSignals.knownOrganizationsFromAssistantHistory,
|
||||
input.sessionSelectedOrganization,
|
||||
input.sessionActiveOrganization,
|
||||
continuityActiveOrganization
|
||||
]);
|
||||
const selectedOrganization =
|
||||
normalizeOrganizationScopeValue(input.sessionSelectedOrganization) ??
|
||||
assistantSignals.lastAssistantSelectedOrganization;
|
||||
const activeOrganization =
|
||||
normalizeOrganizationScopeValue(input.sessionActiveOrganization) ??
|
||||
selectedOrganization ??
|
||||
assistantSignals.lastAssistantActiveOrganization ??
|
||||
continuityActiveOrganization ??
|
||||
(knownOrganizations.length === 1 ? knownOrganizations[0] : null);
|
||||
const organizationClarificationCandidates = Array.isArray(input.lastOrganizationClarificationDebug?.organization_candidates)
|
||||
? mergeKnownOrganizations([
|
||||
...input.lastOrganizationClarificationDebug.organization_candidates,
|
||||
...knownOrganizations,
|
||||
selectedOrganization,
|
||||
activeOrganization,
|
||||
continuityActiveOrganization
|
||||
])
|
||||
: [];
|
||||
const organizationClarificationSelectionFromScope = selectedOrganization ?? activeOrganization;
|
||||
|
||||
return {
|
||||
continuitySnapshot,
|
||||
continuityActiveOrganization,
|
||||
lastAssistantSelectedOrganization: assistantSignals.lastAssistantSelectedOrganization,
|
||||
lastAssistantActiveOrganization: assistantSignals.lastAssistantActiveOrganization,
|
||||
knownOrganizations,
|
||||
selectedOrganization,
|
||||
activeOrganization,
|
||||
organizationClarificationCandidates,
|
||||
organizationClarificationSelectionFromScope
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ import {
|
|||
normalizeOrganizationScopeValue
|
||||
} from "./assistantOrganizationMatcher";
|
||||
import {
|
||||
resolveAssistantContinuitySnapshot,
|
||||
isGroundedAddressDebug,
|
||||
readAddressDebugOrganization
|
||||
resolveAssistantOrganizationAuthority
|
||||
} from "./assistantContinuityPolicy";
|
||||
|
||||
const DATA_SCOPE_CACHE_TTL_MS = 60_000;
|
||||
|
|
@ -297,40 +295,13 @@ export function createAssistantDataScopePolicy(deps: AssistantDataScopePolicyDep
|
|||
}
|
||||
|
||||
function findLastAssistantActiveOrganization(items: unknown[]): string | null {
|
||||
const continuitySnapshot = resolveAssistantContinuitySnapshot({
|
||||
const authority = resolveAssistantOrganizationAuthority({
|
||||
sessionItems: items,
|
||||
toNonEmptyString: normalizeOrganizationScopeValue
|
||||
toNonEmptyString: normalizeOrganizationScopeValue,
|
||||
normalizeOrganizationScopeValue,
|
||||
mergeKnownOrganizations
|
||||
});
|
||||
const continuityOrganization = normalizeOrganizationScopeValue(continuitySnapshot.activeOrganization);
|
||||
|
||||
for (let index = (Array.isArray(items) ? items.length : 0) - 1; index >= 0; index -= 1) {
|
||||
const item = Array.isArray(items) ? items[index] : null;
|
||||
if (!item || typeof item !== "object" || (item as { role?: string }).role !== "assistant") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const debug = (item as { debug?: Record<string, unknown> }).debug;
|
||||
if (!debug || typeof debug !== "object") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const direct = normalizeOrganizationScopeValue(debug.assistant_active_organization);
|
||||
if (direct) {
|
||||
return direct;
|
||||
}
|
||||
const selected = normalizeOrganizationScopeValue(debug.living_chat_selected_organization);
|
||||
if (selected) {
|
||||
return selected;
|
||||
}
|
||||
if (isGroundedAddressDebug(debug)) {
|
||||
const groundedOrganization = normalizeOrganizationScopeValue(readAddressDebugOrganization(debug));
|
||||
if (groundedOrganization) {
|
||||
return groundedOrganization;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return continuityOrganization;
|
||||
return normalizeOrganizationScopeValue(authority.activeOrganization);
|
||||
}
|
||||
|
||||
function extractOrganizationFactsFromRows(rows: unknown[]): AssistantOrganizationFacts {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
buildInventoryHistoryCapabilityFollowupReply as buildInventoryHistoryCapabilityFollowupReplyFromPolicy,
|
||||
resolveAssistantLivingChatMemoryContext
|
||||
} from "./assistantMemoryRecapPolicy";
|
||||
import { resolveAssistantContinuitySnapshot } from "./assistantContinuityPolicy";
|
||||
import { resolveAssistantOrganizationAuthority } from "./assistantContinuityPolicy";
|
||||
|
||||
export interface AssistantLivingChatSessionScopeInput {
|
||||
knownOrganizations?: unknown[];
|
||||
|
|
@ -249,10 +249,18 @@ export async function runAssistantLivingChatRuntime(
|
|||
input: AssistantLivingChatRuntimeInput
|
||||
): Promise<AssistantLivingChatRuntimeOutput> {
|
||||
const userMessage = String(input.userMessage ?? "");
|
||||
const continuitySnapshot = resolveAssistantContinuitySnapshot({
|
||||
const organizationAuthority = resolveAssistantOrganizationAuthority({
|
||||
sessionItems: input.sessionItems,
|
||||
toNonEmptyString: input.toNonEmptyString
|
||||
sessionKnownOrganizations: Array.isArray(input.sessionScope.knownOrganizations)
|
||||
? input.sessionScope.knownOrganizations
|
||||
: [],
|
||||
sessionSelectedOrganization: input.sessionScope.selectedOrganization,
|
||||
sessionActiveOrganization: input.sessionScope.activeOrganization,
|
||||
toNonEmptyString: input.toNonEmptyString,
|
||||
normalizeOrganizationScopeValue: input.toNonEmptyString,
|
||||
mergeKnownOrganizations: input.mergeKnownOrganizations
|
||||
});
|
||||
const continuitySnapshot = organizationAuthority.continuitySnapshot;
|
||||
const dataScopeMetaQuery = input.hasAssistantDataScopeMetaQuestionSignal(userMessage);
|
||||
const capabilityMetaQuery = input.shouldHandleAsAssistantCapabilityMetaQuery(userMessage);
|
||||
const destructiveSignal = input.hasDestructiveDataActionSignal(userMessage);
|
||||
|
|
@ -267,13 +275,10 @@ export async function runAssistantLivingChatRuntime(
|
|||
let livingChatGroundingGuardApplied = false;
|
||||
let livingChatGroundingGuardReason: string | null = null;
|
||||
let livingChatProactiveScopeOfferApplied = false;
|
||||
const continuityActiveOrganization = input.toNonEmptyString(continuitySnapshot.activeOrganization);
|
||||
let knownOrganizations = input.mergeKnownOrganizations([
|
||||
...(Array.isArray(input.sessionScope.knownOrganizations) ? input.sessionScope.knownOrganizations : []),
|
||||
continuityActiveOrganization
|
||||
]);
|
||||
let selectedOrganization = input.toNonEmptyString(input.sessionScope.selectedOrganization);
|
||||
let activeOrganization = input.toNonEmptyString(input.sessionScope.activeOrganization) ?? continuityActiveOrganization;
|
||||
const continuityActiveOrganization = organizationAuthority.continuityActiveOrganization;
|
||||
let knownOrganizations = [...organizationAuthority.knownOrganizations];
|
||||
let selectedOrganization = organizationAuthority.selectedOrganization;
|
||||
let activeOrganization = organizationAuthority.activeOrganization;
|
||||
const memoryRecapContext = resolveAssistantLivingChatMemoryContext({
|
||||
modeDecisionReason: input.modeDecision?.reason ?? null,
|
||||
sessionItems: input.sessionItems
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
// @ts-nocheck
|
||||
import { resolveAssistantContinuitySnapshot } from "./assistantContinuityPolicy";
|
||||
import {
|
||||
resolveAssistantOrganizationAuthority
|
||||
} from "./assistantContinuityPolicy";
|
||||
|
||||
const ADDRESS_INTENTS_KEEP_ADDRESS_LANE = new Set([
|
||||
"period_coverage_profile",
|
||||
|
|
@ -109,26 +111,25 @@ export function createAssistantRoutePolicy(deps) {
|
|||
const sessionOrganizationScope = input?.sessionOrganizationScope && typeof input.sessionOrganizationScope === "object"
|
||||
? input.sessionOrganizationScope
|
||||
: null;
|
||||
const continuitySnapshot = resolveAssistantContinuitySnapshot({
|
||||
const organizationAuthority = resolveAssistantOrganizationAuthority({
|
||||
sessionItems,
|
||||
toNonEmptyString
|
||||
sessionKnownOrganizations: Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
||||
? sessionOrganizationScope.knownOrganizations
|
||||
: [],
|
||||
sessionSelectedOrganization: sessionOrganizationScope?.selectedOrganization,
|
||||
sessionActiveOrganization: sessionOrganizationScope?.activeOrganization,
|
||||
lastOrganizationClarificationDebug: findLastOrganizationClarificationAddressDebug(sessionItems),
|
||||
toNonEmptyString,
|
||||
normalizeOrganizationScopeValue,
|
||||
mergeKnownOrganizations
|
||||
});
|
||||
const continuityActiveOrganization = normalizeOrganizationScopeValue(sessionOrganizationScope?.activeOrganization) ??
|
||||
normalizeOrganizationScopeValue(continuitySnapshot.activeOrganization);
|
||||
const continuitySnapshot = organizationAuthority.continuitySnapshot;
|
||||
const continuityActiveOrganization = organizationAuthority.continuityActiveOrganization;
|
||||
const lastGroundedAddressDebug = findLastGroundedAddressAnswerDebug(sessionItems) ??
|
||||
continuitySnapshot.lastGroundedAddressDebug;
|
||||
const lastOrganizationClarificationDebug = findLastOrganizationClarificationAddressDebug(sessionItems);
|
||||
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
|
||||
? mergeKnownOrganizations([
|
||||
...lastOrganizationClarificationDebug.organization_candidates,
|
||||
...((Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
||||
? sessionOrganizationScope.knownOrganizations
|
||||
: [])),
|
||||
continuityActiveOrganization
|
||||
])
|
||||
: [];
|
||||
const organizationClarificationSelectionFromScope = normalizeOrganizationScopeValue(sessionOrganizationScope?.selectedOrganization) ??
|
||||
continuityActiveOrganization;
|
||||
const organizationClarificationCandidates = organizationAuthority.organizationClarificationCandidates;
|
||||
const organizationClarificationSelectionFromScope = organizationAuthority.organizationClarificationSelectionFromScope;
|
||||
const organizationClarificationSelection = resolveOrganizationSelectionFromMessage(rawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(repairedRawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(effectiveAddressUserMessage, organizationClarificationCandidates) ??
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { resolveAssistantOrganizationAuthority } from "../src/services/assistantContinuityPolicy";
|
||||
|
||||
describe("assistantContinuityPolicy organization authority", () => {
|
||||
it("prefers explicit assistant organization authority over older grounded continuity and merges known organizations once", () => {
|
||||
const authority = resolveAssistantOrganizationAuthority({
|
||||
sessionItems: [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
answer_grounding_check: { status: "grounded" },
|
||||
extracted_filters: {
|
||||
organization: "Org Grounded",
|
||||
as_of_date: "2020-03-31"
|
||||
},
|
||||
detected_intent: "receivables_confirmed_as_of_date"
|
||||
}
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "living_chat",
|
||||
living_chat_selected_organization: "Org Selected",
|
||||
assistant_active_organization: "Org Selected",
|
||||
assistant_known_organizations: ["Org Selected", "Org Known"],
|
||||
living_chat_data_scope_probe_organizations: ["Org Probe"]
|
||||
}
|
||||
}
|
||||
],
|
||||
sessionKnownOrganizations: ["Org Session"],
|
||||
lastOrganizationClarificationDebug: {
|
||||
organization_candidates: ["Org Candidate", "Org Selected"]
|
||||
}
|
||||
});
|
||||
|
||||
expect(authority.continuitySnapshot.activeOrganization).toBe("Org Grounded");
|
||||
expect(authority.continuityActiveOrganization).toBe("Org Selected");
|
||||
expect(authority.selectedOrganization).toBe("Org Selected");
|
||||
expect(authority.activeOrganization).toBe("Org Selected");
|
||||
expect(authority.knownOrganizations).toEqual([
|
||||
"Org Session",
|
||||
"Org Selected",
|
||||
"Org Known",
|
||||
"Org Probe",
|
||||
"Org Grounded"
|
||||
]);
|
||||
expect(authority.organizationClarificationCandidates).toEqual([
|
||||
"Org Candidate",
|
||||
"Org Selected",
|
||||
"Org Session",
|
||||
"Org Known",
|
||||
"Org Probe",
|
||||
"Org Grounded"
|
||||
]);
|
||||
expect(authority.organizationClarificationSelectionFromScope).toBe("Org Selected");
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createAssistantDataScopePolicy } from "../src/services/assistantDataScopePolicy";
|
||||
|
||||
function createPolicy() {
|
||||
return createAssistantDataScopePolicy({
|
||||
activeMcpChannel: "default",
|
||||
mcpProxyUrl: "http://localhost",
|
||||
executeAddressMcpQuery: vi.fn(async () => ({ rows: [] })),
|
||||
repairAddressMojibake: (value: unknown) => String(value ?? "")
|
||||
});
|
||||
}
|
||||
|
||||
describe("assistantDataScopePolicy", () => {
|
||||
it("recovers active organization from assistant-side living chat authority when grounded answer is absent", () => {
|
||||
const policy = createPolicy();
|
||||
|
||||
const activeOrganization = policy.findLastAssistantActiveOrganization([
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "living_chat",
|
||||
living_chat_selected_organization: "Org Selected",
|
||||
assistant_active_organization: "Org Selected",
|
||||
assistant_known_organizations: ["Org Selected", "Org Backup"]
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
expect(activeOrganization).toBe("Org Selected");
|
||||
});
|
||||
});
|
||||
|
|
@ -916,5 +916,59 @@
|
|||
"question_text": "а по Альтернативе Плюс сколько лет активности в базе 1С?",
|
||||
"answer_text": "На данный момент не удается точно определить, сколько лет Альтернатива Плюс активна в базе 1С — данные по истории деятельности контрагента в текущем сценарии недоступны.\n\nПроверено: документы, платежи и договоры по ООО «Альтернатива Плюс» за последние периоды. Результатов, подтверждающих начало активности, не обнаружено.\n\nМогу уточнить остаток по счёту или проверить наличие договоров/платежей за конкретные годы — сообщите, какой период вас интересует."
|
||||
}
|
||||
},
|
||||
{
|
||||
"annotation_id": "ann-mo4e8t0p-3n95ceh",
|
||||
"run_id": "assistant-stage1-9VDVeE165e",
|
||||
"case_id": "SAVED-001",
|
||||
"session_id": "assistant-stage1-9VDVeE165e-SAVED-001",
|
||||
"message_index": 35,
|
||||
"rating": 1,
|
||||
"comment": "написано что самый дохожный год 2020 8 мультов а слоджом в сл сообщении за 17 год заработали 12 смультов - хуйцня",
|
||||
"manual_case_decision": "needs_dialog_policy_fix",
|
||||
"annotation_author": "manual_reviewer",
|
||||
"resolved": true,
|
||||
"resolved_at": "2026-04-18T13:52:00.664Z",
|
||||
"resolved_by": "manual_reviewer",
|
||||
"created_at": "2026-04-18T13:49:36.360Z",
|
||||
"updated_at": "2026-04-18T13:52:00.664Z",
|
||||
"context": {
|
||||
"message_id": "msg-dVgccEymIx",
|
||||
"trace_id": "address-7XZM6cSlG2",
|
||||
"reply_type": "factual",
|
||||
"eval_target": "assistant_stage1",
|
||||
"prompt_version": "address_query_runtime_v1",
|
||||
"domain": null,
|
||||
"query_class": null,
|
||||
"question_text": "какой у нас самый доходный год",
|
||||
"answer_text": "Самый доходный год по подтвержденным поступлениям: 2021 (8.560.025,00 ₽ по 13 операциям). Это денежный поток, а не чистая прибыль.\nТоп-3 лет по сумме поступлений:\nКоротко: в выборке 5 заказчиков по 20 подтвержденным платежным строкам.\nПодтвержденный денежный поток в выборке: 18.269.640,00 ₽.\n1. 2021 | сумма: 8.560.025,00 ₽ | операций: 13 | контрагентов: 1 | максимальная разовая сумма: 1.882.400,00 ₽\n2. 2022 | сумма: 6.044.715,00 ₽ | операций: 4 | контрагентов: 4 | максимальная разовая сумма: 2.254.115,00 ₽\n3. 2020 | сумма: 3.664.900,00 ₽ | операций: 3 | контрагентов: 1 | максимальная разовая сумма: 3.248.300,00 ₽"
|
||||
}
|
||||
},
|
||||
{
|
||||
"annotation_id": "ann-mo4ebs7r-4qjefdf",
|
||||
"run_id": "assistant-stage1-9VDVeE165e",
|
||||
"case_id": "SAVED-001",
|
||||
"session_id": "assistant-stage1-9VDVeE165e-SAVED-001",
|
||||
"message_index": 39,
|
||||
"rating": 1,
|
||||
"comment": "не сходиться - там вы райноне 30 млн за все время - надо проверить маршрут",
|
||||
"manual_case_decision": "needs_routing_extension",
|
||||
"annotation_author": "manual_reviewer",
|
||||
"resolved": false,
|
||||
"resolved_at": null,
|
||||
"resolved_by": null,
|
||||
"created_at": "2026-04-18T13:51:55.286Z",
|
||||
"updated_at": "2026-04-18T13:51:55.286Z",
|
||||
"context": {
|
||||
"message_id": "msg-SAw2wG77rB",
|
||||
"trace_id": "address-QszgwNAdwL",
|
||||
"reply_type": "factual",
|
||||
"eval_target": "assistant_stage1",
|
||||
"prompt_version": "address_query_runtime_v1",
|
||||
"domain": null,
|
||||
"query_class": null,
|
||||
"question_text": "сколько вообще денег мы заработали за все время?",
|
||||
"answer_text": "За все доступное время подтверждено 18.269.640,00 ₽ входящих поступлений. Это денежный поток от клиентов, а не чистая прибыль.\n\nПодтверждение:\n- Операций в выборке: 20.\n- Контрагентов в выборке: 5.\n- Самый сильный год по поступлениям: 2021 (8.560.025,00 ₽).\n- Крупнейший контрагент по потоку: Группа СВК (12.224.925,00 ₽)."
|
||||
}
|
||||
}
|
||||
]
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,215 +0,0 @@
|
|||
{
|
||||
"saved_at": "2026-04-16T17:51:50.474Z",
|
||||
"generation_id": "gen-mo1s0m9z-ndf56a3",
|
||||
"mode": "saved_user_sessions",
|
||||
"title": "Ручная сессия 16.04.2026, 20:51:30",
|
||||
"source_session_id": "asst-iIpNheKZpP",
|
||||
"questions": [
|
||||
"привет как дела"
|
||||
],
|
||||
"session": {
|
||||
"session_id": "asst-iIpNheKZpP",
|
||||
"updated_at": "2026-04-16T17:51:30.711Z",
|
||||
"items": [
|
||||
{
|
||||
"message_id": "msg-R2if3yso9x",
|
||||
"session_id": "asst-iIpNheKZpP",
|
||||
"role": "user",
|
||||
"text": "привет как дела",
|
||||
"reply_type": null,
|
||||
"created_at": "2026-04-16T17:51:19.402Z",
|
||||
"trace_id": null,
|
||||
"debug": null
|
||||
},
|
||||
{
|
||||
"message_id": "msg-tRiU0RadHy",
|
||||
"session_id": "asst-iIpNheKZpP",
|
||||
"role": "assistant",
|
||||
"text": "Привет! У меня всё хорошо, спасибо за вопрос. А у тебя как дела? 😊",
|
||||
"reply_type": "factual_with_explanation",
|
||||
"created_at": "2026-04-16T17:51:30.711Z",
|
||||
"trace_id": "chat-2RCiQ9Whq9",
|
||||
"debug": {
|
||||
"trace_id": "chat-2RCiQ9Whq9",
|
||||
"prompt_version": "living_chat_router_v1",
|
||||
"schema_version": "living_chat_router_v1",
|
||||
"fallback_type": "none",
|
||||
"detected_mode": "chat",
|
||||
"detected_mode_confidence": "high",
|
||||
"execution_lane": "living_chat",
|
||||
"living_router_mode": "chat",
|
||||
"living_router_reason": "non_domain_query_indexed",
|
||||
"living_chat_response_source": "llm_chat",
|
||||
"living_chat_script_guard_applied": false,
|
||||
"living_chat_script_guard_reason": null,
|
||||
"living_chat_grounding_guard_applied": false,
|
||||
"living_chat_grounding_guard_reason": null,
|
||||
"living_chat_data_scope_probe_status": null,
|
||||
"living_chat_data_scope_probe_channel": null,
|
||||
"living_chat_data_scope_probe_org_count": 0,
|
||||
"living_chat_data_scope_probe_organizations": [],
|
||||
"living_chat_data_scope_probe_error": null,
|
||||
"living_chat_selected_organization": null,
|
||||
"assistant_known_organizations": [],
|
||||
"assistant_active_organization": null,
|
||||
"address_llm_predecompose_attempted": true,
|
||||
"address_llm_predecompose_applied": false,
|
||||
"address_llm_predecompose_reason": "no_usable_fragment",
|
||||
"address_llm_predecompose_contract": {
|
||||
"schema_version": "address_llm_predecompose_contract_v1",
|
||||
"source_message": "привет как дела",
|
||||
"canonical_message": "привет как дела",
|
||||
"mode": "unsupported",
|
||||
"mode_confidence": "low",
|
||||
"query_shape": "UNKNOWN",
|
||||
"query_shape_confidence": "low",
|
||||
"intent": "unknown",
|
||||
"intent_confidence": "low",
|
||||
"entities": {
|
||||
"account": null,
|
||||
"counterparty": null,
|
||||
"contract": null,
|
||||
"document_type": null,
|
||||
"document_ref": null,
|
||||
"organization": null
|
||||
},
|
||||
"period": {
|
||||
"scope": "unspecified",
|
||||
"period_from": null,
|
||||
"period_to": null,
|
||||
"as_of_date": null,
|
||||
"has_explicit_period": false
|
||||
},
|
||||
"semantics": {
|
||||
"scope_kind": "none",
|
||||
"anchor_kind": "none",
|
||||
"anchor_value": null,
|
||||
"date_scope_kind": "none",
|
||||
"date_basis_hint": null,
|
||||
"self_scope_detected": false,
|
||||
"selected_object_scope_detected": false
|
||||
},
|
||||
"aggregation_profile": "unknown"
|
||||
},
|
||||
"address_semantic_extraction_contract": {
|
||||
"schema_version": "address_semantic_extraction_contract_v1",
|
||||
"source_message": "привет как дела",
|
||||
"canonical_message": "привет как дела",
|
||||
"canonical_rewrite_applied": false,
|
||||
"extraction": {
|
||||
"mode": "unsupported",
|
||||
"mode_confidence": "low",
|
||||
"query_shape": "UNKNOWN",
|
||||
"query_shape_confidence": "low",
|
||||
"intent": "unknown",
|
||||
"intent_confidence": "low",
|
||||
"aggregation_profile": "unknown"
|
||||
},
|
||||
"entities": {
|
||||
"account": null,
|
||||
"counterparty": null,
|
||||
"contract": null,
|
||||
"document_type": null,
|
||||
"document_ref": null,
|
||||
"organization": null
|
||||
},
|
||||
"period": {
|
||||
"scope": "unspecified",
|
||||
"period_from": null,
|
||||
"period_to": null,
|
||||
"as_of_date": null,
|
||||
"has_explicit_period": false
|
||||
},
|
||||
"semantics": {
|
||||
"scope_kind": "none",
|
||||
"anchor_kind": "none",
|
||||
"anchor_value": null,
|
||||
"date_scope_kind": "none",
|
||||
"date_basis_hint": null,
|
||||
"self_scope_detected": false,
|
||||
"selected_object_scope_detected": false
|
||||
},
|
||||
"guard_hints": {
|
||||
"source_data_signal_detected": false,
|
||||
"canonical_data_signal_detected": false,
|
||||
"data_scope_meta_query_detected": false,
|
||||
"deep_investigation_signal_detected": false,
|
||||
"required_anchor_missing": false,
|
||||
"unsupported_low_confidence": true,
|
||||
"semantic_drift_suspected": false
|
||||
},
|
||||
"quality": "low",
|
||||
"valid": false,
|
||||
"apply_canonical_recommended": false,
|
||||
"reason_codes": [
|
||||
"unsupported_low_confidence_contract"
|
||||
]
|
||||
},
|
||||
"orchestration_contract_v1": {
|
||||
"schema_version": "assistant_orchestration_contract_v1",
|
||||
"hard_meta_mode": "non_domain",
|
||||
"address_mode": "unsupported",
|
||||
"address_mode_confidence": "low",
|
||||
"address_intent": "unknown",
|
||||
"address_intent_confidence": "low",
|
||||
"strong_data_signal_detected": false,
|
||||
"data_retrieval_signal_detected": false,
|
||||
"followup_context_detected": false,
|
||||
"unsupported_address_intent_fallback_to_deep": false,
|
||||
"final_decision": {
|
||||
"run_address_lane": false,
|
||||
"tool_gate_decision": "skip_address_lane",
|
||||
"tool_gate_reason": "non_domain_query_indexed",
|
||||
"living_mode": "chat",
|
||||
"living_reason": "non_domain_query_indexed"
|
||||
}
|
||||
},
|
||||
"tool_gate_decision": "skip_address_lane",
|
||||
"tool_gate_reason": "non_domain_query_indexed",
|
||||
"normalized": null,
|
||||
"normalizer_output": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"investigation_state": {
|
||||
"schema_version": "investigation_state_v1",
|
||||
"session_id": "asst-iIpNheKZpP",
|
||||
"status": "idle",
|
||||
"turn_index": 0,
|
||||
"updated_at": "2026-04-16T17:51:19.401Z",
|
||||
"question_id": null,
|
||||
"question_scope_id": null,
|
||||
"scope_origin": null,
|
||||
"focus": {
|
||||
"domain": null,
|
||||
"period": null,
|
||||
"primary_accounts": [],
|
||||
"active_query_subject": null
|
||||
},
|
||||
"narrowing_status": "unknown",
|
||||
"evidence_refs": [],
|
||||
"open_uncertainties": [],
|
||||
"last_answer_mode": null,
|
||||
"followup_context": null,
|
||||
"query_mode_hint": "direct_answer"
|
||||
},
|
||||
"address_navigation_state": {
|
||||
"schema_version": "address_navigation_state_v1",
|
||||
"session_id": "asst-iIpNheKZpP",
|
||||
"updated_at": "2026-04-16T17:51:19.401Z",
|
||||
"session_context": {
|
||||
"active_result_set_id": null,
|
||||
"active_focus_object": null,
|
||||
"last_confirmed_route": null,
|
||||
"date_scope": {
|
||||
"as_of_date": null,
|
||||
"period_from": null,
|
||||
"period_to": null
|
||||
},
|
||||
"organization_scope": null
|
||||
},
|
||||
"result_sets": [],
|
||||
"navigation_history": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
{
|
||||
"suite_id": "assistant_saved_session_runtime_job-DpGiicqgIN",
|
||||
"suite_version": "0.1.0",
|
||||
"schema_version": "assistant_saved_session_runtime_v0_1",
|
||||
"title": "БОЛЬШОЙ ОБЩИЙ Ручная сессия 16.04.2026, 21:26:06",
|
||||
"scenario_count": 1,
|
||||
"case_ids": [
|
||||
"SAVED-001"
|
||||
],
|
||||
"cases": [
|
||||
{
|
||||
"case_id": "SAVED-001",
|
||||
"scenario_tag": "saved_user_sessions_runtime",
|
||||
"title": "БОЛЬШОЙ ОБЩИЙ Ручная сессия 16.04.2026, 21:26:06",
|
||||
"question_type": "followup",
|
||||
"broadness_level": "medium",
|
||||
"turns": [
|
||||
{
|
||||
"user_message": "приветик - че как там дела"
|
||||
},
|
||||
{
|
||||
"user_message": "расскажи что можешь интересного"
|
||||
},
|
||||
{
|
||||
"user_message": "кайф - что там на складе по остаткам?"
|
||||
},
|
||||
{
|
||||
"user_message": "а исторические остатки на другие даты умеешь?"
|
||||
},
|
||||
{
|
||||
"user_message": "давай на июль 2017"
|
||||
},
|
||||
{
|
||||
"user_message": "март 2016"
|
||||
},
|
||||
{
|
||||
"user_message": "По выбранному объекту \"Рабочая станция универсального специалиста (индивидуальное изготовление)\": где взяли это?"
|
||||
},
|
||||
{
|
||||
"user_message": "а кому продали?"
|
||||
},
|
||||
{
|
||||
"user_message": "у тебя написано кто контрагент: рабочая станция - это ошибка?"
|
||||
},
|
||||
{
|
||||
"user_message": "ндс можешь прикинуть на дату покупки рабочей станции?"
|
||||
},
|
||||
{
|
||||
"user_message": "а какой ндс мы должны сгрузить на март 2020?"
|
||||
},
|
||||
{
|
||||
"user_message": "прикинь какой ндс нам надо заплатить на февраль 2017"
|
||||
},
|
||||
{
|
||||
"user_message": "кто у нас самый доходный клиент за все время"
|
||||
},
|
||||
{
|
||||
"user_message": "кто нам должен денег на май 2017"
|
||||
},
|
||||
{
|
||||
"user_message": "а какой ндс мы должны примерно заплатить за этот период?"
|
||||
},
|
||||
{
|
||||
"user_message": "мы должны комуто денег на сегодня?"
|
||||
},
|
||||
{
|
||||
"user_message": "а нам?"
|
||||
},
|
||||
{
|
||||
"user_message": "какой у нас самый доходный год"
|
||||
},
|
||||
{
|
||||
"user_message": "а за 2017 мы скок заработали?"
|
||||
},
|
||||
{
|
||||
"user_message": "сколько вообще денег мы заработали за все время?"
|
||||
},
|
||||
{
|
||||
"user_message": "ты умеешь считать дельту по договорам?"
|
||||
},
|
||||
{
|
||||
"user_message": "по чепурнову покажи все доки"
|
||||
},
|
||||
{
|
||||
"user_message": "а по свк"
|
||||
},
|
||||
{
|
||||
"user_message": "а сейчас у нас есть что на складе?"
|
||||
},
|
||||
{
|
||||
"user_message": "что нам отгружал чепурнов? какой товар или услугу?"
|
||||
},
|
||||
{
|
||||
"user_message": "какие остатки на складе на сегодня"
|
||||
},
|
||||
{
|
||||
"user_message": "остатки на март 2016"
|
||||
},
|
||||
{
|
||||
"user_message": "хвосты покажи по счету 60 на август 2022"
|
||||
},
|
||||
{
|
||||
"user_message": "Есть ли остатки товара, которые закупались очень давно"
|
||||
},
|
||||
{
|
||||
"user_message": "Какие конкретно номенклатуры формируют остаток по складу на май 2020"
|
||||
},
|
||||
{
|
||||
"user_message": "а по Альтернативе Плюс сколько лет активности в базе 1С?"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,10 +1,7 @@
|
|||
{
|
||||
"suite_id": "assistant_saved_session_gen-mo1s0m9z-ndf56a3",
|
||||
"suite_id": "assistant_saved_session_runtime_job-L2a8rqCJma",
|
||||
"suite_version": "0.1.0",
|
||||
"schema_version": "assistant_saved_session_suite_v0_1",
|
||||
"generated_at": "2026-04-16T17:51:50.471Z",
|
||||
"generation_id": "gen-mo1s0m9z-ndf56a3",
|
||||
"mode": "saved_user_sessions",
|
||||
"schema_version": "assistant_saved_session_runtime_v0_1",
|
||||
"title": "Ручная сессия 16.04.2026, 20:51:30",
|
||||
"scenario_count": 1,
|
||||
"case_ids": [
|
||||
|
|
@ -13,7 +10,7 @@
|
|||
"cases": [
|
||||
{
|
||||
"case_id": "SAVED-001",
|
||||
"scenario_tag": "saved_user_sessions",
|
||||
"scenario_tag": "saved_user_sessions_runtime",
|
||||
"title": "Ручная сессия 16.04.2026, 20:51:30",
|
||||
"question_type": "direct",
|
||||
"broadness_level": "medium",
|
||||
Loading…
Reference in New Issue