NODEDC_1C/llm_normalizer/backend/dist/services/assistantBoundaryPolicy.js

195 lines
9.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createAssistantBoundaryPolicy = createAssistantBoundaryPolicy;
function normalizeSelectedOrganization(value, normalizeOrganizationScopeValue) {
return normalizeOrganizationScopeValue(value) ?? String(value ?? "").trim();
}
function containsCjkChars(text) {
const source = String(text ?? "");
if (!source) {
return false;
}
return /[\u3400-\u9FFF\uF900-\uFAFF]/u.test(source);
}
function containsLetterLikeChars(text) {
const source = String(text ?? "");
if (!source) {
return false;
}
return /[A-Za-z\u0400-\u04FF]/u.test(source);
}
function createAssistantBoundaryPolicy(deps) {
function buildAssistantDataScopeContractReply(scopeProbe = null) {
const organizations = Array.isArray(scopeProbe?.organizations)
? scopeProbe.organizations
.map((item) => normalizeSelectedOrganization(item, deps.normalizeOrganizationScopeValue))
.filter((item) => item.length > 0)
.filter((item, index, array) => array.indexOf(item) === index)
: [];
if (organizations.length === 1) {
return [
`Сейчас доступна организация: ${organizations[0]}.`,
"Могу сразу показать по ней документы, операции, договоры или остатки."
].join(" ");
}
if (organizations.length > 1) {
const preview = organizations.slice(0, 10).join(", ");
return [
`Сейчас доступны организации (${organizations.length}): ${preview}.`,
"Скажите, по какой организации смотреть данные."
].join(" ");
}
if (scopeProbe?.status === "unresolved_with_error" && scopeProbe?.error) {
return [
"Сейчас не удалось определить список организаций из подключенной базы.",
`Техническая причина: ${scopeProbe.error}.`,
"Проверьте подключение, и я сразу назову доступный контур."
].join(" ");
}
return [
"Сейчас вижу только данные текущего подключенного контура.",
"Если в нем несколько организаций, скажите, по какой смотреть данные."
].join(" ");
}
function buildAssistantProactiveOrganizationOfferReply(scopeProbe = null) {
const organizations = Array.isArray(scopeProbe?.organizations)
? scopeProbe.organizations
.map((item) => normalizeSelectedOrganization(item, deps.normalizeOrganizationScopeValue))
.filter((item) => item.length > 0)
.filter((item, index, array) => array.indexOf(item) === index)
: [];
if (organizations.length === 1) {
return [
`Если дальше пойдём в данные 1С, могу сразу держать в контуре ${organizations[0]}.`,
"Можно просто писать вопрос по документам, остаткам, НДС или контрагентам."
].join(" ");
}
if (organizations.length > 1) {
const preview = organizations.slice(0, 10).join(", ");
return [
`Если дальше пойдём в данные 1С, могу сразу зафиксировать организацию: ${preview}.`,
"Просто напишите название компании, и дальше буду держать её активной в этой сессии."
].join(" ");
}
return "";
}
function buildAssistantDataScopeSelectionReply(organization) {
const selected = normalizeSelectedOrganization(organization, deps.normalizeOrganizationScopeValue);
return [
`Отлично, фиксирую рабочую организацию: ${selected}.`,
"Дальше буду держать этот контур как активный, пока вы не переключите организацию."
].join(" ");
}
function buildAssistantOrganizationFactBoundaryReply(organization) {
const selected = normalizeSelectedOrganization(organization, deps.normalizeOrganizationScopeValue);
if (selected) {
return [
`По организации ${selected} не буду называть дату/возраст без live-подтвержденного источника.`,
"Если нужно, запрошу факт из 1С и верну только подтвержденный ответ."
].join(" ");
}
return [
"Не буду называть дату/возраст организации без live-подтвержденного источника.",
"Сначала получу факт из 1С, потом дам точный ответ."
].join(" ");
}
function buildAssistantOperationalBoundaryReply() {
return [
"Понимаю, что ситуация срочная.",
"Я не могу сам настраивать 1С или менять базу/конфигурацию.",
"Могу помочь безопасно: разберем симптомы и подготовим точные шаги для вашего 1С/ИТ-админа."
].join(" ");
}
function buildAssistantSafetyRefusalReply() {
return [
"Я не могу помогать с удалением базы или скрытием данных.",
"Если вам угрожает опасность, срочно звоните 112 и следуйте указаниям экстренных служб.",
"По 1С могу дать только безопасные диагностические рекомендации."
].join(" ");
}
function applyLivingChatScriptGuard(chatText, userMessage) {
const source = String(chatText ?? "").trim();
if (!source) {
return {
text: "",
applied: false,
reason: null
};
}
if (!containsCjkChars(source) || containsCjkChars(userMessage)) {
return {
text: source,
applied: false,
reason: null
};
}
const stripped = source
.replace(/[\u3400-\u9FFF\uF900-\uFAFF]+/gu, "")
.replace(/[,。、!?;:()【】]/gu, "")
.replace(/\s{2,}/g, " ")
.replace(/\s+([,.!?;:])/g, "$1")
.trim();
if (stripped && containsLetterLikeChars(stripped)) {
return {
text: stripped,
applied: true,
reason: "unexpected_cjk_fragment_stripped"
};
}
return {
text: "Понял. Сформулируйте, что именно нужно по данным 1С, и я помогу по шагам.",
applied: true,
reason: "unexpected_cjk_fragment_fallback"
};
}
function applyLivingChatGroundingGuard(input) {
const userMessage = String(input?.userMessage ?? "");
const chatText = String(input?.chatText ?? "").trim();
const organization = deps.toNonEmptyString(input?.organization);
if (!chatText) {
return {
text: chatText,
applied: false,
reason: null
};
}
if (!deps.hasOrganizationFactLookupSignal(userMessage)) {
return {
text: chatText,
applied: false,
reason: null
};
}
if (/(?:не\s+могу|не\s+вижу|после\s+проверки|live|подтвержден)/i.test(chatText)) {
return {
text: chatText,
applied: false,
reason: null
};
}
const hasSpecificUnverifiedFact = /(?:\b\d{1,2}[./-]\d{1,2}[./-](?:\d{2}|\d{4})\b|\b(?:19|20)\d{2}\b|\b\d+\s+лет\b)/i.test(chatText);
if (!hasSpecificUnverifiedFact) {
return {
text: chatText,
applied: false,
reason: null
};
}
return {
text: buildAssistantOrganizationFactBoundaryReply(organization),
applied: true,
reason: "organization_fact_without_live_source_blocked"
};
}
return {
buildAssistantDataScopeContractReply,
buildAssistantProactiveOrganizationOfferReply,
buildAssistantDataScopeSelectionReply,
buildAssistantOrganizationFactBoundaryReply,
buildAssistantOperationalBoundaryReply,
buildAssistantSafetyRefusalReply,
applyLivingChatScriptGuard,
applyLivingChatGroundingGuard
};
}