그래프 10. 완전 레퍼런스 — 필드, 에러 코드, 안전 설계 체크리스트
이 문서는 euleragent graph 모듈의 완전한 참조 문서입니다. 튜토리얼에서 배운 모든 개념의
빠른 참조 자료로 사용하십시오.
1. Graph YAML 전체 필드 테이블
1.1 최상위 필드
| 필드 |
타입 |
필수 |
설명 |
id |
string |
O |
그래프 식별자. 관례: graph.{name} |
version |
integer |
O |
그래프 버전 번호 (1부터 시작) |
category |
string |
O |
카테고리 (research, engineering, marketing 등) |
description |
string |
O |
그래프 설명 |
checkpointer |
boolean/string |
조건부 |
interrupt_before/interrupt_after 사용 시 필수. true, "memory", "sqlite" 중 하나 |
state_schema |
dict |
조건부 |
parallel_groups 사용 시 필수 |
parallel_groups |
list |
X |
병렬 팬아웃/팬인 그룹 목록 |
defaults |
dict |
O |
실행 제약 (max_iterations 포함) |
nodes |
list |
O |
노드 목록 |
edges |
list |
O |
엣지 목록 |
finalize |
dict |
O |
최종 노드 설정 |
1.2 state_schema 필드
state_schema:
<key_name>:
type: string | integer | float | list | dict
merge: append_list | sum_int | concat_str | last_write | first_write
| 필드 |
타입 |
필수 |
설명 |
type |
enum |
O |
상태 값의 데이터 타입 |
merge |
enum |
O |
병렬 쓰기 시 충돌 해결 전략 (리듀서) |
1.3 parallel_groups 필드
parallel_groups:
- id: <group_id>
branches: [<node_id>, ...]
join: <join_node_id>
| 필드 |
타입 |
필수 |
설명 |
id |
string |
O |
그룹 식별자 |
branches |
list[string] |
O |
병렬 실행할 노드 ID 목록 (2~8개) |
join |
string |
O |
팬인 조인 노드 ID |
1.4 defaults 필드
| 필드 |
타입 |
필수 |
설명 |
max_iterations |
integer |
루프 시 필수 |
루프 기준 노드 최대 실행 횟수 |
max_total_tool_calls |
integer |
X |
전체 도구 호출 예산 |
max_web_search_calls |
integer |
X |
웹 검색 호출 예산 |
1.5 nodes 필드 (Graph 전용 추가 필드)
Pattern의 모든 노드 필드에 추가되는 Graph 전용 필드:
| 필드 |
타입 |
필수 |
설명 |
id |
string |
O |
노드 식별자 (그래프 내 유일) |
kind |
enum |
O |
llm, judge, finalize |
runner |
dict |
kind=llm 시 |
실행 모드 설정 |
runner.mode |
enum |
O |
plan, execute |
runner.force_tool |
string |
X |
강제 도구 (web.search, file.write, shell.exec) |
runner.exclude_tools |
list |
X |
제외할 도구 목록 |
runner.min_proposals |
integer |
X |
plan 모드 최소 제안 수 |
runner.max_loops |
integer |
X |
노드 내부 최대 재시도 횟수 |
judge |
dict |
kind=judge 시 |
Judge 설정 |
judge.schema |
string |
O |
평가 스키마 ID |
judge.route_values |
list |
O |
가능한 라우팅 값 목록 |
artifacts |
dict |
X |
결과물 설정 |
artifacts.primary |
string |
X |
주 결과물 파일명 |
guardrails |
dict |
X |
도구 사용 제약 |
guardrails.tool_call_budget |
dict |
X |
도구별 호출 횟수 제한 |
writes_state |
list |
브랜치 필수 |
이 노드가 쓰는 state_schema 키 목록. 모든 키는 state_schema에 선언되어야 함 (미선언 시 STATE_UPDATE_OUTSIDE_DECLARED_WRITES) |
reads_state |
list |
X |
이 노드가 읽는 state_schema 키 목록. 미선언 키는 READS_STATE_UNKNOWN_KEY 경고 발생 |
interrupt_before |
boolean |
X |
실행 전 LangGraph 체크포인트 일시 정지. 최상위 checkpointer: 필수 |
interrupt_after |
boolean |
X |
실행 후 LangGraph 체크포인트 일시 정지. 최상위 checkpointer: 필수 |
1.6 edges 필드
| 필드 |
타입 |
필수 |
설명 |
from |
string |
O |
소스 노드 ID |
to |
string |
O |
타겟 노드 ID (finalize 포함) |
when |
string |
O |
조건 표현식 |
when 조건 표현식:
- "true" — 항상 진행
- "approvals_resolved" — HITL 승인 큐 비워진 후
- "judge.route == <value>" — judge가 특정 값을 반환했을 때
1.7 finalize 필드
| 필드 |
타입 |
필수 |
설명 |
artifact |
string |
O |
최종 결과물 파일명 |
2. Pattern → Graph 비교 표
| 항목 |
Pattern |
Graph |
| YAML 구조 |
동일 |
Pattern 상위 집합 |
| 실행 엔진 |
euleragent 순차 실행기 |
LangGraph StateGraph |
| state_schema |
없음 |
추가 (병렬 시 필수) |
| parallel_groups |
없음 |
추가 (팬아웃/팬인) |
| interrupt_before/after |
없음 |
추가 (체크포인트 기반) |
| judge 라우팅 |
런타임 조건 평가 |
add_conditional_edges 컴파일 |
| writes_state / reads_state |
없음 |
추가 (병렬 브랜치 문서화) |
| graph_type (IR) |
"pattern" |
"graph" |
| CLI 명령 |
pattern validate/show |
graph validate/show/compile |
| 안정성 |
안정 |
실험적 |
| 병렬 실행 |
불가 |
가능 |
| 중간 상태 저장 |
불가 |
LangGraph 체크포인터 |
3. 에러/경고 코드 전체 테이블
3.1 의미론적 에러 코드 (신규)
| # |
에러 코드 |
발생 조건 |
해결 방법 |
| L |
INTERRUPT_REQUIRES_CHECKPOINTER |
interrupt_before/after 사용 시 최상위 checkpointer: 없음 |
checkpointer: memory 추가 |
| M |
STATE_UPDATE_OUTSIDE_DECLARED_WRITES |
노드의 writes_state 키가 state_schema에 없음 |
키를 state_schema에 추가하거나 writes_state에서 제거 |
| P3b |
PARALLEL_JOIN_IN_BRANCHES |
join 노드가 자신의 branches 목록에 포함됨 |
branches에서 join 노드 제거 |
3.2 경고 코드 (ok=True, 비차단)
| # |
경고 코드 |
발생 조건 |
권장 조치 |
| N |
READS_STATE_UNKNOWN_KEY |
reads_state 키가 state_schema에 없음 |
오타 확인 또는 state_schema에 추가 |
| P14 |
PARALLEL_ORDER_DEPENDENT_MERGE |
병렬 그룹에서 append_list 사용 |
항목에 source/rank 필드 추가 후 조인에서 정렬 |
3.3 병렬 에러 코드 (14개)
| # |
에러 코드 |
발생 조건 |
해결 방법 |
| 1 |
PARALLEL_JOIN_MISSING |
parallel_groups에 join이 없음 |
join: <노드ID> 추가 |
| 2 |
PARALLEL_JOIN_MULTIPLE |
하나의 그룹에 join이 2개 |
join을 하나로 줄이기 |
| 3 |
PARALLEL_FANOUT_MISSING |
브랜치로 가는 팬아웃 엣지 없음 |
소스 → 브랜치 엣지 추가 |
| 4 |
PARALLEL_JOIN_EDGE_MISSING |
브랜치 → 조인 엣지 없음 |
브랜치 → join 엣지 추가 |
| 5 |
PARALLEL_BRANCH_NOT_CONVERGING |
브랜치가 조인 노드로 수렴하지 않음 |
팬인 엣지 수정 |
| 6 |
PARALLEL_STATE_SCHEMA_MISSING |
parallel_groups 있지만 state_schema 없음 |
최상위에 state_schema 추가 |
| 7 |
PARALLEL_STATE_KEY_MERGE_MISSING |
브랜치가 쓰는 state key에 merge 없음 |
state_schema에 해당 key의 merge 추가 |
| 8 |
PARALLEL_NONDETERMINISTIC_MERGE |
복수 브랜치가 last_write 또는 first_write key 공유 |
append_list, sum_int, concat_str 중 하나로 변경 |
| 9 |
STATE_SCHEMA_MERGE_TYPE_MISMATCH |
타입+merge 금지 조합 사용 |
호환성 표 참조하여 수정 |
| 10 |
PARALLEL_SIDE_EFFECT_FORBIDDEN |
브랜치에서 shell.exec/file.write force_tool |
조인 이후 노드에서 수행 |
| 11 |
PARALLEL_BRANCH_LIMIT_EXCEEDED |
브랜치 수 9개 이상 |
8개 이하로 줄이기 |
| 12 |
PARALLEL_METADATA_DROPPED |
브랜치 노드에 writes_state 없음 |
writes_state: [] 명시 |
| 13 |
PARALLEL_FINALIZE_BEFORE_JOIN |
브랜치가 finalize로 직접 라우팅 |
조인 노드 → finalize 순서로 수정 |
| 14 |
LANGGRAPH_COMPILE_FAILED |
LangGraph 버전 불일치 또는 IR 오류 |
LangGraph 1.0.9+ 설치, euleragent doctor 실행 |
4. 타입+Merge 호환성 매트릭스
각 셀의 기호: O = 허용, X = 금지 (STATE_SCHEMA_MERGE_TYPE_MISMATCH), O = 권장
| 타입 \ Merge |
append_list |
sum_int |
concat_str |
last_write |
first_write |
list |
O |
X |
X |
O |
O |
string |
X |
X |
O |
O |
O |
integer |
X |
O |
X |
O |
O |
float |
X |
X |
X |
O |
O |
dict |
X |
X |
X |
O |
O |
굵게 표시: 해당 타입의 권장 merge 전략
4.1 권장 조합 요약
# 병렬 결과 수집 (리스트) → append_list
findings:
type: list
merge: append_list
# 숫자 합산 → sum_int
count:
type: integer
merge: sum_int
# 텍스트 연결 (병렬) → concat_str
notes:
type: string
merge: concat_str
# 단일 노드 쓰기 → last_write
summary:
type: string # 또는 float, dict
merge: last_write
# 최초 쓰기 우선 → first_write
initial_value:
type: string # 또는 다른 타입
merge: first_write
5. 안전한 병렬 설계 체크리스트 (16개 항목)
설계 단계
□ 1. 진정한 병렬 실행이 필요한지 확인
순차로 충분하면 Pattern 사용 (더 단순하고 안정적)
□ 2. 브랜치 수 결정 (2~8개)
처음에는 2~3개로 시작하고 필요 시 확장
□ 3. state_schema 설계 완료
모든 브랜치의 writes_state 키 목록화
각 키의 type과 merge 전략 결정
□ 4. 공유 키(여러 브랜치가 씀) 식별
공유 list 키 → append_list
공유 integer 키 → sum_int
공유 string 키 → concat_str 또는 last_write(비결정론적 허용 시)
□ 5. 단일 브랜치 키 식별
단일 브랜치 키 → last_write (안전)
선언 단계
□ 6. state_schema 최상위에 선언
□ 7. parallel_groups 선언 (id, branches, join)
□ 8. 모든 브랜치 노드에 writes_state 선언
상태를 쓰지 않아도 writes_state: [] 명시
□ 9. 조인 노드에 reads_state 선언 (문서화)
□ 10. 팬아웃 엣지 선언 (소스 → 모든 브랜치)
□ 11. 팬인 엣지 선언 (모든 브랜치 → 조인 노드)
HITL 브랜치는 when: "approvals_resolved"
자율 브랜치는 when: "true"
□ 12. interrupt_before/after 사용 시 최상위 checkpointer: 선언
미선언 시 INTERRUPT_REQUIRES_CHECKPOINTER 에러 (정적 검증 실패)
최소: checkpointer: memory
검증 단계
□ 13. euleragent graph validate 실행 및 통과 확인
□ 14. PARALLEL_NONDETERMINISTIC_MERGE / PARALLEL_ORDER_DEPENDENT_MERGE 경고 검토
비결정론적 결과 허용 여부 확인; append_list 시 source/rank 필드 고려
□ 15. euleragent graph compile로 IR 생성 및 검토
langgraph_builder 섹션에서 add_edges 확인
□ 16. 실험적 기능 사용 주의사항 재확인
00_disclaimer.md 참조
6. LangGraph StateGraph 컴파일 상세
Graph YAML의 각 요소가 LangGraph의 어떤 메서드로 매핑되는지 보여줍니다.
6.1 매핑 테이블
| Graph YAML 요소 |
LangGraph API |
state_schema (전체) |
StateGraph(TypedDict) 타입 정의 |
state_schema.*.merge: append_list |
Annotated[list, operator.add] |
state_schema.*.merge: sum_int |
Annotated[int, operator.add] |
state_schema.*.merge: concat_str |
Annotated[str, operator.add] |
state_schema.*.merge: last_write |
기본 타입 (어노테이션 없음) |
state_schema.*.merge: first_write |
커스텀 first_write 리듀서 |
nodes[*] |
graph.add_node(id, fn) |
| 첫 번째 노드 |
graph.set_entry_point(id) |
finalize 노드 |
graph.set_finish_point(id) |
edges[*] (when="true") |
graph.add_edge(from, to) |
edges[*] (when="judge.route==X") |
graph.add_conditional_edges(...) |
parallel_groups[*] |
팬아웃/팬인 엣지 (add_edge 복수) |
interrupt_before: [...] |
graph.compile(interrupt_before=[...]) |
interrupt_after: [...] |
graph.compile(interrupt_after=[...]) |
defaults.max_iterations |
런타임 가드 (LangGraph 외부) |
6.2 전체 컴파일 예시 (개념적 Python 코드)
다음은 parallel_research_with_quality.yaml이 LangGraph 코드로 변환되는 모습입니다.
from langgraph.graph import StateGraph, END
from typing import Annotated, TypedDict
import operator
# state_schema → TypedDict
class GraphState(TypedDict):
findings: Annotated[list, operator.add] # append_list
source_count: Annotated[int, operator.add] # sum_int
final_summary: str # last_write (기본)
route: str # judge 라우팅용 내부 키
# StateGraph 초기화
graph = StateGraph(GraphState)
# 노드 추가
graph.add_node("plan", plan_fn)
graph.add_node("web_search", web_search_fn)
graph.add_node("local_search", local_search_fn)
graph.add_node("doc_search", doc_search_fn)
graph.add_node("merge_findings", merge_findings_fn)
graph.add_node("evaluate", evaluate_fn)
graph.add_node("revise", revise_fn)
# 진입점
graph.set_entry_point("plan")
# 팬아웃 엣지 (plan → 브랜치들)
graph.add_edge("plan", "web_search")
graph.add_edge("plan", "local_search")
graph.add_edge("plan", "doc_search")
# 팬인 엣지 (브랜치들 → 조인)
graph.add_edge("web_search", "merge_findings")
graph.add_edge("local_search", "merge_findings")
graph.add_edge("doc_search", "merge_findings")
# 하류 엣지
graph.add_edge("merge_findings", "evaluate")
graph.add_edge("revise", "evaluate")
# Judge 조건부 엣지
def route_evaluate(state: GraphState) -> str:
return state.get("route", "finalize")
graph.add_conditional_edges(
"evaluate",
route_evaluate,
{"finalize": END, "revise": "revise"}
)
# 컴파일 (interrupt 없는 경우)
compiled = graph.compile()
# 또는 interrupt 있는 경우
compiled = graph.compile(
checkpointer=checkpointer,
interrupt_before=["publish"], # interrupt_before 노드 목록
interrupt_after=["draft"] # interrupt_after 노드 목록
)
6.3 first_write 리듀서 구현 (개념적)
first_write는 LangGraph 기본 제공 어노테이션이 아니므로 커스텀 리듀서로 구현됩니다.
def first_write_reducer(old_value, new_value):
"""첫 번째 쓰기 값을 유지. None이면 new_value 사용."""
if old_value is None:
return new_value
return old_value # 이미 값이 있으면 새 값 무시
# TypedDict에서의 선언
class GraphState(TypedDict):
primary_result: Annotated[str, first_write_reducer]
7. 실험적 기능 사용 시 주의사항 요약
00_disclaimer.md의 전체 고지 내용을 참조하십시오.
7.1 안전 수준 분류
| 기능 |
안전 수준 |
설명 |
| 선형 Graph (병렬 없음) |
안정적 |
Pattern과 동등 |
| judge 라우팅 |
안정적 |
Pattern과 동등한 동작 |
| interrupt_before/after |
실험적 |
LangGraph 버전 의존 |
| 2-브랜치 parallel_groups |
실험적 |
기본 검증 완료 |
| 3-8브랜치 parallel_groups |
실험적 |
철저한 테스트 필요 |
graph run 명령 |
실험적 |
일부 시나리오 미검증 |
7.2 버전 업그레이드 시 필수 작업
# euleragent 또는 langgraph 업그레이드 후
pip install --upgrade euleragent langgraph
# 모든 Graph YAML 재검증
for yaml_file in examples/graphs/**/*.yaml; do
echo "검증: $yaml_file"
euleragent graph validate "$yaml_file"
done
# 컴파일된 IR 재생성
euleragent graph compile my_graph.yaml --out my_graph_compiled.json
7.3 프로덕션 배포 전 체크리스트
□ euleragent doctor 실행 → 모든 항목 통과 확인
□ graph validate 통과 (오류 0개)
□ graph validate --format json 출력에서 경고 검토
□ graph compile 성공
□ 스테이징 환경에서 10회 이상 반복 실행 테스트
□ 병렬 결과의 비결정론적 변동 허용 범위 확인
□ HITL 승인 워크플로우 테스트
□ interrupt hooks 재개 시나리오 테스트
□ 감사 로그 (approvals.jsonl, tool_calls.jsonl) 검토
□ 최대 비용 시나리오 계산 (max_iterations × max_total_tool_calls)
빠른 참조 카드
CLI 명령
euleragent graph list # 그래프 목록
euleragent graph show <path> # 구조 보기
euleragent graph validate <path> # 검증
euleragent graph validate <path> --format json # JSON 형식 검증
euleragent graph compile <path> # IR 컴파일
euleragent graph compile <path> --out file.json # IR 파일로 저장
최소 병렬 그래프 템플릿
id: graph.template
version: 1
category: demo
description: 최소 병렬 그래프 템플릿
state_schema:
results:
type: list
merge: append_list
defaults:
max_iterations: 2
max_total_tool_calls: 30
parallel_groups:
- id: my_group
branches: [branch_a, branch_b]
join: join_node
nodes:
- id: source
kind: llm
runner: {mode: execute}
- id: branch_a
kind: llm
runner: {mode: execute}
writes_state: [results]
- id: branch_b
kind: llm
runner: {mode: execute}
writes_state: [results]
- id: join_node
kind: llm
runner: {mode: execute}
reads_state: [results]
artifacts:
primary: output.md
edges:
- {from: source, to: branch_a, when: "true"}
- {from: source, to: branch_b, when: "true"}
- {from: branch_a, to: join_node, when: "true"}
- {from: branch_b, to: join_node, when: "true"}
- {from: join_node, to: finalize, when: "true"}
finalize:
artifact: output.md
이전: 09_parallel_with_quality.md | 목차: README.md