已合并
docs: 新增 ES 构图 API 特性分析文档 #4684
GengChao创建于 9月2日
docs: 新增 ES 构图 API 特性分析文档 #4684
已合并
GengChao创建于 9月2日
共 2 个文件变更+732-0
@@ -0,0 +1,366 @@
1+# ES (Eager Style) Graph Building API Feature Analysis
2+ 
3+## 1. Feature Background
4+ 
5+### 1.1 Why ES Is Needed
6+ 
7+In the traditional graph building system of GE (Graph Engine), users build computation graphs through the **Operator series interfaces** (`op::Data`, `op::Add`, and the `set_input_x`/`set_attr_x` series) in a "nodes-and-edges separated" manner: first instantiate Operator objects based on operator prototypes (IR), then set inputs, outputs, and attributes one by one, and finally assemble them into a `ge::Graph`. This approach is flexible and powerful, serving as the foundational capability for comprehensive scenarios such as graph modification and graph traversal, but it has obvious pain points as a pure graph building interface:
8+ 
9+- **Cumbersome to use**: requires awareness of the complete prototype definition and item-by-item setting, resulting in large code volume;
10+- **Errors hard to detect**: connection and attribute errors are often deferred until the graph compilation stage;
11+- **No compatibility guarantee**: C++ interfaces have no ABI compatibility commitment, nor forward/backward compatibility design.
12+ 
13+Mainstream graph building interfaces in the industry (PyTorch Eager, etc.) generally adopt a **functional style** — expressing edge connections between nodes directly through function calls, which is simple and exposes errors at compile time. GE therefore introduced the **ES (Eager Style) graph building API**: a set of **functional-style graph building interfaces** with syntax similar to PyTorch Eager scripts, whose core philosophy is **expressing edge connections and IR information passing directly through function calls**. For the definition and positioning of ES, see [ES_definition.md](../../../zh/user_guides/graph_dev/es_graph/ES_definition.md) (Chinese).
14+ 
15+### 1.2 Design Philosophy: Faithful to IR, Generated Rather Than Handwritten
16+ 
17+The core idea of ES can be summarized in two sentences — **the API is a faithful mapping of IR; the implementation is generated rather than handwritten**.
18+ 
19+- Each IR prototype maps to a graph building function with the same name: function parameters correspond to operator inputs and attributes in order, and return values correspond to outputs; the function body completes node creation and edge connection based on `EsGraphBuilder`, reusing GE's existing `Graph`/`GNode` data structures without introducing a second graph model;
20+- All operator-level graph building APIs are automatically generated by the code generator `gen_esb` reading operator prototypes, avoiding handwritten interface wrappers for thousands of operators, ensuring consistency and maintainability from the source.
21+ 
22+Direct benefits of this design:
23+ 
24+- Users build graphs in an intuitive way like `add = data0 + data1`, with usability capabilities such as operator overloading and direct value passing;
25+- When IR changes, only regeneration is needed, no manual synchronization;
26+- Multi-language (C / C++ / Python) capabilities are naturally consistent.
27+ 
28+### 1.3 Design Goals
29+ 
30+| Goal | Description |
31+|------|------|
32+| Ease of use | Functional style, significantly less code than the nodes-and-edges separated manner, with certain foolproofing capabilities |
33+| Multi-language | Native support for C, C++, and Python with consistent style |
34+| Auto-generation | Automatic CodeGen based on operator prototypes + minimal manual annotation, released with the operator package (OPP) |
35+| API/ABI compatibility | After upgrading/downgrading CANN versions, APPs not using new capabilities can compile and run without code changes |
36+| IR semantic compatibility | When the graph building version differs from the runtime environment IR version, GE automatically adapts the semantics |
37+ 
38+---
39+ 
40+## 2. Historical Background and Constraint Scenarios
41+ 
42+ES is not an independent interface set that can evolve freely: the component that interacts most closely with it is the **operator component** (operator repository / operator package OPP), and several key design decisions come from **implicit constraints** of cross-component collaboration. Understanding these constraints is essential to understanding ES's evolution boundaries, which scenarios should not use ES, and the adaptation scope when IR changes.
43+ 
44+### 2.1 Existing Public APIs Cannot Be Modified
45+ 
46+Once ES's external public APIs are released, they **cannot be modified**, for two reasons:
47+ 
48+- **External attribute**: ES APIs are externally released external interfaces, and external users (APPs, passes in operator repositories, etc.) will directly depend on existing symbols and signatures;
49+- **Independent upgrade/downgrade of operator packages**: operator packages and GE packages support independent upgrade and downgrade. If ES modifies existing APIs, the operator package will not find matching ES symbols after upgrade/downgrade, directly rendering the operator package unusable.
50+ 
51+### 2.2 New APIs Must Satisfy the Compatibility Cycle
52+ 
53+ES **can add** new APIs, but calling new APIs on the operator repository side must satisfy the compatibility cycle requirement — generally, calls are allowed only **one year after** the API is released. The reason is again the independent upgrade/downgrade of operator packages: a new operator package must be able to run with old GE packages within the compatibility window, where the symbols corresponding to the new APIs do not yet exist; after the one-year window, old GE packages without those symbols exit the support scope, and the new APIs can then be safely called by operator repositories.
54+ 
55+This constraint is consistent with Chapter 7's "CANN compatibility requirement: backward compatible for one year and forward compatible for one year after commercial interface release", and is a concrete manifestation of the overall compatibility requirement in the ES ↔ operator component collaboration.
56+ 
57+### 2.3 CompliantNodeBuilder v1/v2 Interfaces and the codegen ABI Constraint
58+ 
59+The `compliant_node_builder.h` where `CompliantNodeBuilder` resides contains two sets of IR definition interfaces:
60+ 
61+- **v1 interfaces** (`IrInputDef` / `IrOutputDef` / `IrAttrDef`): legacy interfaces whose structs contain `std::string` fields and are **not ABI-safe**;
62+- **v2 interfaces** (`IrInputDefV2` / `IrOutputDefV2` / `IrAttrDefV2`): subsequently provided **ABI-safe** interfaces that express strings via `char_t*`, avoiding the problem of `std::string` layout varying with compiler ABI configurations.
63+ 
64+The code generated by ES codegen currently still calls the **v1 interfaces**, which is also due to the independent upgrade/downgrade constraint: if the newly generated operator package ES APIs depended on v2, they would fail to find symbols when running with old GE packages within the compatibility window (which do not yet contain v2 definitions). Currently, the ABI risk of v1 interfaces is mitigated by **unifying the ABI standard between internal GE and the operator component** (e.g., `_GLIBCXX_USE_CXX11_ABI`, which `generate_es_package.cmake` sets fixedly); once the v2 interfaces have been released for the full one-year compatibility window, codegen can switch to calling the v2 series interfaces.
65+ 
66+Since codegen **does not distinguish between external user calls and internal operator calls**, the derived constraint for external users is: if using the ES graph building interfaces built into operator packages (linking `libes_<module>.so`), the user's compilation target must maintain the same ABI standard as the CANN internals; otherwise, undefined behavior may occur due to inconsistent `std::string` layouts.
67+ 
68+### 2.4 Scenario Boundary: ES Is Positioned for Graph Building, Not Graph Modification
69+ 
70+- ES APIs are currently released in operator packages (OPP) and **positioned for graph building**. Typical users are the new Pattern matching Pass mechanism (the `PatternFusionPass` system): constructing matching template graphs in pattern definitions and replacement graphs in `Replacement`;
71+- ES **does not handle graph modification**. Graph modification scenarios (the new Pass mechanism other than "graph match-and-replace", or legacy Passes directly traversing and modifying graphs) should not use ES; continue using nodes-and-edges separated interfaces such as `GNode`. The two approaches interoperate via the `GetProducer` bridge (see 3.2).
72+ 
73+### 2.5 ES API Count Depends on the Operator Package and Handling of Missing APIs
74+ 
75+The number of ES operator-level APIs depends on the number of operators supported in the corresponding operator package — an ES API exists only if the prototype exists in the operator package. When a missing ES API is found:
76+ 
77+- **General path**: push the open-source operator repository to fill in the missing APIs (regenerated with the package after the prototype is completed);
78+- **Reasonable absence scenario**: when a Pattern Pass "matches old nodes and replaces them with new nodes", the new operators introduced by the replacement exist only in the compile-time replacement graph and **do not exist at execution time**; in this case it is reasonable for the operator package not to provide the operator prototype and ES API. However, constructing the replacement graph does require that ES API. The **recommended approach** is:
79+ 1. Use `add_es_library` provided by `generate_es_package.cmake`, passing a self-constructed temporary prototype so via `OPP_PROTO_TARGET` to generate ES APIs for that (batch of) operator(s);
80+ 2. **Statically link** the ES API artifacts into the pass target;
81+ 3. The operator package only packages the pass target, **without packaging** the corresponding header files, py files, and other ES artifacts.
82+ 
83+ For specific usage, see [generate_es_package_cmake_readme.md](../../user_guides/es_graph/tools/generate_es_package_cmake_readme.md); for engineering practice, refer to the UT project in the GE repository that depends on `es_ut` interfaces: `tests/ge/ut/ge/graph/eager_style_graph_builder/graph_construction_test` (which generates `es_ut_test` from the `stub_geir_ops` temporary prototype library).
84+ 
85+### 2.6 Evolving: required-to-optional Compatibility Judgment Pending Adaptation
86+ 
87+When ES historical prototypes generate C++ overloaded interfaces, they validate whether prototype changes are compatible (`AnalyzeDiff` of `overload_planner`, see 6.3). Currently, changes recognized as compatible are limited to **adding optional inputs and adding optional attributes with default values**, with the required flag required to remain strictly identical across the version chain. Operators now frequently propose that "**required-to-optional**" (required → optional) should also be treated as a compatible scenario. If this request is finally approved through review, ES needs to adapt two places:
88+ 
89+- The compatibility judgment and overload planning of `overload_planner`: relax the comparison of required flags in `DiffInputs`/`DiffAttrs`, and synchronously adjust the merge safety and baseline generation strategies (already marked with `TTODO(es_compat)` in the code);
90+- The judgment logic in IR semantic compatibility handling `ir_definitions_recover.cc` (see 6.5): the forward/backward compatibility judgments and the fill-in/removal rules need to cover the required → optional scenario.
91+ 
92+---
93+ 
94+## 3. User Use Scenarios
95+ 
96+### 3.1 Typical Use Scenarios
97+ 
98+| Scenario | Description |
99+|------|------|
100+| **Pure graph building (functional recommended)** | Offline modeling, quickly building verification graphs, Transformer-scale large graph building |
101+| **Python full workflow** | Build graphs in Python then compile and execute directly via `ge.Session`, forming a closed loop |
102+| **C/C++ lightweight integration** | C applications build graphs through opaque pointer interfaces; C++ applications enjoy operator overloading and RAII |
103+| **Custom operator engineering** | Custom operators (including AscendC) reuse the ES generator to generate graph building APIs for their own operators |
104+| **Graph modification/traversal** | Comprehensive scenarios still recommend the Operator series nodes-and-edges separated interfaces; the two interoperate via bridges such as `GetProducer` |
105+ 
106+### 3.2 Relationship with Traditional Graph Building Entries
107+ 
108+```mermaid
109+flowchart TB
110+ subgraph APP["User APP"]
111+ direction LR
112+ ES["ES functional graph building (recommended)<br/>C / C++ / Python<br/>EsGraphBuilder / GraphBuilder(Python)"]
113+ OP["Operator nodes-and-edges separated building (comprehensive scenarios)<br/>C++<br/>op::Data / op::Add / Graph"]
114+ end
115+ ES --> GRAPH["ge::Graph (reusing the same graph model)"]
116+ OP --> GRAPH
117+ GRAPH --> GERT["GE compilation/execution<br/>Session / offline compile"]
118+```
119+ 
120+Both ES and Operator series interfaces produce `ge::Graph`; users can freely choose by scenario or mix them: during ES graph building, `EsTensorHolder::GetProducer` can retrieve the `GNode` and switch to the nodes-and-edges separated manner to continue modifying the graph.
121+ 
122+Multi-language samples cover control edges, control operators, dynamic inputs/outputs, optional inputs, common/private attributes, operator overloading, Transformer, collective communication (HCCL), and other scenarios; see [examples/es](../../../../examples/es/README.md). For samples of custom-generating ES APIs for your own operators, see [examples/custom_es_api](../../../../examples/custom_es_api/README.md).
123+ 
124+---
125+ 
126+## 4. External Interfaces
127+ 
128+### 4.1 Interface Layering
129+ 
130+ES interfaces are divided into two layers: the **base data structure layer** (`EsGraphBuilder`, `EsTensorHolder`, etc., delivered with the GE package) and the **generated operator graph building API layer** (`Add`, `MatMul`, etc., delivered with the OPP package).
131+ 
132+### 4.2 C Interfaces (Base Structures)
133+ 
134+Defined in `inc/external/ge/eager_style_graph_builder/c/esb_funcs.h`, implemented in `compiler/graph/eager_style_graph_builder/es_base_struct/esb_funcs.cc`. Core interfaces:
135+ 
136+```
137+EsCreateGraphBuilder(name) → Create graph builder EsCGraphBuilder
138+EsDestroyGraphBuilder(builder) → Destroy the builder and all process resources it manages
139+EsCreateGraphInput(builder, index) → Add the index-th graph input (Data node)
140+EsCreateConstInt64/Int32/UInt64/UInt32/Float → Create constant nodes of the specified dtype
141+EsCreateVectorXxx / EsCreateScalarXxx → Create vector / scalar constants
142+EsCreateVariable(builder, index, name) → Create a variable node
143+EsCreateEsCTensor / EsCreateEsCTensorFromFile → Create carriers for Tensor-type attributes
144+EsBuildGraphAndReset(builder) → Finalize the graph, producing EsCGraph
145+EsSetOutput(tensor, index) → Set graph output
146+EsAddControlEdge(dest, srcs, num) → Set control edges
147+EsSetInt64/String/BoolAttrForGraph|Tensor|Node → Set graph-level/tensor-level/node-level private attributes
148+```
149+ 
150+C interface objects (`EsCGraphBuilder`, `EsCTensorHolder`, `EsCGraph`) are all **opaque pointers**, exposed only through forward declarations; their internal structures are invisible and can only be manipulated through ES interfaces — this is the foundation of C-side ABI stability.
151+ 
152+### 4.3 C++ Interfaces (Base Structures)
153+ 
154+Defined in the `inc/external/ge/eager_style_graph_builder/cpp/` directory:
155+ 
156+- `es_graph_builder.h`: `EsGraphBuilder`, a graph building helper class providing `CreateInputs<N>`, `CreateScalar`, `SetOutput`, `BuildAndReset`, etc.;
157+- `es_tensor_holder.h`: `EsTensorHolder`, a lightweight holder of operator outputs/graph nodes, providing `GetProducer` (retrieve `GNode`), `AddControlEdge`, `SetAttr`/`SetAttrForNode`;
158+- `es_tensor_like.h`: `EsTensorLike`, a numeric input wrapper class that normalizes scalars/vectors into `EsTensorHolder` (automatically creating Const nodes);
159+- `compliant_node_builder.h`: `CompliantNodeBuilder`, a compatibility node builder (see 6.4 for details).
160+ 
161+The C++ layer is implemented as **pure header files + forced inlining (FORCE_INLINE)**, directly calling the underlying stable C interfaces, with no independent symbol dependencies at link time.
162+ 
163+### 4.4 Python Interfaces (Base Structures)
164+ 
165+Defined in the `api/python/ge/ge/es/` directory:
166+ 
167+- `graph_builder.py`: `GraphBuilder`, providing `create_input(s)`, `create_const_*`, `create_scalar_*`, `create_variable`, `set_graph_output`, `build_and_reset`, as well as `set_graph_attr_*` / `set_tensor_attr_*` / `set_node_attr_*`;
168+- `tensor_holder.py`: `TensorHolder`, supporting `+`/`-`/`*`/`/` operator overloading and control dependency setup;
169+- `tensor_like.py`: direct value passing support (normalization of scalars and nested lists);
170+- `_plugin_loader.py`: plugin loading based on `ge.es.plugins` entry points (see 6.6).
171+ 
172+### 4.5 Generated Operator Graph Building APIs
173+ 
174+Each IR prototype maps as follows (see the mapping chapter of [ES_definition.md](../../../zh/user_guides/graph_dev/es_graph/ES_definition.md) for details):
175+ 
176+- **Function name**: the IR type name (`Foo` for C++/Python, `EsFoo` for C);
177+- **Parameters**: inputs (`EsTensorHolder`/`EsTensorLike`) followed by attributes (C++ expresses optional attributes via default parameters; Python expresses them via keyword arguments);
178+- **Return values**: single output returns a `TensorHolder`; multiple outputs return a struct with members named after the outputs;
179+- **Dynamic inputs/outputs**: expressed as pointer arrays/`vector` + count; the number of dynamic outputs is explicitly specified by the user or obtained through registered derivation rules;
180+- **Artifact splitting**: one `es_Foo.h` / `es_Foo_c.h` / `es_Foo.py` per operator, plus `es_all_ops.h` / `es_all_ops_c.h` / `es_all` aggregate entry points.
181+ 
182+### 4.6 Resource Management Constraints
183+ 
184+- All intermediate resources during graph building (`EsCTensorHolder`, dynamic outputs, passed-in Tensor attributes and subgraphs) are uniformly held by `EsCGraphBuilder` and released when the builder is destroyed; users only need to manage two objects: `EsCGraphBuilder*` and the final `EsCGraph*`;
185+- `Tensor`-type attributes and subgraphs **transfer ownership** to the builder after being passed in and cannot be operated on afterwards;
186+- The builder exists only during the graph building phase: after `BuildAndReset`, the internal state is encapsulated into a returned `Graph`, and the builder and process resources are released.
187+ 
188+---
189+ 
190+## 5. Overall Architecture
191+ 
192+### 5.1 Three Major Components
193+ 
194+```mermaid
195+flowchart TB
196+ APP["User APP<br/>C: EsFoo(...) / C++: es::Foo(...) / Python: Foo(...)"]
197+ GEN["Generated ES graph building APIs (owned by the OPP package, generated at build time, not entering the source repository)<br/>es_&lt;op&gt;.h / es_&lt;op&gt;_c.h / es_&lt;op&gt;.py + aggregate headers / aggregate Python package"]
198+ BASE["ES base data structures (owned by the GE package)<br/>EsGraphBuilder / EsTensorHolder / EsTensorLike / CompliantNodeBuilder<br/>(C implementation es_base_struct as the core, C++ / Python as syntactic wrappers)"]
199+ TOOL["ES Code Generator (owned by the GE package)<br/>gen_esb: read prototypes + history registry → generate C/C++/Python graph building APIs"]
200+ GERT["GE graph compilation and execution<br/>Operator series C++ interfaces / Session / offline compile / IR semantic compatibility"]
201+ APP -->|calls| GEN
202+ GEN -->|based on| BASE
203+ BASE -->|produces ge::Graph| GERT
204+ TOOL -.->|generates| GEN
205+```
206+ 
207+### 5.2 Data Flow Overview
208+ 
209+```mermaid
210+flowchart LR
211+ A[IR prototype definition REG_OP] --> B[gen_esb code generator]
212+ H[History registry] --> B
213+ B --> C[Generated ES API<br/>headers/so/wheel]
214+ C --> D[User APP graph building]
215+ D --> E[User direct-built graph ge::Graph]
216+ E --> F{Runtime OPP version<br/>same as building version?}
217+ F -- Yes --> G[GE compilation and execution]
218+ F -- No --> S[IR semantic compatibility handling<br/>ir_definitions_recover]
219+ S -- adaptable --> G
220+ S -- not adaptable --> X[Error exit]
221+```
222+ 
223+Building a usable GE graph consists of two phases overall:
224+ 
225+1. **Graph building phase**: the user APP builds the graph depending on the OPP version of the build environment, obtaining the "user direct-built graph";
226+2. **IR semantic compatibility handling phase**: when the runtime environment OPP version differs from the building version, GE parses the direct-built graph semantics based on the IR version at building time and attempts to adjust it into a "compatible graph" that conforms to the runtime environment capabilities; if the conversion cannot be completed, an error is returned and the process terminates.
227+ 
228+### 5.3 Layered Wrapping Strategy
229+ 
230+`es` takes the **C language implementation as the core**, wrapped upward into C++ and Python:
231+ 
232+- Shared capabilities (compatibility guarantee, common graph building functions, resource management) are concentrated in the C Builder layer (`es_base_struct`);
233+- The C++ layer is a pure header-file syntactic wrapper (RAII, overloading, default parameters, operator overloading);
234+- The Python layer wraps the C dynamic library via ctypes, introducing no additional dependencies;
235+- The wrapping of the same C core by three languages guarantees **consistent multi-language behavior** and **single-point implementation maintenance**.
236+ 
237+---
238+ 
239+## 6. Core Implementation
240+ 
241+### 6.1 es_base_struct: C Core and Lifecycle Management
242+ 
243+Implemented in `compiler/graph/eager_style_graph_builder/es_base_struct/`, the single-point core of the entire ES:
244+ 
245+- `es_c_graph_builder.cc` (`EsCGraphBuilder`): internally holds `graph_` (`ge::Graph`) and `resource_holder_` (`std::list<std::unique_ptr<ResourceHolder>>`). A `ResourceHolder` consists of a resource pointer and a custom deleter, uniformly managing the `EsCTensorHolder` returned by operator interfaces, dynamic outputs, and user-passed Tensor attributes and subgraphs, released together when the builder is destroyed;
246+- `es_c_tensor_holder.cc` (`EsCTensorHolder`): only holds a reference to its owning builder, `producer_` (`ge::GNode`), and the output index, owning no resources;
247+- `es_tensor_like.cc` (`EsTensorLike` implementation): resolves the owning builder from input parameters and normalizes numeric values into constant nodes;
248+- `esb_funcs.cc`: C function entry points, responsible for parameter validation and error code conversion.
249+ 
250+The generated code of graph building APIs calls `CompliantNodeBuilder` to complete Operator instantiation and edge connection (reusing existing interfaces such as `Graph::AddEdge`), without creating a separate edge connection mechanism.
251+ 
252+### 6.2 es_generator: gen_esb Code Generator
253+ 
254+Implemented in `compiler/graph/eager_style_graph_builder/es_generator/`, with tool entry `main.cc` and the generation flow orchestrated by `gen_esb.cc`:
255+ 
256+1. **Prototype collection**: `GeIrCollector::CollectAndCreateAllOps` loads all prototypes from the operator package pointed to by `ASCEND_OPP_PATH` and sorts them by type;
257+2. **Generator management**: `GeneratorManager` uniformly schedules four types of generators — `CGenerator` (C interfaces and implementations), `CppGenerator` (C++ inline wrappers and overload versions), `PyGenerator` (Python modules), and `UtilsGenerator` (common auxiliary code);
258+3. **Compatibility input**: `CppGenerator` additionally consumes the "history registry", generating function signatures and overload sets that satisfy compatibility requirements based on the differences between the current and historical versions;
259+4. **Artifact output**: generates header/source files and aggregate files at operator granularity.
260+ 
261+The tool supports two modes: **code generation mode** (generating three-language graph building APIs) and **history registry generation mode** (registering current-version prototypes into the history registry). For usage, see [gen_esb.md](../../user_guides/es_graph/tools/gen_esb.md).
262+ 
263+### 6.3 History Registry and C++ Overload Planning
264+ 
265+The `es_generator/history/` subdirectory implements the core mechanism of compatibility-aware generation (see [history_op_registry_protocol.md](../modules/es_graph/design/history_op_registry_protocol.md) for the protocol):
266+ 
267+- `history_registry_reader/writer/interface`: structured reading and writing of the history registry, recording all historical prototype definitions by commercial release version;
268+- `ir_proto_codec`: encoding and decoding of prototype information;
269+- `overload_planner`: compares current and historical IR to plan the set of C++ overload versions to generate — overloads exist for **compatibility guarantee** rather than calling convenience (adding multiple optional inputs simultaneously introduces only one new overload version);
270+- `ambiguity_checker`: validates that the generated overload set is free of ambiguity;
271+- `default_value_policy` / `attr_type_traits`: default value (including floating-point tolerance) and attribute type handling policies.
272+ 
273+Build integration is completed by `generate_es_package.cmake`, adopting a **single-file mode**: clean the directory → invoke gen_esb to generate → aggregate into `all_in_one.cpp` → compile once into `libes_<module>.so` and simultaneously build the Python wheel, avoiding multi-file management and race condition issues.
274+ 
275+### 6.4 CompliantNodeBuilder: Optionality and Tolerance Determination
276+ 
277+`CompliantNodeBuilder` (`compliant_node_builder.h/.cc`) serves as the determination basis for compatibility semantics:
278+ 
279+- **"Configured or not" determination for optional attributes**: if the passed-in value equals the IR default value, it is regarded as unconfigured (this state is for subsequent semantic compatibility flows);
280+- **Floating-point tolerance comparison**: floating-point attributes use an absolute error tolerance (approximately `1e-5`) to determine equality with the default value, avoiding "configured" misjudgments caused by floating-point representation errors;
281+- Provides template utilities such as `CreateFrom` and `ValuesEqual` that uniformly convert arbitrary types into `AttrValue` and perform equality comparison.
282+ 
283+This header simultaneously provides two sets of IR definition interfaces: v1 (not ABI-safe) and v2 (ABI-safe); codegen currently still generates v1 calls. See 2.3 for the evolution background and constraints.
284+ 
285+### 6.5 IR Semantic Compatibility Handling
286+ 
287+When the graph building version of a statically linked APP differs from the runtime environment IR version, compatibility handling is undertaken by the GE compilation chain, with the core implementation in `graph_metadef/graph/ir/ir_definitions_recover.cc` (`RecoverIrUtils`):
288+ 
289+- Compatibility is determined solely from the **structural differences** between the two versions' IR definitions, without awareness of version numbers or compatibility direction;
290+- Supported compatible changes are limited to: adding optional inputs, adding optional attributes with default values, and adding supported data types;
291+- Processing rules: if the direct-built graph uses capabilities not present in the compatible graph → error exit; if new capabilities are unused → remove redundant attributes/inputs or fill in default values;
292+- If the direct-built graph IR has both additions and removals relative to the compatible graph, an incompatible modification has occurred, and processing terminates directly.
293+ 
294+For the complete rule table and scenario analysis, see the "IR Semantic Compatibility Design" chapter of [architecture_design.md](../modules/es_graph/design/architecture_design.md).
295+ 
296+### 6.6 Python Wrapping and Plugin Mechanism
297+ 
298+- **ctypes wrapping**: `api/python/ge/ge/_capi/pyes_graph_builder_wrapper.py` declares function prototypes (argtypes/restype) for `libesb` (the base structure library) and each generated library (such as `libes_math`); `GraphBuilder`/`TensorHolder` only hold handles and forward method calls;
299+- **Ownership inversion**: the Python-layer `TensorHolder` strongly references the `GraphBuilder` (`_builder` field), opposite to the C++ layer's "builder holds Holder" direction, preventing dangling underlying handles after the builder is garbage collected. For design analysis, see [ownership_analysis.md](../modules/es_graph/design/ownership_analysis.md);
300+- **Plugin loading**: `_plugin_loader.py` discovers and loads ES Python packages released by operator sub-packages (such as `es_math`, `es_nn`) via `ge.es.plugins` entry points, dynamically mounting them into the `ge.es` namespace; users `import ge.es` to obtain the graph building functions of all installed operators;
301+- **Scope syntactic sugar**: `GraphBuilder` provides `attr_scope` (node-level private attribute batching) and `control_dependencies` (control dependency scope) context managers, supporting nested stacking.
302+ 
303+---
304+ 
305+## 7. Compatibility Design
306+ 
307+API/ABI forward/backward compatibility is the primary design constraint of ES (CANN compatibility requirement: backward compatible for one year and forward compatible for one year after commercial interface release). Three-language strategy:
308+ 
309+| Dimension | C | C++ | Python |
310+|------|----|----|--------|
311+| Optional attribute expression | Fixed parameter list; default value information not retained (a passed-in value equal to the default is regarded as unconfigured) | Default parameters | Keyword arguments + default values |
312+| Optional input expression | No difference from normal inputs; `nullptr` means unused | Overload versions | Optional positional arguments (`Optional[Tensor]=None`) |
313+| ABI guarantee | **Static linking** of `libesb` series `.a` inlined into the APP, frozen at compile time | Pure header files + FORCE_INLINE calling C, no independent ABI | Dynamic language, naturally free of ABI issues |
314+| Forward compatibility risk | C function signatures are immutable; users are recommended to copy `libesb.a` and headers into the project's `third_party` | Misusing new overloads breaks forward compatibility; `std::nullptr_t` + `[[deprecated]]` overloads provide compile-time foolproofing | Positional/keyword argument rules are naturally compatible with extension |
315+ 
316+For the complete design of the C++ overload mechanism (including the boundaries of the foolproofing mechanism and the rejection analysis of the `esFooV2` version-number scheme), see [es_cxx_compatibility_design.md](../modules/es_graph/design/es_cxx_compatibility_design.md).
317+ 
318+---
319+ 
320+## 8. Key Differences from Traditional Graph Building
321+ 
322+| Dimension | Operator nodes-and-edges separated building | ES functional building |
323+|------|----------------------|--------------|
324+| Style | Explicitly instantiate Operators and set items one by one | Function calls are edge connections, supporting operator overloading and direct value passing |
325+| Error timing | Some errors deferred to graph compilation | Exposed immediately at compile time (C/C++) / call time |
326+| Language | C++ | C, C++, Python |
327+| Interface source | Handwritten maintenance | Automatic CodeGen based on IR |
328+| ABI compatibility | No commitment | C static linking + C++ inlining + Python dynamic loading |
329+| Flexibility | Universal for graph building, modification, and traversal | Oriented toward pure graph building scenarios, interoperating with the traditional manner via `GetProducer` |
330+| Artifact | `ge::Graph` | `ge::Graph` (same graph model) |
331+ 
332+---
333+ 
334+## 9. Key File Index
335+ 
336+| File Path | Responsibility |
337+|---------|------|
338+| `inc/external/ge/eager_style_graph_builder/c/esb_funcs.h` | C external interfaces (builder lifecycle, constant creation, control edges, attribute setting) |
339+| `inc/external/ge/eager_style_graph_builder/cpp/es_graph_builder.h` | `EsGraphBuilder` C++ wrapper |
340+| `inc/external/ge/eager_style_graph_builder/cpp/es_tensor_holder.h` | `EsTensorHolder` C++ wrapper |
341+| `inc/external/ge/eager_style_graph_builder/cpp/es_tensor_like.h` | `EsTensorLike` numeric input wrapper |
342+| `inc/external/ge/eager_style_graph_builder/cpp/compliant_node_builder.h` | `CompliantNodeBuilder` compatibility node building |
343+| `compiler/graph/eager_style_graph_builder/es_base_struct/es_c_graph_builder.cc` | `EsCGraphBuilder` core implementation (unified resource management) |
344+| `compiler/graph/eager_style_graph_builder/es_base_struct/esb_funcs.cc` | C function entry implementation |
345+| `compiler/graph/eager_style_graph_builder/es_generator/main.cc` | gen_esb tool entry |
346+| `compiler/graph/eager_style_graph_builder/es_generator/gen_esb.cc` | Generation flow orchestration (`CreateGenerators`/`CollectAndSortAllOps`) |
347+| `compiler/graph/eager_style_graph_builder/es_generator/ge_ir_collector.h` | Prototype collection (`GeIrCollector`) |
348+| `compiler/graph/eager_style_graph_builder/es_generator/generator_manager.h` | Generator scheduling (`GeneratorManager`) |
349+| `compiler/graph/eager_style_graph_builder/es_generator/c_generator.h` / `cpp_generator.h` / `py_generator.h` | Three-language code generators |
350+| `compiler/graph/eager_style_graph_builder/es_generator/history/history_registry_reader.cc` | History registry reading |
351+| `compiler/graph/eager_style_graph_builder/es_generator/history/overload_planner.cc` | C++ overload version planning |
352+| `graph_metadef/graph/ir/ir_definitions_recover.cc` | IR semantic compatibility handling (`RecoverIrUtils`) |
353+| `api/python/ge/ge/es/graph_builder.py` | Python `GraphBuilder` |
354+| `api/python/ge/ge/es/tensor_holder.py` | Python `TensorHolder` |
355+| `api/python/ge/ge/es/_plugin_loader.py` | ES Python plugin (entry points) loading |
356+| `api/python/ge/ge/_capi/pyes_graph_builder_wrapper.py` | ctypes binding declarations |
357+| `docs/en/user_guides/es_graph/README.md` | ES documentation navigation |
358+| `docs/en/user_guides/es_graph/api/es_cpp.md` | ES C/C++ interface description |
359+| `docs/en/user_guides/es_graph/api/es_python.md` | ES Python interface description |
360+| `docs/en/user_guides/es_graph/tools/gen_esb.md` | gen_esb tool usage guide |
361+| `docs/en/user_guides/es_graph/tools/generate_es_package_cmake_readme.md` | `add_es_library` usage guide (generating ES APIs from temporary prototypes) |
362+| `docs/en/design/modules/es_graph/design/architecture_design.md` | ES architecture design (primary reference for this feature analysis) |
363+| `docs/en/design/modules/es_graph/design/es_cxx_compatibility_design.md` | C++ compatibility dedicated design |
364+| `docs/en/design/modules/es_graph/design/ownership_analysis.md` | Python/C++ ownership relationship analysis |
365+| `examples/es/README.md` | Multi-language graph building sample collection |
366+| `examples/custom_es_api/README.md` | Custom ES API sample |
@@ -0,0 +1,366 @@
1+# ES (Eager Style) 构图 API 特性分析
2+ 
3+## 1. 特性背景
4+ 
5+### 1.1 为什么需要 ES
6+ 
7+在 GE(Graph Engine)的传统构图体系中,用户通过 **Operator 系列接口**(`op::Data`、`op::Add` 及 `set_input_x`/`set_attr_x` 系列)以"点边分离"的方式构建计算图:先基于算子原型(IR)实例化 Operator 对象,再逐一设置输入、输出与属性,最后组装为 `ge::Graph`。该方式灵活、功能强大,是改图、遍历图等综合场景的基础能力,但作为纯构图接口存在明显痛点:
8+ 
9+- **使用繁琐**:需感知原型的完整定义并逐项设置,代码量大;
10+- **犯错不易察觉**:连边、属性错误往往推迟到图编译阶段才暴露;
11+- **无兼容性保障**:C++ 接口无 ABI 兼容承诺,亦无前后向兼容设计。
12+ 
13+业界主流构图接口(PyTorch Eager 等)普遍采用**函数式风格**——通过函数调用直接表达节点间的连边关系,简单且可在编译期暴露错误。GE 由此推出 **ES(Eager Style)构图 API**:一套语法类似 PyTorch Eager 脚本的**函数风格构图接口**,其核心理念是**通过函数调用直接表达连边关系与 IR 信息传递**。关于 ES 的定义与定位,详见 [ES_definition.md](../../user_guides/graph_dev/es_graph/ES_definition.md)。
14+ 
15+### 1.2 设计哲学:忠实于 IR、生成而非手写
16+ 
17+ES 的核心思想可以概括为两句话——**API 是 IR 的忠实映射;实现是生成的而非手写的**。
18+ 
19+- 每个 IR 原型映射为一个同名的构图函数:函数参数依次对应算子输入与属性,返回值对应输出;函数体内部基于 `EsGraphBuilder` 完成节点创建与连边,复用 GE 既有 `Graph`/`GNode` 数据结构,不引入第二套图模型;
20+- 全部算子级构图 API 由代码生成器 `gen_esb` 读取算子原型自动生成,避免手写数千个算子的接口封装,从源头保证一致性与可维护性。
21+ 
22+这种设计带来的直接收益是:
23+ 
24+- 用户以 `add = data0 + data1` 的直觉方式构图,兼具运算符重载、数值直传等易用性能力;
25+- IR 变更时仅需重新生成,无需人工同步;
26+- 多语言(C / C++ / Python)能力天然一致。
27+ 
28+### 1.3 设计目标
29+ 
30+| 目标 | 说明 |
31+|------|------|
32+| 易用 | 函数式风格,代码量显著少于点边分离方式,具备一定防呆能力 |
33+| 多语言 | 原生支持 C、C++、Python 三种语言,风格一致 |
34+| 自动生成 | 基于算子原型 + 少量人工标注自动 CodeGen,随算子包(OPP)发布 |
35+| API/ABI 兼容 | 升级/降级 CANN 版本后,不使用新能力的 APP 无需改代码即可编译、运行 |
36+| IR 语义兼容 | 构图版本与运行环境 IR 版本不一致时,由 GE 自动适配语义 |
37+ 
38+---
39+ 
40+## 2. 历史背景与约束场景
41+ 
42+ES 并非一套可以自由演进的独立接口:与其交互最密切的组件是**算子组件**(算子仓 / 算子包 OPP),多个关键设计决策来自跨组件协同的**隐形约束**。理解这些约束,才能理解 ES 的演进边界、哪些场景不应使用 ES,以及 IR 变更时的适配范围。
43+ 
44+### 2.1 已有 public API 不可修改
45+ 
46+ES 的对外 public API 一经发布即**不可修改**,原因有二:
47+ 
48+- **external 属性**:ES 的 API 是对外发布的 external 接口,外部使用方(APP、算子仓的 pass 等)会直接依赖既有符号与签名;
49+- **算子包独立升降级**:算子包与 GE 包支持独立升降级,若 ES 修改已有 API,升降级后算子包将找不到匹配的 ES 符号,直接导致算子包不可用。
50+ 
51+### 2.2 新增 API 需满足兼容周期
52+ 
53+ES 可以**新增** API,但算子仓侧调用新增 API 需满足兼容周期要求——一般为 API 发布**一年之后**才允许调用。原因同样是算子包独立升降级:新的算子包必须能配合兼容窗口内的老 GE 包运行,而老 GE 包中尚不存在新增 API 对应的符号;一年窗口过后,存量无该符号的老 GE 包退出支持范围,新 API 才可被算子仓安全调用。
54+ 
55+该约束与第 7 章"CANN 兼容性要求:商发接口发布后向后兼容一年、向前兼容一年"一脉相承,是总体兼容性要求在 ES ↔ 算子组件协同上的具体体现。
56+ 
57+### 2.3 CompliantNodeBuilder 的 v1/v2 接口与 codegen 的 ABI 约束
58+ 
59+`CompliantNodeBuilder` 所在的 `compliant_node_builder.h` 中同时存在两套 IR 定义接口:
60+ 
61+- **v1 接口**(`IrInputDef` / `IrOutputDef` / `IrAttrDef`):老接口,结构体中包含 `std::string` 字段,**非 ABI 安全**;
62+- **v2 接口**(`IrInputDefV2` / `IrOutputDefV2` / `IrAttrDefV2`):后续提供的 **ABI 安全** 接口,以 `char_t*` 表达字符串,规避 `std::string` 布局随编译器 ABI 配置差异的问题。
63+ 
64+当前 ES codegen 生成的代码仍调用 **v1 接口**,这同样是独立升降级约束所致:新生成的算子包 ES API 若依赖 v2,配合兼容窗口内的老 GE 包(其中尚无 v2 定义)运行将找不到符号。当前通过**统一内部 GE 与算子组件的 ABI 标准**(如 `_GLIBCXX_USE_CXX11_ABI`,`generate_es_package.cmake` 已固定设置)来规避 v1 接口的 ABI 风险;待 v2 接口发布满一年兼容窗口后,codegen 可切换为调用 v2 系列接口。
65+ 
66+由于 codegen **不区分外部用户调用与内部算子调用**,对外部用户的衍生约束是:若使用算子包内置的 ES 构图接口(链接 `libes_<module>.so`),用户的编译 target 需与 CANN 内部保持相同的 ABI 标准,否则可能因 `std::string` 布局不一致引发未定义行为。
67+ 
68+### 2.4 场景边界:ES 定位构图、不负责改图
69+ 
70+- ES 的 API 目前发布在算子包(OPP)中,**定位是构图**。典型使用者是新的 Pattern 匹配 Pass 机制(`PatternFusionPass` 体系):在 pattern 定义中构造匹配模板图、在 `Replacement` 中构造替换图;
71+- ES **不负责改图**。改图场景(非"图匹配-替换"的新 Pass 机制、老 Pass 直接遍历并修改图)不应使用 ES,应继续使用GNode等点边分离接口;两种方式可经 `GetProducer` 桥梁互通(见 3.2)。
72+ 
73+### 2.5 ES API 数量依赖算子包及缺失 API 的处理
74+ 
75+ES 算子级 API 的数目取决于对应算子包中算子支持的个数——原型在算子包中存在,才有对应的 ES API。发现缺失 ES API 时:
76+ 
77+- **一般路径**:推动算子开源仓补齐缺失的 API(原型补齐后随包重新生成);
78+- **合理缺失场景**:Pattern Pass"匹配老节点、替换为新节点"时,替换引入的新算子只存在于编译期的替换图中,**执行期并不存在**对应的算子,此时算子包不提供该算子原型及 ES API 是合理的。但构造替换图又确需该 ES API,**推荐做法**是:
79+ 1. 使用 `generate_es_package.cmake` 提供的 `add_es_library`,通过 `OPP_PROTO_TARGET` 传入自构造的临时原型 so,为该(批)算子生成 ES API;
80+ 2. 将 ES API 产物**静态链接**到 pass 的 target 上;
81+ 3. 算子包仅打包 pass 的 target,**不打包**对应的头文件、py 文件等 ES 产物。
82+ 
83+ 具体用法参见 [generate_es_package_cmake_readme.md](../../user_guides/es_graph/tools/generate_es_package_cmake_readme.md),工程实践可参考 GE 仓依赖 `es_ut` 接口的 UT 工程 `tests/ge/ut/ge/graph/eager_style_graph_builder/graph_construction_test`(以 `stub_geir_ops` 临时原型库生成 `es_ut_test`)。
84+ 
85+### 2.6 演进中:必选变可选的兼容性判定待适配
86+ 
87+ES 历史原型生成 C++ 重载接口时,会对原型变化是否兼容做校验(`overload_planner` 的 `AnalyzeDiff`,见 6.3)。当前认定兼容的变更限定为**新增可选输入、新增带默认值的可选属性**,且版本链上要求 required 标记严格一致。目前有算子频繁提出"**必选变可选**"(required → optional)也应视为兼容场景。该诉求若最终评审通过,ES 需同步适配两处:
88+ 
89+- `overload_planner` 的兼容性判定与重载规划:放宽 `DiffInputs`/`DiffAttrs` 中 required 标记的比较,并同步调整合并安全性与基线生成策略(代码中已以 `TTODO(es_compat)` 标注);
90+- IR 语义兼容处理 `ir_definitions_recover.cc` 中的判断逻辑(见 6.5):前向/后向兼容的判定与补齐/删除规则需覆盖 required → optional 场景。
91+ 
92+---
93+ 
94+## 3. 用户使用场景
95+ 
96+### 3.1 典型使用场景
97+ 
98+| 场景 | 说明 |
99+|------|------|
100+| **纯构图(推荐函数式)** | 离线建模、快速搭建验证图、Transformer 级大图搭建 |
101+| **Python 全流程** | Python 构图后直接经 `ge.Session` 编译执行,形成闭环 |
102+| **C/C++ 轻量集成** | C 应用通过不透明指针接口构图,C++ 应用享受运算符重载与 RAII |
103+| **自定义算子工程** | 自定义算子(含 AscendC)复用 ES 生成器为自有算子生成构图 API |
104+| **改图/遍历图** | 综合场景仍建议使用 Operator 系列点边分离接口,两者可经 `GetProducer` 等桥梁互通 |
105+ 
106+### 3.2 与传统构图入口的关系
107+ 
108+```mermaid
109+flowchart TB
110+ subgraph APP["用户 APP"]
111+ direction LR
112+ ES["ES 函数式构图(推荐)<br/>C / C++ / Python<br/>EsGraphBuilder / GraphBuilder(Python)"]
113+ OP["Operator 点边分离构图(综合场景)<br/>C++<br/>op::Data / op::Add / Graph"]
114+ end
115+ ES --> GRAPH["ge::Graph(复用同一图模型)"]
116+ OP --> GRAPH
117+ GRAPH --> GERT["GE 编译/执行<br/>Session / offline compile"]
118+```
119+ 
120+ES 与 Operator 系列接口产物均为 `ge::Graph`,用户可按场景自由选择,亦可混合使用:ES 构图过程中可通过 `EsTensorHolder::GetProducer` 取回 `GNode`,切换到点边分离方式继续改图。
121+ 
122+多语言样例覆盖控制边、控制算子、动态输入/输出、可选输入、普通/私有属性、运算符重载、Transformer、集合通信(HCCL)等场景,参见 [examples/es](../../../../examples/es/README.md);为自有算子自定义生成 ES API 的样例参见 [examples/custom_es_api](../../../../examples/custom_es_api/README.md)。
123+ 
124+---
125+ 
126+## 4. 对外接口
127+ 
128+### 4.1 接口分层
129+ 
130+ES 的接口分为两层:**基础数据结构层**(`EsGraphBuilder`、`EsTensorHolder` 等,随 GE 包交付)与**生成的算子构图 API 层**(`Add`、`MatMul` 等,随 OPP 包交付)。
131+ 
132+### 4.2 C 接口(基础结构)
133+ 
134+定义在 `inc/external/ge/eager_style_graph_builder/c/esb_funcs.h`,实现位于 `compiler/graph/eager_style_graph_builder/es_base_struct/esb_funcs.cc`。核心接口:
135+ 
136+```
137+EsCreateGraphBuilder(name) → 创建图构建器 EsCGraphBuilder
138+EsDestroyGraphBuilder(builder) → 销毁构建器及其管理的全部过程资源
139+EsCreateGraphInput(builder, index) → 添加第 index 个图输入(Data 节点)
140+EsCreateConstInt64/Int32/UInt64/UInt32/Float → 创建指定 dtype 的常量节点
141+EsCreateVectorXxx / EsCreateScalarXxx → 创建向量 / 标量常量
142+EsCreateVariable(builder, index, name) → 创建变量节点
143+EsCreateEsCTensor / EsCreateEsCTensorFromFile → 创建 Tensor 类型属性的载体
144+EsBuildGraphAndReset(builder) → 结构图,产出 EsCGraph
145+EsSetOutput(tensor, index) → 设置图输出
146+EsAddControlEdge(dest, srcs, num) → 设置控制边
147+EsSetInt64/String/BoolAttrForGraph|Tensor|Node → 设置图级/张量级/节点级私有属性
148+```
149+ 
150+C 接口对象(`EsCGraphBuilder`、`EsCTensorHolder`、`EsCGraph`)均为**不透明指针**,仅以前置声明暴露,内部结构不可见,只能通过 ES 接口操作——这是 C 侧 ABI 稳定性的基础。
151+ 
152+### 4.3 C++ 接口(基础结构)
153+ 
154+定义在 `inc/external/ge/eager_style_graph_builder/cpp/` 目录:
155+ 
156+- `es_graph_builder.h`:`EsGraphBuilder`,构图辅助类,提供 `CreateInputs<N>`、`CreateScalar`、`SetOutput`、`BuildAndReset` 等方法;
157+- `es_tensor_holder.h`:`EsTensorHolder`,算子输出/图节点的轻量持有者,提供 `GetProducer`(取回 `GNode`)、`AddControlEdge`、`SetAttr`/`SetAttrForNode`;
158+- `es_tensor_like.h`:`EsTensorLike`,数值输入包装类,将标量/向量归一化为 `EsTensorHolder`(自动创建 Const 节点);
159+- `compliant_node_builder.h`:`CompliantNodeBuilder`,兼容性节点构建器(详见 6.4)。
160+ 
161+C++ 层为**纯头文件 + 强制内联(FORCE_INLINE)**实现,直接调用底层稳定 C 接口,链接期无独立符号依赖。
162+ 
163+### 4.4 Python 接口(基础结构)
164+ 
165+定义在 `api/python/ge/ge/es/` 目录:
166+ 
167+- `graph_builder.py`:`GraphBuilder`,提供 `create_input(s)`、`create_const_*`、`create_scalar_*`、`create_variable`、`set_graph_output`、`build_and_reset`,以及 `set_graph_attr_*` / `set_tensor_attr_*` / `set_node_attr_*`;
168+- `tensor_holder.py`:`TensorHolder`,支持 `+`/`-`/`*`/`/` 运算符重载与控制依赖设置;
169+- `tensor_like.py`:数值直传支持(标量与嵌套列表归一化);
170+- `_plugin_loader.py`:基于 `ge.es.plugins` entry points 的插件加载(见 6.6)。
171+ 
172+### 4.5 生成的算子构图 API
173+ 
174+每个 IR 原型映射规则如下(详见 [ES_definition.md](../../user_guides/graph_dev/es_graph/ES_definition.md) 的映射章节):
175+ 
176+- **函数名**:IR 类型名(C++/Python 为 `Foo`,C 为 `EsFoo`);
177+- **参数**:依次为输入(`EsTensorHolder`/`EsTensorLike`)与属性(C++ 以默认参数表达可选属性,Python 以关键字参数表达);
178+- **返回值**:单输出返回 `TensorHolder`,多输出返回与输出名同名的结构体;
179+- **动态输入/输出**:以指针数组/`vector` + 计数表达,动态输出个数由用户显式指定或经注册的推导规则获得;
180+- **产物拆分**:每个算子一个 `es_Foo.h` / `es_Foo_c.h` / `es_Foo.py`,并提供 `es_all_ops.h` / `es_all_ops_c.h` / `es_all` 聚合入口。
181+ 
182+### 4.6 资源管理约束
183+ 
184+- 构图期间所有中间资源(`EsCTensorHolder`、动态输出、传入的 Tensor 属性与子图)由 `EsCGraphBuilder` 统一持有,随构建器销毁释放;用户仅需管理 `EsCGraphBuilder*` 与最终 `EsCGraph*` 两个对象;
185+- `Tensor` 类型属性与子图在传入接口后**所有权转移**给构建器,传入后不可再操作;
186+- 构建器只在构图阶段存在:`BuildAndReset` 后内部状态封装为 `Graph` 返回,构建器及过程资源释放。
187+ 
188+---
189+ 
190+## 5. 整体架构
191+ 
192+### 5.1 三大组件
193+ 
194+```mermaid
195+flowchart TB
196+ APP["用户 APP<br/>C: EsFoo(...) / C++: es::Foo(...) / Python: Foo(...)"]
197+ GEN["Generated ES 构图 API(归属 OPP 包,构建期生成,不入手源码仓)<br/>es_&lt;op&gt;.h / es_&lt;op&gt;_c.h / es_&lt;op&gt;.py + 聚合头文件 / 聚合 Python 包"]
198+ BASE["ES 基础数据结构(归属 GE 包)<br/>EsGraphBuilder / EsTensorHolder / EsTensorLike / CompliantNodeBuilder<br/>(C 实现 es_base_struct 为核心,C++ / Python 为语法封装)"]
199+ TOOL["ES Code Generator(归属 GE 包)<br/>gen_esb:读原型 + 历史原型库 → 生成 C/C++/Python 构图 API"]
200+ GERT["GE 图编译与执行<br/>Operator 系列 C++ 接口 / Session / offline compile / IR 语义兼容"]
201+ APP -->|调用| GEN
202+ GEN -->|基于| BASE
203+ BASE -->|产出 ge::Graph| GERT
204+ TOOL -.->|生成| GEN
205+```
206+ 
207+### 5.2 数据流总览
208+ 
209+```mermaid
210+flowchart LR
211+ A[IR 原型定义 REG_OP] --> B[gen_esb 代码生成器]
212+ H[历史原型库] --> B
213+ B --> C[Generated ES API<br/>头文件/so/wheel]
214+ C --> D[用户 APP 构图]
215+ D --> E[用户直构图 ge::Graph]
216+ E --> F{运行环境 OPP 版本<br/>与构图版本一致?}
217+ F -- 是 --> G[GE 编译执行]
218+ F -- 否 --> S[IR 语义兼容性处理<br/>ir_definitions_recover]
219+ S -- 可适配 --> G
220+ S -- 不可适配 --> X[报错退出]
221+```
222+ 
223+构造可用的 GE 图整体分为两个阶段:
224+ 
225+1. **构图阶段**:用户 APP 依赖构建环境的 OPP 版本完成构图,得到"用户直构图";
226+2. **IR 语义兼容性处理阶段**:当运行环境 OPP 版本与构图版本不一致时,GE 基于构图时 IR 版本解析直构图语义,尝试调整为符合运行环境能力的"兼容图";无法完成转换时返回错误终止。
227+ 
228+### 5.3 分层封装策略
229+ 
230+`es` 以 **C 语言实现为核心**,向上封装为 C++ 与 Python:
231+ 
232+- 共用能力(兼容性保障、公共构图函数、资源管理)集中在 C Builder 层(`es_base_struct`);
233+- C++ 层是纯头文件的语法封装(RAII、重载、默认参数、运算符重载);
234+- Python 层基于 C 动态库以 ctypes 封装,不引入额外依赖;
235+- 三语言对同一 C 核心的封装,保证了**多语言行为一致**与**实现单点维护**。
236+ 
237+---
238+ 
239+## 6. 核心实现
240+ 
241+### 6.1 es_base_struct:C 核心与生命周期管理
242+ 
243+实现位于 `compiler/graph/eager_style_graph_builder/es_base_struct/`,是整套 ES 的单点核心:
244+ 
245+- `es_c_graph_builder.cc`(`EsCGraphBuilder`):内部持有 `graph_`(`ge::Graph`)与 `resource_holder_`(`std::list<std::unique_ptr<ResourceHolder>>`)。`ResourceHolder` 由资源指针与自定义 deleter 组成,统一管理算子接口返回的 `EsCTensorHolder`、动态输出、用户传入的 Tensor 属性与子图,随构建器析构统一释放;
246+- `es_c_tensor_holder.cc`(`EsCTensorHolder`):仅持有对所属构建器的引用、`producer_`(`ge::GNode`)与输出索引,不拥有资源;
247+- `es_tensor_like.cc`(`EsTensorLike` 实现):从输入参数中解析所属构建器,将数值归一化为常量节点;
248+- `esb_funcs.cc`:C 函数入口,负责参数校验与错误码转换。
249+ 
250+构图 API 的生成代码调用 `CompliantNodeBuilder` 完成 Operator 实例化与连边(复用 `Graph::AddEdge` 等既有接口),不另建连边机制。
251+ 
252+### 6.2 es_generator:gen_esb 代码生成器
253+ 
254+实现位于 `compiler/graph/eager_style_graph_builder/es_generator/`,工具入口 `main.cc`,生成流程由 `gen_esb.cc` 编排:
255+ 
256+1. **原型收集**:`GeIrCollector::CollectAndCreateAllOps` 从 `ASCEND_OPP_PATH` 指向的算子包加载全部原型并按类型排序;
257+2. **生成器管理**:`GeneratorManager` 统一调度四类生成器——`CGenerator`(C 接口与实现)、`CppGenerator`(C++ 内联封装与重载版本)、`PyGenerator`(Python 模块)、`UtilsGenerator`(公共辅助代码);
258+3. **兼容性输入**:`CppGenerator` 额外消费"历史原型库",结合当前版本与历史版本差异生成满足兼容性要求的函数签名与重载集合;
259+4. **产物输出**:按算子粒度生成头文件/源文件与聚合文件。
260+ 
261+工具支持两种模式:**代码生成模式**(生成三语言构图 API)与**历史原型库生成模式**(将当前版本原型登记入历史库)。使用方法详见 [gen_esb.md](../../user_guides/es_graph/tools/gen_esb.md)。
262+ 
263+### 6.3 历史原型库与 C++ 重载规划
264+ 
265+`es_generator/history/` 子目录实现了兼容性生成的核心机制(协议说明见 [history_op_registry_protocol.md](../modules/es_graph/design/history_op_registry_protocol.md)):
266+ 
267+- `history_registry_reader/writer/interface`:历史原型库的结构化读写,按商发版本记录全部历史原型定义;
268+- `ir_proto_codec`:原型信息的编解码;
269+- `overload_planner`:对比当前与历史 IR,规划需要生成的 C++ 重载版本集合——重载的目的是**兼容性保障**而非调用便利(同时新增多个可选输入只引入一个新重载版本);
270+- `ambiguity_checker`:校验生成的重载集合无二义性;
271+- `default_value_policy` / `attr_type_traits`:默认值(含浮点容差)与属性类型处理策略。
272+ 
273+构建集成由 `generate_es_package.cmake` 完成,采用**单文件模式**:清理目录 → 调用 gen_esb 生成 → 汇总写入 `all_in_one.cpp` → 一次编译为 `libes_<module>.so` 并同步构建 Python wheel,避免多文件管理与竞态问题。
274+ 
275+### 6.4 CompliantNodeBuilder:可选性与容差判定
276+ 
277+`CompliantNodeBuilder`(`compliant_node_builder.h/.cc`)服务于兼容性语义的判定基础:
278+ 
279+- **可选属性"是否配置"判定**:若传入值与 IR 默认值一致则视为未配置(该状态供后续语义兼容流程使用);
280+- **浮点容差比较**:浮点属性以绝对误差容差(约 `1e-5`)判定是否等于默认值,规避浮点表示误差导致的"已配置"误判;
281+- 提供 `CreateFrom`、`ValuesEqual` 等模板工具,将任意类型统一转换为 `AttrValue` 并完成相等性比较。
282+ 
283+该头文件同时提供 v1(非 ABI 安全)与 v2(ABI 安全)两套 IR 定义接口,codegen 当前仍生成 v1 调用,演进背景与约束见 2.3。
284+ 
285+### 6.5 IR 语义兼容处理
286+ 
287+当静态链接的 APP 构图版本与运行环境 IR 版本不一致时,兼容性处理由 GE 编译链路承接,核心实现在 `graph_metadef/graph/ir/ir_definitions_recover.cc`(`RecoverIrUtils`):
288+ 
289+- 仅依赖两版本 IR 定义间的**结构差异**判定兼容性,无需感知版本号或兼容方向;
290+- 支持的兼容性变更限定为:新增可选输入、新增带默认值的可选属性、新增支持的数据类型;
291+- 处理规则:直构图使用了兼容图中不存在的能力 → 报错退出;未使用新增能力 → 删除多余属性/输入或按默认值补齐;
292+- 若直构图 IR 相对兼容图同时存在新增与缺失项,说明发生过不兼容修改,直接终止。
293+ 
294+完整规则表与场景分析见 [architecture_design.md](../modules/es_graph/design/architecture_design.md) 的"IR 语义兼容设计"章节。
295+ 
296+### 6.6 Python 封装与插件机制
297+ 
298+- **ctypes 封装**:`api/python/ge/ge/_capi/pyes_graph_builder_wrapper.py` 对 `libesb`(基础结构库)与各生成库(如 `libes_math`)声明函数原型(argtypes/restype),`GraphBuilder`/`TensorHolder` 仅做句柄持有与方法转发;
299+- **所有权反转**:Python 层 `TensorHolder` 强引用 `GraphBuilder`(`_builder` 字段),与 C++ 层"构建器持有 Holder"的方向相反,用于防止构建器被 GC 后底层句柄悬空。设计分析见 [ownership_analysis.md](../modules/es_graph/design/ownership_analysis.md);
300+- **插件加载**:`_plugin_loader.py` 通过 `ge.es.plugins` entry points 发现并加载算子分包发布的 ES Python 包(如 `es_math`、`es_nn`),动态挂载到 `ge.es` 命名空间,用户 `import ge.es` 即获得全部已安装算子的构图函数;
301+- **作用域语法糖**:`GraphBuilder` 提供 `attr_scope`(节点级私有属性批处理)与 `control_dependencies`(控制依赖作用域)上下文管理器,支持嵌套叠加。
302+ 
303+---
304+ 
305+## 7. 兼容性设计
306+ 
307+API/ABI 前后向兼容是 ES 的首要设计约束(CANN 兼容性要求:商发接口发布后向后兼容一年、向前兼容一年)。三语言策略:
308+ 
309+| 维度 | C | C++ | Python |
310+|------|----|----|--------|
311+| 可选属性表达 | 固定参数列表,默认值信息不保留(传入值等于默认值视为未配置) | 默认参数 | 关键字参数 + 默认值 |
312+| 可选输入表达 | 与普通输入无差别,`nullptr` 表示未使用 | 重载版本 | 可选位置参数(`Optional[Tensor]=None`) |
313+| ABI 保障 | **静态链接** `libesb` 系列 `.a` 内联进 APP,编译期冻结 | 纯头文件 + FORCE_INLINE 调 C,无独立 ABI | 动态语言,天然无 ABI 问题 |
314+| 前向兼容风险 | C 函数签名不可变,推荐用户将 `libesb.a` 与头文件拷贝至工程 `third_party` | 误用新重载会破坏前向兼容,以 `std::nullptr_t` + `[[deprecated]]` 重载做编译期防呆 | 位置/关键字参数规则天然兼容扩展 |
315+ 
316+C++ 重载机制的完整设计(含防呆机制的边界与 `esFooV2` 版本号方案的否定分析)见 [es_cxx_compatibility_design.md](../modules/es_graph/design/es_cxx_compatibility_design.md)。
317+ 
318+---
319+ 
320+## 8. 与传统构图方式的关键差异
321+ 
322+| 维度 | Operator 点边分离构图 | ES 函数式构图 |
323+|------|----------------------|--------------|
324+| 风格 | 显式实例化 Operator 并逐项设置 | 函数调用即连边,支持运算符重载与数值直传 |
325+| 出错时机 | 部分错误推迟到图编译期 | 编译期(C/C++)/调用期即时暴露 |
326+| 语言 | C++ | C、C++、Python |
327+| 接口来源 | 手写维护 | 基于 IR 自动 CodeGen |
328+| ABI 兼容 | 无承诺 | C 静态链接 + C++ 内联 + Python 动态加载 |
329+| 灵活性 | 构图、改图、遍历图通用 | 面向纯构图场景,经 `GetProducer` 与传统方式互通 |
330+| 产物 | `ge::Graph` | `ge::Graph`(同一图模型) |
331+ 
332+---
333+ 
334+## 9. 关键文件索引
335+ 
336+| 文件路径 | 职责 |
337+|---------|------|
338+| `inc/external/ge/eager_style_graph_builder/c/esb_funcs.h` | C 对外接口(构建器生命周期、常量创建、控制边、属性设置) |
339+| `inc/external/ge/eager_style_graph_builder/cpp/es_graph_builder.h` | `EsGraphBuilder` C++ 封装 |
340+| `inc/external/ge/eager_style_graph_builder/cpp/es_tensor_holder.h` | `EsTensorHolder` C++ 封装 |
341+| `inc/external/ge/eager_style_graph_builder/cpp/es_tensor_like.h` | `EsTensorLike` 数值输入包装 |
342+| `inc/external/ge/eager_style_graph_builder/cpp/compliant_node_builder.h` | `CompliantNodeBuilder` 兼容性节点构建 |
343+| `compiler/graph/eager_style_graph_builder/es_base_struct/es_c_graph_builder.cc` | `EsCGraphBuilder` 核心实现(资源统一管理) |
344+| `compiler/graph/eager_style_graph_builder/es_base_struct/esb_funcs.cc` | C 函数入口实现 |
345+| `compiler/graph/eager_style_graph_builder/es_generator/main.cc` | gen_esb 工具入口 |
346+| `compiler/graph/eager_style_graph_builder/es_generator/gen_esb.cc` | 生成流程编排(`CreateGenerators`/`CollectAndSortAllOps`) |
347+| `compiler/graph/eager_style_graph_builder/es_generator/ge_ir_collector.h` | 原型收集(`GeIrCollector`) |
348+| `compiler/graph/eager_style_graph_builder/es_generator/generator_manager.h` | 生成器调度(`GeneratorManager`) |
349+| `compiler/graph/eager_style_graph_builder/es_generator/c_generator.h` / `cpp_generator.h` / `py_generator.h` | 三语言代码生成器 |
350+| `compiler/graph/eager_style_graph_builder/es_generator/history/history_registry_reader.cc` | 历史原型库读取 |
351+| `compiler/graph/eager_style_graph_builder/es_generator/history/overload_planner.cc` | C++ 重载版本规划 |
352+| `graph_metadef/graph/ir/ir_definitions_recover.cc` | IR 语义兼容处理(`RecoverIrUtils`) |
353+| `api/python/ge/ge/es/graph_builder.py` | Python `GraphBuilder` |
354+| `api/python/ge/ge/es/tensor_holder.py` | Python `TensorHolder` |
355+| `api/python/ge/ge/es/_plugin_loader.py` | ES Python 插件(entry points)加载 |
356+| `api/python/ge/ge/_capi/pyes_graph_builder_wrapper.py` | ctypes 绑定声明 |
357+| `docs/zh/user_guides/es_graph/README.md` | ES 文档导航 |
358+| `docs/zh/user_guides/es_graph/api/es_cpp.md` | ES C/C++ 接口说明 |
359+| `docs/zh/user_guides/es_graph/api/es_python.md` | ES Python 接口说明 |
360+| `docs/zh/user_guides/es_graph/tools/gen_esb.md` | gen_esb 工具使用说明 |
361+| `docs/zh/user_guides/es_graph/tools/generate_es_package_cmake_readme.md` | `add_es_library` 使用指南(临时原型生成 ES API) |
362+| `docs/zh/design/modules/es_graph/design/architecture_design.md` | ES 架构设计(本特性分析的主要参考) |
363+| `docs/zh/design/modules/es_graph/design/es_cxx_compatibility_design.md` | C++ 兼容性专项设计 |
364+| `docs/zh/design/modules/es_graph/design/ownership_analysis.md` | Python/C++ 所有权关系分析 |
365+| `examples/es/README.md` | 多语言构图样例集 |
366+| `examples/custom_es_api/README.md` | 自定义 ES API 样例 |