Planner Autonomy: ранжировать catalog chain templates
This commit is contained in:
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ASSISTANT_MCP_CATALOG_PLAN_REVIEW_SCHEMA_VERSION = exports.ASSISTANT_MCP_CATALOG_INDEX_SCHEMA_VERSION = void 0;
|
||||
exports.searchAssistantMcpCatalogPrimitivesByDecompositionCandidates = searchAssistantMcpCatalogPrimitivesByDecompositionCandidates;
|
||||
exports.searchAssistantMcpCatalogPrimitivesByFactAxis = searchAssistantMcpCatalogPrimitivesByFactAxis;
|
||||
exports.searchAssistantMcpCatalogChainTemplatesByFactAxis = searchAssistantMcpCatalogChainTemplatesByFactAxis;
|
||||
exports.searchAssistantMcpCatalogPrimitivesByMetadataSurface = searchAssistantMcpCatalogPrimitivesByMetadataSurface;
|
||||
exports.buildAssistantMcpCatalogIndex = buildAssistantMcpCatalogIndex;
|
||||
exports.getAssistantMcpCatalogPrimitive = getAssistantMcpCatalogPrimitive;
|
||||
@@ -700,6 +701,53 @@ function searchAssistantMcpCatalogPrimitivesByFactAxis(input) {
|
||||
.sort((left, right) => right.score - left.score)
|
||||
.map((item) => item.primitive);
|
||||
}
|
||||
function searchAssistantMcpCatalogChainTemplatesByFactAxis(input) {
|
||||
const requiredAxisSet = toStringSet(input.required_axes ?? []);
|
||||
const desiredTags = tagSetFromFactAxisInput({
|
||||
business_fact_family: input.business_fact_family,
|
||||
action_family: input.action_family,
|
||||
required_axes: input.required_axes,
|
||||
comparison_need: input.comparison_need,
|
||||
ranking_need: input.ranking_need,
|
||||
aggregation_need: input.aggregation_need
|
||||
});
|
||||
const scored = [];
|
||||
for (const template of CHAIN_TEMPLATES) {
|
||||
const factMatch = matchesPlanningToken(input.business_fact_family, template.supported_fact_families);
|
||||
const actionMatch = matchesPlanningToken(input.action_family, template.supported_action_families);
|
||||
const tagMatches = template.planning_tags.filter((tag) => desiredTags.has(normalizePlanningToken(tag)));
|
||||
const axisOverlap = template.base_required_axes.filter((axis) => requiredAxisSet.has(axis)).length;
|
||||
let score = 0;
|
||||
if (factMatch) {
|
||||
score += 8;
|
||||
}
|
||||
if (actionMatch) {
|
||||
score += 5;
|
||||
}
|
||||
score += tagMatches.length * 2;
|
||||
score += axisOverlap;
|
||||
if (input.comparison_need && template.planning_tags.some((tag) => normalizePlanningToken(tag) === "comparison")) {
|
||||
score += 6;
|
||||
}
|
||||
if (input.ranking_need && template.planning_tags.some((tag) => normalizePlanningToken(tag) === "ranking")) {
|
||||
score += 6;
|
||||
}
|
||||
if (input.aggregation_need === "by_month" &&
|
||||
template.planning_tags.some((tag) => normalizePlanningToken(tag) === "monthly_aggregation")) {
|
||||
score += 4;
|
||||
}
|
||||
if (score <= 0) {
|
||||
continue;
|
||||
}
|
||||
scored.push({
|
||||
chainId: template.chain_id,
|
||||
score
|
||||
});
|
||||
}
|
||||
return scored
|
||||
.sort((left, right) => right.score - left.score)
|
||||
.map((item) => item.chainId);
|
||||
}
|
||||
function searchAssistantMcpCatalogPrimitivesByMetadataSurface(input) {
|
||||
const allowAggregateByAxis = input.allow_aggregate_by_axis !== false;
|
||||
const requiredAxisSet = toStringSet(input.required_axes ?? []);
|
||||
|
||||
@@ -278,6 +278,20 @@ function selectPrimitivesFromGraphAndCatalog(input) {
|
||||
if (factAxisPrimitives.length > 0) {
|
||||
reasonCodes.push("planner_selected_catalog_primitives_from_fact_axis_search");
|
||||
}
|
||||
const chainTemplateMatches = input.dataNeedGraph
|
||||
? (0, assistantMcpCatalogIndex_1.searchAssistantMcpCatalogChainTemplatesByFactAxis)({
|
||||
business_fact_family: input.dataNeedGraph.business_fact_family,
|
||||
action_family: input.actionFamily ?? input.dataNeedGraph.action_family,
|
||||
required_axes: input.requiredAxes,
|
||||
comparison_need: input.dataNeedGraph.comparison_need,
|
||||
ranking_need: input.dataNeedGraph.ranking_need,
|
||||
aggregation_need: input.dataNeedGraph.aggregation_need
|
||||
})
|
||||
: [];
|
||||
if (chainTemplateMatches.length > 0) {
|
||||
reasonCodes.push("planner_scored_catalog_chain_templates_from_fact_axis");
|
||||
reasonCodes.push(`planner_catalog_chain_template_search_top_${chainTemplateMatches[0]}`);
|
||||
}
|
||||
const combinedCatalogPrimitives = [];
|
||||
for (const primitive of decompositionPrimitives) {
|
||||
if (!combinedCatalogPrimitives.includes(primitive)) {
|
||||
|
||||
@@ -622,6 +622,15 @@ export interface AssistantMcpCatalogFactAxisSearchInput {
|
||||
allow_aggregate_by_axis?: boolean;
|
||||
}
|
||||
|
||||
export interface AssistantMcpCatalogChainTemplateSearchInput {
|
||||
business_fact_family?: string | null;
|
||||
action_family?: string | null;
|
||||
required_axes?: string[];
|
||||
comparison_need?: string | null;
|
||||
ranking_need?: string | null;
|
||||
aggregation_need?: string | null;
|
||||
}
|
||||
|
||||
export interface AssistantMcpCatalogPrimitiveSearchInput {
|
||||
decomposition_candidates: string[];
|
||||
allow_aggregate_by_axis?: boolean;
|
||||
@@ -847,6 +856,62 @@ export function searchAssistantMcpCatalogPrimitivesByFactAxis(
|
||||
.map((item) => item.primitive);
|
||||
}
|
||||
|
||||
export function searchAssistantMcpCatalogChainTemplatesByFactAxis(
|
||||
input: AssistantMcpCatalogChainTemplateSearchInput
|
||||
): AssistantMcpCatalogChainTemplateId[] {
|
||||
const requiredAxisSet = toStringSet(input.required_axes ?? []);
|
||||
const desiredTags = tagSetFromFactAxisInput({
|
||||
business_fact_family: input.business_fact_family,
|
||||
action_family: input.action_family,
|
||||
required_axes: input.required_axes,
|
||||
comparison_need: input.comparison_need,
|
||||
ranking_need: input.ranking_need,
|
||||
aggregation_need: input.aggregation_need
|
||||
});
|
||||
const scored: Array<{ chainId: AssistantMcpCatalogChainTemplateId; score: number }> = [];
|
||||
|
||||
for (const template of CHAIN_TEMPLATES) {
|
||||
const factMatch = matchesPlanningToken(input.business_fact_family, template.supported_fact_families);
|
||||
const actionMatch = matchesPlanningToken(input.action_family, template.supported_action_families);
|
||||
const tagMatches = template.planning_tags.filter((tag) => desiredTags.has(normalizePlanningToken(tag)));
|
||||
const axisOverlap = template.base_required_axes.filter((axis) => requiredAxisSet.has(axis)).length;
|
||||
|
||||
let score = 0;
|
||||
if (factMatch) {
|
||||
score += 8;
|
||||
}
|
||||
if (actionMatch) {
|
||||
score += 5;
|
||||
}
|
||||
score += tagMatches.length * 2;
|
||||
score += axisOverlap;
|
||||
if (input.comparison_need && template.planning_tags.some((tag) => normalizePlanningToken(tag) === "comparison")) {
|
||||
score += 6;
|
||||
}
|
||||
if (input.ranking_need && template.planning_tags.some((tag) => normalizePlanningToken(tag) === "ranking")) {
|
||||
score += 6;
|
||||
}
|
||||
if (
|
||||
input.aggregation_need === "by_month" &&
|
||||
template.planning_tags.some((tag) => normalizePlanningToken(tag) === "monthly_aggregation")
|
||||
) {
|
||||
score += 4;
|
||||
}
|
||||
|
||||
if (score <= 0) {
|
||||
continue;
|
||||
}
|
||||
scored.push({
|
||||
chainId: template.chain_id,
|
||||
score
|
||||
});
|
||||
}
|
||||
|
||||
return scored
|
||||
.sort((left, right) => right.score - left.score)
|
||||
.map((item) => item.chainId);
|
||||
}
|
||||
|
||||
export function searchAssistantMcpCatalogPrimitivesByMetadataSurface(
|
||||
input: AssistantMcpCatalogMetadataSurfaceSearchInput
|
||||
): AssistantMcpDiscoveryPrimitive[] {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from "./assistantMcpDiscoveryPolicy";
|
||||
import {
|
||||
getAssistantMcpCatalogChainTemplate,
|
||||
searchAssistantMcpCatalogChainTemplatesByFactAxis,
|
||||
searchAssistantMcpCatalogPrimitivesByDecompositionCandidates,
|
||||
searchAssistantMcpCatalogPrimitivesByFactAxis,
|
||||
searchAssistantMcpCatalogPrimitivesByMetadataSurface,
|
||||
@@ -457,6 +458,21 @@ function selectPrimitivesFromGraphAndCatalog(input: {
|
||||
reasonCodes.push("planner_selected_catalog_primitives_from_fact_axis_search");
|
||||
}
|
||||
|
||||
const chainTemplateMatches = input.dataNeedGraph
|
||||
? searchAssistantMcpCatalogChainTemplatesByFactAxis({
|
||||
business_fact_family: input.dataNeedGraph.business_fact_family,
|
||||
action_family: input.actionFamily ?? input.dataNeedGraph.action_family,
|
||||
required_axes: input.requiredAxes,
|
||||
comparison_need: input.dataNeedGraph.comparison_need,
|
||||
ranking_need: input.dataNeedGraph.ranking_need,
|
||||
aggregation_need: input.dataNeedGraph.aggregation_need
|
||||
})
|
||||
: [];
|
||||
if (chainTemplateMatches.length > 0) {
|
||||
reasonCodes.push("planner_scored_catalog_chain_templates_from_fact_axis");
|
||||
reasonCodes.push(`planner_catalog_chain_template_search_top_${chainTemplateMatches[0]}`);
|
||||
}
|
||||
|
||||
const combinedCatalogPrimitives: AssistantMcpDiscoveryPrimitive[] = [];
|
||||
for (const primitive of decompositionPrimitives) {
|
||||
if (!combinedCatalogPrimitives.includes(primitive)) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
getAssistantMcpCatalogChainTemplate,
|
||||
getAssistantMcpCatalogPrimitive,
|
||||
reviewAssistantMcpDiscoveryPlanAgainstCatalog,
|
||||
searchAssistantMcpCatalogChainTemplatesByFactAxis,
|
||||
searchAssistantMcpCatalogPrimitivesByDecompositionCandidates,
|
||||
searchAssistantMcpCatalogPrimitivesByFactAxis,
|
||||
searchAssistantMcpCatalogPrimitivesByMetadataSurface
|
||||
@@ -151,6 +152,30 @@ describe("assistant MCP catalog index", () => {
|
||||
expect(primitives).toEqual(["resolve_entity_reference", "query_documents", "probe_coverage"]);
|
||||
});
|
||||
|
||||
it("can score reviewed chain templates directly from fact family and required axes", () => {
|
||||
const documentTemplates = searchAssistantMcpCatalogChainTemplatesByFactAxis({
|
||||
business_fact_family: "document_evidence",
|
||||
action_family: "list_documents",
|
||||
required_axes: ["counterparty", "period", "coverage_target"]
|
||||
});
|
||||
const comparisonTemplates = searchAssistantMcpCatalogChainTemplatesByFactAxis({
|
||||
business_fact_family: "value_flow",
|
||||
action_family: "net_value_flow",
|
||||
comparison_need: "incoming_vs_outgoing",
|
||||
required_axes: ["organization", "period", "amount", "coverage_target"]
|
||||
});
|
||||
const rankingTemplates = searchAssistantMcpCatalogChainTemplatesByFactAxis({
|
||||
business_fact_family: "value_flow",
|
||||
action_family: "turnover",
|
||||
ranking_need: "top_desc",
|
||||
required_axes: ["organization", "period", "aggregate_axis", "amount", "coverage_target"]
|
||||
});
|
||||
|
||||
expect(documentTemplates[0]).toBe("document_evidence");
|
||||
expect(comparisonTemplates[0]).toBe("value_flow_comparison");
|
||||
expect(rankingTemplates[0]).toBe("value_flow_ranking");
|
||||
});
|
||||
|
||||
it("can search reviewed primitives for inventory stock snapshot chains", () => {
|
||||
const primitives = searchAssistantMcpCatalogPrimitivesByFactAxis({
|
||||
business_fact_family: "inventory_stock_snapshot",
|
||||
|
||||
@@ -51,6 +51,8 @@ describe("assistant MCP discovery planner", () => {
|
||||
expect(result.reason_codes).toContain("planner_enabled_chunked_coverage_probe_budget");
|
||||
expect(result.reason_codes).toContain("planner_consumed_data_need_graph_v1");
|
||||
expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_decomposition_candidates");
|
||||
expect(result.reason_codes).toContain("planner_scored_catalog_chain_templates_from_fact_axis");
|
||||
expect(result.reason_codes).toContain("planner_catalog_chain_template_search_top_value_flow");
|
||||
});
|
||||
|
||||
it("keeps a value-flow plan in clarification state when period axis is missing", () => {
|
||||
@@ -145,6 +147,7 @@ describe("assistant MCP discovery planner", () => {
|
||||
expect(result.proposed_primitives).toEqual(["resolve_entity_reference", "query_documents", "probe_coverage"]);
|
||||
expect(result.reason_codes).toContain("planner_selected_catalog_primitives_from_fact_axis_search");
|
||||
expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_document_evidence");
|
||||
expect(result.reason_codes).toContain("planner_catalog_chain_template_search_top_document_evidence");
|
||||
expect(result.reason_codes).not.toContain("planner_fell_back_to_recipe_primitives_after_empty_catalog_search");
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user