Закрепить selected-object закупки в 1С ассистенте

This commit is contained in:
2026-06-02 11:22:30 +03:00
parent d77bcbe482
commit b07bcecc83
15 changed files with 400 additions and 10 deletions
@@ -126,7 +126,7 @@ function hasSelectedObjectInventoryPurchaseDocumentsSignal(text: string): boolea
const value = String(text ?? "");
if (
hasSelectedObjectInventoryCue(value) &&
/(?:по\s+каким\s+документам\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*купили|по\s+каким\s+документам\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*купили|какими\s+документами\s+(?:был\s+)?куплен|покажи\s+документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(
/(?:по\s+каким\s+документам\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*купили|по\s+каким\s+документам\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)?\s*купили|какими\s+документами\s+(?:был\s+)?куплен|каким\s+документом\s+подтвержден[ао]?\s+закупк|какой\s+документ\s+подтвержда[её]т\s+закупк|документ[а-яё]*\s+подтвержд[а-яё]*\s+закупк|закупк[а-яё]*\s+подтвержден[а-яё]*\s+документ|покажи\s+документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(
value
)
) {
@@ -721,6 +721,14 @@ function shouldRestoreInventoryRootFrame(
}
function hasSelectedObjectInventorySignal(text: string): boolean {
const repairedSelectedObjectText = textWithRepairedVariant(String(text ?? ""));
if (
/(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+ней|по\s+нему|по\s+ним|for\s+selected\s+object|selected\s+object)/iu.test(
repairedSelectedObjectText
)
) {
return true;
}
if (
/(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+ней|по\s+нему|по\s+ним|for\s+selected\s+object|selected\s+object)/iu.test(
String(text ?? "")
@@ -762,8 +770,11 @@ export function hasInventorySupplierFollowupCue(text: string): boolean {
}
export function hasInventoryPurchaseDocumentsFollowupCue(text: string): boolean {
const value = String(text ?? "");
const value = textWithRepairedVariant(String(text ?? ""));
return (
/(?:по\s+каким\s+документам\s+(?:это|его|ее|её|этот\s+товар|эту\s+позицию)\s+купили|по\s+каким\s+документам\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|какими\s+документами\s+(?:был\s+)?куплен|каким\s+документом\s+(?:это|его|ее|её|она|он|этот\s+товар|эта\s+позиция|эту\s+позицию)?\s*(?:подтвержден[оа]?|подтверждена\s+закупка|доказан[оа]?|закрыт[оа]?)|закупк[аи]\s+подтвержден[аы]?\s+документ|покажи\s+документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему))/iu.test(
value
) ||
/(?:по\s+каким\s+документам\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|по\s+каким\s+документам\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|какими\s+документами\s+(?:был\s+)?куплен|каким\s+документом\s+(?:это|его|её|ее|она|он|этот\s+товар|эта\s+позиция|эту\s+позицию)?\s*(?:подтвержден[оа]?|доказан[оа]?|закрыт[оа]?)|покажи\s+документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(
value
) ||
@@ -875,10 +886,17 @@ export function hasInventoryMarginRankingFollowupCue(text: string): boolean {
}
export function hasAddressFollowupContextSignal(text: string): boolean {
const normalized = String(text ?? "").trim();
const normalized = textWithRepairedVariant(String(text ?? "")).trim();
if (!normalized) {
return false;
}
if (
/(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+ней|по\s+нему|по\s+ним|for\s+selected\s+object|selected\s+object)/iu.test(
normalized
)
) {
return true;
}
if (hasInventoryMarginRankingFollowupCue(normalized)) {
return true;
}
@@ -134,6 +134,10 @@ function inventoryPartyListOrUnknown(parties: string[]): string {
return parties.length > 0 ? parties.slice(0, 4).join("; ") : "не выделен отдельным полем";
}
function omitAmountFromInventoryTraceEvidence(lines: string[]): string[] {
return lines.map((line) => String(line ?? "").replace(/\s+\|\s+сумма:\s+[^|]+(?=\s+\||$)/gu, ""));
}
function normalizeInventoryReplyEntityToken(value: string | null | undefined): string | null {
const normalized = String(value ?? "")
.trim()
@@ -535,7 +539,7 @@ export function composeInventoryReply(
appendInventorySection(
lines,
"Опорные документы:",
deps.formatInventoryTraceRows(purchaseRows, INVENTORY_TRACE_EVIDENCE_ROW_LIMIT)
omitAmountFromInventoryTraceEvidence(deps.formatInventoryTraceRows(purchaseRows, INVENTORY_TRACE_EVIDENCE_ROW_LIMIT))
);
if (purchaseRows.length > INVENTORY_TRACE_EVIDENCE_ROW_LIMIT) {
lines.push(
@@ -3255,6 +3255,11 @@ function hasPredecomposeExplicitDrilldownSignal(text) {
return ADDRESS_DOCS_SIGNAL_PATTERN.test(source) || ADDRESS_BANK_SIGNAL_PATTERN.test(source) || ADDRESS_CONTRACT_SIGNAL_PATTERN.test(source);
}
function hasSelectedObjectInventoryFollowupSignalForPredecompose(text) {
const source = String(text ?? "");
const repaired = repairAddressMojibake(source);
if (/(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(`${source} ${repaired}`)) {
return true;
}
return /(?:по\s+выбранному\s+объекту|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(String(text ?? ""));
}
function isInventorySelectedObjectFollowupIntent(intent) {
@@ -3540,6 +3545,33 @@ async function runAddressLlmPreDecompose(normalizerService, payload, userMessage
}, userMessage);
}
const candidateAnchorQuality = evaluateAddressAnchorQuality(candidate);
const candidateHasSemanticSelectedObjectScope =
candidateMeta?.semanticHints?.selected_object_scope_detected === true ||
candidateMeta?.semanticHints?.scope_target_kind === "selected_object";
const candidateTextForSelectedObjectCounterpartyDegradation = compactWhitespace(repairAddressMojibake(String(candidate ?? "")).toLowerCase());
const candidateUsesSelectedObjectAsCounterpartyAnchor =
/(?:^|\s)по\s+контрагент\p{L}*\s+(?:выбран\p{L}*|selected(?:\s+object)?|object|объект\p{L}*)/iu.test(candidateTextForSelectedObjectCounterpartyDegradation) ||
/(?:^|\s)counterparty\s+(?:selected(?:\s+object)?|object)/iu.test(candidateTextForSelectedObjectCounterpartyDegradation);
const candidateTurnsSelectedObjectIntoCounterparty =
sourceHasSelectedObjectInventoryFollowup &&
(candidateInjectsGenericDocsIntent || candidateUsesSelectedObjectAsCounterpartyAnchor) &&
(candidateHasSemanticSelectedObjectScope || candidateUsesSelectedObjectAsCounterpartyAnchor) &&
(candidateAnchorQuality.anchorType === "counterparty" || candidateUsesSelectedObjectAsCounterpartyAnchor) &&
(candidateAnchorQuality.quality < 2 || candidateUsesSelectedObjectAsCounterpartyAnchor);
if (candidateTurnsSelectedObjectIntoCounterparty) {
return attachAddressPredecomposeContract({
...baseMeta,
attempted: true,
applied: false,
traceId: normalized?.trace_id ?? null,
llmCanonicalCandidateDetected: true,
effectiveMessage: userMessage,
reason: "normalized_fragment_rejected_selected_object_counterparty_degradation",
fallbackRuleHit: null,
sanitizedUserMessage,
semanticHints: candidateMeta?.semanticHints ?? null
}, userMessage);
}
const sameIntentForAnchorSafety = sourceAnchorQuality.intent !== "unknown" && sourceAnchorQuality.intent === candidateAnchorQuality.intent;
const sourceSelectedObjectItemAnchorValue = toNonEmptyString((0, addressFilterExtractor_1.extractSelectedObjectQuotedValue)(userMessage)) ??
toNonEmptyString((0, addressFilterExtractor_1.extractSelectedObjectQuotedValue)(repairedSourceMessage || userMessage));
@@ -195,6 +195,24 @@ export function createAssistantTransitionPolicy(deps) {
if (samples.length === 0) {
return null;
}
if (
samples.some((sample) =>
/(?:где\s+(?:мы\s+)?взял|откуда\s+(?:мы\s+)?взял|у\s+кого\s+купил|кто\s+постав|поставщик|источник\s+поступл|от\s+кого\s+поступ)/iu.test(
sample
)
)
) {
return "inventory_purchase_provenance_for_item";
}
if (
samples.some((sample) =>
/(?:документ|накладн|счет|счёт|акт|поступл|подтвержден|подтверждена\s+закупка|закупк[аи]\s+подтвержден)/iu.test(
sample
)
)
) {
return "inventory_purchase_documents_for_item";
}
if (
suggestedIntents.includes("bank_operations_by_counterparty") &&
samples.some((sample) => hasBankOperationsPivotCue(sample) || hasReadableBankOperationsPivotCue(sample))
@@ -233,6 +251,13 @@ export function createAssistantTransitionPolicy(deps) {
if (!normalized) {
return false;
}
if (
/(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(
normalized
)
) {
return true;
}
return /(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции|по\s+этой\s+позиции|по\s+этому\s+товару|selected\s+object)/iu.test(
normalized
);
@@ -240,6 +265,12 @@ export function createAssistantTransitionPolicy(deps) {
function extractSelectedObjectLabel(text) {
const repaired = deps.repairAddressMojibake(String(text ?? ""));
const unicodeQuotedMatch = repaired.match(
/(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции)\s*[:,-]?\s*[«"]([^"»]+?)["»]/iu
);
if (unicodeQuotedMatch?.[1]) {
return deps.toNonEmptyString(unicodeQuotedMatch[1]);
}
const quotedMatch = repaired.match(
/(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции)\s*[:,-]?\s*[«"]([^"»]+?)["»]/iu
);