ARCH: детерминировать ответ на неподдержанный текущий смысл

This commit is contained in:
2026-04-19 21:16:22 +03:00
parent d9a85c1619
commit b339a8f8ca
3 changed files with 131 additions and 6 deletions
@@ -78,6 +78,43 @@ function buildDeterministicSmalltalkLeadReply(): string {
return "\u041f\u0440\u0438\u0432\u0435\u0442! \u0412\u0441\u0451 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e.";
}
function asRecord(value: unknown): Record<string, unknown> | null {
return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : null;
}
function firstMeaningEntityLabel(assistantTurnMeaning: Record<string, unknown> | null): string | null {
const candidates = Array.isArray(assistantTurnMeaning?.explicit_entity_candidates)
? assistantTurnMeaning?.explicit_entity_candidates
: [];
for (const candidate of candidates) {
const record = asRecord(candidate);
const value = typeof record?.value === "string" ? record.value.trim() : "";
if (value.length > 0) {
return value;
}
}
return null;
}
function buildUnsupportedCurrentTurnMeaningBoundaryReply(input: {
assistantTurnMeaning: Record<string, unknown> | null;
}): string {
const family =
typeof input.assistantTurnMeaning?.unsupported_but_understood_family === "string"
? input.assistantTurnMeaning.unsupported_but_understood_family
: null;
const entityLabel = firstMeaningEntityLabel(input.assistantTurnMeaning);
if (family === "counterparty_value_or_turnover") {
const entityPart = entityLabel ? ` \u043f\u043e \u00ab${entityLabel}\u00bb` : "";
return [
`\u042f \u043f\u043e\u043d\u044f\u043b \u0432\u043e\u043f\u0440\u043e\u0441: \u043d\u0443\u0436\u0435\u043d \u043e\u0431\u043e\u0440\u043e\u0442${entityPart}.`,
"\u0422\u043e\u0447\u043d\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u0434\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u0440\u0430\u0441\u0447\u0451\u0442\u0430 \u0435\u0449\u0451 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u043d\u0435 \u0431\u0443\u0434\u0443 \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u0440\u043e\u0448\u043b\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0438\u043b\u0438 \u0441\u0442\u0430\u0440\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430.",
"\u041c\u043e\u0433\u0443 \u043f\u043e\u043a\u0430 \u043d\u0430\u0434\u0451\u0436\u043d\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b, \u0434\u043e\u0433\u043e\u0432\u043e\u0440\u044b \u0438\u043b\u0438 \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0443."
].join(" ");
}
return "\u042f \u043f\u043e\u043d\u044f\u043b \u0441\u043c\u044b\u0441\u043b \u043d\u043e\u0432\u043e\u0433\u043e \u0432\u043e\u043f\u0440\u043e\u0441\u0430, \u043d\u043e \u0442\u043e\u0447\u043d\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u0435\u0449\u0451 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d. \u041d\u0435 \u0431\u0443\u0434\u0443 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u0440\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u043a\u0430\u043a \u0431\u0443\u0434\u0442\u043e \u044d\u0442\u043e \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435.";
}
export async function runAssistantLivingChatRuntime(
input: AssistantLivingChatRuntimeInput
): Promise<AssistantLivingChatRuntimeOutput> {
@@ -112,6 +149,15 @@ export async function runAssistantLivingChatRuntime(
let knownOrganizations = [...organizationAuthority.knownOrganizations];
let selectedOrganization = organizationAuthority.selectedOrganization;
let activeOrganization = organizationAuthority.activeOrganization;
const addressRuntimeMeta = (input.addressRuntimeMeta && typeof input.addressRuntimeMeta === "object"
? input.addressRuntimeMeta
: {}) as Record<string, unknown>;
const orchestrationContract = asRecord(addressRuntimeMeta.orchestrationContract);
const assistantTurnMeaning = asRecord(orchestrationContract?.assistant_turn_meaning);
const unsupportedCurrentTurnMeaningBoundary = Boolean(
input.modeDecision?.reason === "unsupported_current_turn_meaning_boundary" ||
orchestrationContract?.unsupported_current_turn_meaning_boundary === true
);
const memoryRecapContext = resolveAssistantLivingChatMemoryContext({
modeDecisionReason: input.modeDecision?.reason ?? null,
sessionItems: input.sessionItems
@@ -142,6 +188,11 @@ export async function runAssistantLivingChatRuntime(
dataScopeProbe?.status === "resolved"
? "deterministic_data_scope_contract_live"
: "deterministic_data_scope_contract";
} else if (unsupportedCurrentTurnMeaningBoundary) {
chatText = buildUnsupportedCurrentTurnMeaningBoundaryReply({
assistantTurnMeaning
});
livingChatSource = "deterministic_unsupported_current_turn_boundary";
} else if ((selectedOrganization || activeOrganization) && input.hasOrganizationFactLookupSignal(userMessage)) {
const scopedOrganization = selectedOrganization ?? activeOrganization ?? null;
chatText = input.buildAssistantOrganizationFactBoundaryReply(scopedOrganization);
@@ -254,9 +305,6 @@ export async function runAssistantLivingChatRuntime(
};
}
const addressRuntimeMeta = (input.addressRuntimeMeta && typeof input.addressRuntimeMeta === "object"
? input.addressRuntimeMeta
: {}) as Record<string, unknown>;
const predecomposeContract =
addressRuntimeMeta.predecomposeContract && typeof addressRuntimeMeta.predecomposeContract === "object"
? (addressRuntimeMeta.predecomposeContract as Record<string, unknown>)