ARCH: добить entity-resolution chain и очистить stale runtime
This commit is contained in:
@@ -8,7 +8,10 @@ import {
|
||||
type AssistantMcpDiscoveryRuntimeDryRunContract,
|
||||
type AssistantMcpDiscoveryRuntimeStepContract
|
||||
} from "./assistantMcpDiscoveryRuntimeAdapter";
|
||||
import type { AssistantMcpDiscoveryPlannerContract } from "./assistantMcpDiscoveryPlanner";
|
||||
import type {
|
||||
AssistantMcpDiscoveryChainId,
|
||||
AssistantMcpDiscoveryPlannerContract
|
||||
} from "./assistantMcpDiscoveryPlanner";
|
||||
import {
|
||||
resolveAssistantMcpDiscoveryEvidence,
|
||||
type AssistantMcpDiscoveryEvidenceContract,
|
||||
@@ -133,6 +136,18 @@ export interface AssistantMcpDiscoveryDerivedMetadataSurface {
|
||||
inference_basis: "confirmed_1c_metadata_surface_rows";
|
||||
}
|
||||
|
||||
export interface AssistantMcpDiscoveryDerivedEntityResolution {
|
||||
requested_entity: string | null;
|
||||
resolution_status: "resolved" | "ambiguous" | "not_found";
|
||||
resolved_entity: string | null;
|
||||
resolved_reference: string | null;
|
||||
matched_rows: number;
|
||||
checked_candidates: string[];
|
||||
ambiguity_candidates: string[];
|
||||
confidence: "high" | "medium" | "low" | null;
|
||||
inference_basis: "catalog_counterparty_search_rows";
|
||||
}
|
||||
|
||||
interface AssistantMcpDiscoveryCoverageAwareQueryResult extends AddressMcpQueryExecutorResult {
|
||||
coverage_limited_by_probe_limit: boolean;
|
||||
coverage_recovered_by_period_chunking: boolean;
|
||||
@@ -149,6 +164,7 @@ interface AssistantMcpDiscoveryCoverageAwareQueryExecution {
|
||||
|
||||
export type AssistantMcpDiscoveryPilotScope =
|
||||
| "metadata_inspection_v1"
|
||||
| "entity_resolution_search_v1"
|
||||
| "counterparty_movement_evidence_query_movements_v1"
|
||||
| "counterparty_document_evidence_query_documents_v1"
|
||||
| "counterparty_lifecycle_query_documents_v1"
|
||||
@@ -169,6 +185,7 @@ export interface AssistantMcpDiscoveryPilotExecutionContract {
|
||||
evidence: AssistantMcpDiscoveryEvidenceContract;
|
||||
source_rows_summary: string | null;
|
||||
derived_metadata_surface: AssistantMcpDiscoveryDerivedMetadataSurface | null;
|
||||
derived_entity_resolution: AssistantMcpDiscoveryDerivedEntityResolution | null;
|
||||
derived_activity_period: AssistantMcpDiscoveryDerivedActivityPeriod | null;
|
||||
derived_value_flow: AssistantMcpDiscoveryDerivedValueFlow | null;
|
||||
derived_bidirectional_value_flow: AssistantMcpDiscoveryDerivedBidirectionalValueFlow | null;
|
||||
@@ -183,6 +200,41 @@ const DEFAULT_DEPS: ResolvedAssistantMcpDiscoveryPilotExecutorDeps = {
|
||||
executeAddressMcpMetadata
|
||||
};
|
||||
|
||||
const ENTITY_RESOLUTION_COUNTERPARTY_LOOKUP_LIMIT = 1000;
|
||||
const ENTITY_RESOLUTION_COUNTERPARTY_QUERY_TEMPLATE = `
|
||||
ВЫБРАТЬ ПЕРВЫЕ __LIMIT__
|
||||
ПРЕДСТАВЛЕНИЕ(Контрагенты.Ссылка) КАК Контрагент,
|
||||
ПРЕДСТАВЛЕНИЕ(Контрагенты.Ссылка) КАК Counterparty,
|
||||
Контрагенты.Ссылка КАК КонтрагентСсылка,
|
||||
Контрагенты.Ссылка КАК CounterpartyRef,
|
||||
Контрагенты.Наименование КАК Наименование
|
||||
ИЗ
|
||||
Справочник.Контрагенты КАК Контрагенты
|
||||
`;
|
||||
const ENTITY_RESOLUTION_STOPWORDS = new Set([
|
||||
"ооо",
|
||||
"ао",
|
||||
"зао",
|
||||
"ип",
|
||||
"llc",
|
||||
"ltd",
|
||||
"company",
|
||||
"контрагент",
|
||||
"counterparty",
|
||||
"поставщик",
|
||||
"supplier",
|
||||
"клиент",
|
||||
"customer",
|
||||
"в",
|
||||
"1с",
|
||||
"1c",
|
||||
"найди",
|
||||
"найти",
|
||||
"поищи",
|
||||
"search",
|
||||
"find"
|
||||
]);
|
||||
|
||||
function toNonEmptyString(value: unknown): string | null {
|
||||
if (value === null || value === undefined) {
|
||||
return null;
|
||||
@@ -282,7 +334,170 @@ function buildValueFlowFilters(planner: AssistantMcpDiscoveryPlannerContract): A
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeEntityResolutionText(value: string | null): string {
|
||||
return String(value ?? "")
|
||||
.toLowerCase()
|
||||
.replace(/ё/g, "е")
|
||||
.replace(/[«»"'`]/g, " ")
|
||||
.replace(/[^\p{L}\p{N}\s-]+/gu, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function tokenizeEntityResolutionText(value: string | null): string[] {
|
||||
return normalizeEntityResolutionText(value)
|
||||
.split(" ")
|
||||
.map((token) => token.trim())
|
||||
.filter((token) => token.length >= 2 && !ENTITY_RESOLUTION_STOPWORDS.has(token));
|
||||
}
|
||||
|
||||
function isLowQualityEntityResolutionAnchor(value: string | null): boolean {
|
||||
return tokenizeEntityResolutionText(value).length <= 0;
|
||||
}
|
||||
|
||||
function entityResolutionCandidateName(row: Record<string, unknown>): string | null {
|
||||
const candidates = [
|
||||
row["Контрагент"],
|
||||
row["Counterparty"],
|
||||
row["Наименование"],
|
||||
row["name"],
|
||||
row["Name"],
|
||||
row["registrator"],
|
||||
row["Registrator"]
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
const text = toNonEmptyString(candidate);
|
||||
if (text) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function entityResolutionCandidateRef(row: Record<string, unknown>): string | null {
|
||||
const candidates = [row["КонтрагентСсылка"], row["CounterpartyRef"], row["ref"], row["Ref"]];
|
||||
for (const candidate of candidates) {
|
||||
const text = toNonEmptyString(candidate);
|
||||
if (text) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function scoreEntityResolutionCandidate(name: string, requested: string): number | null {
|
||||
const normalizedName = normalizeEntityResolutionText(name);
|
||||
const normalizedRequested = normalizeEntityResolutionText(requested);
|
||||
const requestedTokens = tokenizeEntityResolutionText(requested);
|
||||
if (!normalizedName || !normalizedRequested || requestedTokens.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let score = 0;
|
||||
if (normalizedName === normalizedRequested) {
|
||||
score += 10_000;
|
||||
} else if (normalizedName.includes(normalizedRequested)) {
|
||||
score += 5_000;
|
||||
} else if (normalizedRequested.includes(normalizedName) && normalizedName.length >= 4) {
|
||||
score += 2_000;
|
||||
}
|
||||
|
||||
for (const token of requestedTokens) {
|
||||
if (!normalizedName.includes(token)) {
|
||||
return null;
|
||||
}
|
||||
score += Math.max(40, token.length * 20);
|
||||
}
|
||||
|
||||
score -= Math.abs(normalizedName.length - normalizedRequested.length);
|
||||
return score;
|
||||
}
|
||||
|
||||
function deriveEntityResolution(
|
||||
result: AddressMcpQueryExecutorResult | null,
|
||||
requestedEntity: string | null
|
||||
): AssistantMcpDiscoveryDerivedEntityResolution | null {
|
||||
if (!result || result.error || !requestedEntity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const checkedCandidates = uniqueCandidateStrings(
|
||||
result.raw_rows
|
||||
.map((row) => entityResolutionCandidateName(row))
|
||||
.filter((value): value is string => Boolean(value))
|
||||
);
|
||||
const scoredCandidates = checkedCandidates
|
||||
.map((name) => {
|
||||
const score = scoreEntityResolutionCandidate(name, requestedEntity);
|
||||
return score === null ? null : { name, score };
|
||||
})
|
||||
.filter((value): value is { name: string; score: number } => Boolean(value))
|
||||
.sort((left, right) => right.score - left.score || left.name.length - right.name.length || left.name.localeCompare(right.name, "ru"));
|
||||
|
||||
if (scoredCandidates.length <= 0) {
|
||||
return {
|
||||
requested_entity: requestedEntity,
|
||||
resolution_status: "not_found",
|
||||
resolved_entity: null,
|
||||
resolved_reference: null,
|
||||
matched_rows: result.rows.length,
|
||||
checked_candidates: checkedCandidates.slice(0, 12),
|
||||
ambiguity_candidates: [],
|
||||
confidence: null,
|
||||
inference_basis: "catalog_counterparty_search_rows"
|
||||
};
|
||||
}
|
||||
|
||||
const bestCandidate = scoredCandidates[0];
|
||||
const bestNormalized = normalizeEntityResolutionText(bestCandidate.name);
|
||||
const requestedNormalized = normalizeEntityResolutionText(requestedEntity);
|
||||
const requestedTokens = tokenizeEntityResolutionText(requestedEntity);
|
||||
const exactMatch = bestNormalized === requestedNormalized;
|
||||
const strongContains = requestedTokens.length > 1 && bestNormalized.includes(requestedNormalized);
|
||||
const topCandidates = scoredCandidates.filter((candidate) => candidate.score === bestCandidate.score);
|
||||
|
||||
if (topCandidates.length > 1 && !exactMatch && !strongContains) {
|
||||
return {
|
||||
requested_entity: requestedEntity,
|
||||
resolution_status: "ambiguous",
|
||||
resolved_entity: null,
|
||||
resolved_reference: null,
|
||||
matched_rows: result.rows.length,
|
||||
checked_candidates: checkedCandidates.slice(0, 12),
|
||||
ambiguity_candidates: topCandidates.map((candidate) => candidate.name).slice(0, 6),
|
||||
confidence: "low",
|
||||
inference_basis: "catalog_counterparty_search_rows"
|
||||
};
|
||||
}
|
||||
|
||||
const matchedRow =
|
||||
result.raw_rows.find((row) => normalizeEntityResolutionText(entityResolutionCandidateName(row)) === bestNormalized) ?? null;
|
||||
|
||||
return {
|
||||
requested_entity: requestedEntity,
|
||||
resolution_status: "resolved",
|
||||
resolved_entity: bestCandidate.name,
|
||||
resolved_reference: matchedRow ? entityResolutionCandidateRef(matchedRow) : null,
|
||||
matched_rows: result.rows.length,
|
||||
checked_candidates: checkedCandidates.slice(0, 12),
|
||||
ambiguity_candidates: [],
|
||||
confidence: exactMatch ? "high" : strongContains ? "medium" : "low",
|
||||
inference_basis: "catalog_counterparty_search_rows"
|
||||
};
|
||||
}
|
||||
|
||||
function uniqueCandidateStrings(values: string[]): string[] {
|
||||
const result: string[] = [];
|
||||
for (const value of values) {
|
||||
pushUnique(result, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function isLifecyclePilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean {
|
||||
if (planner.selected_chain_id === "lifecycle") {
|
||||
return true;
|
||||
}
|
||||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||||
@@ -294,6 +509,9 @@ function isLifecyclePilotEligible(planner: AssistantMcpDiscoveryPlannerContract)
|
||||
}
|
||||
|
||||
function isDocumentEvidencePilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean {
|
||||
if (planner.selected_chain_id === "document_evidence") {
|
||||
return true;
|
||||
}
|
||||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||||
@@ -306,6 +524,9 @@ function isDocumentEvidencePilotEligible(planner: AssistantMcpDiscoveryPlannerCo
|
||||
}
|
||||
|
||||
function isMovementEvidencePilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean {
|
||||
if (planner.selected_chain_id === "movement_evidence") {
|
||||
return true;
|
||||
}
|
||||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||||
@@ -323,6 +544,9 @@ function isMovementEvidencePilotEligible(planner: AssistantMcpDiscoveryPlannerCo
|
||||
}
|
||||
|
||||
function isValueFlowPilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean {
|
||||
if (planner.selected_chain_id === "value_flow") {
|
||||
return true;
|
||||
}
|
||||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||||
@@ -339,6 +563,12 @@ function isValueFlowPilotEligible(planner: AssistantMcpDiscoveryPlannerContract)
|
||||
}
|
||||
|
||||
function isMetadataPilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean {
|
||||
if (
|
||||
planner.selected_chain_id === "metadata_inspection" ||
|
||||
planner.selected_chain_id === "metadata_lane_clarification"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||||
@@ -356,6 +586,25 @@ function isMetadataPilotEligible(planner: AssistantMcpDiscoveryPlannerContract):
|
||||
);
|
||||
}
|
||||
|
||||
function isEntityResolutionPilotEligible(planner: AssistantMcpDiscoveryPlannerContract): boolean {
|
||||
if (planner.selected_chain_id === "entity_resolution") {
|
||||
return true;
|
||||
}
|
||||
const meaning = planner.discovery_plan.turn_meaning_ref;
|
||||
const domain = String(meaning?.asked_domain_family ?? "").toLowerCase();
|
||||
const action = String(meaning?.asked_action_family ?? "").toLowerCase();
|
||||
const unsupported = String(meaning?.unsupported_but_understood_family ?? "").toLowerCase();
|
||||
const semanticNeed = String(planner.semantic_data_need ?? "").toLowerCase();
|
||||
const combined = `${domain} ${action} ${unsupported} ${semanticNeed}`;
|
||||
return (
|
||||
planner.proposed_primitives.includes("search_business_entity") &&
|
||||
(combined.includes("entity_resolution") ||
|
||||
combined.includes("search_business_entity") ||
|
||||
combined.includes("entity discovery") ||
|
||||
combined.includes("counterparty search"))
|
||||
);
|
||||
}
|
||||
|
||||
function metadataScopeForPlanner(planner: AssistantMcpDiscoveryPlannerContract): string | null {
|
||||
const entityCandidate = firstEntityCandidate(planner);
|
||||
if (entityCandidate) {
|
||||
@@ -715,6 +964,16 @@ function summarizeMetadataRows(result: AddressMcpMetadataRowsResult): string | n
|
||||
return `${result.fetched_rows} MCP metadata rows fetched`;
|
||||
}
|
||||
|
||||
function summarizeEntityResolutionRows(result: AddressMcpQueryExecutorResult): string | null {
|
||||
if (result.error) {
|
||||
return null;
|
||||
}
|
||||
if (result.fetched_rows <= 0) {
|
||||
return "0 MCP catalog rows fetched";
|
||||
}
|
||||
return `${result.fetched_rows} MCP catalog rows fetched for entity search`;
|
||||
}
|
||||
|
||||
function metadataRowText(row: Record<string, unknown>, keys: string[]): string | null {
|
||||
for (const key of keys) {
|
||||
const text = toNonEmptyString(row[key]);
|
||||
@@ -752,6 +1011,19 @@ function metadataEntitySet(row: Record<string, unknown>): string | null {
|
||||
]);
|
||||
}
|
||||
|
||||
function inferMetadataEntitySetFromObjectName(objectName: string | null): string | null {
|
||||
const text = String(objectName ?? "").trim();
|
||||
if (!text) {
|
||||
return null;
|
||||
}
|
||||
const dotIndex = text.indexOf(".");
|
||||
if (dotIndex <= 0) {
|
||||
return null;
|
||||
}
|
||||
const entitySet = text.slice(0, dotIndex).trim();
|
||||
return entitySet.length > 0 ? entitySet : null;
|
||||
}
|
||||
|
||||
function metadataChildNames(value: unknown): string[] {
|
||||
if (!Array.isArray(value)) {
|
||||
return [];
|
||||
@@ -908,7 +1180,7 @@ function deriveMetadataSurface(
|
||||
if (objectName) {
|
||||
pushUnique(matchedObjects, objectName);
|
||||
}
|
||||
const entitySet = metadataEntitySet(row);
|
||||
const entitySet = metadataEntitySet(row) ?? inferMetadataEntitySetFromObjectName(objectName);
|
||||
if (entitySet) {
|
||||
pushUnique(availableEntitySets, entitySet);
|
||||
}
|
||||
@@ -997,6 +1269,67 @@ function buildMetadataUnknownFacts(
|
||||
return ["No matching 1C metadata objects were confirmed by this MCP metadata probe"];
|
||||
}
|
||||
|
||||
function buildEntityResolutionConfirmedFacts(
|
||||
resolution: AssistantMcpDiscoveryDerivedEntityResolution | null
|
||||
): string[] {
|
||||
if (!resolution || resolution.resolution_status !== "resolved" || !resolution.resolved_entity) {
|
||||
return [];
|
||||
}
|
||||
if (resolution.requested_entity && normalizeEntityResolutionText(resolution.requested_entity) === normalizeEntityResolutionText(resolution.resolved_entity)) {
|
||||
return [`В проверенном каталожном срезе 1С найден контрагент: ${resolution.resolved_entity}`];
|
||||
}
|
||||
return [
|
||||
`В проверенном каталожном срезе 1С найден наиболее вероятный контрагент: ${resolution.resolved_entity}`
|
||||
];
|
||||
}
|
||||
|
||||
function buildEntityResolutionInferredFacts(
|
||||
resolution: AssistantMcpDiscoveryDerivedEntityResolution | null
|
||||
): string[] {
|
||||
if (!resolution) {
|
||||
return [];
|
||||
}
|
||||
if (resolution.resolution_status === "resolved") {
|
||||
const facts = ["Пока проверено только заземление сущности по каталогу 1С; документы, движения и денежные показатели еще не проверялись"];
|
||||
if (resolution.requested_entity && resolution.resolved_entity) {
|
||||
const requestedNormalized = normalizeEntityResolutionText(resolution.requested_entity);
|
||||
const resolvedNormalized = normalizeEntityResolutionText(resolution.resolved_entity);
|
||||
if (requestedNormalized !== resolvedNormalized) {
|
||||
facts.push("Контрагент выбран как ближайшее подтвержденное совпадение имени в проверенном каталоге 1С");
|
||||
}
|
||||
}
|
||||
return facts;
|
||||
}
|
||||
if (resolution.resolution_status === "ambiguous") {
|
||||
return ["В проверенном каталожном срезе осталось несколько близких кандидатов, поэтому точного контрагента в 1С еще нужно уточнить"];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function buildEntityResolutionUnknownFacts(
|
||||
resolution: AssistantMcpDiscoveryDerivedEntityResolution | null,
|
||||
requestedEntity: string | null
|
||||
): string[] {
|
||||
if (!resolution) {
|
||||
return ["По проверенному каталожному поиску 1С не удалось заземлить сущность контрагента"];
|
||||
}
|
||||
const unknownFacts = ["Документы, движения и денежные показатели по этому контрагенту еще не проверялись; пока был только каталожный поиск"];
|
||||
if (resolution.resolution_status === "ambiguous" && resolution.ambiguity_candidates.length > 0) {
|
||||
unknownFacts.unshift(
|
||||
`Точное заземление контрагента в 1С остается неоднозначным между вариантами: ${resolution.ambiguity_candidates.join(", ")}`
|
||||
);
|
||||
return unknownFacts;
|
||||
}
|
||||
if (resolution.resolution_status === "not_found") {
|
||||
unknownFacts.unshift(
|
||||
requestedEntity
|
||||
? `В проверенном каталожном срезе 1С не подтвержден контрагент с именем "${requestedEntity}"`
|
||||
: "В проверенном каталожном срезе 1С не подтвержден подходящий контрагент"
|
||||
);
|
||||
}
|
||||
return unknownFacts;
|
||||
}
|
||||
|
||||
function rowDateValue(row: Record<string, unknown>): string | null {
|
||||
const candidates = [
|
||||
row["Период"],
|
||||
@@ -1562,22 +1895,25 @@ function buildEmptyEvidence(
|
||||
}
|
||||
|
||||
function pilotScopeForPlanner(planner: AssistantMcpDiscoveryPlannerContract): AssistantMcpDiscoveryPilotScope {
|
||||
if (planner.reason_codes.includes("planner_selected_metadata_lane_clarification_recipe")) {
|
||||
return "metadata_inspection_v1";
|
||||
switch (planner.selected_chain_id) {
|
||||
case "metadata_lane_clarification":
|
||||
case "metadata_inspection":
|
||||
return "metadata_inspection_v1";
|
||||
case "movement_evidence":
|
||||
return "counterparty_movement_evidence_query_movements_v1";
|
||||
case "value_flow":
|
||||
return valueFlowPilotProfile(planner).scope;
|
||||
case "document_evidence":
|
||||
return "counterparty_document_evidence_query_documents_v1";
|
||||
case "lifecycle":
|
||||
return "counterparty_lifecycle_query_documents_v1";
|
||||
case "entity_resolution":
|
||||
return "entity_resolution_search_v1";
|
||||
}
|
||||
if (isMetadataPilotEligible(planner)) {
|
||||
return "metadata_inspection_v1";
|
||||
}
|
||||
if (isMovementEvidencePilotEligible(planner)) {
|
||||
return "counterparty_movement_evidence_query_movements_v1";
|
||||
}
|
||||
if (isValueFlowPilotEligible(planner)) {
|
||||
return valueFlowPilotProfile(planner).scope;
|
||||
}
|
||||
if (isDocumentEvidencePilotEligible(planner)) {
|
||||
return "counterparty_document_evidence_query_documents_v1";
|
||||
}
|
||||
return "counterparty_lifecycle_query_documents_v1";
|
||||
}
|
||||
|
||||
function isLivePilotChainSupported(chainId: AssistantMcpDiscoveryChainId): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function executeAssistantMcpDiscoveryPilot(
|
||||
@@ -1612,6 +1948,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
evidence,
|
||||
source_rows_summary: null,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
@@ -1636,6 +1973,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
evidence,
|
||||
source_rows_summary: null,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
@@ -1649,8 +1987,18 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
const movementPilotEligible = isMovementEvidencePilotEligible(planner);
|
||||
const lifecyclePilotEligible = isLifecyclePilotEligible(planner);
|
||||
const valueFlowPilotEligible = isValueFlowPilotEligible(planner);
|
||||
const entityResolutionPilotEligible = isEntityResolutionPilotEligible(planner);
|
||||
const livePilotChainSupported = isLivePilotChainSupported(planner.selected_chain_id);
|
||||
|
||||
if (!metadataPilotEligible && !documentPilotEligible && !movementPilotEligible && !lifecyclePilotEligible && !valueFlowPilotEligible) {
|
||||
if (
|
||||
!livePilotChainSupported ||
|
||||
(!metadataPilotEligible &&
|
||||
!documentPilotEligible &&
|
||||
!movementPilotEligible &&
|
||||
!lifecyclePilotEligible &&
|
||||
!valueFlowPilotEligible &&
|
||||
!entityResolutionPilotEligible)
|
||||
) {
|
||||
pushReason(reasonCodes, "pilot_scope_unsupported_for_live_execution");
|
||||
for (const step of dryRun.execution_steps) {
|
||||
skippedPrimitives.push(step.primitive_id);
|
||||
@@ -1670,6 +2018,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
evidence,
|
||||
source_rows_summary: null,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
@@ -1737,13 +2086,109 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
evidence,
|
||||
source_rows_summary: sourceRowsSummary,
|
||||
derived_metadata_surface: derivedMetadataSurface,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
query_limitations: queryLimitations,
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (entityResolutionPilotEligible) {
|
||||
let queryResult: AddressMcpQueryExecutorResult | null = null;
|
||||
const requestedEntity = counterparty;
|
||||
if (isLowQualityEntityResolutionAnchor(requestedEntity)) {
|
||||
pushReason(reasonCodes, "pilot_entity_resolution_anchor_missing_or_low_quality");
|
||||
const evidence = buildEmptyEvidence(planner, dryRun, probeResults, "Entity-resolution needs a clearer counterparty name");
|
||||
return {
|
||||
schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||
pilot_status: "skipped_needs_clarification",
|
||||
pilot_scope: "entity_resolution_search_v1",
|
||||
dry_run: dryRun,
|
||||
mcp_execution_performed: false,
|
||||
executed_primitives: executedPrimitives,
|
||||
skipped_primitives: skippedPrimitives,
|
||||
probe_results: probeResults,
|
||||
evidence,
|
||||
source_rows_summary: null,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
query_limitations: ["Entity-resolution needs a clearer counterparty name"],
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
|
||||
for (const step of dryRun.execution_steps) {
|
||||
if (step.primitive_id !== "search_business_entity") {
|
||||
skippedPrimitives.push(step.primitive_id);
|
||||
probeResults.push(skippedProbeResult(step, "pilot_only_executes_search_business_entity"));
|
||||
continue;
|
||||
}
|
||||
queryResult = await runtimeDeps.executeAddressMcpQuery({
|
||||
query: ENTITY_RESOLUTION_COUNTERPARTY_QUERY_TEMPLATE.replaceAll(
|
||||
"__LIMIT__",
|
||||
String(ENTITY_RESOLUTION_COUNTERPARTY_LOOKUP_LIMIT)
|
||||
),
|
||||
limit: ENTITY_RESOLUTION_COUNTERPARTY_LOOKUP_LIMIT
|
||||
});
|
||||
pushUnique(executedPrimitives, step.primitive_id);
|
||||
probeResults.push(queryResultToProbeResult(step.primitive_id, queryResult));
|
||||
if (queryResult.error) {
|
||||
pushUnique(queryLimitations, queryResult.error);
|
||||
pushReason(reasonCodes, "pilot_search_business_entity_mcp_error");
|
||||
} else {
|
||||
pushReason(reasonCodes, "pilot_search_business_entity_mcp_executed");
|
||||
}
|
||||
}
|
||||
|
||||
const sourceRowsSummary = queryResult ? summarizeEntityResolutionRows(queryResult) : null;
|
||||
const derivedEntityResolution = deriveEntityResolution(queryResult, requestedEntity);
|
||||
if (derivedEntityResolution?.resolution_status === "resolved") {
|
||||
pushReason(reasonCodes, "pilot_derived_entity_resolution_from_catalog_rows");
|
||||
}
|
||||
if (derivedEntityResolution?.resolution_status === "ambiguous") {
|
||||
pushReason(reasonCodes, "pilot_entity_resolution_ambiguity_requires_clarification");
|
||||
}
|
||||
if (derivedEntityResolution?.resolution_status === "not_found") {
|
||||
pushReason(reasonCodes, "pilot_entity_resolution_not_found_in_checked_catalog");
|
||||
}
|
||||
const evidence = resolveAssistantMcpDiscoveryEvidence({
|
||||
plan: planner.discovery_plan,
|
||||
probeResults,
|
||||
confirmedFacts: buildEntityResolutionConfirmedFacts(derivedEntityResolution),
|
||||
inferredFacts: buildEntityResolutionInferredFacts(derivedEntityResolution),
|
||||
unknownFacts: buildEntityResolutionUnknownFacts(derivedEntityResolution, requestedEntity),
|
||||
sourceRowsSummary,
|
||||
queryLimitations,
|
||||
recommendedNextProbe: "resolve_entity_reference"
|
||||
});
|
||||
|
||||
return {
|
||||
schema_version: ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||
policy_owner: "assistantMcpDiscoveryPilotExecutor",
|
||||
pilot_status: "executed",
|
||||
pilot_scope: "entity_resolution_search_v1",
|
||||
dry_run: dryRun,
|
||||
mcp_execution_performed: executedPrimitives.length > 0,
|
||||
executed_primitives: executedPrimitives,
|
||||
skipped_primitives: skippedPrimitives,
|
||||
probe_results: probeResults,
|
||||
evidence,
|
||||
source_rows_summary: sourceRowsSummary,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: derivedEntityResolution,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
query_limitations: queryLimitations,
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
|
||||
if (documentPilotEligible) {
|
||||
let queryResult: AddressMcpQueryExecutorResult | null = null;
|
||||
@@ -1765,6 +2210,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
evidence,
|
||||
source_rows_summary: null,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
@@ -1820,6 +2266,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
evidence,
|
||||
source_rows_summary: sourceRowsSummary,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
@@ -1848,6 +2295,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
evidence,
|
||||
source_rows_summary: null,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
@@ -1903,6 +2351,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
evidence,
|
||||
source_rows_summary: sourceRowsSummary,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
@@ -1936,6 +2385,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
evidence,
|
||||
source_rows_summary: null,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
@@ -2035,6 +2485,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
evidence,
|
||||
source_rows_summary: sourceRowsSummary,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: derivedBidirectionalValueFlow,
|
||||
@@ -2061,6 +2512,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
evidence,
|
||||
source_rows_summary: null,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
@@ -2144,6 +2596,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
evidence,
|
||||
source_rows_summary: sourceRowsSummary,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: derivedValueFlow,
|
||||
derived_bidirectional_value_flow: null,
|
||||
@@ -2171,6 +2624,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
evidence,
|
||||
source_rows_summary: null,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: null,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
@@ -2230,6 +2684,7 @@ export async function executeAssistantMcpDiscoveryPilot(
|
||||
evidence,
|
||||
source_rows_summary: sourceRowsSummary,
|
||||
derived_metadata_surface: null,
|
||||
derived_entity_resolution: null,
|
||||
derived_activity_period: derivedActivityPeriod,
|
||||
derived_value_flow: null,
|
||||
derived_bidirectional_value_flow: null,
|
||||
|
||||
Reference in New Issue
Block a user