소스 검색

docs(variable): optimize variable docs by codex (#979)

Yiwei Mao 2 달 전
부모
커밋
b9e558215f

+ 1 - 1
apps/docs/src/en/guide/variable/_meta.json

@@ -1,7 +1,7 @@
 [
   "basic",
-  "concept",
   "variable-output",
   "variable-consume",
+  "concept",
   "custom-scope-chain"
 ]

+ 31 - 7
apps/docs/src/en/guide/variable/basic.mdx

@@ -10,6 +10,12 @@ The VariableEngine in this document belongs to the **Design** of FlowGram, which
 
 :::
 
+## Reading Path
+
+- Build a mental model for “what a variable is” and “why the variable engine matters” here first.
+- Then get hands-on: [Output Variables](./variable-output.mdx) → [Consume Variables](./variable-consume.mdx) so you learn to produce variables before reading them.
+- When questions about scope or types pop up during practice, return to [Core Concepts](./concept.mdx) for detailed terminology.
+
 ## What is a Variable?
 
 Imagine you're building a complex Lego model where each module needs to connect precisely. In the world of Workflows, **variables** play a similar role as "connectors." They are the "messengers" used to pass information between different nodes.
@@ -18,7 +24,7 @@ Simply put, a variable is a named container where you can store various things,
 
 A variable typically consists of three parts:
 
-- **Name (Unique Identifier)**: Like your name, it allows everyone to find this variable accurately. For example, `userName`, `orderId`.
+- **Name (Unique Identifier)**: Similar to a personal name, it lets you pinpoint a specific variable. For example, `userName`, `orderId`.
 - **Value**: The content inside the container. It can be a number `123`, text `"Hello FlowGram!"`, or a switch state `true` / `false`.
 - **Type**: Specifies what kind of things this container can hold. For instance, some can only hold numbers, while others can only hold text.
 
@@ -35,6 +41,12 @@ For example, in an "Intelligent Q&A" flow:
   </div>
 </div>
 
+### Design-Time Definition vs. Runtime Evaluation
+
+In **design time** (while drawing the flow), you only need to determine the **definition** of a variable: its name, type, and optional metadata. The variable engine manages these definitions as structured data.
+
+When entering **runtime**, FlowGram assigns values to each execution based on those definitions. Focus on structure and constraints during design; at runtime every node can rely on the same definitions to read and write data reliably.
+
 ## Why Do You Need a Variable Engine?
 
 As the complexity of workflows increases, so do the number and management difficulty of variables.
@@ -48,7 +60,7 @@ Enabling the Variable Engine will bring you the following core advantages:
 <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "25px" }}>
   <div style={{ gridColumn: "span 2" }}>
     <b>Scope Constraints: Precise Data Access Control</b>
-    <p className="rs-tip">The Variable Engine can precisely control the effective range (i.e., scope) of each variable. Like having specific keys for different rooms, it ensures that variables are only accessed within the intended nodes, effectively preventing data pollution and unexpected logical errors.</p>
+    <p className="rs-tip">The variable engine precisely controls the effective range (scope) of each variable. Like giving every room its own key, it keeps variables accessible only to the intended nodes, preventing data pollution and surprise logic errors.</p>
     <div style={{display: "flex", gap: "25px"}}>
       <div>
         <img loading="lazy" src="/variable/variable-scope-feature-1.png" alt="Scope Constraint Example 1" />
@@ -56,24 +68,30 @@ Enabling the Variable Engine will bring you the following core advantages:
       </div>
       <div>
         <img loading="lazy" src="/variable/variable-scope-feature-2.png" alt="Scope Constraint Example 2" />
-        <p style={{marginTop: '10px'}}>The `LLM` node is in a `Condition` branch, like being in a separate room. The `End` node outside naturally cannot access its `result` variable.</p>
+        <p style={{marginTop: '10px'}}>The `LLM` node lives inside a `Condition` branch—effectively a separate room—so the `End` node outside cannot access its `result` variable.</p>
       </div>
     </div>
   </div>
   <div>
     <b>Variable Structure Insight: Easily Understand Complex Data</b>
-    <p className="rs-tip">When a variable becomes complex (e.g., an object with many levels), the Variable Engine allows you to explore its internal structure layer by layer, like peeling an onion, with all details at your fingertips.</p>
+    <p className="rs-tip">When a variable has a complex structure (for example, a deeply nested object), the variable engine lets you expand it layer by layer so every piece of data stays easy to locate.</p>
     <img loading="lazy" src="/variable/variable-tree-management.gif" alt="Variable Structure Perspective" />
-    <p style={{marginTop: '10px'}}>In this diagram, you can see the output variables of all nodes and their hierarchical relationships, like a lush tree.</p>
+    <p style={{marginTop: '10px'}}>You can review every node’s output variables and their hierarchy at a glance, almost like inspecting a well-organized tree.</p>
   </div>
   <div>
     <b>Automatic Type Inference: A Spark of Genius</b>
-    <p className="rs-tip">You no longer need to tell each variable what its type should be. The Variable Engine, like your "soulmate," will automatically infer its type based on the context.</p>
+    <p className="rs-tip">No need to declare every variable type by hand. The variable engine infers types from context automatically.</p>
     <img loading="lazy" src="/variable/variable-batch-auto-infer.gif" />
-    <p style={{marginTop: '10px'}}>For example, when the type of the `arr` variable in the `Start` node changes, the type of the `item` output by the `Batch` node will also be automatically updated to ensure type consistency.</p>
+    <p style={{marginTop: '10px'}}>For example, if the `Start` node’s `arr` variable changes type, the `Batch` node’s `item` output updates automatically to stay aligned.</p>
   </div>
 </div>
 
+:::tip
+
+Get the read/write flow working first; when you need a deeper understanding of scope chains, ASTs, declarations, or expressions, go back to [Core Concepts](./concept.mdx).
+
+:::
+
 ## How to Enable the Variable Engine?
 
 You can enable the Variable Engine with a simple configuration to experience its powerful features.
@@ -89,3 +107,9 @@ You can enable the Variable Engine with a simple configuration to experience its
   }
 }
 ```
+
+## What to Read Next (Suggested Order)
+
+- [Output Variables](./variable-output.mdx): Learn how to produce variables in nodes, plugins, and global scope.
+- [Consume Variables](./variable-consume.mdx): Then master how to read variables safely in nodes and UI.
+- [Variable Concepts](./concept.mdx): Finally, revisit scope, AST, declarations, types, and other core terms.

+ 70 - 46
apps/docs/src/en/guide/variable/concept.mdx

@@ -7,26 +7,35 @@ description: Introduces the core concepts of the variable engine.
 
 :::tip
 
-The variable engine has many abstract concepts. This article uses 🌟 to mark a batch of concepts that you can **prioritize understanding**.
+Complete [Output Variables](./variable-output.mdx) → [Consume Variables](./variable-consume.mdx) first, then come back to this article as a reference manual. We use 🌟 to highlight concepts worth mastering early.
 
 :::
 
+## Reading Path
+
+- Skim the terminology navigator below to confirm the term you need is covered.
+- Study the “Core Concepts” diagram to link variables, scopes, and AST into one picture.
+- Jump to the sections you need based on the problem at hand—no need to read in order.
+
 :::info{title="📖 Quick Terminology Lookup"}
 
-- [**Variable**](#variable) 🌟
-- [**Scope**](#scope-) 🌟: A container that aggregates a series of variable information and maintains dependencies with other scopes.
-- [**AST**](#ast-) 🌟: Scopes use AST to store variable information.
-- [**ASTNode**](#astnode): The basic unit for storing variable information.
-- [**ASTNodeJSON**](#astnodejson): The JSON representation of an ASTNode.
-- [**Declaration**](#declaration) 🌟: Identifier + Definition.
-- [**Type**](#type) 🌟: Constraints on the value of a variable.
-- [**Expression**](#expression): Combines several variables in a specific way to return a new variable.
-- [**Scope Chain**](#scope-chain): Defines which scopes' variables a scope can reference.
-- [**Dependency Scope**](#dependency-scope): Which scopes' output variables a scope can access.
-- [**Covering Scope**](#covering-scope): Which scopes can access the output variables of a scope.
-- [**Node Scope**](#node-scope) 🌟: Can access the output variables of upstream nodes, and its output variables can also be accessed by downstream nodes.
-- [**Node Private Scope**](#node-private-scope): The node's private scope can only be accessed by the node itself or its child nodes.
-- [**Global Scope**](#global-scope): Variables in the global scope can be accessed by all node scopes.
+- **Core Ideas**
+  - [Variable](#variable) 🌟: A data container defined during design and evaluated at runtime.
+  - [Scope](#scope-) 🌟: A container for variables that also maintains relationships with other scopes.
+  - [AST](#ast-) 🌟: The structured storage used to keep variable information inside a scope.
+- **AST Related**
+  - [ASTNode](#astnode): Nodes in the AST tree, each describing a piece of variable information.
+  - [ASTNodeJSON](#astnodejson): The JSON serialization of an ASTNode.
+  - [Declaration](#declaration) 🌟: Identifier + definition, the smallest information unit in the variable engine.
+  - [Type](#type) 🌟: A definition that constrains the range of possible values for a variable.
+  - [Expression](#expression): Consumes variables and produces a new one.
+- **Scope Relationships**
+  - [Scope Chain](#scope-chain): Determines which other scopes a scope can read variables from.
+  - [Dependency Scope](#dependency-scope): The upstream scopes whose outputs are readable here.
+  - [Covering Scope](#covering-scope): The downstream scopes that can read this scope’s outputs.
+  - [Node Scope](#node-scope) 🌟: The public variable set of a node.
+  - [Node Private Scope](#node-private-scope): Variables only accessible to the node and its children.
+  - [Global Scope](#global-scope): Shared variables readable from every scope.
 
 :::
 
@@ -34,14 +43,29 @@ The variable engine has many abstract concepts. This article uses 🌟 to mark a
 
 ## Core Concepts
 
-The core concepts of the variable engine can be summarized in the following diagram:
+You can connect the variable engine’s core concepts through the diagram below:
 
 <img src="/variable/concept/concepts-en.png" alt="Variable Core Concepts Relationship Diagram" width="600" />
 
+:::info{title="How to read the diagram"}
+
+- Green nodes represent **what the information is**, such as variables, types, or expressions.
+- Red nodes represent **how the information is stored**, namely AST nodes.
+- Purple nodes represent **where the information lives**, i.e., scopes.
+- Dashed nodes and lines depict **how the information flows**, which is the scope chain.
+
+:::
+
+To keep things tangible, hold on to one real-world case:
+
+> A “Batch” node reads the array output from an upstream HTTP node → iterates to produce `item` → and continues using `item` inside its child nodes.
+
+Every concept mentioned in that workflow appears below, so feel free to cross-reference as you read.
+
 
 ### Variable
 
-See [Variable Introduction](./basic.mdx) for details.
+Variables are containers defined during design time and evaluated during runtime. See [Variable Introduction](./basic.mdx) for a deeper primer.
 
 :::warning{title="⚠️ Different Focus on Variables in Design and Runtime"}
 
@@ -51,9 +75,9 @@ See [Variable Introduction](./basic.mdx) for details.
 
 ### Scope 🌟
 
-A Scope is a **container**: it aggregates a series of **variable information** and maintains **dependencies with other scopes**.
+A scope is a **container** that bundles **variable information** and keeps track of **relationships with other scopes**. In short, a scope decides “who can access which variables.”
 
-The range of a scope can be defined according to different business scenarios:
+Its boundaries vary by business scenario; the three most common cases are:
 
 | Scene | Example |
 | :--- | :--- |
@@ -65,26 +89,26 @@ The range of a scope can be defined according to different business scenarios:
 
 :::warning{title="Why does FlowGram abstract the concept of a scope outside of nodes?"}
 
-1. A node is not equivalent to a scope.
-2. Some scopes (e.g., global scope) are not related to nodes.
-3. A node can have multiple scopes (e.g., loop private scope).
+1. Node ≠ scope: a single node may need both a public scope and a private one.
+2. Some scopes, like the global drawer, live outside of any node.
+3. Certain nodes require multiple layers of scopes (e.g., a loop’s private scope), which can’t be expressed with the node concept alone.
 
 :::
 
 ### AST 🌟
 
-A Scope stores variable information through an `AST`.
+Scopes store variable information through an `AST`. Treat it as a tree where each node describes a declaration, type, or expression.
 
 :::tip
 
-You can access the `AST` tree within a scope via `scope.ast` to perform CRUD operations on variable information.
+Through `scope.ast` you can access the tree inside a scope and perform CRUD operations on variable information.
 
 :::
 
 
 #### ASTNode
 
-`ASTNode` is the **basic information unit** used in the variable engine to **store variable information**. It can model various **variable information**, including:
+`ASTNode` is the **basic information unit** used in the variable engine to **store variable information**. It can model various **pieces of information**:
 
 - **Declarations**: such as `VariableDeclaration`, used to declare new variables.
 - **Types**: such as `StringType`, used to represent the String type.
@@ -101,9 +125,9 @@ You can access the `AST` tree within a scope via `scope.ast` to perform CRUD ope
 
 #### ASTNodeJSON
 
-`ASTNodeJSON` is the **pure JSON serialization** representation of an `ASTNode`.
+`ASTNodeJSON` is the **pure JSON serialization** of an `ASTNode`. We usually construct it on the design side and let the variable engine instantiate it later.
 
-`ASTNodeJSON` includes a `kind` field to indicate the type of the `ASTNode`:
+Its most important field is `kind`, which indicates the type of the `ASTNode`:
 
 ```tsx
 /**
@@ -117,7 +141,7 @@ You can access the `AST` tree within a scope via `scope.ast` to perform CRUD ope
 }
 ```
 
-When using the variable engine, users describe variable information with `ASTNodeJSON`, which is then **instantiated** into an `ASTNode` by the variable engine and added to the scope.
+When using the variable engine, we describe variable information with `ASTNodeJSON`. The engine then **instantiates** it into an `ASTNode` and stores it in the scope.
 
 ```tsx
 /**
@@ -187,12 +211,12 @@ ASTFactory.createVariableDeclaration({
 
 
 
-### Declaration
+### Declaration 🌟
 
-Declaration = Identifier (Key) + Definition. In design mode, a declaration is an `ASTNode` that stores an identifier and its definition.
+Declaration = Identifier (Key) + Definition. In design mode, a declaration is an `ASTNode` that stores an identifier plus variable information—the smallest unit that can be referenced.
 
-- Identifier (Key): The index for accessing the declaration.
-- Definition: The information defined by the declaration. For example, a variable's definition = type + right-hand value.
+- Identifier (Key): The index used to access a declaration.
+- Definition: The information carried by the declaration. For a variable, the definition = type + right-hand value.
 
 
 :::info{title="Example: Declarations in JavaScript"}
@@ -253,9 +277,9 @@ The variable engine currently only provides **variable field declaration** (`Bas
 
 
 
-### Type
+### Type 🌟
 
-Types are used to **constrain the range of variable values**. In design mode, a type is an `ASTNode`.
+Types **constrain the range of variable values**. In design mode, a type is also an `ASTNode`. Understanding types helps you reason about what a variable can store and what an expression returns.
 
 The variable engine has built-in **basic types** from JSON:
 - `StringType`: string
@@ -271,7 +295,7 @@ It also adds:
 
 ### Expression
 
-An expression takes **0 or more variables as input**, computes them in a **specific way**, and returns a new **variable**.
+An expression takes **0 or more variables as input**, processes them in a **specific way**, and returns a new **variable**. Design time only records “who it depends on” and the inferred return type—runtime handles the actual value calculation.
 
 ```mermaid
 graph LR
@@ -284,7 +308,7 @@ Expression --returns--> Output_Variable
 style Expression stroke:#333,stroke-width:3px;
 ```
 
-In **design mode**, an expression is an `ASTNode`. In modeling, we only need to focus on:
+In **design mode**, an expression is an `ASTNode`. Modeling focuses on:
 
 - Which variable declarations does the expression **use**?
 - How is the expression's **return type** inferred?
@@ -370,21 +394,21 @@ graph LR
 
 ### Scope Chain
 
-The Scope Chain defines **which scopes' variables a scope can reference**. It is an abstract class, and specific business scenarios can implement custom scope chains.
+The scope chain defines **which other scopes a scope can read variables from**. Think of it as the whitelist for variable access. The variable engine exposes an abstract class, and product teams can implement custom scope chains as needed.
 
-The variable engine has built-in implementations for two types of scope chains: **free-layout scope chain** and **fixed-layout scope chain**.
+Out of the box, the engine ships with **free-layout** and **fixed-layout** scope chain implementations.
 
 
 #### Dependency Scope
 
-`Dependency Scope` = Which scopes' output variables a scope can access.
+`Dependency scope` = the upstream scopes whose output variables the current scope can access.
 
 You can access a scope's `Dependency Scope` via `scope.depScopes`.
 
 
 #### Covering Scope
 
-`Covering Scope` = Which scopes can access the output variables of a scope.
+`Covering scope` = the downstream scopes that can access the current scope’s output variables.
 
 You can access a scope's `Covering Scope` via `scope.coverScopes`.
 
@@ -393,7 +417,7 @@ You can access a scope's `Covering Scope` via `scope.coverScopes`.
 
 FlowGram defines the following special types of scopes in the canvas:
 
-### Node Scope
+### Node Scope 🌟
 
 Also known as `Node Public Scope`, this scope can access the variables of the `Node Scope` of **upstream nodes**, and its output variable declarations can also be accessed by the `Node Scope` of **downstream nodes**.
 
@@ -457,7 +481,7 @@ graph BT
 
 ### Global Scope
 
-Variables in the `Global Scope` can be accessed by **all node scopes and node private scopes**, but it cannot access variables from other scopes.
+Variables in the `Global Scope` are readable from **all node scopes and node private scopes**, yet the global scope itself **does not depend on any other scope**. It’s ideal for configuration, constants, environment context, and other shared data.
 
 For how to set the global scope, see [Output Global Variables](./variable-output#output-global-variables). Its scope chain relationship is shown in the figure below:
 
@@ -489,18 +513,18 @@ graph RL
 
 ![Architecture Diagram](/variable/concept/arch-en.png)
 
-The variable engine is designed following the DIP (Dependency Inversion Principle) and is divided into three layers according to code stability, abstraction level, and proximity to the business:
+The variable engine follows the Dependency Inversion Principle (DIP) and is split into three layers according to code stability, abstraction level, and proximity to business logic:
 
 ### Variable Abstraction Layer
 
-This is the highest abstraction level in the variable architecture and the most stable part of the code. The abstraction layer defines abstract classes for core concepts such as `ASTNode`, `Scope`, and `ScopeChain`.
+This abstraction layer is the most stable part of the architecture. It defines the core interfaces—`ASTNode`, `Scope`, `ScopeChain`, and more—that upper layers extend.
 
 ### Variable Implementation Layer
 
-This part of the variable architecture changes more frequently and may be adjusted for different business needs. The engine has a built-in set of stable `ASTNode` implementations and `ScopeChain` implementations. When users have complex variable requirements, they can register new `ASTNode`s or override existing `ASTNode`s through dependency injection for customization.
+This layer sits closer to product requirements and evolves more often. The engine provides a set of built-in `ASTNode` and `ScopeChain` implementations, and you can register new ones or override defaults via dependency injection when your domain demands it.
 
 ### Variable Material Layer
 
-Based on the Facade pattern, this layer improves the usability of variables by encapsulating complex variable logic into simple, out-of-the-box variable materials.
+The outermost layer adopts the Facade pattern to boost usability, packaging complex capabilities into “materials” that can be reused directly.
 
 - For the use of variable materials, see: [Materials](/materials/introduction)

+ 8 - 1
apps/docs/src/en/guide/variable/custom-scope-chain.mdx

@@ -4,9 +4,16 @@ description: How to customize the scope chain
 
 # Scope Chain
 
+:::info{title="Before You Read"}
+
+- We recommend completing [Output Variables](./variable-output.mdx) and [Consume Variables](./variable-consume.mdx) first.
+- If the idea of scopes still feels fuzzy, review [Core Concepts – Scope Chain](./concept#scope-chain) before diving in.
+
+:::
+
 ## Default Scope Chain Logic
 
-For details, see: [Scope in Canvas](./concept#variables-in-canvas)
+For details, see: [Scope in Canvas](./concept#variables-in-the-canvas)
 
 
 ## Customize in `editor-props`

+ 41 - 5
apps/docs/src/en/guide/variable/variable-consume.mdx

@@ -6,6 +6,14 @@ description: Introduction to consuming variables output by FlowGram's variable e
 
 In FlowGram, when a node wants to use variables from preceding nodes, it needs to consume those variables.
 
+:::info{title="Reading Tips"}
+
+- We recommend finishing [Output Variables](./variable-output.mdx) first so you know how variables are produced; this guide is the second stop focused on “how to access them.”
+- To quickly preview the variable selector experience, try [VariableSelector](#variableselector) first; continue with the API sections when you need code-level access to variable lists.
+- All examples assume the **node scope**. When private or global scopes show up, refer to [Core Concepts – Variables in the Canvas](./concept#variables-in-the-canvas) for additional context.
+
+:::
+
 ## `VariableSelector`
 
 To make it easier for you to integrate variable selection functionality into your applications, the official materials provide the `VariableSelector` component.
@@ -16,6 +24,14 @@ See documentation: [VariableSelector](/materials/components/variable-selector)
 
 In canvas nodes, we often need to get **variables available in the current scope** and display them in a tree structure for users to select and operate.
 
+:::info{title="Common Needs at a Glance"}
+
+- **Just list variables** → `useAvailableVariables`
+- **Need drill-down for objects/arrays** → `ASTMatch` + recursive rendering
+- **Need precise subscriptions** → go straight to `scope.available`
+
+:::
+
 ### `useAvailableVariables`
 
 `useAvailableVariables` is a lightweight Hook that directly returns an array of variables available in the current scope (`VariableDeclaration[]`).
@@ -43,6 +59,12 @@ return availableVariables.map(renderVariable);
 
 When a variable's type is `Object`, we often need to be able to "drill down" into its interior to access its properties. The `ASTMatch.isObject` method can help us determine if a variable type is an object. If it is, we can recursively render its `properties`.
 
+:::tip
+
+Each layer of the variable tree is essentially a declaration (`BaseVariableField`). For objects, `properties` gives you the next-level declaration array.
+
+:::
+
 ```tsx pure title="use-variable-tree.tsx" {12}
 import {
   type BaseVariableField,
@@ -99,9 +121,17 @@ const renderVariable = (variable: BaseVariableField) => ({
 
 `scope.available` is one of the cores of the variable system, which can perform more advanced variable retrieval and monitoring actions on **variables available within the scope**.
 
+:::info{title="When to reach for scope.available"}
+
+- You need to read or validate a variable by `keyPath`.
+- You want to manipulate visibility outside of React hooks (e.g., in plugins or services).
+- You need fine-grained subscriptions without refreshing the entire list.
+
+:::
+
 ### `useScopeAvailable`
 
-`useScopeAvailable` can directly return `scope.available` in React
+`useScopeAvailable` can directly return `scope.available` in React.
 
 ```tsx
 import { useScopeAvailable } from '@flowgram.ai/free-layout-editor';
@@ -115,7 +145,7 @@ console.log(available.variables);
 console.log(available.getByKeyPath(['start_0', 'xxx']));
 
 // Monitor changes in a single variable
-available.trackByKeyPath(['start_0', xxx], () => {
+available.trackByKeyPath(['start_0', 'xxx'], () => {
   // ...
 })
 ```
@@ -165,6 +195,12 @@ const validateVariableInNode = (keyPath: string, node: FlowNodeEntity) => {
 
 When you only care about changes to a specific variable field (including variables nested in Object or Array), `trackByKeyPath` allows you to precisely "subscribe" to updates of that variable without causing component re-renders due to changes in other unrelated variables, thus achieving more refined performance optimization.
 
+:::tip
+
+Combine it with `autoRefresh: false` to avoid wide re-renders—only update local state when the tracked variable changes.
+
+:::
+
 ```tsx {6,13-17}
 import { useScopeAvailable } from '@flowgram.ai/fixed-layout-editor';
 import { useEffect, useState } from 'react';
@@ -255,7 +291,7 @@ These APIs all return a `Disposable` object. To avoid memory leaks and unnecessa
 
 ### `useOutputVariables`
 
-useOutputVariables can get **output variables of the current scope** and **automatically trigger a refresh** when the output variable list or drill-down changes.
+`useOutputVariables` can get **output variables of the current scope** and **automatically trigger a refresh** when the output variable list or drill-down changes.
 
 ```tsx
 const variables = useOutputVariables();
@@ -263,7 +299,7 @@ const variables = useOutputVariables();
 
 :::tip
 
-useOutputVariables is available in flowgram@0.5.6 and later versions. If using an earlier version, you can implement it with the following code:
+`useOutputVariables` is available in flowgram@0.5.6 and later versions. If you are on an earlier version, you can implement it with the following code:
 
 ```tsx
 const scope = useCurrentScope();
@@ -298,7 +334,7 @@ scope.available
 
 ### Setting Current Scope
 
-You can set the current scope through [`ScopeProvider`](https://flowgram.ai/auto-docs/editor/functions/ScopeProvider)
+You can set the current scope through [`ScopeProvider`](https://flowgram.ai/auto-docs/editor/functions/ScopeProvider).
 
 ```tsx
 // set the scope of current node

+ 50 - 7
apps/docs/src/en/guide/variable/variable-output.mdx

@@ -10,16 +10,38 @@ We primarily categorize output variables into three types:
 2. **Output Node Private Variables**: Output variables limited to the node's interior (including child nodes) and not accessible by external nodes.
 3. **Output Global Variables**: Available throughout the entire flow, readable by any node, suitable for storing public states or configurations.
 
+:::info{title="Reading Guide"}
+
+- After [Variable Introduction](./basic.mdx), start here to practice how variables are produced.
+- If you work from form configuration, begin with “Method 1: Synchronization via Form Side Effects.” If you need runtime logic or batch updates, skip to the plugin or UI sections.
+- Every example uses `ASTFactory`; revisit [Core Concepts – AST](./concept#ast-) if you need a refresher.
+
+:::
+
 ## Output Node Variables
 
-Output node variables are bound to the lifecycle of the current node. When a node is created, the variables are born; when a node is deleted, the variables disappear with it.
+Output node variables are bound to the lifecycle of the current node: they are created with the node and removed when the node is deleted. (See [Node Scope](./concept#node-scope) for details.)
 
 We typically have three ways to output node variables:
 
+:::info{title="How to pick a method"}
+
+- Variable definitions tied to form inputs → Method 1.
+- Variables generated at runtime or synchronized in batches → Method 2.
+- Writing variables directly in UI is only for temporary debugging; avoid Method 3 in production.
+
+:::
+
 ### Method 1: Synchronization via Form Side Effects
 
 [Form side effects](/guide/form/form#side-effects-effect) are usually configured in the node's `form-meta.ts` file and are the most common way to define node output variables.
 
+:::info{title="When to use it"}
+
+- The node’s variable model can be derived from form fields.
+
+:::
+
 #### `provideJsonSchemaOutputs`
 
 If the structure of the output variables required by a node matches the [JSON Schema](https://json-schema.org/) structure, you can use the `provideJsonSchemaOutputs` side effect (Effect) material.
@@ -196,7 +218,7 @@ export const formMeta = {
             meta: {
               title: `Title_${value}`,
             },
-            key: `uid_${node.id}`,
+            key: `uid_${context.node.id}`,
             type: ASTFactory.createString(),
           })
         )
@@ -216,7 +238,7 @@ export const formMeta = {
             meta: {
               title: `Title_${value}`,
             },
-            key: `uid_${node.id}_2`,
+            key: `uid_${context.node.id}_2`,
             type: ASTFactory.createNumber(),
           })
         )
@@ -236,6 +258,13 @@ export const formMeta = {
 
 In addition to static configuration in forms, we can also freely and dynamically manipulate node variables in plugins through `node.scope`.
 
+:::info{title="When to use it"}
+
+- You need to create or adjust variables across multiple nodes in bulk.
+- You want to auto-populate default variables when the canvas initializes.
+
+:::
+
 #### Updating via Specified Node's Scope
 
 The following example demonstrates how to obtain the `Scope` of the start node in the `onInit` lifecycle of a plugin and perform a series of operations on its variables.
@@ -363,7 +392,13 @@ export const formMeta = {
 
 ## Output Node Private Variables
 
-Private variables are variables that can only be accessed within the current node and its child nodes. (See: [Node Private Scope](./concept#node-private-scope))
+Private variables are variables that can only be accessed within the current node and its child nodes. (See [Node Private Scope](./concept#node-private-scope).)
+
+:::tip
+
+Quick rule of thumb: if a variable only serves the node’s internal implementation and shouldn’t be exposed downstream, keep it in `node.privateScope`.
+
+:::
 
 Here we only list two methods, and other methods can be inferred from [Output Node Variables](#output-node-variables).
 
@@ -386,7 +421,7 @@ export const formMeta =  {
     'path.to.value': createEffectFromVariableProvider({
       scope: 'private',
       // parse form value to variable
-      parse(v: string) {
+      parse(v: string, { node }) {
         return [{
           meta: {
             title: `Private_${v}`,
@@ -422,7 +457,7 @@ export const formMeta = {
             meta: {
               title: `Your Private Variable Title`,
             },
-            key: `uid_${node.id}`,
+            key: `uid_${context.node.id}`,
             type: ASTFactory.createInteger(),
           })
         )
@@ -440,7 +475,15 @@ export const formMeta = {
 
 ## Output Global Variables
 
-Global variables are like the "shared memory" of the entire flow, accessible and modifiable by any node or plugin. They are very suitable for storing states that run through the entire flow, such as user information, environment configurations, etc.
+Global variables are like the “shared memory” of the entire flow—any node or plugin can read and modify them. They work well for state that persists across the flow, such as user information or environment configuration. (See [Global Scope](./concept#global-scope).)
+
+:::info{title="When to choose the global scope"}
+
+- The variable is reused across multiple nodes or even plugins.
+- The variable should be decoupled from a specific node (e.g., environment config, user context).
+- You need to write it during initialization so downstream nodes can simply read it.
+
+:::
 
 Similar to node variables, we also have two main ways to obtain the global variable scope (`GlobalScope`).
 

+ 1 - 1
apps/docs/src/zh/guide/variable/_meta.json

@@ -1,7 +1,7 @@
 [
   "basic",
-  "concept",
   "variable-output",
   "variable-consume",
+  "concept",
   "custom-scope-chain"
 ]

+ 28 - 5
apps/docs/src/zh/guide/variable/basic.mdx

@@ -11,6 +11,12 @@ description: 介绍什么是变量,以及变量引擎的作用
 
 :::
 
+## 阅读路径
+
+- 在这里先建立「变量是什么」以及「为什么需要变量引擎」的整体心智。
+- 接下来建议直接上手:[输出变量](./variable-output.mdx) → [消费变量](./variable-consume.mdx),先学会如何在节点中产出变量,再去读取。
+- 当在实践中遇到作用域或类型等疑问时,再回到[核心概念](./concept.mdx)查阅详细术语。
+
 ## 什么是变量?
 
 想象一下,你在搭建一个复杂的乐高模型,每个模块都需要精确地连接在一起。在工作流(Workflow)的世界里,**变量**就扮演着类似“连接件”的角色。它们是用来在不同节点之间传递信息的“信使”。
@@ -19,7 +25,7 @@ description: 介绍什么是变量,以及变量引擎的作用
 
 一个变量通常由三部分组成:
 
-- **名字(唯一标识符)**:就像你的名字一样,它让大家能准确地找到这个变量。例如 `userName`、`orderId`。
+- **名字(唯一标识符)**:类似个人姓名,用于准确定位某个变量。例如 `userName`、`orderId`。
 - **值**:容器里装的东西。它可以是数字 `123`,文字 `"Hello FlowGram!"`,或者一个开关状态 `true` / `false`。
 - **类型**:规定了这个容器能装哪种东西。比如,有的只能装数字,有的只能装文字。
 
@@ -36,6 +42,12 @@ description: 介绍什么是变量,以及变量引擎的作用
   </div>
 </div>
 
+### 设计态定义 vs. 运行态取值
+
+在**设计态**(绘制流程时),我们只需要确定变量的**定义**:名字、类型以及可选的元信息。变量引擎会把这些定义管理成结构化的数据。
+
+到了**运行态**,FlowGram 会依据这些定义为每次执行赋值。因此在设计时,先关注结构与约束;在运行时,所有节点就能依赖这些定义稳定地读写实际数据。
+
 ## 为什么需要变量引擎?
 
 随着工作流复杂度的提升,变量的数量和管理难度也随之增加。
@@ -57,24 +69,30 @@ description: 介绍什么是变量,以及变量引擎的作用
       </div>
       <div>
         <img loading="lazy" src="/variable/variable-scope-feature-2.png" alt="作用域约束示例 2" />
-        <p style={{marginTop: '10px'}}>`LLM` 节点在一个 `Condition` 分支里,像是在一个独立的房间。外面的 `End` 节点自然就拿不到它的 `result` 变量了。</p>
+        <p style={{marginTop: '10px'}}>`LLM` 节点处于 `Condition` 分支,等同于位于独立空间;外部的 `End` 节点无法访问其 `result` 变量。</p>
       </div>
     </div>
   </div>
   <div>
     <b>变量结构透视:轻松洞悉复杂数据</b>
-    <p className="rs-tip">当变量变得复杂时(例如一个包含许多层级的对象),变量引擎能让你像剥洋葱一样,一层层地深入探索它的内部结构,所有细节都尽在掌握。</p>
+    <p className="rs-tip">当变量结构较复杂(例如包含多层嵌套的对象)时,变量引擎支持逐层展开,便于定位每一项数据。</p>
     <img loading="lazy" src="/variable/variable-tree-management.gif" alt="变量结构透视" />
-    <p style={{marginTop: '10px'}}>这张图里,你能看到所有节点的输出变量,以及它们之间的层级关系,像一棵枝繁叶茂的树。</p>
+    <p style={{marginTop: '10px'}}>下图展示了所有节点的输出变量与层级关系,便于整体审视变量结构。</p>
   </div>
   <div>
     <b>类型自动推导:心有灵犀一点通</b>
-    <p className="rs-tip">你不用再挨个告诉每个变量它应该是什么类型,变量引擎会像你的“灵魂伴侣”一样,根据上下文自动推导出它的类型。</p>
+    <p className="rs-tip">无需为每个变量逐一指定类型,变量引擎可依据上下文自动完成推导。</p>
     <img loading="lazy" src="/variable/variable-batch-auto-infer.gif" alt="类型自动推导" />
     <p style={{marginTop: '10px'}}>例如,当 `Start` 节点中 `arr` 变量的类型发生变更时,`Batch` 节点输出的 `item` 类型也会自动同步更新,确保了类型的一致性。</p>
   </div>
 </div>
 
+:::tip
+
+建议先把变量读写跑通;当你需要深入理解作用域链、AST、声明、表达式等底层模型时,再阅读[变量概念](./concept.mdx)。
+
+:::
+
 ## 如何开启变量引擎?
 
 您可以通过简单的配置来启用变量引擎,以体验其强大的功能。
@@ -91,3 +109,8 @@ description: 介绍什么是变量,以及变量引擎的作用
 }
 ```
 
+## 接下来可以看看(推荐顺序)
+
+- [输出变量](./variable-output.mdx):先学会在节点、插件、全局作用域中产出变量。
+- [消费变量](./variable-consume.mdx):再学习如何在节点、UI 中安全读取变量。
+- [变量概念](./concept.mdx):最后回顾作用域、AST、声明、类型等核心名词。

+ 64 - 44
apps/docs/src/zh/guide/variable/concept.mdx

@@ -7,26 +7,35 @@ description: 介绍变量引擎的核心概念
 
 :::tip
 
-变量引擎概念较多且抽象。本文通过 🌟 标记出了一批可以**优先理解**的概念。
+建议先完成[输出变量](./variable-output.mdx)→[消费变量](./variable-consume.mdx)的动手实践,再回到本文作为参考手册。我们通过 🌟 标记出可**优先掌握**的概念。
 
 :::
 
+## 阅读路径
+
+- 可以先快速浏览下方术语导航,确认自己要查的名词是否在其中。
+- 阅读「核心概念」关系图,建立变量、作用域、AST 的整体框架。
+- 按需跳转到对应小节,结合当前遇到的问题查阅细节,不必顺序阅读。
+
 :::info{title="📖 术语快速查询"}
 
-- [**变量**](#变量) 🌟
-- [**作用域**](#作用域-) 🌟:一种容器,聚合了一系列变量信息,同时维护与其他作用域的依赖关系。
-- [**AST**](#ast-) 🌟:作用域通过 AST 存储变量信息。
-- [**ASTNode**](#astnode):存储变量信息的基本单元。
-- [**ASTNodeJSON**](#astnodejson):ASTNode 的 JSON 表示。
-- [**声明**](#声明) 🌟:标识符 + 定义。
-- [**类型**](#类型) 🌟:变量值的约束。
-- [**表达式**](#表达式):通过特定方法组合若干变量,返回一个新的变量。
-- [**作用域链**](#作用域链):定义一个作用域可以引用哪些作用域的变量。
-- [**依赖作用域**](#依赖作用域):作用域可以访问哪些作用域的输出变量。
-- [**覆盖作用域**](#覆盖作用域):作用域的输出变量可以被哪些作用域访问。
-- [**节点作用域**](#节点作用域) 🌟:可以访问上游节点的输出变量,其输出变量也可以被下游节点访问。
-- [**节点私有作用域**](#节点私有作用域):节点私有作用域只能被节点本身或者子节点访问。
-- [**全局作用域**](#全局作用域):所有节点的作用域都可以访问全局作用域的变量。
+- **概念本体**
+  - [变量](#变量) 🌟:流程设计阶段定义出来、运行时才求值的数据容器。
+  - [作用域](#作用域-) 🌟:变量的容器,同时维护与其他作用域的依赖关系。
+  - [AST](#ast-) 🌟:作用域内变量信息的结构化存储方式。
+- **AST 相关**
+  - [ASTNode](#astnode):AST 树中的节点,表示一段变量信息。
+  - [ASTNodeJSON](#astnodejson):ASTNode 的 JSON 序列化形式。
+  - [声明](#声明) 🌟:标识符 + 定义,变量引擎的最小信息单元。
+  - [类型](#类型) 🌟:用于约束变量值范围的定义。
+  - [表达式](#表达式):输入若干变量后计算得到新变量。
+- **作用域关系**
+  - [作用域链](#作用域链):决定一个作用域能引用哪些其他作用域。
+  - [依赖作用域](#依赖作用域):当前作用域可读取的上游作用域集合。
+  - [覆盖作用域](#覆盖作用域):可以访问当前作用域输出变量的下游集合。
+  - [节点作用域](#节点作用域) 🌟:节点公开的变量集合。
+  - [节点私有作用域](#节点私有作用域):仅限节点自身与子节点访问的变量。
+  - [全局作用域](#全局作用域):所有节点都可读的共享变量。
 
 :::
 
@@ -34,14 +43,28 @@ description: 介绍变量引擎的核心概念
 
 ## 核心概念
 
-变量引擎核心概念可以通过下图总结
+变量引擎核心概念可以通过下图串起来理解
 
 <img src="/variable/concept/concepts-zh.png" alt="变量核心概念关系图" width="600" />
 
+:::info{title="读图重点"}
+
+- 绿色节点代表「信息是什么」,如变量、类型、表达式。
+- 红色节点代表「信息怎么存」,即 AST 节点。
+- 紫色节点代表「信息放在哪」,即作用域。
+- 虚线节点及线条代表「信息怎么流动」,即作用域链。
+
+:::
+
+为了降低抽象程度,可以先记住一个真实案例:
+
+> 「批处理节点」读取「上游 HTTP 节点」的数组输出 → 遍历得到 `item` → 在子节点里继续使用 `item`。
+
+这个过程涉及的所有名词都在下文出现,阅读时可随时对照。
 
 ### 变量
 
-详见 [变量介绍](./basic.mdx)
+变量是在设计态定义、在运行态求值的数据容器。进一步了解可参考 [变量介绍](./basic.mdx)。
 
 :::warning{title="⚠️ 变量在设计和运行中的关注点不同"}
 
@@ -51,9 +74,9 @@ description: 介绍变量引擎的核心概念
 
 ### 作用域 🌟
 
-作用域(Scope)是一种**容器**:容器内聚合了一系列**变量信息**,同时维护了**与其他作用域的依赖关系**。
+作用域(Scope)是一种**容器**:容器内聚合了一系列**变量信息**,同时维护了**与其他作用域的依赖关系**。一句话概括:作用域决定「谁可以访问哪些变量」。
 
-作用域的范围可以根据业务场景的不同约定:
+作用域的范围可以根据业务场景的不同约定,常见的三类如下
 
 | 场景 | 示例 |
 | :--- | :--- |
@@ -65,26 +88,26 @@ description: 介绍变量引擎的核心概念
 
 :::warning{title="为什么 FlowGram 要在节点之外,新抽象一个作用域的概念?"}
 
-1. 节点不等同于作用域
-2. 存在一些作用域(如:全局作用域)和节点无关
-3. 一个节点可以存在多个作用域(如:循环私有作用域)
+1. 节点 ≠ 作用域:同一个节点可能需要拆分成公开作用域与私有作用域。
+2. 存在与节点无关的作用域,如面向全局的变量抽屉。
+3. 部分节点需要多层作用域(例:循环的私有作用域),节点概念不足以描述。
 
 :::
 
 ### AST 🌟
 
-作用域(Scope)通过 `AST` 存储变量信息。
+作用域通过 `AST` 存储变量信息。可以把它当作「变量信息」的树形结构:每个节点描述一个声明、类型或表达式。
 
 :::tip
 
-通过 `scope.ast` 可以访问作用域内的 `AST` 树,从而可以对变量信息进行 CRUD 操作。
+通过 `scope.ast` 可以访问作用域内的 `AST` 树,从而对变量信息进行 CRUD 操作。
 
 :::
 
 
 #### ASTNode
 
-`ASTNode` 是变量引擎中用于**存储变量信息**的**基本信息单元**。它可以为各种**变量信息建模**。这些变量信息包括
+`ASTNode` 是变量引擎中用于**存储变量信息**的**基本信息单元**。它可以为各种**变量信息建模**:
 
 - **声明**:如 `VariableDeclaration` ,用于声明新变量。
 - **类型**:如 `StringType`,用于表示 String 类型。
@@ -101,9 +124,9 @@ description: 介绍变量引擎的核心概念
 
 #### ASTNodeJSON
 
-`ASTNodeJSON` 是 `ASTNode` 的**纯 JSON 序列化**表示。
+`ASTNodeJSON` 是 `ASTNode` 的**纯 JSON 序列化**表示。通常我们会在设计端构造它,再交由变量引擎实例化。
 
-`ASTNodeJSON` 包含一个 `kind` 字段,用于表示 `ASTNode` 的类型:
+最关键的字段是 `kind`,用于表示 `ASTNode` 的类型:
 
 ```tsx
 /**
@@ -187,9 +210,9 @@ ASTFactory.createVariableDeclaration({
 
 
 
-### 声明
+### 声明 🌟
 
-声明 = 标识符(Key) + 定义(Definition)。在设计态中,声明是一种存储标识符 + 变量信息的 `ASTNode`
+声明 = 标识符(Key) + 定义(Definition)。在设计态中,声明是一种存储标识符与变量信息的 `ASTNode`,是变量系统的最小「可被引用」单元
 
 - 标识符(Key):访问声明的索引。
 - 定义(Definition):声明定义的信息。如:变量的定义 = 类型 + 右值。
@@ -251,11 +274,9 @@ interface Point {
 
 
 
+### 类型 🌟
 
-
-### 类型
-
-类型用于**约束变量值的范围**。在设计态中,类型是一种 `ASTNode`。
+类型用于**约束变量值的范围**。在设计态中,类型也是一种 `ASTNode`。理解类型有助于掌握「变量能装什么」以及「表达式返回什么」。
 
 变量引擎内置了 JSON 的**基础类型**:
 - `StringType`:字符串
@@ -271,7 +292,7 @@ interface Point {
 
 ### 表达式
 
-表达式**输入 0 个或者多个变量**,并通过**通过特定方式**进行计算,返回一个新的**变量**。
+表达式**输入 0 个或者多个变量**,并通过特定方式进行计算,返回一个新的**变量**。设计态只描述「依赖了谁」和「推导出的类型」,运行态负责真正的值计算。
 
 ```mermaid
 graph LR
@@ -369,21 +390,21 @@ graph LR
 
 ### 作用域链
 
-作用域链(Scope Chain)定义了**一个作用域可以引用哪些作用域的变量**。它是一个抽象类,具体的业务场景可以实现自定义的作用域链。
+作用域链(Scope Chain)定义了**一个作用域可以引用哪些作用域的变量**。可以把它理解成「可读变量的白名单」。变量引擎提供了抽象类,具体业务可以根据实际编排形式实现自定义的作用域链。
 
 变量引擎内置了**自由布局作用域链**和**固定布局作用域链**两种作用域链实现。
 
 
 #### 依赖作用域
 
-`依赖作用域` = 作用域可以访问哪些作用域的输出变量
+`依赖作用域` = 当前作用域可以访问哪些作用域的输出变量
 
 可以通过 `scope.depScopes` 访问作用域的`依赖作用域`。
 
 
 #### 覆盖作用域
 
-`覆盖作用域` = 作用域的输出变量可以被哪些作用域访问
+`覆盖作用域` = 哪些作用域可以访问当前作用域的输出变量。
 
 可以通过 `scope.coverScopes` 访问作用域的`覆盖作用域`。
 
@@ -392,7 +413,7 @@ graph LR
 
 FlowGram 在画布中定义了以下几种特殊的作用域:
 
-### 节点作用域
+### 节点作用域 🌟
 
 又称`节点公开作用域`,作用域可以访问**上游节点**的`节点作用域`的变量,同时其输出变量声明也可以被**下游节点**的`节点作用域`访问。
 
@@ -456,7 +477,7 @@ graph BT
 
 ### 全局作用域
 
-`全局作用域`的变量能被**所有节点作用域和节点私有作用域**访问,但是不能访问其他作用域的变量
+`全局作用域`的变量能被**所有节点作用域和节点私有作用域**访问,但是**自身不依赖其他作用域**。适用于配置、常量、环境变量等公共信息
 
 全局作用域的设置方式详见[输出全局变量](./variable-output#输出全局变量),他的作用域链关系如下图所示:
 
@@ -488,19 +509,18 @@ graph RL
 
 ![架构图](/variable/concept/arch-zh.png)
 
-变量引擎设计上遵循 DIP(依赖反转)原则,按照 代码稳定性、抽象层次 以及和 业务的远近 分为三层:
+变量引擎设计上遵循 DIP(依赖反转)原则,按照代码稳定性、抽象层次以及与业务的距离分为三层:
 
 ### 变量抽象层
 
-变量架构中抽象层次最高,代码也最为稳定的部分。抽象层对 `ASTNode`、`Scope`、`ScopeChain` 等核心概念进行了抽象类定义
+抽象层是最稳定的一层,定义了 `ASTNode`、`Scope`、`ScopeChain` 等核心接口,为上层实现提供扩展约束
 
 ### 变量实现层
 
-变量架构中变动较大,不同业务之间可能存在调整的部分。引擎内置了一批较为稳定的 `ASTNode` 节点和 `ScopeChain` 的实现。当用户存在复杂的变量需求时,可以通过依赖注入注册新的 `ASTNode` 或者重载已有 `ASTNode` 节点实现定制化
+这一层包含更贴近业务的实现,易随产品演化调整。引擎内置了一批稳定的 `ASTNode` 节点和 `ScopeChain` 实现;当业务需要时,可以通过依赖注入注册新的节点或覆盖已有实现
 
 ### 变量物料层
 
-基于外观模式(Facade)的思路提高变量易用性,将复杂的变量逻辑封装为简单开箱即用的变量物料
+最外层通过外观模式(Facade)提升易用性,将复杂能力封装成「物料」给使用者直接复用
 
 - 变量物料的使用详见:[物料](/materials/introduction)
-

+ 7 - 1
apps/docs/src/zh/guide/variable/custom-scope-chain.mdx

@@ -4,6 +4,13 @@ description: 介绍如何定制作用域链
 
 # 作用域链
 
+:::info{title="阅读前提"}
+
+- 建议先完成[输出变量](./variable-output.mdx)、[消费变量](./variable-consume.mdx)的实践。
+- 如果对作用域概念还不够熟悉,可先回顾[核心概念 - 作用域链](./concept#作用域链)。
+
+:::
+
 ## 默认作用域链逻辑
 
 详见:[画布中的作用域](./concept#画布中的变量)
@@ -56,4 +63,3 @@ description: 介绍如何定制作用域链
   }
 }
 ```
-

+ 43 - 5
apps/docs/src/zh/guide/variable/variable-consume.mdx

@@ -6,6 +6,14 @@ description: 介绍如何在 FlowGram 中消费变量引擎输出的变量
 
 在 FlowGram 中,当一个节点想要使用到前序节点的变量,就需要消费变量。
 
+:::info{title="阅读提示"}
+
+- 建议先完成[输出变量](./variable-output.mdx),确保知道变量是如何被产出的;本篇作为第二站,专注于「如何拿到变量」。
+- 如果想快速看看变量选择器的效果,可先用用 [VariableSelector](#variableselector);若需要在代码里拿到变量列表,再继续阅读后续 API 章节。
+- 本文示例默认使用「节点作用域」。涉及节点私有或全局作用域时,可随时参考[核心概念 - 画布中的变量](./concept#画布中的变量)补充理解。
+
+:::
+
 ## `VariableSelector`
 
 为了让你能更轻松地在应用中集成变量选择的功能,官方物料提供了 `VariableSelector` 组件。
@@ -17,6 +25,14 @@ description: 介绍如何在 FlowGram 中消费变量引擎输出的变量
 
 在画布的节点中,我们常常需要获取**当前作用域下可用的变量**,并将它们以树形结构展示出来,方便用户进行选择和操作。
 
+:::info{title="常见需求拆解"}
+
+- **仅需要展示变量列表** → `useAvailableVariables`
+- **需要展示并响应对象/数组的下钻字段** → `ASTMatch` + 递归渲染
+- **需要精准订阅指定变量/监听变化** → 直接操作 `scope.available`
+
+:::
+
 ### `useAvailableVariables`
 
 `useAvailableVariables` 是一个轻量级的 Hook,它直接返回当前作用域可用的变量数组 (`VariableDeclaration[]`)。
@@ -44,6 +60,12 @@ return availableVariables.map(renderVariable);
 
 当变量的类型是 `Object` 时,我们往往需要能够“下钻”到它的内部,获取其属性。`ASTMatch.isObject` 方法可以帮助我们判断一个变量类型是否为对象。如果是,我们就可以递归地渲染它的 `properties`。
 
+:::tip
+
+变量树的每一层其实都是「声明」(`BaseVariableField`)。在对象场景下,`properties` 就是下一级声明数组。
+
+:::
+
 ```tsx pure title="use-variable-tree.tsx" {12}
 import {
   type BaseVariableField,
@@ -67,6 +89,8 @@ const renderVariable = (variable: BaseVariableField) => ({
 
 与 `Object` 类型类似,当遇到 `Array` 类型的变量时,我们也希望能展示它的内部结构。对于数组,我们通常关心的是其元素的类型。`ASTMatch.isArray` 可以判断变量类型是否为数组。值得注意的是,数组的元素类型可能是任意的,甚至可能是另一个数组。因此,我们需要一个递归的辅助函数 `getTypeChildren` 来处理这种情况。
 
+
+
 ```tsx pure title="use-variable-tree.tsx" {13,16}
 import {
   type BaseVariableField,
@@ -102,6 +126,14 @@ const renderVariable = (variable: BaseVariableField) => ({
 
 `scope.available` 是变量系统的核心之一,可以对 **作用域内可用变量** 进行更加高级的变量获取和监听动作。
 
+:::info{title="何时直接用 scope.available?"}
+
+- 需要通过 keyPath 精确读取或校验变量。
+- 需要在 Hook 之外(如插件、服务)操作变量可见性。
+- 需要订阅变量变化但不希望整个列表刷新。
+
+:::
+
 ### `useScopeAvailable`
 
 `useScopeAvailable` 能够在 React 中直接返回 `scope.available`
@@ -118,7 +150,7 @@ console.log(available.variables);
 console.log(available.getByKeyPath(['start_0', 'xxx']));
 
 // 监听单个变量的变化
-available.trackByKeyPath(['start_0', xxx], () => {
+available.trackByKeyPath(['start_0', 'xxx'], () => {
   // ...
 })
 ```
@@ -170,7 +202,13 @@ const validateVariableInNode = (keyPath: string, node: FlowNodeEntity) => {
 
 当你只关心某个特定变量字段(包括嵌套在 Object 或 Array 中的变量)的变化时,`trackByKeyPath` 能让你精准地“订阅”这个变量的更新,而不会因为其他不相关变量的变化导致组件重新渲染,从而实现更精细的性能优化。
 
-```tsx {6,13-17}
+:::tip
+
+配合 `autoRefresh: false` 使用时,可以避免大范围刷新,只在订阅的变量变化时手动更新组件状态。
+
+:::
+
+```tsx {6,13-18}
 import { useScopeAvailable } from '@flowgram.ai/fixed-layout-editor';
 import { useEffect, useState } from 'react';
 
@@ -262,7 +300,7 @@ function AdvancedListenerComponent() {
 ### `useOutputVariables`
 
 
-useOutputVariables 可以获取**当前作用域的输出变量**,并在输出变量列表或者下钻变化时**自动触发刷新**
+`useOutputVariables` 可以获取**当前作用域的输出变量**,并在输出变量列表或者下钻变化时**自动触发刷新**
 
 ```tsx
 const variables = useOutputVariables();
@@ -293,7 +331,7 @@ const variables = scope.variables;
 
 ### 获取当前作用域
 
-可以通过 [`useCurrentScope`](https://flowgram.ai/auto-docs/editor/functions/useCurrentScope)获取当前的作用域。
+可以通过 [`useCurrentScope`](https://flowgram.ai/auto-docs/editor/functions/useCurrentScope) 获取当前的作用域。
 
 ```tsx
 const scope = useCurrentScope()
@@ -306,7 +344,7 @@ scope.available
 
 ### 设定当前作用域
 
-可以通过 [`ScopeProvider`](https://flowgram.ai/auto-docs/editor/functions/ScopeProvider) 设定当前作用域
+可以通过 [`ScopeProvider`](https://flowgram.ai/auto-docs/editor/functions/ScopeProvider) 设定当前作用域
 
 ```tsx
 // set the scope of current node

+ 51 - 8
apps/docs/src/zh/guide/variable/variable-output.mdx

@@ -10,16 +10,38 @@ description: 介绍如何在 FlowGram 中使用变量引擎输出变量
 2. **输出节点私有变量**:输出变量仅限于节点内部(包括子节点),不能被外部节点访问。
 3. **输出全局变量**:贯穿整个流程,任何节点都可以读取,适合存放一些公共状态或配置。
 
+:::info{title="阅读指引"}
+
+- 读完[变量介绍](./basic.mdx)后,可先从这里开始,通过实践搞清楚「变量是如何被产出的」。
+- 若你从表单配置出发,优先阅读「方式一:通过表单副作用同步」;若需要运行时动态创建变量,请看插件或 UI 相关章节。
+- 文中所有示例均使用了 `ASTFactory`,可先对照[变量概念 - AST 小节](./concept#ast-) 理解 AST 的概念。
+
+:::
+
 ## 输出节点变量
 
-输出节点变量,意味着这个变量和当前节点的生命周期是绑定的。当节点被创建时,变量就诞生了;当节点被删除时,变量也随之消失。
+输出节点变量与当前节点的生命周期绑定:节点创建时变量诞生,节点删除时变量消失。(详见:[节点作用域](./concept#节点作用域))
 
 我们通常有三种方式来输出节点变量:
 
+:::info{title="如何选择方式"}
+
+- 变量定义源于表单配置 → 方式一。
+- 变量在运行时根据逻辑生成或批量同步 → 方式二。
+- UI 里直接写变量只作临时调试,正式环境请避免使用方式三。
+
+:::
+
 ### 方式一:通过表单副作用同步
 
 [表单副作用](/guide/form/form#副作用-effect) 通常在节点的 `form-meta.ts` 文件中进行配置,是定义节点输出变量最常见的方式。
 
+:::info{title="适用场景"}
+
+- 节点的变量模型可以从表单项推导出来。
+
+:::
+
 #### `provideJsonSchemaOutputs`
 
 若节点所需输出变量的结构与 [JSON Schema](https://json-schema.org/) 结构匹配,即可使用官方提供的 `provideJsonSchemaOutputs` 副作用物料。
@@ -94,7 +116,7 @@ export const formMeta =  {
           type: ASTFactory.createObject({
             properties: v.map(_item => ASTFactory.createProperty({
               key: _item.name,
-              type: createTypeFromValue(typeValue)
+              type: createTypeFromValue(_item.typeValue)
             }))
           })
         }
@@ -194,7 +216,7 @@ export const formMeta = {
             meta: {
               title: `Title_${value}`,
             },
-            key: `uid_${node.id}`,
+            key: `uid_${context.node.id}`,
             type: ASTFactory.createString(),
           })
         )
@@ -214,7 +236,7 @@ export const formMeta = {
             meta: {
               title: `Title_${value}`,
             },
-            key: `uid_${node.id}_2`,
+            key: `uid_${context.node.id}_2`,
             type: ASTFactory.createNumber(),
           })
         )
@@ -235,6 +257,13 @@ export const formMeta = {
 
 除了在表单中静态配置,我们还可以在插件(Plugin)中,通过 `node.scope` 更新自由动态地操作节点的变量。
 
+:::info{title="适用场景"}
+
+- 需要跨多个节点批量创建或调整变量。
+- 需要在画布初始化阶段自动补齐默认变量。
+
+:::
+
 
 #### 指定节点的 Scope 进行更新
 
@@ -370,7 +399,13 @@ export const formMeta = {
 
 私有变量是指只能在当前节点及其子节点中访问的变量。(详见:[节点私有作用域](./concept#节点私有作用域))
 
-下面只列举其中两种方式,其他方式可以根据[输出节点变量](#输出节点变量)的方式类推
+:::tip
+
+判断是否要使用私有作用域的小诀窍:变量内容只为节点内部实现服务,并不希望暴露给下游节点时,就放到 `node.privateScope`。
+
+:::
+
+下面只列举其中两种方式,其他方式可以根据[输出节点变量](#输出节点变量)的方式类推。
 
 ### 方式一:`createEffectFromVariableProvider`
 
@@ -391,7 +426,7 @@ export const formMeta =  {
     'path.to.value': createEffectFromVariableProvider({
       scope: 'private',
       // parse form value to variable
-      parse(v: string) {
+      parse(v: string, { node }) {
         return [{
           meta: {
             title: `Private_${v}`,
@@ -430,7 +465,7 @@ export const formMeta = {
             meta: {
               title: `Your Private Variable Title`,
             },
-            key: `uid_${node.id}`,
+            key: `uid_${context.node.id}`,
             type: ASTFactory.createInteger(),
           })
         )
@@ -451,7 +486,15 @@ export const formMeta = {
 
 ## 输出全局变量
 
-全局变量就像是整个流程的“共享内存”,任何节点、任何插件都可以访问和修改它。它非常适合用来存储一些贯穿始终的状态,比如用户信息、环境配置等等。
+全局变量就像是整个流程的“共享内存”,任何节点、任何插件都可以访问和修改它。它非常适合用来存储一些贯穿始终的状态,比如用户信息、环境配置等等。(详见:[全局作用域](./concept#全局作用域))
+
+:::info{title="何时选用全局作用域"}
+
+- 变量在多个流程节点甚至插件中被复用。
+- 变量与具体节点解耦,例如环境配置、用户上下文。
+- 需要在流程初始化阶段就写入,后续节点只需读取。
+
+:::
 
 和节点变量类似,我们也有两种主要的方式来获取全局变量的作用域(`GlobalScope`)。