ARCH: добавить вывод длительности активности MCP discovery

This commit is contained in:
2026-04-20 12:35:29 +03:00
parent 72804557aa
commit e85d456576
8 changed files with 293 additions and 3 deletions
@@ -97,6 +97,17 @@ function buildMustNotClaim(pilot) {
}
return claims;
}
function derivedActivityInferenceLine(pilot) {
const period = pilot.derived_activity_period;
if (!period) {
return null;
}
return [
`По подтвержденным строкам активности в 1С период взаимодействия можно оценить примерно как ${period.duration_human_ru}.`,
`Первая найденная активность: ${period.first_activity_date}; последняя найденная активность: ${period.latest_activity_date}.`,
"Это вывод по данным 1С, а не юридически подтвержденный возраст регистрации."
].join(" ");
}
function buildAssistantMcpDiscoveryAnswerDraft(pilot) {
const mode = modeFor(pilot);
const reasonCodes = [...pilot.reason_codes, ...pilot.evidence.reason_codes];
@@ -107,13 +118,17 @@ function buildAssistantMcpDiscoveryAnswerDraft(pilot) {
if (pilot.evidence.inferred_facts.length > 0) {
pushReason(reasonCodes, "answer_contains_bounded_inference");
}
const derivedInferenceLine = derivedActivityInferenceLine(pilot);
const inferenceLines = derivedInferenceLine
? [derivedInferenceLine]
: pilot.evidence.inferred_facts;
return {
schema_version: exports.ASSISTANT_MCP_DISCOVERY_ANSWER_DRAFT_SCHEMA_VERSION,
policy_owner: "assistantMcpDiscoveryAnswerAdapter",
answer_mode: mode,
headline: headlineFor(mode),
confirmed_lines: uniqueStrings(pilot.evidence.confirmed_facts),
inference_lines: uniqueStrings(pilot.evidence.inferred_facts),
inference_lines: uniqueStrings(inferenceLines),
unknown_lines: uniqueStrings(pilot.evidence.unknown_facts),
limitation_lines: userFacingLimitations([...pilot.query_limitations, ...pilot.evidence.query_limitations]),
next_step_line: nextStepFor(mode, pilot),
@@ -116,6 +116,74 @@ function summarizeRows(result) {
}
return `${result.fetched_rows} MCP document rows fetched, ${result.matched_rows} matched lifecycle scope`;
}
function rowDateValue(row) {
const candidates = [
row["Период"],
row["Period"],
row["period"],
row["Дата"],
row["Date"],
row["date"]
];
for (const candidate of candidates) {
const text = toNonEmptyString(candidate);
const match = text?.match(/(\d{4})-(\d{2})-(\d{2})/);
if (match) {
return `${match[1]}-${match[2]}-${match[3]}`;
}
}
return null;
}
function monthDiff(firstIsoDate, latestIsoDate) {
const first = new Date(`${firstIsoDate}T00:00:00.000Z`);
const latest = new Date(`${latestIsoDate}T00:00:00.000Z`);
if (Number.isNaN(first.getTime()) || Number.isNaN(latest.getTime()) || latest < first) {
return 0;
}
let months = (latest.getUTCFullYear() - first.getUTCFullYear()) * 12;
months += latest.getUTCMonth() - first.getUTCMonth();
if (latest.getUTCDate() < first.getUTCDate()) {
months -= 1;
}
return Math.max(0, months);
}
function formatDurationHumanRu(totalMonths) {
const years = Math.floor(totalMonths / 12);
const months = totalMonths % 12;
const parts = [];
if (years > 0) {
parts.push(`${years} ${years === 1 ? "год" : years >= 2 && years <= 4 ? "года" : "лет"}`);
}
if (months > 0) {
parts.push(`${months} ${months === 1 ? "месяц" : months >= 2 && months <= 4 ? "месяца" : "месяцев"}`);
}
return parts.length > 0 ? parts.join(" ") : "меньше месяца";
}
function deriveActivityPeriod(result) {
if (!result || result.error || result.matched_rows <= 0) {
return null;
}
const dates = result.rows
.map((row) => rowDateValue(row))
.filter((value) => Boolean(value))
.sort();
if (dates.length === 0) {
return null;
}
const first = dates[0];
const latest = dates[dates.length - 1];
const totalMonths = monthDiff(first, latest);
return {
first_activity_date: first,
latest_activity_date: latest,
matched_rows: result.matched_rows,
duration_total_months: totalMonths,
duration_years: Math.floor(totalMonths / 12),
duration_months_remainder: totalMonths % 12,
duration_human_ru: formatDurationHumanRu(totalMonths),
inference_basis: "first_and_latest_confirmed_1c_activity_rows"
};
}
function buildConfirmedFacts(result, counterparty) {
if (result.error || result.matched_rows <= 0) {
return [];
@@ -166,6 +234,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
probe_results: probeResults,
evidence,
source_rows_summary: null,
derived_activity_period: null,
query_limitations: ["MCP discovery pilot was blocked before execution"],
reason_codes: reasonCodes
};
@@ -185,6 +254,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
probe_results: probeResults,
evidence,
source_rows_summary: null,
derived_activity_period: null,
query_limitations: ["MCP discovery pilot needs more scope before execution"],
reason_codes: reasonCodes
};
@@ -208,6 +278,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
probe_results: probeResults,
evidence,
source_rows_summary: null,
derived_activity_period: null,
query_limitations: ["MCP discovery pilot scope is not implemented yet"],
reason_codes: reasonCodes
};
@@ -231,6 +302,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
probe_results: probeResults,
evidence,
source_rows_summary: null,
derived_activity_period: null,
query_limitations: ["Lifecycle recipe is not available"],
reason_codes: reasonCodes
};
@@ -258,6 +330,10 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
}
}
const sourceRowsSummary = queryResult ? summarizeRows(queryResult) : null;
const derivedActivityPeriod = deriveActivityPeriod(queryResult);
if (derivedActivityPeriod) {
pushReason(reasonCodes, "pilot_derived_activity_period_from_confirmed_rows");
}
const evidence = (0, assistantMcpDiscoveryPolicy_1.resolveAssistantMcpDiscoveryEvidence)({
plan: planner.discovery_plan,
probeResults,
@@ -280,6 +356,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
probe_results: probeResults,
evidence,
source_rows_summary: sourceRowsSummary,
derived_activity_period: derivedActivityPeriod,
query_limitations: queryLimitations,
reason_codes: reasonCodes
};