ARCH: замкнуть grounded entity follow-up на документы и денежный поток
This commit is contained in:
@@ -174,6 +174,53 @@ function readAssistantMcpDiscoveryDerivedMetadataSurface(
|
||||
return toRecordObject(pilot?.derived_metadata_surface);
|
||||
}
|
||||
|
||||
function readAssistantMcpDiscoveryDerivedEntityResolution(
|
||||
debug: Record<string, unknown> | null
|
||||
): Record<string, unknown> | null {
|
||||
const bridge = readAssistantMcpDiscoveryBridge(debug);
|
||||
const pilot = toRecordObject(bridge?.pilot);
|
||||
return toRecordObject(pilot?.derived_entity_resolution);
|
||||
}
|
||||
|
||||
function collectAssistantMcpDiscoveryEntityCandidates(
|
||||
debug: Record<string, unknown> | null,
|
||||
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
|
||||
): string[] {
|
||||
const result: string[] = [];
|
||||
const resolution = readAssistantMcpDiscoveryDerivedEntityResolution(debug);
|
||||
const pushCandidate = (value: unknown): void => {
|
||||
const text = toNonEmptyString(value);
|
||||
if (text && !result.includes(text)) {
|
||||
result.push(text);
|
||||
}
|
||||
};
|
||||
|
||||
pushCandidate(resolution?.resolved_entity);
|
||||
pushCandidate(resolution?.requested_entity);
|
||||
if (Array.isArray(resolution?.ambiguity_candidates)) {
|
||||
for (const candidate of resolution.ambiguity_candidates) {
|
||||
pushCandidate(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
const discoveryMeaning = readAssistantMcpDiscoveryTurnMeaning(debug);
|
||||
const explicitEntities = Array.isArray(discoveryMeaning?.explicit_entity_candidates)
|
||||
? discoveryMeaning.explicit_entity_candidates
|
||||
: [];
|
||||
for (const entity of explicitEntities) {
|
||||
pushCandidate(candidateValue(entity));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function readAssistantMcpDiscoveryEntityCandidates(
|
||||
debug: Record<string, unknown> | null,
|
||||
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
|
||||
): string[] {
|
||||
return collectAssistantMcpDiscoveryEntityCandidates(debug, toNonEmptyString);
|
||||
}
|
||||
|
||||
export function readAssistantMcpDiscoveryPilotScope(
|
||||
debug: Record<string, unknown> | null,
|
||||
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
|
||||
@@ -386,12 +433,9 @@ export function readAddressDebugCounterparty(
|
||||
if (String(debug?.anchor_type ?? "") === "counterparty") {
|
||||
return toNonEmptyString(debug?.anchor_value_resolved) ?? toNonEmptyString(debug?.anchor_value_raw);
|
||||
}
|
||||
const discoveryMeaning = readAssistantMcpDiscoveryTurnMeaning(debug);
|
||||
const explicitEntities = Array.isArray(discoveryMeaning?.explicit_entity_candidates)
|
||||
? discoveryMeaning?.explicit_entity_candidates
|
||||
: [];
|
||||
for (const entity of explicitEntities) {
|
||||
const text = candidateValue(entity);
|
||||
const discoveryEntities = collectAssistantMcpDiscoveryEntityCandidates(debug, toNonEmptyString);
|
||||
for (const entity of discoveryEntities) {
|
||||
const text = toNonEmptyString(entity);
|
||||
if (text) {
|
||||
return text;
|
||||
}
|
||||
|
||||
@@ -158,6 +158,23 @@ function firstEntityCandidate(pilot: AssistantMcpDiscoveryPilotExecutionContract
|
||||
return null;
|
||||
}
|
||||
|
||||
function explicitDateScope(pilot: AssistantMcpDiscoveryPilotExecutionContract): string | null {
|
||||
const value = pilot.evidence.query_plan.turn_meaning_ref?.explicit_date_scope;
|
||||
if (typeof value !== "string") {
|
||||
return null;
|
||||
}
|
||||
const normalized = value.trim();
|
||||
return normalized.length > 0 ? normalized : null;
|
||||
}
|
||||
|
||||
function documentOrMovementScopeRu(pilot: AssistantMcpDiscoveryPilotExecutionContract): string {
|
||||
const entity = firstEntityCandidate(pilot);
|
||||
const period = explicitDateScope(pilot);
|
||||
const entityPart = entity ? ` по контрагенту ${entity}` : "";
|
||||
const periodPart = period ? ` за ${period}` : " в проверенном окне";
|
||||
return `${entityPart}${periodPart}`;
|
||||
}
|
||||
|
||||
function isMovementLaneClarification(pilot: AssistantMcpDiscoveryPilotExecutionContract): boolean {
|
||||
return (
|
||||
isMovementPilot(pilot) ||
|
||||
@@ -258,7 +275,7 @@ function headlineFor(mode: AssistantMcpDiscoveryAnswerMode, pilot: AssistantMcpD
|
||||
return "По текущему каталожному поиску 1С точный контрагент пока не подтвержден.";
|
||||
}
|
||||
if (isMovementPilot(pilot) && mode === "confirmed_with_bounded_inference") {
|
||||
return "По данным 1С найдены строки движений; ответ ограничен проверенным периодом и найденными строками.";
|
||||
return `По движениям${documentOrMovementScopeRu(pilot)} в 1С найдены подтвержденные строки; ответ ограничен проверенным окном и найденными строками.`;
|
||||
}
|
||||
if (pilot.derived_metadata_surface && mode === "confirmed_with_bounded_inference") {
|
||||
if (pilot.derived_metadata_surface.ambiguity_detected) {
|
||||
@@ -276,7 +293,7 @@ function headlineFor(mode: AssistantMcpDiscoveryAnswerMode, pilot: AssistantMcpD
|
||||
if (pilot.derived_value_flow.value_flow_direction === "outgoing_supplier_payout") {
|
||||
return "По данным 1С найдены строки исходящих платежей/списаний; сумму и помесячную раскладку можно называть только в рамках проверенного периода и найденных строк.";
|
||||
}
|
||||
return "По данным 1С найдены строки денежных движений; сумму и помесячную раскладку можно называть только в рамках проверенного периода и найденных строк.";
|
||||
return "По данным 1С найдены строки входящих денежных поступлений; сумму и помесячную раскладку можно называть только в рамках проверенного периода и найденных строк.";
|
||||
}
|
||||
if (pilot.derived_bidirectional_value_flow && mode === "confirmed_with_bounded_inference") {
|
||||
return "По данным 1С найдены строки входящих и исходящих денежных движений; нетто можно называть только как расчет по найденным строкам и проверенному периоду.";
|
||||
@@ -285,14 +302,23 @@ function headlineFor(mode: AssistantMcpDiscoveryAnswerMode, pilot: AssistantMcpD
|
||||
if (pilot.derived_value_flow.value_flow_direction === "outgoing_supplier_payout") {
|
||||
return "По данным 1С найдены строки исходящих платежей/списаний; сумму можно называть только в рамках проверенного периода и найденных строк.";
|
||||
}
|
||||
return "По данным 1С найдены строки денежных движений; сумму можно называть только в рамках проверенного периода и найденных строк.";
|
||||
return "По данным 1С найдены строки входящих денежных поступлений; сумму можно называть только в рамках проверенного периода и найденных строк.";
|
||||
}
|
||||
if (isDocumentPilot(pilot) && mode === "confirmed_with_bounded_inference") {
|
||||
return "По данным 1С найдены строки документов; ответ ограничен проверенным периодом и найденными строками.";
|
||||
return `По документам${documentOrMovementScopeRu(pilot)} в 1С найдены подтвержденные строки; ответ ограничен проверенным окном и найденными строками.`;
|
||||
}
|
||||
if (isMovementPilot(pilot) && mode === "confirmed_with_bounded_inference") {
|
||||
return `По движениям${documentOrMovementScopeRu(pilot)} в 1С найдены подтвержденные строки; ответ ограничен проверенным окном и найденными строками.`;
|
||||
}
|
||||
if (mode === "confirmed_with_bounded_inference") {
|
||||
return "По данным 1С есть подтвержденная активность; длительность можно оценивать только как вывод из этих строк.";
|
||||
}
|
||||
if (isDocumentPilot(pilot) && mode === "bounded_inference_only") {
|
||||
return `По документам${documentOrMovementScopeRu(pilot)} полный срез не подтвержден; пока есть только ограниченная граница проверенного окна 1С.`;
|
||||
}
|
||||
if (isMovementPilot(pilot) && mode === "bounded_inference_only") {
|
||||
return `По движениям${documentOrMovementScopeRu(pilot)} полный срез не подтвержден; пока есть только ограниченная граница проверенного окна 1С.`;
|
||||
}
|
||||
if (mode === "bounded_inference_only") {
|
||||
return "Точный факт не подтвержден, но есть ограниченная оценка по найденной активности в 1С.";
|
||||
}
|
||||
@@ -523,15 +549,17 @@ function derivedValueFlowConfirmedLine(pilot: AssistantMcpDiscoveryPilotExecutio
|
||||
const counterparty = flow.counterparty ? ` по контрагенту ${flow.counterparty}` : "";
|
||||
const period = flow.period_scope ? ` за период ${flow.period_scope}` : " в проверенном окне";
|
||||
const movementLabel =
|
||||
flow.value_flow_direction === "outgoing_supplier_payout" ? "исходящих платежей/списаний" : "денежных движений";
|
||||
flow.value_flow_direction === "outgoing_supplier_payout"
|
||||
? "исходящих платежей/списаний"
|
||||
: "входящих денежных поступлений";
|
||||
const totalLabel =
|
||||
flow.value_flow_direction === "outgoing_supplier_payout"
|
||||
? "сумма исходящих платежей/списаний составляет"
|
||||
: "сумма составляет";
|
||||
: "сумма входящих денежных поступлений составляет";
|
||||
const caveat =
|
||||
flow.value_flow_direction === "outgoing_supplier_payout"
|
||||
? "Это расчет по найденным строкам 1С, а не подтверждение полного объема платежей вне проверенного окна."
|
||||
: "Это расчет по найденным строкам 1С, а не подтверждение полного оборота вне проверенного окна.";
|
||||
: "Это расчет по найденным строкам 1С, а не подтверждение полного объема поступлений вне проверенного окна.";
|
||||
const dates =
|
||||
flow.first_movement_date && flow.latest_movement_date
|
||||
? ` Первая найденная дата движения: ${flow.first_movement_date}; последняя: ${flow.latest_movement_date}.`
|
||||
|
||||
@@ -1694,26 +1694,38 @@ function buildLifecycleConfirmedFacts(result: AddressMcpQueryExecutorResult, cou
|
||||
];
|
||||
}
|
||||
|
||||
function buildDocumentConfirmedFacts(result: AddressMcpQueryExecutorResult, counterparty: string | null): string[] {
|
||||
if (result.error || result.matched_rows <= 0) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
counterparty
|
||||
? `1C document rows were found for counterparty ${counterparty}`
|
||||
: "1C document rows were found for the requested scope"
|
||||
];
|
||||
function checkedCounterpartySuffixRu(counterparty: string | null): string {
|
||||
return counterparty ? ` по контрагенту ${counterparty}` : "";
|
||||
}
|
||||
|
||||
function buildMovementConfirmedFacts(result: AddressMcpQueryExecutorResult, counterparty: string | null): string[] {
|
||||
function checkedPeriodSuffixRu(periodScope: string | null): string {
|
||||
return periodScope ? ` за ${periodScope}` : " в проверенном окне";
|
||||
}
|
||||
|
||||
function uncheckedPeriodBoundaryRu(periodScope: string | null): string {
|
||||
return periodScope ? ` вне периода ${periodScope}` : " без явно проверенного периода";
|
||||
}
|
||||
|
||||
function buildDocumentConfirmedFacts(
|
||||
result: AddressMcpQueryExecutorResult,
|
||||
counterparty: string | null,
|
||||
periodScope: string | null
|
||||
): string[] {
|
||||
if (result.error || result.matched_rows <= 0) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
counterparty
|
||||
? `1C movement rows were found for counterparty ${counterparty}`
|
||||
: "1C movement rows were found for the requested scope"
|
||||
];
|
||||
return [`В 1С найдены строки документов${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)}.`];
|
||||
}
|
||||
|
||||
function buildMovementConfirmedFacts(
|
||||
result: AddressMcpQueryExecutorResult,
|
||||
counterparty: string | null,
|
||||
periodScope: string | null
|
||||
): string[] {
|
||||
if (result.error || result.matched_rows <= 0) {
|
||||
return [];
|
||||
}
|
||||
return [`В 1С найдены строки движений${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)}.`];
|
||||
}
|
||||
|
||||
function buildValueFlowConfirmedFacts(
|
||||
@@ -1763,18 +1775,40 @@ function buildLifecycleInferredFacts(result: AddressMcpQueryExecutorResult): str
|
||||
return ["Business activity duration may be inferred from first and latest confirmed 1C activity rows"];
|
||||
}
|
||||
|
||||
function buildDocumentInferredFacts(result: AddressMcpQueryExecutorResult): string[] {
|
||||
function buildDocumentInferredFacts(
|
||||
result: AddressMcpQueryExecutorResult,
|
||||
counterparty: string | null,
|
||||
periodScope: string | null
|
||||
): string[] {
|
||||
if (result.error || result.fetched_rows <= 0) {
|
||||
return [];
|
||||
}
|
||||
return ["Counterparty document evidence is limited to confirmed 1C document rows in the checked scope"];
|
||||
if (result.matched_rows <= 0) {
|
||||
return [
|
||||
`По документам${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} удалось проверить только ограниченный срез 1С; подтвержденных строк документов этим поиском не найдено.`
|
||||
];
|
||||
}
|
||||
return [
|
||||
`Срез документов${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} ограничен только подтвержденными строками документов, найденными этим поиском.`
|
||||
];
|
||||
}
|
||||
|
||||
function buildMovementInferredFacts(result: AddressMcpQueryExecutorResult): string[] {
|
||||
function buildMovementInferredFacts(
|
||||
result: AddressMcpQueryExecutorResult,
|
||||
counterparty: string | null,
|
||||
periodScope: string | null
|
||||
): string[] {
|
||||
if (result.error || result.fetched_rows <= 0) {
|
||||
return [];
|
||||
}
|
||||
return ["Counterparty movement evidence is limited to confirmed 1C movement rows in the checked scope"];
|
||||
if (result.matched_rows <= 0) {
|
||||
return [
|
||||
`По движениям${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} удалось проверить только ограниченный срез 1С; подтвержденных строк движений этим поиском не найдено.`
|
||||
];
|
||||
}
|
||||
return [
|
||||
`Срез движений${checkedCounterpartySuffixRu(counterparty)}${checkedPeriodSuffixRu(periodScope)} ограничен только подтвержденными строками движений, найденными этим поиском.`
|
||||
];
|
||||
}
|
||||
|
||||
function buildValueFlowInferredFacts(derived: AssistantMcpDiscoveryDerivedValueFlow | null): string[] {
|
||||
@@ -1820,19 +1854,15 @@ function buildLifecycleUnknownFacts(): string[] {
|
||||
return ["Legal registration date is not proven by this MCP discovery pilot"];
|
||||
}
|
||||
|
||||
function buildDocumentUnknownFacts(periodScope: string | null): string[] {
|
||||
function buildDocumentUnknownFacts(periodScope: string | null, counterparty: string | null): string[] {
|
||||
return [
|
||||
periodScope
|
||||
? "Full document history outside the checked period is not proven by this MCP discovery pilot"
|
||||
: "Full document history is not proven without an explicit checked period"
|
||||
`Полный исторический срез документов${checkedCounterpartySuffixRu(counterparty)}${uncheckedPeriodBoundaryRu(periodScope)} этим поиском не подтвержден.`
|
||||
];
|
||||
}
|
||||
|
||||
function buildMovementUnknownFacts(periodScope: string | null): string[] {
|
||||
function buildMovementUnknownFacts(periodScope: string | null, counterparty: string | null): string[] {
|
||||
return [
|
||||
periodScope
|
||||
? "Full movement history outside the checked period is not proven by this MCP discovery pilot"
|
||||
: "Full movement history is not proven without an explicit checked period"
|
||||
`Полный исторический срез движений${checkedCounterpartySuffixRu(counterparty)}${uncheckedPeriodBoundaryRu(periodScope)} этим поиском не подтвержден.`
|
||||
];
|
||||
}
|
||||
|
||||
@@ -2245,9 +2275,9 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
const evidence = resolveAssistantMcpDiscoveryEvidence({
|
||||
plan: planner.discovery_plan,
|
||||
probeResults,
|
||||
confirmedFacts: queryResult ? buildDocumentConfirmedFacts(queryResult, counterparty) : [],
|
||||
inferredFacts: queryResult ? buildDocumentInferredFacts(queryResult) : [],
|
||||
unknownFacts: buildDocumentUnknownFacts(dateScope),
|
||||
confirmedFacts: queryResult ? buildDocumentConfirmedFacts(queryResult, counterparty, dateScope) : [],
|
||||
inferredFacts: queryResult ? buildDocumentInferredFacts(queryResult, counterparty, dateScope) : [],
|
||||
unknownFacts: buildDocumentUnknownFacts(dateScope, counterparty),
|
||||
sourceRowsSummary,
|
||||
queryLimitations,
|
||||
recommendedNextProbe: "explain_evidence_basis"
|
||||
@@ -2330,9 +2360,9 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
const evidence = resolveAssistantMcpDiscoveryEvidence({
|
||||
plan: planner.discovery_plan,
|
||||
probeResults,
|
||||
confirmedFacts: queryResult ? buildMovementConfirmedFacts(queryResult, counterparty) : [],
|
||||
inferredFacts: queryResult ? buildMovementInferredFacts(queryResult) : [],
|
||||
unknownFacts: buildMovementUnknownFacts(dateScope),
|
||||
confirmedFacts: queryResult ? buildMovementConfirmedFacts(queryResult, counterparty, dateScope) : [],
|
||||
inferredFacts: queryResult ? buildMovementInferredFacts(queryResult, counterparty, dateScope) : [],
|
||||
unknownFacts: buildMovementUnknownFacts(dateScope, counterparty),
|
||||
sourceRowsSummary,
|
||||
queryLimitations,
|
||||
recommendedNextProbe: "explain_evidence_basis"
|
||||
|
||||
@@ -100,10 +100,10 @@ function localizeLine(value: string): string {
|
||||
}
|
||||
const valueFlowMatch = value.match(/^1C value-flow rows were found for counterparty\s+(.+)$/i);
|
||||
if (valueFlowMatch) {
|
||||
return `В 1С найдены строки денежных движений по контрагенту ${valueFlowMatch[1]}.`;
|
||||
return `В 1С найдены строки входящих денежных поступлений по контрагенту ${valueFlowMatch[1]}.`;
|
||||
}
|
||||
if (/^1C value-flow rows were found for the requested counterparty scope$/i.test(value)) {
|
||||
return "В 1С найдены строки денежных движений по запрошенному контрагентскому контуру.";
|
||||
return "В 1С найдены строки входящих денежных поступлений по запрошенному контрагентскому контуру.";
|
||||
}
|
||||
const documentRowsMatch = value.match(/^1C document rows were found for counterparty\s+(.+)$/i);
|
||||
if (documentRowsMatch) {
|
||||
@@ -152,10 +152,10 @@ function localizeLine(value: string): string {
|
||||
return "Срез движений ограничен только подтвержденными строками движений в проверенном окне.";
|
||||
}
|
||||
if (/^Counterparty value-flow total was calculated from confirmed 1C movement rows$/i.test(value)) {
|
||||
return "Сумма рассчитана только по подтвержденным строкам денежных движений в 1С.";
|
||||
return "Сумма входящих поступлений рассчитана только по подтвержденным строкам поступлений в 1С.";
|
||||
}
|
||||
if (/^Counterparty monthly value-flow breakdown was grouped by month over confirmed 1C movement rows$/i.test(value)) {
|
||||
return "Помесячная раскладка денежного потока сгруппирована только по подтвержденным строкам движений 1С.";
|
||||
return "Помесячная раскладка входящих поступлений построена только по подтвержденным строкам поступлений в 1С.";
|
||||
}
|
||||
if (/^Counterparty supplier-payout total was calculated from confirmed 1C outgoing payment rows$/i.test(value)) {
|
||||
return "Сумма исходящих платежей рассчитана только по подтвержденным строкам списаний в 1С.";
|
||||
@@ -225,10 +225,10 @@ function localizeLine(value: string): string {
|
||||
return "Полное покрытие запрошенного периода по двустороннему денежному потоку не подтверждено: хотя бы одна сторона проверки достигла лимита найденных строк.";
|
||||
}
|
||||
if (/^Full turnover outside the checked period is not proven by this MCP discovery pilot$/i.test(value)) {
|
||||
return "Полный оборот вне проверенного периода этим поиском не подтвержден.";
|
||||
return "Полный объем входящих поступлений вне проверенного периода этим поиском не подтвержден.";
|
||||
}
|
||||
if (/^Full all-time turnover is not proven without an explicit checked period$/i.test(value)) {
|
||||
return "Полный оборот за все время без явно проверенного периода не подтвержден.";
|
||||
return "Полный объем входящих поступлений за все время без явно проверенного периода не подтвержден.";
|
||||
}
|
||||
if (/^Full document history outside the checked period is not proven by this MCP discovery pilot$/i.test(value)) {
|
||||
return "Полный исторический срез документов вне проверенного периода этим поиском не подтвержден.";
|
||||
|
||||
@@ -68,6 +68,27 @@ function pushUnique(target: string[], value: unknown): void {
|
||||
}
|
||||
}
|
||||
|
||||
function isReferentialEntityPlaceholder(value: string): boolean {
|
||||
return /^(?:\u043d\u0435\u043c\u0443|\u043d\u0435\u0439|\u043d\u0438\u043c|\u043d\u0438\u043c\u0438|\u0435\u0433\u043e|\u0435\u0435|\u0435\u0451|\u0438\u0445|\u044d\u0442\u043e\u043c\u0443|\u044d\u0442\u043e\u0439|\u044d\u0442\u0438\u043c|\u044d\u0442\u0438\u043c\u0438|\u044d\u0442\u043e\u043c)$/iu.test(
|
||||
value.trim()
|
||||
);
|
||||
}
|
||||
|
||||
function pushScopedEntityCandidate(
|
||||
target: string[],
|
||||
value: unknown,
|
||||
groundedFollowupEntity: string | null
|
||||
): void {
|
||||
const text = candidateValue(value);
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
if (groundedFollowupEntity && isReferentialEntityPlaceholder(text)) {
|
||||
return;
|
||||
}
|
||||
pushUnique(target, text);
|
||||
}
|
||||
|
||||
function canonicalizeEntityResolutionCandidate(value: string): string {
|
||||
return normalizeEntityResolutionCandidate(value)
|
||||
.replace(/^(?:\u0441\s+\u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435\u043c\s+)/iu, "")
|
||||
@@ -194,6 +215,13 @@ function mapPilotScopeToFollowupMeaning(
|
||||
unsupported: "movement_evidence"
|
||||
};
|
||||
}
|
||||
if (pilotScope === "counterparty_document_evidence_query_documents_v1") {
|
||||
return {
|
||||
domain: "documents",
|
||||
action: "list_documents",
|
||||
unsupported: "document_evidence"
|
||||
};
|
||||
}
|
||||
if (pilotScope === "counterparty_supplier_payout_query_movements_v1") {
|
||||
return {
|
||||
domain: "counterparty_value",
|
||||
@@ -300,7 +328,8 @@ function collectFollowupDiscoverySeed(followupContext: Record<string, unknown> |
|
||||
toNonEmptyString(rootFilters?.counterparty) ??
|
||||
(toNonEmptyString(followupContext?.previous_anchor_type) === "counterparty"
|
||||
? toNonEmptyString(followupContext?.previous_anchor_value)
|
||||
: null);
|
||||
: null) ??
|
||||
(discoveryEntities[0] ?? null);
|
||||
const organization =
|
||||
toNonEmptyString(previousFilters?.organization) ??
|
||||
toNonEmptyString(rootFilters?.organization) ??
|
||||
@@ -351,7 +380,7 @@ function hasLifecycleSignal(text: string): boolean {
|
||||
}
|
||||
|
||||
function hasValueFlowSignal(text: string): boolean {
|
||||
return /(?:оборот|выручк|оплат|плат[её]ж|заплат|перечисл|списан|расход|исходящ|поступлен|денежн[а-яёa-z0-9_-]*\s+поток|supplier|value[-\s]?flow|turnover|revenue|payment|payout|outflow|cash\s+flow)/iu.test(
|
||||
return /(?:оборот|выручк|оплат|плат[её]ж|заплат|перечисл|списан|расход|исходящ|входящ|получ(?:ил|ено|ен)|поступил|поступлен|денежн[а-яёa-z0-9_-]*\s+поток|supplier|value[-\s]?flow|turnover|revenue|payment|payout|outflow|cash\s+flow)/iu.test(
|
||||
text
|
||||
);
|
||||
}
|
||||
@@ -410,6 +439,18 @@ function hasMovementEvidenceFollowupSignal(text: string): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
function hasPronounDocumentEvidenceFollowupSignal(text: string): boolean {
|
||||
return /(?:\u043f\u043e\s+(?:\u043d\u0435\u043c\u0443|\u043d\u0435\u0439|\u044d\u0442\u043e\u043c\u0443|\u044d\u0442\u043e\u0439)\s+(?:\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u0430\u043c|\u044b)?|\u0441\u0447(?:[Рµe]С‚|\u0435\u0442)[-\u2011 ]?\u0444\u0430\u043a\u0442\u0443\u0440(?:\u044b|\u0430)?|\u043d\u0430\u043a\u043b\u0430\u0434\u043d(?:\u044b\u0435|\u0430\u044f)?|\u0430\u043a\u0442(?:\u044b)?))/iu.test(
|
||||
text
|
||||
);
|
||||
}
|
||||
|
||||
function hasPronounMovementEvidenceFollowupSignal(text: string): boolean {
|
||||
return /(?:\u043f\u043e\s+(?:\u043d\u0435\u043c\u0443|\u043d\u0435\u0439|\u044d\u0442\u043e\u043c\u0443|\u044d\u0442\u043e\u0439)\s+(?:\u0434\u0432\u0438\u0436\u0435\u043d(?:\u0438\u044f\u043c|\u0438\u044f)?|\u043f\u043b\u0430\u0442[Рµe]\u0436(?:\u0430\u043c|\u0438)?|\u043e\u043f\u0435\u0440\u0430\u0446(?:\u0438\u044f\u043c|\u0438\u0438|\u0438\u044e)|\u043f\u0440\u043e\u0432\u043e\u0434\u043a(?:\u0430\u043c|\u0438)|\u0441\u043f\u0438\u0441\u0430\u043d(?:\u0438\u044f\u043c|\u0438\u0435)|\u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d(?:\u0438\u044f\u043c|\u0438\u0435)))/iu.test(
|
||||
text
|
||||
);
|
||||
}
|
||||
|
||||
function hasMetadataDownstreamContinuationSignal(text: string): boolean {
|
||||
return /(?:\u0434\u0430\u0432\u0430\u0439\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u0438\u0434(?:\u0435|\u0451)\u043c\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u043f\u043e\u0448\u043b(?:\u0438|\u0451\u043c)\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0439|\u0438\u0449\u0438\s+\u0434\u0430\u043b\u044c\u0448\u0435|\u0438\u0449\u0438\s+\u0434\u0430\u043d\u043d\u044b\u0435|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0434\u0430\u043d\u043d\u044b\u0435|\u043f\u043e\u043a\u0430\u0436\u0438\s+\u0441\u0442\u0440\u043e\u043a\u0438|\u0433\u043b\u0443\u0431\u0436\u0435|\u0447\u0442\u043e\s+\u0434\u0430\u043b\u044c\u0448\u0435|continue|go\s+ahead|go\s+deeper|look\s+deeper|drill\s+down|show\s+(?:data|rows))/iu.test(
|
||||
text
|
||||
@@ -548,7 +589,11 @@ function shouldRunDiscovery(input: {
|
||||
semanticDataNeed: string | null;
|
||||
explicitIntentCandidate: string | null;
|
||||
followupDiscoverySeedApplicable: boolean;
|
||||
forceDiscoveryOverExplicitIntent: boolean;
|
||||
}): boolean {
|
||||
if (input.forceDiscoveryOverExplicitIntent && input.semanticDataNeed) {
|
||||
return true;
|
||||
}
|
||||
if (input.lifecycleSignal || input.unsupported) {
|
||||
return true;
|
||||
}
|
||||
@@ -598,8 +643,10 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
const rawMetadataScopeHint = rawMetadataSignal ? metadataScopeHintFromRawText(rawText) : null;
|
||||
const rawEntityCandidate = rawEntityResolutionSignal ? rawEntityResolutionCandidate(rawEntitySourceText) : null;
|
||||
const entityResolutionSignal = rawEntityResolutionSignal || Boolean(rawEntityCandidate);
|
||||
const metadataDocumentHintSignal = hasDocumentEvidenceFollowupSignal(rawText);
|
||||
const metadataMovementHintSignal = hasMovementEvidenceFollowupSignal(rawText);
|
||||
const metadataDocumentHintSignal =
|
||||
hasDocumentEvidenceFollowupSignal(rawText) || hasPronounDocumentEvidenceFollowupSignal(rawText);
|
||||
const metadataMovementHintSignal =
|
||||
hasMovementEvidenceFollowupSignal(rawText) || hasPronounMovementEvidenceFollowupSignal(rawText);
|
||||
|
||||
const rawDomain = toNonEmptyString(assistantTurnMeaning?.asked_domain_family);
|
||||
const rawAction = toNonEmptyString(assistantTurnMeaning?.asked_action_family);
|
||||
@@ -657,6 +704,47 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
!rawLifecycleSignal &&
|
||||
metadataMovementHintSignal
|
||||
);
|
||||
const entityResolutionGroundedDocumentFollowupApplicable = Boolean(
|
||||
followupSeed.pilotScope === "entity_resolution_search_v1" &&
|
||||
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
|
||||
!rawLifecycleSignal &&
|
||||
!rawValueFlowSignal &&
|
||||
!rawMetadataSignal &&
|
||||
metadataDocumentHintSignal
|
||||
);
|
||||
const entityResolutionGroundedMovementFollowupApplicable = Boolean(
|
||||
followupSeed.pilotScope === "entity_resolution_search_v1" &&
|
||||
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
|
||||
!rawLifecycleSignal &&
|
||||
!rawValueFlowSignal &&
|
||||
!rawMetadataSignal &&
|
||||
metadataMovementHintSignal
|
||||
);
|
||||
const groundedValueFlowFollowupApplicable = Boolean(
|
||||
rawValueFlowSignal &&
|
||||
!rawLifecycleSignal &&
|
||||
!rawMetadataSignal &&
|
||||
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
|
||||
(followupSeed.pilotScope === "entity_resolution_search_v1" ||
|
||||
followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" ||
|
||||
followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1")
|
||||
);
|
||||
const documentEvidenceGroundedMovementFollowupApplicable = Boolean(
|
||||
followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" &&
|
||||
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
|
||||
!rawLifecycleSignal &&
|
||||
!rawValueFlowSignal &&
|
||||
!rawMetadataSignal &&
|
||||
metadataMovementHintSignal
|
||||
);
|
||||
const movementEvidenceGroundedDocumentFollowupApplicable = Boolean(
|
||||
followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" &&
|
||||
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
|
||||
!rawLifecycleSignal &&
|
||||
!rawValueFlowSignal &&
|
||||
!rawMetadataSignal &&
|
||||
metadataDocumentHintSignal
|
||||
);
|
||||
const metadataGroundedLaneContinuationApplicable = Boolean(
|
||||
followupSeed.pilotScope === "metadata_inspection_v1" &&
|
||||
(followupSeed.metadataRouteFamily === "document_evidence" ||
|
||||
@@ -710,11 +798,15 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
const metadataGroundedDocumentLaneApplicable =
|
||||
metadataGroundedDocumentFollowupApplicable ||
|
||||
metadataAmbiguityResolvedDocumentFollowupApplicable ||
|
||||
entityResolutionGroundedDocumentFollowupApplicable ||
|
||||
movementEvidenceGroundedDocumentFollowupApplicable ||
|
||||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "document_evidence") ||
|
||||
metadataAmbiguityCollapsedDocumentLaneContinuationApplicable;
|
||||
const metadataGroundedMovementLaneApplicable =
|
||||
metadataGroundedMovementFollowupApplicable ||
|
||||
metadataAmbiguityResolvedMovementFollowupApplicable ||
|
||||
entityResolutionGroundedMovementFollowupApplicable ||
|
||||
documentEvidenceGroundedMovementFollowupApplicable ||
|
||||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "movement_evidence") ||
|
||||
metadataAmbiguityCollapsedMovementLaneContinuationApplicable;
|
||||
const effectiveMetadataFollowupSeedApplicable =
|
||||
@@ -773,7 +865,8 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
metadataSignal: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable,
|
||||
entityResolutionSignal
|
||||
});
|
||||
const entityCandidates = entityResolutionSignal ? [] : collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates);
|
||||
const groundedFollowupEntity = followupSeed.counterparty ?? followupSeed.discoveryEntity;
|
||||
const entityCandidates = entityResolutionSignal ? [] : [];
|
||||
if (entityResolutionSignal) {
|
||||
pushNormalizedEntityResolutionCandidate(entityCandidates, rawEntityCandidate);
|
||||
for (const candidate of collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates)) {
|
||||
@@ -782,11 +875,20 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
pushNormalizedEntityResolutionCandidate(entityCandidates, predecomposeEntities.counterparty);
|
||||
pushNormalizedEntityResolutionCandidate(entityCandidates, followupSeed.counterparty);
|
||||
} else {
|
||||
pushUnique(entityCandidates, predecomposeEntities.counterparty);
|
||||
pushUnique(entityCandidates, followupSeed.counterparty);
|
||||
pushUnique(entityCandidates, rawEntityCandidate);
|
||||
if (groundedFollowupEntity) {
|
||||
pushUnique(entityCandidates, groundedFollowupEntity);
|
||||
}
|
||||
for (const candidate of collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates)) {
|
||||
pushScopedEntityCandidate(entityCandidates, candidate, groundedFollowupEntity);
|
||||
}
|
||||
pushScopedEntityCandidate(entityCandidates, predecomposeEntities.counterparty, groundedFollowupEntity);
|
||||
if (!groundedFollowupEntity) {
|
||||
pushScopedEntityCandidate(entityCandidates, followupSeed.counterparty, null);
|
||||
pushScopedEntityCandidate(entityCandidates, followupSeed.discoveryEntity, null);
|
||||
}
|
||||
pushScopedEntityCandidate(entityCandidates, rawEntityCandidate, groundedFollowupEntity);
|
||||
}
|
||||
if ((rawMetadataSignal || metadataFollowupSeedApplicable) && !followupSeed.counterparty) {
|
||||
if ((rawMetadataSignal || metadataFollowupSeedApplicable) && !groundedFollowupEntity) {
|
||||
pushUnique(entityCandidates, followupSeed.discoveryEntity);
|
||||
pushUnique(entityCandidates, rawMetadataScopeHint);
|
||||
}
|
||||
@@ -920,7 +1022,13 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
effectiveMetadataFollowupSeedApplicable ||
|
||||
metadataAmbiguityLaneClarificationApplicable ||
|
||||
metadataGroundedMovementLaneApplicable ||
|
||||
metadataGroundedDocumentLaneApplicable
|
||||
metadataGroundedDocumentLaneApplicable ||
|
||||
groundedValueFlowFollowupApplicable,
|
||||
forceDiscoveryOverExplicitIntent:
|
||||
metadataAmbiguityLaneClarificationApplicable ||
|
||||
metadataGroundedMovementLaneApplicable ||
|
||||
metadataGroundedDocumentLaneApplicable ||
|
||||
groundedValueFlowFollowupApplicable
|
||||
});
|
||||
const hasTurnMeaning = Object.keys(cleanTurnMeaning).length > 0;
|
||||
const sourceSignal: AssistantMcpDiscoveryTurnInputSource = assistantTurnMeaning
|
||||
@@ -988,6 +1096,21 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||
if (metadataAmbiguityResolvedMovementFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_metadata_ambiguity_resolved_to_movement_lane");
|
||||
}
|
||||
if (entityResolutionGroundedDocumentFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_entity_resolution_grounded_document_followup");
|
||||
}
|
||||
if (entityResolutionGroundedMovementFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_entity_resolution_grounded_movement_followup");
|
||||
}
|
||||
if (groundedValueFlowFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_grounded_value_flow_followup");
|
||||
}
|
||||
if (documentEvidenceGroundedMovementFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_document_evidence_grounded_movement_followup");
|
||||
}
|
||||
if (movementEvidenceGroundedDocumentFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_movement_evidence_grounded_document_followup");
|
||||
}
|
||||
if (metadataGroundedLaneContinuationApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_metadata_grounded_lane_continuation");
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
readAddressDebugItem,
|
||||
readAssistantMcpDiscoveryMetadataAmbiguityDetected,
|
||||
readAssistantMcpDiscoveryMetadataAmbiguityEntitySets,
|
||||
readAssistantMcpDiscoveryEntityCandidates,
|
||||
readAssistantMcpDiscoveryMetadataRouteFamily,
|
||||
readAssistantMcpDiscoveryMetadataSelectedEntitySet,
|
||||
readAddressDebugTemporalScope,
|
||||
@@ -626,6 +627,10 @@ export function createAssistantTransitionPolicy(deps) {
|
||||
carryoverSourceDebug,
|
||||
deps.toNonEmptyString
|
||||
);
|
||||
const sourceDiscoveryEntityCandidates = readAssistantMcpDiscoveryEntityCandidates(
|
||||
carryoverSourceDebug,
|
||||
deps.toNonEmptyString
|
||||
);
|
||||
const llmExplicitIntent = deps.toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
|
||||
const llmSelectedObjectScopeDetected =
|
||||
llmPreDecomposeMeta?.predecomposeContract?.semantics?.selected_object_scope_detected === true;
|
||||
@@ -958,6 +963,8 @@ export function createAssistantTransitionPolicy(deps) {
|
||||
previous_anchor_type: previousAnchorType ?? undefined,
|
||||
previous_anchor_value: previousAnchor,
|
||||
previous_discovery_pilot_scope: sourceDiscoveryPilotScope ?? undefined,
|
||||
previous_discovery_entity_candidates:
|
||||
sourceDiscoveryEntityCandidates.length > 0 ? sourceDiscoveryEntityCandidates : undefined,
|
||||
previous_discovery_metadata_route_family: sourceDiscoveryMetadataRouteFamily ?? undefined,
|
||||
previous_discovery_metadata_selected_entity_set: sourceDiscoveryMetadataSelectedEntitySet ?? undefined,
|
||||
previous_discovery_metadata_ambiguity_detected: sourceDiscoveryMetadataAmbiguityDetected || undefined,
|
||||
|
||||
Reference in New Issue
Block a user