Workflow Nodes
DocsGPT workflows are composed of Nodes that are connected to form a processing graph. These nodes interact with a Shared State—a global dictionary of variables that persists throughout the execution of the workflow.
The recording starts in a new workflow agent’s builder and drags an AI Agent node, an If / Else node and two End nodes from the palette onto the canvas. It wires Start → AI Agent → If / Else, and the If / Else node’s If and Else branches to one End node each. It gives the AI Agent node the “Espresso One handbook” knowledge source, then sets the If / Else node in Simple mode to a branch named “Refund” where query contains refund. It clicks Publish, opens Preview and asks for a refund: the run steps through Start, the AI Agent, If / Else and End, and the answer is quoted from the handbook.
| Node | Palette group | What it does |
|---|---|---|
| Start | Added automatically | Entry point of the run |
| AI Agent | Core Nodes | Calls a model, with optional knowledge, tools and documents |
| End | Core Nodes | Finishes the run |
| Note | Core Nodes | A comment on the canvas |
| Set State | Logic & Data | Writes variables with CEL expressions |
| If / Else | Logic & Data | Branches on conditions |
| Code | Logic & Data | Runs Python in the sandbox |
The Shared State
Every workflow run maintains a state object (a JSON-like dictionary).
- Initial State: Contains the user’s input query (
query) and chat history (chat_history). - Modifying State: Nodes read from this state and write their outputs back to it.
- Node Outputs: Each node writes its result to
node_<node_id>_output.
Two ways to reference state
Which syntax a field takes depends on the field, so check this table before
typing {{ }} anywhere:
| Field | Node | Syntax |
|---|---|---|
| Prompt Template | AI Agent | Template — {{variable_name}} |
| Output Template | End | Template — {{variable_name}} |
| Expression | Set State | CEL — bare variable_name |
| Expression | Condition | CEL — bare variable_name |
Template fields substitute {{variable_name}} into surrounding text, so
Analysis: {{analysis_result}} produces a string.
Expression fields are evaluated as Common Expression
Language , where a bare name is the variable. Write
query, not {{query}} — the braces are a syntax error there, and saving a
workflow that contains one is rejected with the correction.
Start and End Nodes
Every workflow has exactly one Start node, added for you. It has no
settings and can’t be deleted; the run begins there with query and
chat_history in the state.
An End node finishes the run. Add End nodes from the palette where branches
should stop (several branches can share one); a workflow needs at least one,
and each needs an incoming connection. End nodes have no settings in the builder. A workflow saved or
imported through the API can give an End node an output_template (template
syntax, like a Prompt Template); its rendered text is sent to the user as the
last part of the answer.
AI Agent Node
The AI Agent Node is the core processing unit. It uses a Large Language Model (LLM) to generate text, answer questions, or perform tasks using tools.
Settings
Model
- Agent Type: Classic (retrieves from the node’s knowledge before answering) or Research (plans and searches in several steps).
- Model: the model this node calls. Every agent node needs one.
Prompt
- System Prompt: instructions that define the node’s persona and
constraints. Defaults to
You are a helpful assistant. - Prompt Template: the text sent to the model as the user turn. It supports
template variables, for example
Summarize the following text: {{user_input_text}}. If left empty, it defaults to the initial user query ({{query}}).
Knowledge
- Knowledge: sources this node searches.
- Tools: tools the node’s model can call.
A tool that requires approval fails the node, and with it the run: a workflow can’t pause for someone to approve a call. Turn approval off for tools you use in workflow nodes. Nodes also run only the instance guardrail floor, not the parent agent’s guardrails; see Guardrails scope.
Output
- Output Variable: the name of the variable where the result will be saved.
The result is always saved as
node_<node_id>_outputtoo; set this to something meaningful, likesummaryortranslated_text. - Stream output to user: shows this node’s answer in the chat as it’s written, in addition to saving it to the state. On by default.
Advanced settings
- Documents: which documents the node sees — All input docs, None,
or Choose… a specific set (upstream artifact references or refs such as
A1). - File passing: how a chosen document reaches the model. Auto sends the file natively when the model accepts that file type and extracts it to text otherwise; Native always sends the file (and fails on an unsupported type); Extract always sends extracted text.
- Structured Output (JSON Schema): makes the model return JSON matching the schema. The parsed, validated object is saved as the node’s output instead of text. The model must support structured output.
Set State Node
The Set State Node allows you to manipulate variables within the shared state directly without calling an LLM. This is useful for initialization, formatting, or control flow logic.
Operations
You can define multiple assignments in a single node. Each assignment has two parts:
- Variable: the state key to write to.
- Value (CEL): a CEL expression evaluated against the current state. Its result becomes the variable’s value.
Reference state variables by bare name. {{ }} is template syntax and does
not work here — an operation must supply both parts, or it is rejected
when you save.
| Goal | Variable | Value (CEL) |
|---|---|---|
| Initialize a counter | retry_count | 0 |
| Increment a counter | retry_count | retry_count + 1 |
| Copy a node’s output to a stable name | context | node_search_1_output |
| Build a string | formatted_response | "Analysis: " + analysis_result |
| Append to a list | history_list | history_list + [last_result] |
| Derive a boolean | needs_review | size(context) < 100 |
CEL supports arithmetic, string concatenation with +, comparisons,
&&/||, ternaries (cond ? a : b), and built-ins such as size(),
startsWith(), and contains().
Usage Examples
- Loop Counters: Initialize
retry_countto0before a loop, then set it toretry_count + 1inside the loop. - Accumulators: Collect results across branches with
history_list + [last_result]. - Renaming: Copy a previous node’s output to a generic name (target
context, expressionnode_search_1_output) so later nodes can use one standard variable.
Condition Node
The Condition Node (If / Else in the palette) branches the workflow. Each case pairs a condition with an outgoing branch; the first case whose condition is true wins, and execution follows that branch.
The node has two modes:
- Simple: pick a Variable, an Operator (
=,!=,>,<,>=,<=, contains or starts) and a Value. The builder turns each row into a CEL expression, for examplecategory == "refund"orquery.contains("invoice"). The variable can be a dotted path such asaudit.high_risk_accounts, and every case must name one. - Advanced: write each case as a CEL expression yourself.
Both modes run the same way:
- Expressions use the same syntax as the Set State node: reference state by
bare name, not
{{ }}. - Every condition node needs an else branch, which is taken when no case matches.
- Each case with an expression must have an outgoing edge, and every branch must eventually reach an end node.
| Goal | Expression |
|---|---|
| Route on a previous answer | node_classify_1_output == "refund" |
| Guard on retrieved context | size(context) > 0 |
| Combine checks | needs_review && retry_count < 3 |
| Match text | query.contains("invoice") |
A case whose expression fails to evaluate at run time is skipped and the next case is tried, so a workflow that always lands on else usually means an expression is referencing a variable that no earlier node writes.
Code Node
The Code Node runs a Python script in a sandboxed session bound to the workflow run. Use it to transform data, parse files, cross-check documents, or build a report that later nodes consume. Every Code node and tool call in one run shares the same session.
-
Code: the script to run. It is never templated:
{{ }}in the code is literal text. The script reads the workflow state fromstate.jsonin its working directory (chat_historyis left out):import json state = json.load(open("state.json")) total = len(state["query"])The script can’t change the shared state. Editing
state.jsonhas no effect on later nodes; pass results through the node’s output instead. -
Input files: artifacts to stage in the workspace before the script runs, under
inputs/<filename>. Each entry is a state variable holding an artifact reference, a full artifact id or a short ref such asA1from this run. -
Output Variable: an extra name for the node’s output, which is always saved as
node_<node_id>_output. -
Timeout (seconds): optional. It can’t exceed
SANDBOX_EXEC_TIMEOUT, which is also the default. -
Structured Output (JSON Schema): optional; see the output rules below.
What the node outputs:
| Setup | Node output |
|---|---|
| No schema, the script created or changed files | A reference to the first captured artifact (artifact_id, version, mime_type, filename, …) |
| No schema, no files | {"artifacts": [], "status": "ok"} |
| JSON schema set | The JSON the script prints to stdout, validated against the schema. When files were captured too, they are added under an artifacts key. |
Use a schema when later nodes need to branch on a decision: print one JSON
object, for example print(json.dumps({"approved": total > 10})), then test
node_code_1_output.approved in an If / Else node. Stdout that isn’t valid JSON,
or doesn’t match the schema, fails the node.
Files the script creates or changes are saved as artifacts and shown in the run
view; files under tmp/ and temporary files (.tmp, .lock, .pyc) are
skipped. A script that raises an error fails the node and the run. See
Artifacts and Code Execution for the
sandbox backends and configuration.
Note Node
A Note holds free text for whoever edits the workflow: what a branch is for, or what still needs doing. It has no effect on a run.
Graph rules
The builder checks these before a preview, save or publish, and lists every rule a workflow breaks:
- The workflow has a name, exactly one Start node, at least one End node and at least one AI Agent node.
- The Start node connects to another node, and every End node has an incoming connection.
- Every AI Agent node has a model selected. A node with a JSON schema needs a valid schema and a model that supports structured output.
- Every If / Else node has at least one case with an expression, an Else branch and at least two outgoing connections. Each case with an expression has exactly one connection, each connection comes from a known case, and in Simple mode each case names a variable.
- Every If / Else branch eventually reaches an End node.
- Every Code node has code, and any JSON schema it has is valid.
A run stops after 50 node steps, so a loop needs an exit condition.
One workflow per agent
A workflow belongs to the agent it was built in, and deleting a workflow agent
deletes its workflow. Over the API, creating an agent (POST /api/create_agent)
with a workflow that another agent already uses, or switching an agent to one
with PUT /api/update_agent/<agent_id>, answers 409 with “This workflow
belongs to another agent”. Create a new workflow for the new agent instead.