优化skill,添加特征树、编辑窗口
This commit is contained in:
@@ -15,7 +15,11 @@ Choose a backend before modeling, keep its source as the editable authority, and
|
||||
only its promoted schema `2.0` generalized library.
|
||||
Never read `cad-experience-plugin/parser/input`, `parser/output`, or private
|
||||
case JSON during creation, modification, or reconstruction.
|
||||
2. Run the deterministic router from this skill directory:
|
||||
2. Run the deterministic router from this skill directory. For a new model it
|
||||
matches only the requested primary part against the SimpleCADAPI standard
|
||||
part catalog in `scripts/capabilities.json`. A catalog match selects
|
||||
SimpleCADAPI; every unmatched request selects text-to-cad/build123d. It does
|
||||
not read capability documents or let an LLM choose the backend:
|
||||
|
||||
```bash
|
||||
python3 scripts/route.py "<request>" --explain
|
||||
@@ -46,9 +50,9 @@ Choose a backend before modeling, keep its source as the editable authority, and
|
||||
geometry acceptance, parameter perturbation acceptance, and artifact
|
||||
publication. A route decision alone is not a completed reconstruction.
|
||||
3. Follow the selected route unless a hard environmental constraint makes it unavailable. If overriding it, record the reason in the task manifest.
|
||||
4. Load only the selected backend reference from `references/capabilities.md` and the matching installed Skill:
|
||||
- `build123d`: use `$cad`; prefer it for custom brackets, housings, shafts, flanges, fixtures, source-level assemblies, and freeform/custom surface work.
|
||||
- `simplecadapi`: use an installed SimpleCADAPI 2.0.2 Skill/runtime; prefer it when a standard factory exists, or when replayable ModelResult graphs, semantic lineage, physical units, tolerance chains, scene packages, or CAD translators materially improve the result. Its standard library includes gears, racks, ring and bevel gears, bearings, bolts, nuts, roller-chain sprockets, cycloidal parts, and reducer families.
|
||||
4. Use the selected backend directly:
|
||||
- `build123d`: use `$cad` for custom brackets, housings, shafts, flanges, fixtures, source-level assemblies, and every new part outside the catalog.
|
||||
- `simplecadapi`: use the installed SimpleCADAPI 2.0.2 runtime only for catalogued standard primary parts: gears, ring gears, racks, cycloidal discs, ball bearings, roller-chain sprockets, bolts, and nuts.
|
||||
5. When the route reports `requirement_refinement` or `visual_repair`, refine the brief, generate, execute, inspect multiple views, and repair no more than three visual mismatch rounds before asking the user.
|
||||
6. Generate into a task-owned directory and start CAD Viewer with that task directory as `--dir`; do not expose the repository-wide fixture library for a task review. Write one DesignIR 3.0 `*.designir.json` per part and treat it as the editable geometry contract; keep `cad-task.json` as the execution record. Read `references/designir-3.0.md` for both semantic authoring and uploaded STEP reconstruction. `references/designir-2.0.md` is migration-only. Never change backend during an edit unless conversion is explicitly requested; modify the recorded authoritative layer.
|
||||
For a supplied STEP/STP, use `scripts/route.py --execute`; do not manually
|
||||
@@ -90,8 +94,9 @@ Choose a backend before modeling, keep its source as the editable authority, and
|
||||
- Prefer an explicit user backend when it can produce the editable STEP contract.
|
||||
- Preserve the backend of an explicitly supplied DesignIR or Python generator unless conversion is requested.
|
||||
- Do not assign a permanent default backend. Score native-source continuity, factory coverage, semantic/replay needs, geometry vocabulary, assembly needs, and validation needs.
|
||||
- Prefer SimpleCADAPI when a supported standard mechanical factory or its model graph, semantic topology, unit/tolerance, scene, or translator capabilities materially improve editability and verification.
|
||||
- Prefer build123d for custom machined parts, source-driven assemblies, and freeform or surface-heavy work not covered by a SimpleCADAPI factory or tested operation vocabulary.
|
||||
- For a new model, do not use semantic, graph, tolerance, assembly, or generic mechanical keywords to choose a backend. Use the standard-part catalog only.
|
||||
- Select SimpleCADAPI only when the primary requested part matches an entry in `scripts/capabilities.json`. Standard-part words used only as features, such as bolt holes or bearing seats, are excluded from that match.
|
||||
- Select build123d for every unmatched new request, including custom machined parts, source-driven assemblies, and freeform or surface-heavy work.
|
||||
- For uploaded STEP, report SurfaceIR as the effective reconstruction engine. The selected authoring backend describes future semantic repair/edit work; it is not allowed to read the teacher STEP during reconstruction.
|
||||
- Prefer source quality and future modification over the shortest first generation.
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# text-to-cad / build123d Agent Capability Guide
|
||||
|
||||
This document is the required backend capability brief for CAD Agent Studio
|
||||
when considering the `build123d` route through text-to-cad. It is intentionally
|
||||
an Agent-facing decision document, not a geometry template.
|
||||
|
||||
## Role
|
||||
|
||||
Use this route when the Agent will author backend-native `build123d` Python
|
||||
source and the server will only execute, validate, normalize to DesignIR 3.0,
|
||||
and publish artifacts.
|
||||
|
||||
text-to-cad owns orchestration, artifact contracts, DesignIR normalization,
|
||||
SurfaceIR reconstruction for uploaded STEP, validation, repair workflow, and
|
||||
Viewer handoff. For direct new model generation, `build123d` source is the
|
||||
editable source of truth.
|
||||
|
||||
## Studio Documentation Discipline
|
||||
|
||||
For CAD Agent Studio backend selection, this document is sufficient. Decide
|
||||
from the capability boundary here: what text-to-cad/build123d is strong at,
|
||||
what it is weak at, and whether the primary requested object matches those
|
||||
strengths.
|
||||
|
||||
Do not read build123d API manuals, skill internals, or implementation guides
|
||||
during ordinary backend selection. The selection task only needs to know what
|
||||
the tool can generate, not the exact API signatures.
|
||||
|
||||
## Strong Fits
|
||||
|
||||
- Custom mechanical parts: brackets, flanges, wheel hub adapters, housings,
|
||||
plates, fixtures, covers, shafts, bushings, levers, cranks, connecting rods.
|
||||
- Feature-rich machined parts with cylinders, boxes, revolve-like stacks,
|
||||
holes, through cuts, counterbores, pockets, bosses, ribs, shells, chamfers,
|
||||
fillets, lofts, sweeps, and source-level construction logic.
|
||||
- Requests where words such as bolt, nut, bearing, gear, or sprocket describe
|
||||
holes, mounts, clearances, interfaces, or surrounding features rather than
|
||||
asking for those standard parts as the primary object.
|
||||
- Geometry where future edits should preserve readable Python parameters and
|
||||
explicit construction order.
|
||||
|
||||
## Weak Fits
|
||||
|
||||
- The requested primary object is a standard mechanical component already
|
||||
covered by a SimpleCADAPI standard factory, such as a gear, bearing, bolt,
|
||||
nut, roller-chain sprocket, rack, or reducer.
|
||||
- The request specifically requires SimpleCADAPI model graph replay, semantic
|
||||
topology lineage, unit/tolerance chains, translator workflows, or strict
|
||||
B-Rep comparison.
|
||||
|
||||
## Agent Source Contract
|
||||
|
||||
When choosing this backend, submit raw Python source to `generate_cad` with:
|
||||
|
||||
- `selectedBackend = "build123d"`
|
||||
- `sourceKind = "build123d_python"`
|
||||
- A script that accepts `--step` and `--metadata`
|
||||
- STEP export written exactly to the `--step` path
|
||||
- Metadata JSON written exactly to the `--metadata` path
|
||||
- stdout JSON is optional diagnostic output; file artifacts are the source of
|
||||
truth for execution success
|
||||
|
||||
If exposing editable parameters, include a top-level block:
|
||||
|
||||
```python
|
||||
# CAD_AGENT_PARAMETERS_START
|
||||
PARAMETERS = {
|
||||
"example": 1.0
|
||||
}
|
||||
# CAD_AGENT_PARAMETERS_END
|
||||
```
|
||||
|
||||
Each editable metadata parameter must include `name`, `value`, `unit`,
|
||||
`editable`, `binding_kind`, `parameter_path`, and `regenerate_adapter`.
|
||||
Use `binding_kind = "python_constant"` and `regenerate_adapter = "build123d"`
|
||||
for parameters bound to the `PARAMETERS` block.
|
||||
|
||||
Do not submit diagnostic, API-probing, topology-probing, radius-sweep, or smoke
|
||||
test scripts as `nativeSource`. The source must be the final model generator
|
||||
for the user's part and must write the requested file artifacts in one
|
||||
execution. If this compact reference is insufficient, report the missing
|
||||
reference instead of using `generate_cad` as an exploration tool.
|
||||
|
||||
## Decision Rule
|
||||
|
||||
Choose build123d when the full requested part is a custom feature model and no
|
||||
SimpleCADAPI standard factory directly represents the primary object. Do not
|
||||
route to another backend merely because a feature name contains a standard part
|
||||
word. For example, bolt holes or nut clearances in a flange remain features of
|
||||
the flange, not a request to generate a bolt or nut.
|
||||
@@ -16,6 +16,78 @@
|
||||
"simplecadapi_brep_compare"
|
||||
]
|
||||
},
|
||||
"routing_catalog": {
|
||||
"selection_rule": "For a new model, choose SimpleCADAPI only when the requested primary object matches one catalog entry. Feature-context matches are excluded. Every other request chooses build123d.",
|
||||
"source": "SimpleCADAPI/src/simplecadapi/std/{gear,bearing,chain,fastener}.py",
|
||||
"simplecadapi_standard_part_types": [
|
||||
{
|
||||
"type": "spur_gear",
|
||||
"factory": "simplecadapi.std.gear.make_spur_gear_rsolid",
|
||||
"aliases": ["齿轮", "直齿轮", "正齿轮", "spur gear"],
|
||||
"feature_context_exclusions": ["齿轮孔", "齿轮安装", "齿轮座", "齿轮箱", "gear hole", "gear mount", "gear housing", "gearbox"]
|
||||
},
|
||||
{
|
||||
"type": "straight_bevel_gear",
|
||||
"factory": "simplecadapi.std.gear.make_straight_bevel_gear_rsolid",
|
||||
"aliases": ["直齿锥齿轮", "锥齿轮", "straight bevel gear", "bevel gear"],
|
||||
"feature_context_exclusions": ["锥齿轮箱", "bevel gearbox", "锥齿轮座"]
|
||||
},
|
||||
{
|
||||
"type": "helical_gear",
|
||||
"factory": "simplecadapi.std.gear.make_helical_gear_rsolid",
|
||||
"aliases": ["斜齿轮", "helical gear"],
|
||||
"feature_context_exclusions": ["斜齿轮箱", "helical gearbox", "斜齿轮座"]
|
||||
},
|
||||
{
|
||||
"type": "herringbone_gear",
|
||||
"factory": "simplecadapi.std.gear.make_herringbone_gear_rsolid",
|
||||
"aliases": ["人字齿轮", "herringbone gear"],
|
||||
"feature_context_exclusions": ["人字齿轮箱", "herringbone gearbox", "人字齿轮座"]
|
||||
},
|
||||
{
|
||||
"type": "ring_gear",
|
||||
"factory": "simplecadapi.std.gear.make_spur_ring_gear_rsolid",
|
||||
"aliases": ["内齿圈", "齿圈", "ring gear"],
|
||||
"feature_context_exclusions": ["齿圈座", "ring gear housing", "ring gear mount"]
|
||||
},
|
||||
{
|
||||
"type": "gear_rack",
|
||||
"factory": "simplecadapi.std.gear.make_spur_rack_rsolid",
|
||||
"aliases": ["齿条", "直齿条", "斜齿条", "人字齿条", "gear rack", "rack gear", "spur rack", "helical rack", "herringbone rack"],
|
||||
"feature_context_exclusions": ["齿条安装", "齿条座", "rack mount", "rack housing"]
|
||||
},
|
||||
{
|
||||
"type": "cycloidal_disc",
|
||||
"factory": "simplecadapi.std.gear.make_cycloidal_disc_rsolid",
|
||||
"aliases": ["摆线盘", "摆线轮", "cycloidal disc"],
|
||||
"feature_context_exclusions": ["摆线减速器", "cycloidal reducer", "摆线针轮减速器"]
|
||||
},
|
||||
{
|
||||
"type": "ball_bearing",
|
||||
"factory": "simplecadapi.std.bearing.make_ball_bearing_rassembly",
|
||||
"aliases": ["轴承", "滚动轴承", "球轴承", "深沟球轴承", "ball bearing"],
|
||||
"feature_context_exclusions": ["轴承座", "轴承壳", "轴承孔", "轴承安装", "bearing housing", "bearing seat", "bearing hole", "bearing mount"]
|
||||
},
|
||||
{
|
||||
"type": "roller_chain_sprocket",
|
||||
"factory": "simplecadapi.std.chain.make_roller_chain_sprocket_rsolid",
|
||||
"aliases": ["滚子链轮", "链轮", "roller chain sprocket", "sprocket"],
|
||||
"feature_context_exclusions": ["链轮座", "链轮箱", "链轮安装", "sprocket housing", "sprocket mount"]
|
||||
},
|
||||
{
|
||||
"type": "bolt",
|
||||
"factory": "simplecadapi.std.fastener.make_bolt_rsolid",
|
||||
"aliases": ["螺栓", "六角螺栓", "内六角螺栓", "螺栓本体", "bolt", "hex bolt", "socket head bolt"],
|
||||
"feature_context_exclusions": ["螺栓孔", "螺栓圆", "螺栓阵列", "bolt hole", "bolt circle", "bolt pattern"]
|
||||
},
|
||||
{
|
||||
"type": "nut",
|
||||
"factory": "simplecadapi.std.fastener.make_nut_rsolid",
|
||||
"aliases": ["螺母", "六角螺母", "螺母本体", "nut", "hex nut"],
|
||||
"feature_context_exclusions": ["螺母槽", "螺母孔", "螺母避让", "nut pocket", "nut hole", "nut clearance"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"backends": {
|
||||
"build123d": {
|
||||
"project": "text-to-cad",
|
||||
|
||||
@@ -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