优化skill,添加特征树、编辑窗口

This commit is contained in:
2026-08-07 17:44:12 +08:00
parent 35e18ca956
commit 5fbcf2c0b8
70 changed files with 13076 additions and 465 deletions
+177 -10
View File
@@ -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.",
]
),
},