본문으로 건너뛰기

Tool 스펙 스키마

이 페이지는 Fascia의 Tool(실행 가능한 서버 로직 단위) 스펙에 대한 전체 JSON Schema를 정의합니다. Tool은 백엔드 연산의 기본 구성 요소로, API 엔드포인트, 웹훅 핸들러, 크론 작업에 해당합니다. 각 Tool은 트리거, Flow 그래프(실행 로직을 정의하는 방향 비순환 그래프), 입출력 스키마, 정책을 포함하는 스펙으로 정의됩니다.

JSON Schema

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["name", "version", "description", "trigger", "input", "output", "flow"],
"properties": {
"name": {
"type": "string",
"pattern": "^[a-z][a-zA-Z0-9]*(\\.[a-z][a-zA-Z0-9]*)*$",
"description": "camelCase tool name (e.g., createReservation, auth.login)"
},
"version": {
"type": "integer",
"minimum": 1
},
"description": { "type": "string" },
"trigger": { "$ref": "#/$defs/Trigger" },
"input": {
"type": "object",
"description": "JSON Schema for input validation"
},
"output": {
"type": "object",
"description": "JSON Schema for output format"
},
"flow": { "$ref": "#/$defs/FlowGraph" },
"policies": {
"type": "array",
"items": { "type": "string" },
"description": "References to Policy spec names"
},
"auth": { "$ref": "#/$defs/AuthConfig" },
"riskLevel": {
"enum": ["green", "yellow", "red"],
"description": "Assigned by Risk Engine (not user-defined)"
},
"concurrencyStrategy": {
"enum": ["none", "optimistic", "pessimistic"],
"description": "Auto-selected based on flow analysis"
},
"idempotencyKey": {
"type": "string",
"description": "Value DSL expression to derive idempotency key from input"
}
}
}

최상위 속성

속성타입필수설명
namestringcamelCase Tool 이름입니다. 네임스페이스를 위한 점 표기법이 허용됩니다 (예: auth.login). ^[a-z][a-zA-Z0-9]*(\.[a-z][a-zA-Z0-9]*)*$ 패턴을 따라야 합니다.
versioninteger불변 버전 번호. 1부터 시작합니다.
descriptionstringTool이 수행하는 작업에 대한 사람이 읽을 수 있는 설명입니다.
triggerTriggerTool이 호출되는 방식입니다. Trigger 참조.
inputobject입력 페이로드를 정의하는 JSON Schema입니다. Execution Contract의 1단계에서 검증됩니다.
outputobject응답 형식을 정의하는 JSON Schema입니다.
flowFlowGraph실행 로직을 정의하는 방향 비순환 그래프입니다. FlowGraph 참조.
policiesstring[]아니오실행 전에 적용할 Policy 스펙 이름 배열입니다.
authAuthConfig아니오인증 및 인가 설정입니다. AuthConfig 참조.
riskLevelenum아니오Risk Engine이 분석 후 할당하는 값입니다. green, yellow, red 중 하나입니다. 사용자가 직접 설정할 수 없습니다. Risk 규칙 참조.
concurrencyStrategyenum아니오Flow 분석에 기반하여 자동 선택됩니다. none, optimistic, pessimistic 중 하나입니다.
idempotencyKeystring아니오입력에서 고유 키를 도출하여 중복 실행을 방지하는 Value DSL 표현식입니다.

정의

Trigger

Tool이 호출되는 방식을 정의합니다.

{
"type": "object",
"required": ["type"],
"properties": {
"type": { "enum": ["http", "webhook", "cron", "queue"] },
"method": {
"enum": ["GET", "POST", "PUT", "DELETE"],
"description": "For HTTP triggers"
},
"path": {
"type": "string",
"description": "Custom path for HTTP triggers"
},
"schedule": {
"type": "string",
"description": "Cron expression for cron triggers"
},
"webhookSecret": {
"type": "string",
"description": "Secret name in Secret Manager"
}
}
}
속성타입필수설명
typeenum트리거 유형: http, webhook, cron, queue 중 하나입니다.
methodenum아니오HTTP 메서드입니다. type"http"일 때만 적용됩니다.
pathstring아니오사용자 정의 URL 경로입니다. type"http"일 때만 적용됩니다.
schedulestring아니오크론 표현식입니다 (예: 0 */6 * * *). type"cron"일 때 필수입니다.
webhookSecretstring아니오웹훅 서명 검증에 사용되는 GCP Secret Manager의 시크릿 이름입니다. type"webhook"일 때 적용됩니다.

트리거 유형

유형호출 방식사용 사례
httpHTTP를 통한 tool.call(name, payload)외부 클라이언트의 Tool API 호출
webhook외부 서비스 콜백결제 제공자 알림, 서드파티 연동
cron스케줄에 따른 실행일일 리포트, 정리 작업, 반복 태스크
queue메시지 큐 이벤트 (향후 지원)비동기 처리, 이벤트 기반 워크플로

FlowGraph

Tool의 실행 로직을 정의하는 방향 비순환 그래프(DAG)입니다.

{
"type": "object",
"required": ["nodes", "edges", "startNode"],
"properties": {
"nodes": {
"type": "object",
"additionalProperties": { "$ref": "#/$defs/FlowNode" }
},
"edges": {
"type": "array",
"items": { "$ref": "#/$defs/FlowEdge" }
},
"startNode": { "type": "string" }
}
}
속성타입필수설명
nodesobject노드 ID에서 FlowNode 정의로의 맵입니다. 키는 고유한 문자열 식별자입니다.
edgesFlowEdge[]노드 간의 연결 목록입니다. FlowEdge 참조.
startNodestring실행이 시작되는 노드 ID입니다. nodes의 키를 참조해야 합니다.

FlowNode

Flow 그래프 내의 단일 작업입니다.

{
"type": "object",
"required": ["type"],
"properties": {
"type": {
"enum": [
"read", "write", "transform", "if", "switch",
"retry", "timeout", "payment", "email", "sms",
"httpRequest", "transaction", "policyCheck", "assert"
]
},
"config": {
"type": "object",
"description": "Node-type-specific configuration"
},
"position": {
"type": "object",
"properties": {
"x": { "type": "number" },
"y": { "type": "number" }
},
"description": "Visual position in Flow Studio canvas"
}
}
}
속성타입필수설명
typeenum노드 유형입니다. 수행할 작업을 결정합니다. 노드 유형 참조.
configobject아니오노드 유형별 설정입니다 (예: read의 Entity 이름, transform의 표현식).
positionobject아니오Flow Studio에서 렌더링할 시각적 좌표 (x, y)입니다. 실행에는 영향을 주지 않습니다.

노드 유형

범주노드설명
DatareadEntity 또는 Composite에서 데이터를 조회합니다
DatawriteEntity를 생성, 수정, 상태 전이, 소프트 삭제합니다
LogictransformValue DSL 표현식을 사용하여 데이터를 변환합니다
Logicif조건 분기 (참/거짓 두 개의 출력)
Logicswitch값에 기반한 다중 분기
Logicretry실패 시 하위 노드를 재시도합니다 (횟수 및 백오프 설정 가능)
Logictimeout하위 노드에 실행 시간 제한을 적용합니다
Externalpayment결제 서비스를 호출합니다 (예: Stripe, Toss)
Externalemail이메일을 전송합니다
ExternalsmsSMS를 전송합니다
ExternalhttpRequest외부 HTTP API를 호출합니다
Safetytransaction자식 노드를 감싸는 명시적 트랜잭션 경계를 정의합니다
SafetypolicyCheck런타임에 Policy를 평가합니다
SafetyassertInvariant를 강제합니다. 단언문이 실패하면 실행을 중단합니다

FlowEdge

두 노드 사이의 방향성 연결로, 데이터 흐름과 실행 순서를 정의합니다.

{
"type": "object",
"required": ["from", "to"],
"properties": {
"from": { "type": "string", "description": "Source node ID" },
"to": { "type": "string", "description": "Target node ID" },
"label": {
"type": "string",
"description": "Branch label (for if/switch nodes)"
},
"dataMapping": {
"type": "object",
"description": "Maps output fields from source to input fields of target"
}
}
}
속성타입필수설명
fromstring출발 노드 ID입니다. nodes의 키를 참조해야 합니다.
tostring도착 노드 ID입니다. nodes의 키를 참조해야 합니다.
labelstring아니오조건 노드의 분기 레이블입니다. if 노드의 경우 "true" 또는 "false"를 사용합니다. switch 노드의 경우 해당하는 케이스 값을 사용합니다.
dataMappingobject아니오출발 노드의 출력 필드를 도착 노드의 입력 필드로 매핑하는 방법을 정의합니다.

AuthConfig

Tool의 인증 및 인가 설정입니다.

{
"type": "object",
"properties": {
"required": {
"type": "boolean",
"default": true
},
"allowedRoles": {
"type": "array",
"items": { "type": "string" },
"description": "Roles that can execute this tool. Empty = all roles."
}
}
}
속성타입필수설명
requiredboolean아니오이 Tool을 호출하기 위해 인증이 필요한지 여부입니다. 기본값은 true입니다.
allowedRolesstring[]아니오이 Tool을 실행할 수 있는 역할 목록입니다 (예: ["admin", "staff"]). 빈 배열이거나 생략할 경우, 인증된 모든 역할이 허용됩니다.

Flow 그래프 검증 규칙

Risk Engine은 배포 전에 모든 Flow 그래프를 다음 규칙에 따라 검증합니다:

  1. DAG 요구사항 -- 그래프는 반드시 방향 비순환 그래프여야 합니다. 순환은 허용되지 않습니다 (Red 리스크 레벨 발생).
  2. 단일 시작 노드 -- 정확히 하나의 startNode가 존재해야 하며, nodes 맵의 유효한 노드를 참조해야 합니다.
  3. 종단 도달 가능성 -- 모든 실행 경로는 최소 하나의 종단 노드(나가는 엣지가 없는 노드)에 도달해야 합니다.
  4. Write 노드의 트랜잭션 포함 -- 모든 write 노드는 transaction 경계 내에 포함되어야 합니다. 트랜잭션 경계가 없으면 Red 리스크 레벨이 발생합니다.
  5. External 노드의 재시도 설정 -- External 노드 (payment, email, sms, httpRequest)에는 retry 노드가 설정되어야 합니다. 재시도가 누락되면 Yellow 리스크 레벨이 발생합니다.
  6. 고아 노드 없음 -- 모든 노드는 startNode에서 도달 가능해야 합니다. 도달 불가능한 노드는 잘못된 Flow를 나타냅니다.

Execution Contract

모든 Tool 실행은 이 정확한 9단계 시퀀스를 따릅니다. 예외는 없습니다.

단계작업
1input JSON Schema에 대해 입력을 검증합니다
2인가를 수행합니다 (JWT 검증 + RBAC + 행 수준 정책)
3정책을 검사합니다 (참조된 모든 정책을 평가)
4데이터베이스 트랜잭션을 시작합니다
5Flow 그래프를 실행합니다 (노드별로 엣지를 따라 순회)
6수정된 모든 Entity에 대해 Invariant를 강제합니다
7트랜잭션을 커밋합니다 (실패 시 롤백)
8감사 로그 항목을 기록합니다
9정규화된 출력을 반환합니다

어떤 단계에서도 LLM이 관여하지 않습니다. 모든 동작은 결정적입니다.

예시

createReservation Tool 스펙입니다:

{
"name": "createReservation",
"version": 1,
"description": "Create a new vehicle reservation",
"trigger": {
"type": "http",
"method": "POST",
"path": "/reservations"
},
"input": {
"type": "object",
"required": ["customerId", "vehicleId", "startDate", "endDate"],
"properties": {
"customerId": { "type": "string", "format": "uuid" },
"vehicleId": { "type": "string", "format": "uuid" },
"startDate": { "type": "string", "format": "date-time" },
"endDate": { "type": "string", "format": "date-time" },
"notes": { "type": "string" }
}
},
"output": {
"type": "object",
"properties": {
"id": { "type": "string", "format": "uuid" },
"status": { "type": "string" },
"totalPrice": { "type": "number" }
}
},
"flow": {
"startNode": "calcPrice",
"nodes": {
"calcPrice": {
"type": "transform",
"config": {
"expression": "diffDays(input.startDate, input.endDate) * vehicle.dailyRate"
}
},
"txn": {
"type": "transaction"
},
"createRecord": {
"type": "write",
"config": {
"entity": "Reservation",
"operation": "create",
"fields": {
"customerId": "input.customerId",
"vehicleId": "input.vehicleId",
"startDate": "input.startDate",
"endDate": "input.endDate",
"totalPrice": "calcPrice.result",
"notes": "input.notes"
}
}
},
"checkInvariants": {
"type": "assert",
"config": {
"expression": "createRecord.result.totalPrice > 0",
"message": "Total price must be positive"
}
}
},
"edges": [
{ "from": "calcPrice", "to": "txn" },
{ "from": "txn", "to": "createRecord" },
{ "from": "createRecord", "to": "checkInvariants" }
]
},
"policies": ["maxRowLimit"],
"auth": {
"required": true,
"allowedRoles": ["admin", "staff", "customer"]
},
"idempotencyKey": "concat(input.customerId, '-', input.vehicleId, '-', input.startDate)"
}

관련 문서