Skip to content

Zio.Ontology()

Create a design document and build according to it. Both are part of the same bundle — the former is drawing paper, while the latter is filling that paper with values.

The design document specifies which nodes have which attributes and how they connect to each other. It is the paper drawn in the web Ontology Schema Editor. When writing a skill in an IDE, there is no screen, so you create it here and verify later on the web.

When the owner describes the workflow in natural language, this is where it gets turned into a design document. The SDK fills in coordinates, colors, and internal identifiers—just write the meaning.

from zio_ontology import Zio
got = Zio.ontology.create(
title="업무관리",
nodes=[
{"name": "Worklog", "title": "업무일지", "display_property": "subject",
"properties": [
{"name": "worklog_id", "title": "일지ID", "unique": True, "source": "@worklog_id"},
{"name": "subject", "title": "제목", "source": "@subject"},
]},
{"name": "Work", "title": "작업",
"properties": [
{"name": "work_name", "title": "작업명", "unique": True, "source": "@works.name"},
]},
],
edges=[{"source": "Worklog", "target": "Work", "rel": "HAS_WORK", "label": "일지의 작업"}],
)
tid = got["twin_id"]
for w in got["warnings"]:
print(w) # 오류가 안 나서 놓치기 쉬운 것들. 담당자에게 그대로 옮기십시오
Method Description
Zio.ontology.create(title, nodes, edges) Creates a new design document and returns its twin_id. You may omit nodes and edges; they can be added later one by one.
Zio.ontology.list() List of design documents: {twin_id, title, status, node_count, edge_count}.
↓ The following are for working with a single design document — the prefix Zio.ontology(twin_id) appears before the dot. They are not called directly as Zio.ontology. like list() or create(). See the “Build Design Document” section below for how to use them.
Zio.ontology(twin_id).info() Reads the entire design document: {twin_id, title, nodes[], edges[], warnings[]}.
.update(title, description, status) Updates the name label of the design document.
.drop() Deletes the design document. Existing Neo4j nodes are untouched—the design document is just a reference sheet; even if there are hundreds of thousands of nodes, you can still delete the sheet.
.add_node(name, title, properties=[], display_property=None) Adds a node. display_property is the name shown inside the graph circle and takes one property name. Names starting with _ refer to nodes in another twin; they are created with only the primary key (PK)—see the box below.
.update_node(name, title, description, display_property) Updates a node’s label. You cannot change name—changing it does not affect already‑created nodes with that label (the design document is not retroactive). Calling rename_node throws an AttributeError explaining why and how to proceed. If you only need the name to look different, update title; if you truly need a new node, create one, delete the old one, and decide what to do with the old label’s nodes yourself.
.drop_node(name) Deletes a node. All connected edges are also removed, and the number of deleted edges is returned. Leaving orphaned edges causes broken visuals on the web interface.
.add_property(node, name, title, type, unique, required, source) Adds a property field. Read the two boxes below to understand unique and source.
.update_property(node, name, ...) Updates only the specified fields; the rest remain unchanged.
.drop_property(node, name) Removes a property. If it was the display_property, that slot is cleared as well—edges pointing to the removed property appear as empty circles in the graph.
.add_edge(source, target, rel, label, source_key, target_key) Connects two nodes. rel is the actual Neo4j relationship name; e.g., add_edge("Worklog", "Author", "WRITTEN_BY") creates (Worklog)-[:WRITTEN_BY]->(Author). label is only for display. source_key and target_key specify which properties to join on (can be a list if multiple).
.drop_edge(source, target, rel=None) If rel is omitted, all edges between the two nodes are removed.

Underscore (_) — Shortcut for Referencing Nodes in Other Twins

Section titled “Underscore (_) — Shortcut for Referencing Nodes in Other Twins”

When you need to connect to an existing node inside another twin (e.g., shared Author or Project), do not redefine it here; instead prefix its name with _. This notation appears only on the design document and indicates that the node is linked. Keep these two rules:

  1. Prefix the node name with _. (_Author, _Project)
  2. Only list the primary key (PK) properties. The join requires only the PK, so other fields are omitted for readability.

When to use _ — If this twin is responsible for populating that node’s values (via collection), treat it as a normal node; if another twin already populates it and you just need to link, use _. You do not need to first check whether the node exists in another twin—there is no guarantee that identical names refer to the same node. The decision criterion is whether you are the owner of the data.

The key point: You can create it in the design document, but it does not exist in Neo4j until you merge it. When saving, _ is stripped and a MERGE occurs on the same label (_Project → graph’s Project). This is not a redefinition; it merely references the node with its PK, leaving title, ownership, and display name untouched. Therefore, no new node is inserted when you use an underscore.

Zio.ontology.create(title="WBS", nodes=[
{"name": "Worklog", "properties": [
{"name": "worklog_id", "unique": True, "source": "@id"}]},
{"name": "_Project", "properties": [ # Reference node in another twin (PK only)
{"name": "project_code", "unique": True, "source": "@project_code"}]},
], edges=[{"source": "Worklog", "target": "_Project", "rel": "BELONGS_TO"}])

You can create it directly with add_node or create in the SDK—no need to edit it later via the web. If you provide a source for the reference node’s PK, collection data will automatically link; otherwise, when calling .save(), only the PK is sent for _Project.

unique — Key that Determines Whether a Node Already Exists

Section titled “unique — Key that Determines Whether a Node Already Exists”

This is the most important property field. It determines whether a node is already present or new.

Number of unique fields What Happens
0 A new node is created on every run (CREATE, not MERGE). Errors may surface only weeks later.
1 The value is used for MERGE. If it exists, it is kept; if not, a new node is created.
2 or more The values are joined as keys via _. If any key field is empty, the entire node is skipped.

If a key’s value is missing, that particular node is simply omitted—preventing ghost nodes without keys.

If omitted, the property remains empty. No error occurs; the node is created with an empty field.

Entry Meaning
@issues.title The title inside the issues collection of the collected data
@worklog_id The worklog_id from the collected data
Plain text The literal string becomes the value (constant)

If the part before @ is an array, a node is created for each element—@issues.title with three items creates three nodes. This is the only way to expand an array into multiple nodes.

Node properties coexist flatly with system fields; if they overlap, the system field wins silently without error. The SDK blocks this in advance.

Name Overlap Causes
id, source, target, labels These are used as graph identifiers; edges will not be drawn.
title, twin_id System overwrites them on save.
x, y, z, vx, vy, vz, fx, fy, fz, index, color These override the graph’s visual rendering.
Names starting with _ Reserved for system (_display).
name Not blocked, but if used it becomes the automatic display name.

We build exactly as drawn in the design document, holding it in our hands. The design document also specifies nodes and edges. Skills only pass values — they do not write any line about which edge goes in which direction.

Zio.node().merge() creates a single node. Edges must be attached directly with merge_relation(). If the number of nodes grows to two or more and even forms a tree, industry‑specific managers will draw the design document and then draw the same diagram again in Python. This bundle takes over that role.

Method Description
Zio.ontology(twin_id, entry_node=None) Retrieves one page of the design document. twin_id is the UUID of the design document and is stored as the node’s twin_id attribute unchanged. entry_node is the root node name and is only needed for writing.
.save(data) Creates nodes and edges exactly as drawn in the design document. data is {node_name: [instances, …]}.

The two arguments are the same as the agent’s “ontology node connection”. The two fields where you pick a design document and an entry node in the agent builder become arguments here.

# 에이전트가 부르는 모양 (수집 경로 — 값을 설계서의 @경로가 끌어온다)
execute_ontology_entry("lAOZtMeH", "Worklog", state)
# 스킬이 부르는 모양 (값을 직접 준다)
Zio.ontology("tNpBkLim", "WbsItem").save({
"WbsItem": [
{"project_code": "PRJ-0071", "item_name": "GS25 8월 정기점검",
"status": "진행중", "progress_pct": 62,
"parent_item_name": "GS25 정기점검 3Q"},
{"project_code": "PRJ-0071", "item_name": "GS25 정기점검 3Q",
"status": "진행중"},
],
"_Project": [{"project_code": "PRJ-0071"}],
"_Work": [{"worklog_id": "weekly_sjlee1_2026_32", "work_name": "8월 정기점검"}],
})

With this single call, two WbsItem nodes are created, and the edges REPORT_ON, BELONGS_TO, and HAS_SUBTASK are attached together. There is no edge code in the snippet.

Position Rule
Node name Write it exactly as it appears in the design document. Underscore prefixes are kept (_Project). The SDK strips the label — the graph receives Project. Names not present in the design document are quietly ignored.
Instance If a list, each element is one node. A single dict is also accepted. Attribute names match those in the design document.
Do not include name, _display, title, twin_idthe SDK attaches these automatically. Including them overrides calculated values.

Edges are attached only between nodes that were passed in this call. Even if the design document shows an edge, if one side is not passed, that edge will not be created.

# _Project 를 안 넘기면
Zio.ontology("tNpBkLim", "WbsItem").save({"WbsItem": [...]})
# → WbsItem 은 선다
# → WbsItem -[BELONGS_TO]-> Project 는 안 선다
# 열쇠만 넘기면 붙는다
"_Project": [{"project_code": "PRJ-0071"}]

Therefore only underscore‑prefixed key nodes are passed as keys. The purpose is to find and attach existing nodes; the attributes of those nodes are filled by the design document that created them. Passing only the key means none of the node’s attributes change.

Self‑referencing edges are paired by the design document

Section titled “Self‑referencing edges are paired by the design document”

Edges that connect the same node, such as in a WBS tree or precedence tasks, are paired by the design document’s “key mapping.” The skill merely records the parent name as an attribute.

Edge sourceKey targetKey
HAS_SUBTASK project_code, item_name project_code, parent_item_name
PRECEDES project_code, item_name project_code, previous_item_name

If the referenced parent does not yet exist, a placeholder node with only a name is created. When the parent’s value arrives later, it will be filled in. This approach was deemed better than breaking the tree.

{
"written": True, # 썼는가
"saved": {"WbsItem": 2, "_Project": 1, "_Work": 1}, # 실제로 세운 수
"skipped": [
{"node": "WbsItem", "index": 3, "reason": "열쇠가 비었습니다"},
],
"message": "Data saved successfully to Neo4j.",
}

saved represents the number of items passed, not the number that succeeded. If they differ, see skipped.

written indicates whether something was written rather than whether it succeeded. If the root key is empty and nothing is written, this is a judgment, not a failure — the value simply did not arrive, and at that point a retry is warranted.

Rule Reason
All or none Nodes and edges are created in one batch. If something goes wrong midway, nothing is written to the graph. A partially drawn graph does not exist.
Do not create if key is empty Prevents nodes that cannot be found from being created. The instance is skipped while the rest proceeds.
Idempotent on repeated runs If a node is missing, it is created; if it exists, it is updated. Re‑passing the same values does not increase node count.
Only update passed fields For existing nodes, only the fields provided in this call are written. Even if the design document changes, previously written nodes are not rewritten.
Zio.node().merge() Zio.ontology().save()
What it builds One node Nodes and edges drawn in the design document
Edges Attached directly with merge_relation() Handled by the design document
Design document Not consulted Consulted
When writing Update a single attribute field, reflect retries Build everything inferred as a whole

The node given as entry_node is the root of this data set. If its key is empty, the entire save operation is aborted — without a root, any subsequent traversal cannot find it. This is why WbsItem was provided as the root in the example above.