콘텐츠로 이동

Zio.Ontology()

설계서를 만들고, 그 설계서대로 세웁니다. 둘 다 이 묶음입니다 — 앞엣것은 종이를 그리는 일이고, 뒤엣것은 그 종이대로 을 넣는 일입니다.

설계서에는 어떤 노드에 어떤 속성이 있고 무엇이 무엇과 이어지는지가 적혀 있습니다. 웹의 온톨로지 스키마 에디터에서 그리는 그 종이입니다. IDE 에서 스킬을 짤 때는 화면이 없으니, 여기서 만들고 확인은 나중에 웹에서 합니다.

담당자가 자연어로 업무 흐름을 말하면 그것을 설계서로 옮기는 자리입니다. 좌표·색·내부 식별자는 SDK 가 채우니 뜻만 적으십시오.

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) 설계서를 새로 세우고 twin_id 를 돌려줍니다. nodes·edges 는 안 줘도 되고, 나중에 낱개로 더해도 됩니다.
Zio.ontology.list() 설계서 목록. {twin_id, title, status, node_count, edge_count}.
↓ 여기부터는 설계서 하나를 집어 거는 것들입니다 — 점(.) 앞에 Zio.ontology(twin_id) 가 옵니다. list()·create() 처럼 Zio.ontology. 로 바로 부르지 않습니다. 집는 법은 아래 설계서대로 세우기 절을 보십시오.
Zio.ontology(twin_id).info() 설계서를 통째로 읽습니다. {twin_id, title, nodes[], edges[], warnings[]}.
.update(title, description, status) 설계서의 이름표를 고칩니다.
.drop() 설계서를 지웁니다. 이미 쌓인 Neo4j 노드는 보지도 건드리지도 않습니다 — 설계서는 노드를 세울 때 참고하는 종이일 뿐이라, 십만 건이 쌓였다고 종이를 못 지울 이유가 없습니다.
.add_node(name, title, properties=[], display_property=None) 노드를 세웁니다. display_property 는 그래프에서 원 안에 뜨는 이름이고 속성 이름 하나를 줍니다. _ 로 시작하는 이름은 다른 트윈의 노드를 가리키는 참조 노드로, 열쇠(PK)만 들고 세웁니다 — 아래 상자를 보십시오.
.update_node(name, title, description, display_property) 노드의 이름표를 고칩니다. name 은 못 바꿉니다 — 바꿔도 이미 그 라벨로 쌓인 노드는 안 따라옵니다(설계서는 지나간 데이터에 소급되지 않습니다). rename_node 를 부르면 왜 없고 어떻게 해야 하는지를 담은 AttributeError 가 옵니다 — 이름만 다르게 보이면 되는 것이면 title 을 고치고, 정말로 갈아야 하면 새로 세우고 옛것을 지운 뒤 옛 라벨의 노드를 어떻게 할지는 사람이 정합니다.
.drop_node(name) 노드를 지웁니다. 붙어 있던 엣지도 함께 지우고 몇 개였는지 돌려줍니다. 남겨 두면 허공을 가리키는 엣지가 되어 웹에서 화면이 깨집니다.
.add_property(node, name, title, type, unique, required, source) 속성 한 칸을 더합니다. uniquesource 가 무엇인지는 아래 두 상자를 꼭 읽으십시오.
.update_property(node, name, ...) 준 칸만 고치고 나머지는 그대로 둡니다.
.drop_property(node, name) 속성을 뺍니다. 그것이 display_property 였으면 그 칸도 같이 비웁니다 — 없어진 속성을 가리키는 표시명은 그래프에 빈 원으로 보입니다.
.add_edge(source, target, rel, label, source_key, target_key) 노드 둘을 잇습니다. relNeo4j 에 실제로 서는 관계 이름입니다 — add_edge("Worklog", "Author", "WRITTEN_BY") 는 그래프에 (Worklog)-[:WRITTEN_BY]->(Author) 로 섭니다. label 은 화면에만 쓰는 설명입니다. source_key·target_key 는 어느 속성끼리 맞춰 이을지 정하는 조인 키입니다(여럿이면 목록).
.drop_edge(source, target, rel=None) rel 을 안 주면 그 두 노드 사이의 엣지를 모두 지웁니다.

언더바(_) — 다른 트윈의 노드를 가리키는 지름길

섹션 제목: “언더바(_) — 다른 트윈의 노드를 가리키는 지름길”

설계서를 그리다 다른 트윈 안에 이미 있는 노드(예: 공용 Author·Project)에 이어야 할 때, 그 노드를 여기서 다시 정의하지 않고 이름 앞에 _ 를 붙여 가리킵니다. 화면에서 「이 노드가 저기 있는 노드와 이어진다」를 보여 주는 설계서 위의 표기입니다. 두 규칙만 지키면 됩니다.

  1. 노드 이름 앞에 _ 를 붙입니다. (_Author, _Project)
  2. 속성은 열쇠(PK)만 적습니다. 가리키는 데 필요한 건 짝을 맞출 열쇠뿐이라, 나머지 칸은 의미가 없습니다 — 읽기 좋으라고 열쇠 한둘만 남깁니다.

언제 _ 를 쓰나 — 이 트윈이 그 노드의 값을 (수집으로) 채우는 주인이면 일반 노드로, 이미 다른 트윈이 채우고 있고 당신은 잇기만 하면 될 때 _ 로. 「다른 트윈에 있는지 먼저 찾아볼」 필요는 없습니다 — 같은 이름이 같은 노드라는 보장이 없고, 판단 기준은 존재 여부가 아니라 **「내가 채울 주인인가」**입니다.

**「설계서에서는 만들 수 있고, Neo4j 에는 서지 않는다」**가 핵심입니다. 저장할 때 _ 는 벗겨져 같은 라벨의 그 노드로 MERGE 됩니다(_Project → 그래프의 Project) — 재정의가 아니라 가리키기라, 열쇠만 들고 와 잇기만 하고 그 노드의 제목·소속·표시명은 덮어쓰지 않습니다. 그래서 실제 insert/update 때 _ 붙은 노드가 새로 생기는 일은 없습니다.

Zio.ontology.create(title=“WBS”, nodes=[ {“name”: “Worklog”, “properties”: [ {“name”: “worklog_id”, “unique”: True, “source”: “@id”}]}, {“name”: “_Project”, “properties”: [ # 다른 트윈 노드 참조 (열쇠만) {“name”: “project_code”, “unique”: True, “source”: “@project_code”}]}, ], edges=[{“source”: “Worklog”, “target”: “_Project”, “rel”: “BELONGS_TO”}])

add_node·createSDK 에서 바로 만들 수 있습니다 — 웹으로 되돌아가 고칠 필요가 없습니다. 참조 노드의 열쇠에 source 를 주면 수집 데이터가 그 값을 채워 자동으로 이어지고, 값을 직접 넘길 땐 아래 .save() 에서 _Project열쇠만 실어 보냅니다.

unique — 같은 노드인지 가르는 열쇠

섹션 제목: “unique — 같은 노드인지 가르는 열쇠”

속성 중 가장 중요한 칸입니다. 이것으로 「이미 있는 그 노드」인지 「새 노드」인지를 가릅니다.

unique 개수 무슨 일이 일어나나
0 개 실행할 때마다 노드가 새로 쌓입니다(MERGE 가 아니라 CREATE). 오류가 안 나서 몇 주 뒤에야 압니다.
1 개 그 값으로 MERGE. 있으면 두고 없으면 만듭니다.
2 개 이상 값을 _ 로 이어 열쇠로 씁니다. 한 칸이라도 비면 그 건은 통째로 안 섭니다.

정한 열쇠의 값이 비면 그 건만 건너뜁니다 — 열쇠 없는 유령 노드를 막는 것입니다.

안 주면 그 속성은 늘 비어 있습니다. 오류는 안 나고 그 칸만 빠진 채 노드가 섭니다.

적는 것
@issues.title 수집 데이터의 issues 안의 title
@worklog_id 수집 데이터의 worklog_id
그냥 글자 적어 둔 글자가 곧 값 (상수)

@ 뒤 경로의 앞부분이 배열이면 원소마다 노드가 하나씩 섭니다 — @issues.title 에서 issues 가 셋이면 노드도 셋입니다. 배열을 노드 여럿으로 펴는 유일한 방법입니다.

노드 속성은 시스템 필드와 한 자리에 평평하게 담깁니다. 그래서 겹치면 시스템 쪽이 이기고, 오류 없이 조용히 어긋납니다. SDK 가 미리 막습니다.

이름 겹치면
id · source · target · labels 그래프가 식별자로 씁니다. 연결선이 안 그려집니다
title · twin_id 저장할 때 시스템이 덮어씁니다
x·y·z·vx·vy·vz·fx·fy·fz·index·color 그래프 화면이 덮어씁니다
_ 로 시작하는 것 시스템 예약입니다(_display)
name 막지는 않지만, 쓰면 자동 표시명이 그 값으로 고정됩니다

설계서를 손에 쥐고 그리는 대로 세웁니다. 노드도 엣지도 설계서가 압니다. 스킬은 값만 넘깁니다 — 어느 엣지를 어느 방향으로 걸지는 한 줄도 쓰지 않습니다.

Zio.node().merge()노드 하나를 세웁니다. 엣지는 merge_relation() 으로 직접 걸어야 합니다. 노드가 둘 셋으로 늘고 트리까지 있으면, 산업별 담당자가 설계서를 그려 놓고도 같은 그림을 파이썬으로 또 그리게 됩니다. 그 자리를 이 묶음이 대신합니다.

Method Description
Zio.ontology(twin_id, entry_node=None) 설계서 한 장을 집습니다. twin_id 는 설계서 uuid 이고, 노드에 twin_id 속성으로 그대로 박히는 값입니다. entry_node뿌리 노드 이름으로, 쓰기에만 필요합니다.
.save(data) 설계서에 그려진 대로 노드와 엣지를 세웁니다. data{노드이름: [인스턴스, …]} 입니다.

두 인자는 에이전트의 「온톨로지 노드 연결」과 같은 것입니다. 에이전트 빌더에서 설계서와 진입 노드를 고르는 그 두 칸이 여기서는 인자가 됩니다.

# 에이전트가 부르는 모양 (수집 경로 — 값을 설계서의 @경로가 끌어온다)
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월 정기점검"}],
})

이 한 번으로 WbsItem 두 개가 서고, REPORT_ON · BELONGS_TO · HAS_SUBTASK 가 함께 걸립니다. 엣지 이야기는 코드에 없습니다.

자리 규칙
노드 이름 설계서에 적힌 그대로 씁니다. 언더바 접두도 그대로입니다 (_Project). 라벨은 SDK 가 벗겨 냅니다 — 그래프에는 Project 로 들어갑니다. 설계서에 없는 이름은 조용히 무시됩니다.
인스턴스 목록이면 원소 하나가 노드 하나입니다. 낱개 dict 도 받습니다. 속성 이름은 설계서의 속성명입니다.
안 넣는 것 name · _display · title · twin_idSDK 가 붙입니다. 넣으면 계산된 값을 밀어냅니다.

엣지는 이번에 넘긴 노드끼리만 걸립니다. 설계서에 그려져 있어도 한쪽을 안 넘기면 그 엣지는 안 섭니다.

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

그래서 언더바 접두 노드는 열쇠만 넘깁니다. 이미 있는 노드를 찾아 붙이는 것이 목적이고, 속성은 그 노드를 만든 설계서가 채웁니다. 열쇠만 넘기면 그 노드의 속성은 하나도 안 바뀝니다.

자기 자신을 가리키는 엣지는 설계서가 짝을 짓습니다

섹션 제목: “자기 자신을 가리키는 엣지는 설계서가 짝을 짓습니다”

WBS 트리나 선행 작업처럼 같은 노드끼리 잇는 엣지는 설계서의 「키 매핑」이 짝을 정합니다. 스킬은 부모 이름을 속성으로 적어 두기만 하면 됩니다.

엣지 sourceKey targetKey
HAS_SUBTASK project_code, item_name project_code, parent_item_name
PRECEDES project_code, item_name project_code, previous_item_name

가리키는 부모가 아직 없으면 이름만 든 껍데기로 세웁니다. 나중에 그 부모의 값이 들어오면 채워집니다. 트리가 끊기는 것보다 낫다고 보았습니다.

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

saved넘긴 수가 아니라 선 수입니다. 둘이 다르면 skipped 를 보십시오.

written“썼는가” 이지 “성공했는가” 가 아닙니다. 뿌리 열쇠가 비어 아무것도 안 쓴 것은 실패가 아니라 판단입니다 — 값이 안 온 것뿐이고, 그때는 되묻기를 걸 자리입니다.

규칙
전부 되거나 전부 안 된다 노드와 엣지를 다 만든 뒤 한 번에 실행합니다. 도중에 어긋나면 그래프에는 아무것도 안 쓰입니다. 반쯤 선 그래프가 없습니다.
열쇠가 비면 안 세운다 아무도 못 찾는 노드가 생기지 않게 합니다. 그 인스턴스만 건너뛰고 나머지는 그대로 섭니다.
두 번 돌려도 같다 열쇠로 찾아 없으면 만들고 있으면 갱신합니다. 같은 값을 다시 넘겨도 노드가 늘지 않습니다.
넘긴 칸만 갱신한다 이미 있는 노드에는 이번에 값을 준 칸만 씁니다. 설계서를 고쳐도 지나간 노드를 다시 쓰지 않습니다.
Zio.node().merge() Zio.ontology().save()
세우는 것 노드 하나 설계서에 그려진 노드와 엣지
엣지 merge_relation() 으로 직접 설계서가 안다
설계서 안 본다 본다
쓸 때 속성 한 칸 고치기 · 되묻기 반영 추론이 만든 것을 통째로 세우기

뿌리가 없으면 아무것도 안 씁니다

섹션 제목: “뿌리가 없으면 아무것도 안 씁니다”

entry_node 로 준 노드가 이번 데이터의 뿌리입니다. 그 노드의 열쇠가 비어 있으면 저장을 통째로 중단합니다 — 뿌리 없이 가지만 남으면 나중에 아무도 그것을 찾아가지 못합니다. 위 예에서 WbsItem 을 뿌리로 준 이유가 이것입니다.