优化skill,添加特征树、编辑窗口
This commit is contained in:
@@ -181,6 +181,49 @@ def contains_any(text: str, terms: tuple[str, ...]) -> bool:
|
||||
return any(term in text for term in terms)
|
||||
|
||||
|
||||
def simplecadapi_standard_part_match(
|
||||
request: str,
|
||||
registry: dict[str, object],
|
||||
) -> dict[str, object] | None:
|
||||
"""Match only a requested *primary* standard part, never a feature word.
|
||||
|
||||
The catalog is maintained beside the router from the actual SimpleCADAPI
|
||||
``simplecadapi.std`` factories. This is intentionally deterministic: an
|
||||
unmatched or ambiguous request is a build123d request.
|
||||
"""
|
||||
catalog = registry.get("routing_catalog", {})
|
||||
rules = (
|
||||
catalog.get("simplecadapi_standard_part_types", [])
|
||||
if isinstance(catalog, dict)
|
||||
else []
|
||||
)
|
||||
text = request.casefold()
|
||||
best_match: tuple[int, dict[str, object], list[str]] | None = None
|
||||
for raw_rule in rules:
|
||||
if not isinstance(raw_rule, dict):
|
||||
continue
|
||||
aliases = [str(value).casefold() for value in raw_rule.get("aliases", [])]
|
||||
exclusions = [str(value).casefold() for value in raw_rule.get("feature_context_exclusions", [])]
|
||||
matched_aliases = [alias for alias in aliases if alias and alias in text]
|
||||
if not matched_aliases:
|
||||
continue
|
||||
if any(exclusion and exclusion in text for exclusion in exclusions):
|
||||
continue
|
||||
# Generic terms such as "gear" must never hide a more specific
|
||||
# standard-part type such as "herringbone gear".
|
||||
specificity = max(len(alias) for alias in matched_aliases)
|
||||
if best_match is None or specificity > best_match[0]:
|
||||
best_match = (specificity, raw_rule, matched_aliases)
|
||||
if best_match is None:
|
||||
return None
|
||||
_, rule, matched_aliases = best_match
|
||||
return {
|
||||
"type": rule.get("type"),
|
||||
"factory": rule.get("factory"),
|
||||
"matched_aliases": matched_aliases,
|
||||
}
|
||||
|
||||
|
||||
def infer_experience_query(request: str) -> tuple[str | None, list[str]]:
|
||||
text = request.lower()
|
||||
family = next(
|
||||
@@ -559,6 +602,127 @@ def route(args: argparse.Namespace) -> dict[str, object]:
|
||||
assembly=args.assembly,
|
||||
source_backend=source_backend,
|
||||
)
|
||||
if not edit_context and args.backend == "auto":
|
||||
registry = json.loads(REGISTRY_PATH.read_text(encoding="utf-8"))
|
||||
standard_part = simplecadapi_standard_part_match(request, registry)
|
||||
if not standard_part:
|
||||
requirements = [
|
||||
requirement
|
||||
for requirement in requirements
|
||||
if requirement != "standard_mechanical_factory"
|
||||
]
|
||||
agent_experiences = (
|
||||
experience_context.get("experiences", [])
|
||||
if isinstance(experience_context, dict)
|
||||
else []
|
||||
)
|
||||
agent_generalized_methods = [
|
||||
{
|
||||
key: item[key]
|
||||
for key in (
|
||||
"id",
|
||||
"kind",
|
||||
"guidance",
|
||||
"check",
|
||||
"repair",
|
||||
"confidence",
|
||||
"reconstruction_grammar",
|
||||
)
|
||||
if isinstance(item, dict) and key in item
|
||||
}
|
||||
for item in agent_experiences
|
||||
if isinstance(item, dict)
|
||||
]
|
||||
agent_reconstruction_grammars = [
|
||||
item["reconstruction_grammar"]
|
||||
for item in agent_experiences
|
||||
if isinstance(item, dict)
|
||||
and item.get("kind") == "reconstruction_grammar"
|
||||
and "reconstruction_grammar" in item
|
||||
]
|
||||
selected_name = "simplecadapi" if standard_part else "build123d"
|
||||
backend_info = registry["backends"][selected_name]
|
||||
source_of_truth = (
|
||||
"simplecadapi model graph with native Python source"
|
||||
if selected_name == "simplecadapi"
|
||||
else "build123d native Python source"
|
||||
)
|
||||
selection_reason = (
|
||||
"matched SimpleCADAPI standard-part catalog entry "
|
||||
f"{standard_part['type']} ({standard_part['factory']})"
|
||||
if standard_part
|
||||
else "no SimpleCADAPI standard-part catalog entry matched; use the general text-to-cad/build123d backend"
|
||||
)
|
||||
return {
|
||||
"schema_version": "1.0",
|
||||
"request": request,
|
||||
"selected_backend": selected_name,
|
||||
"runner_skill": backend_info["runner_skill"],
|
||||
"project": backend_info["project"],
|
||||
"adapter": None,
|
||||
"confidence": "high",
|
||||
"routing_policy": {
|
||||
"version": "4.0",
|
||||
"selection_scope": "new_model_primary_part_type",
|
||||
"requirements": requirements,
|
||||
"decision_authority": "cad_router_standard_part_catalog",
|
||||
"keyword_backend_scoring": True,
|
||||
"fallback_policy": "unmatched_requests_use_build123d",
|
||||
"standard_part_match": standard_part,
|
||||
"server_side_template_generation": False,
|
||||
"effective_execution_engine": selected_name,
|
||||
"validation_engines": ["backend_geometry_validation"],
|
||||
},
|
||||
"backend_scores": [
|
||||
{
|
||||
"backend": selected_name,
|
||||
"score": 100,
|
||||
"reasons": [selection_reason],
|
||||
},
|
||||
{
|
||||
"backend": "build123d" if selected_name == "simplecadapi" else "simplecadapi",
|
||||
"score": 0,
|
||||
"reasons": ["not selected by the standard-part catalog"],
|
||||
},
|
||||
],
|
||||
"fallback_order": ["build123d"] if selected_name == "simplecadapi" else [],
|
||||
"workflow_profiles": [],
|
||||
"artifact_contract": {
|
||||
"primary_format": "step",
|
||||
"editable_contract": "DesignIR 3.0 normalized contract",
|
||||
"editable_contract_role": "contract_after_backend_generation",
|
||||
"authoritative_geometry_source": source_of_truth,
|
||||
},
|
||||
"source_of_truth": source_of_truth,
|
||||
"experience_context": experience_context,
|
||||
"design_plan": {
|
||||
"plan_kind": "deterministic_standard_part_route",
|
||||
"operation": "create",
|
||||
"part_family": experience_family or "unclassified",
|
||||
"requested_feature_roles": sorted(set(experience_features)),
|
||||
"selected_backend": selected_name,
|
||||
"source_of_truth": source_of_truth,
|
||||
"standard_part_match": standard_part,
|
||||
"generalized_methods": agent_generalized_methods,
|
||||
"reconstruction_grammars": agent_reconstruction_grammars,
|
||||
"experience_status": (
|
||||
"matched"
|
||||
if experience_context and experience_context.get("experiences")
|
||||
else "no_promoted_method_matched"
|
||||
),
|
||||
"execution_steps": [
|
||||
"CAD Router selects the backend from the standard-part catalog.",
|
||||
"Agent writes backend-native Python source for the full request.",
|
||||
"Server executes the submitted source and rejects missing artifacts.",
|
||||
"Server normalizes backend output into DesignIR 3.0.",
|
||||
],
|
||||
},
|
||||
"project_contributions": {
|
||||
"text-to-cad": "default backend for every request outside the SimpleCADAPI standard-part catalog",
|
||||
"SimpleCADAPI": "backend for the catalogued standard mechanical part types only",
|
||||
},
|
||||
"availability": None,
|
||||
}
|
||||
scores = {
|
||||
"build123d": BackendScore(
|
||||
"build123d",
|
||||
@@ -692,12 +856,13 @@ def route(args: argparse.Namespace) -> dict[str, object]:
|
||||
|
||||
registry = json.loads(REGISTRY_PATH.read_text(encoding="utf-8"))
|
||||
backend_info = registry["backends"][selected.name]
|
||||
editable_contract = "DesignIR 3.0"
|
||||
source_of_truth = (
|
||||
"per-part DesignIR 3.0 with an isolated SurfaceIR compiler"
|
||||
if teacher_step_reconstruction
|
||||
else "per-part semantic DesignIR 3.0 with an isolated backend compiler"
|
||||
)
|
||||
editable_contract = "DesignIR 3.0 normalized contract"
|
||||
if teacher_step_reconstruction:
|
||||
source_of_truth = "surfaceir_designir"
|
||||
elif selected.name == "simplecadapi":
|
||||
source_of_truth = "simplecadapi model graph with native Python source"
|
||||
else:
|
||||
source_of_truth = "build123d native Python source"
|
||||
gap = selected.score - max((scores[name].score for name in scores if name != selected.name), default=0)
|
||||
confidence = "high" if gap >= 40 else "medium" if gap >= 15 else "low"
|
||||
result: dict[str, object] = {
|
||||
@@ -740,6 +905,8 @@ def route(args: argparse.Namespace) -> dict[str, object]:
|
||||
"artifact_contract": {
|
||||
"primary_format": "step",
|
||||
"editable_contract": editable_contract,
|
||||
"editable_contract_role": "contract_after_backend_generation",
|
||||
"authoritative_geometry_source": source_of_truth,
|
||||
},
|
||||
"source_of_truth": source_of_truth,
|
||||
"experience_context": experience_context,
|
||||
@@ -831,10 +998,10 @@ def route(args: argparse.Namespace) -> dict[str, object]:
|
||||
if edit_context
|
||||
else [
|
||||
"Resolve explicit user dimensions and preserve them as authoritative parameters.",
|
||||
"Instantiate matched reconstruction grammars as DesignIR parameters, datums, constraints, and canonical feature stages; never copy teacher geometry.",
|
||||
"Translate requested feature roles and matched generalized methods into backend operations.",
|
||||
"Generate the editable source of truth and STEP artifact.",
|
||||
"Validate topology, dimensions, feature relationships, surface mix, and export integrity against the grammar validation roles.",
|
||||
"Translate requested feature roles and matched generalized methods into the selected backend's native operations.",
|
||||
"Generate backend-native source or model graph and export the primary STEP artifact.",
|
||||
"Normalize backend metadata into DesignIR 3.0 for parameters, feature tree, validation, and future edit bindings.",
|
||||
"Validate topology, dimensions, feature relationships, surface mix, and export integrity against the brief.",
|
||||
]
|
||||
),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user