LLM 시대의 TLA+ 입문: 프롬프트로 승리하기
TLA+의 복잡한 문법이 LLM(대형 언어 모델) 시대를 맞아 진입 장벽이 크게 낮아졌습니다. 이제 엔지니어는 시스템과 '올바름(Correctness)'을 정의하는 것에 집중하고, 실제 모델 검사(Model Checking) 코드는 프롬프트로 쉽게 생성해 복잡한 분산 시스템이나 알고리즘을 검증할 수 있습니다. 본문은 고전적인 콩 시뮬레이션 문제를 통해 TLA+의 기본 개념과 상태 변환 논리를 설명합니다.
대부분의 엔지니어들이 TLA+를 사용할 때 처음 하는 불만은 문법이 까다롭다는 것입니다. 코드보다는 LaTeX처럼 보이기 때문입니다. 하지만 이제 최신 LLM(대형 언어 모델)은 TLA+를 쉽게 생성할 수 있습니다. 여전히 시스템을 이해하고 '올바름(Correctness)'이 무엇을 의미하는지 정의하는 것은 개발자의 책임이며, 시제 논리(Temporal logic)에 대한 높은 수준의 이해가 필요합니다. 이 글에서는 시제 논리에 대해 설명하겠습니다. 마지막에는 Claude를 사용해 TLA+ 스펙(Spec)을 시작하는 예시 프롬프트를 보여드리겠습니다. 새로운 글을 보려면 구독하세요.
간단한 예제 문제 # 다음은 고전적인 퍼즐입니다. 콩이 들어있는 통이 있습니다. 각 콩은 흰색 또는 검은색입니다. 통은 처음에 비어있지 않습니다. 콩이 적어도 2개 이상 있는 동안 다음을 반복합니다: 콩 2개를 고릅니다. 두 콩이 같은 색이면: 두 개 모두 버리고, 흰색 콩 1개를 넣습니다. 두 콩이 다른 색이면: 두 개 모두 버리고, 검은색 콩 1개를 넣습니다. 두 가지 질문이 있습니다: 콩의 수가 0이 될 수 있을까요? 알고리즘이 b = 1(검은 콩 1개)로 종료된다면, 처음에는 어떤 상태였어야 할까요?
머리를 쥐어짜며 생각해볼 수도 있습니다. 아니면 이를 TLA+로 작성하고 모델 검사기(Model checker)가 두 질문에 자동으로 답하게 할 수도 있습니다. 이 방법의 핵심은 생각하는 과정을 피하는 것입니다. 적어도 기계가 당신의 생각이 맞았는지 검증하게 만드는 것입니다. 아니면 친구들이나 연구 논문의 동료 평가 위원회(peer-review panel)에게 당신의 생각이 옳다는 것을 납득시키는 것입니다.
논리 공식이 상태 기계(State machine)를 만드는 방법 # TLA+는 1990년대 레슬리 램포트(Leslie Lamport)가 발명했습니다. TLA는 '행동의 시제 논리(Temporal Logic of Actions)'를 의미하며, TLA+는 이 특정 언어의 이름입니다. TLA+는 기본적인 부울 논리(Boolean logic)와 집합(sets), 함수(functions), 한정자(Quantification, '모든 것에 대해(for all)'와 '존재한다(there exists)')를 가지고 있습니다. 또한 곧 보게 될 시제 논리 연산자들도 있습니다.
TLA+로 명세(Specification)를 작성할 때, 당신은 상태 기계를 정의하는 논리 공식을 작성하는 것입니다. 이 기계는 고정된 변수 세트를 가지고 있으며, 각 상태는 변수들에 할당된 값들입니다. 콩 통 문제의 경우, 변수는 w(흰색 콩의 수)와 b(검은색 콩의 수)입니다. 각 상태는 w와 b에 할당된 값입니다. 동작(Behavior)은 상태의 연속이며, 명세는 허용되는 동작들의 집합입니다.
초기 상태 # 우리는 초기 상태 규칙이 필요합니다. 즉, 우리가 시작할 수 있는 상태에만 참으로 평가되는 조건(Predicate)입니다. 영어로는 "통은 초기에 비어있지 않다", 즉 w + b > 0 입니다. 다음 중 이 조건과 일치하는 초기 상태는 어느 것일까요?
b = 0 /\ w = 0 b = 0 /\ w = 4 b = 6 /\ w = 1 b = 1 /\ w = "foo"
TLA+에서 "/"는 "그리고(and)"를 의미하므로, b = 0 /\ w = 0은 "b = 0 그리고 w = 0"을 뜻합니다. 두 번째와 세 번째 상태가 조건과 일치합니다. 첫 번째는 w와 b의 합이 0이므로 일치하지 않고, 마지막 상태는 1과 문자열 "foo"를 더할 수 없으므로 말이 안 됩니다.
TLA+에는 타입 시스템(Type system)이 없고 집합(sets)만 있으므로, w가 문자열이 되는 것을 막을 수 없습니다. 램포트(Lamport)는 이런 상태를 '어리석은(silly)' 것이라고 부릅니다. 우리는 w와 b가 자연수여야 한다고 지정함으로써 이런 어리석은 상태를 방지합니다:
EXTENDS Integers Init == w \in Nat /\ b \in Nat /\ w + b > 0
EXTENDS Integers는 자연수의 집합인 Nat와 같은 정수 처리에 필요한 모든 것을 가져옵니다(import). \in은 집합 소속 연산자(∈)입니다. TLA+에서 ==는 '~로 정의된다'를 의미합니다. 이것은 약간 헷갈릴 수 있는데, C 언어와는 반대 개념이기 때문입니다. 즉, 단일 =는 동등성을 검사(tests for equality)하고, ==는 공식(매크로와 같은)의 이름을 붙입니다.
상태 전이(State transitions) # 상태 전이 규칙은 두 상태(현재와 다음)에 대한 조건(Predicate)으로, 어떤 전이가 유효한지를 나타냅니다. 알고리즘을 TLA+의 상태 전이 규칙으로 바꿔 봅시다. 영어에서 시작해 보겠습니다:
흰색 콩 2개: 흰색 2개를 제거하고, 흰색 1개를 추가 -> 최종 효과: w -= 1 검은색 콩 2개: 검은색 2개를 제거하고, 흰색 1개를 추가 -> 최종 효과: b -= 2 하나씩 있는 경우(다른 색상): 흰색 1개와 검은색 1개를 제거하고, 검은색 1개를 추가 -> 최종 효과: w -= 1
첫 번째와 세 번째 경우가 상태에 미치는 효과가 동일하다는 점을 주목하세요. 둘 다 단순히 w에서 1을 빼고 b는 그대로 둡니다. 이것이 바로 이런 종류의 통찰력입니다.