메뉴
HN
Hacker News 22일 전

LLM이 실제 시스템을 정확히 모델링할 수 있을까?

IMP
8/10
핵심 요약

최신 LLM들이 동시성 및 분산 시스템 명세 언어인 TLA+를 사용해 시스템을 모델링하는 역량을 평가한 연구 결과입니다. 연구진이 개발한 자동화 벤치마크 'SysMoBench'에 따르면, 최신 LLM들은 문법이나 기본 실행 단계에서는 거의 완벽한 점수를 기록했지만, 실제 코드와 모델이 일치하는지 검증하는 단계(46%)와 핵심 속성을 만족하는지 확인하는 단계(41%)에서는 대폭 실패했습니다. 이는 현재 AI가 시스템 코드의 실제 구조를 추상화해 정확한 형식 모델을 작성하기보다는, 학습 데이터에 존재하는 교과서적인 예제를 단순히 암기하여 재생산하는 한계를 명확히 보여줍니다.

번역된 본문

에디터 노트: AI는 컴퓨팅 시스템을 위한 응용 형식적 방법(formal methods)의 최전선을 끊임없이 개척하고 있습니다. 이 기사에서 Specula 팀은 동시성 및 분산 시스템을 위한 명세 언어인 TLA+를 활용하여, 에이전트 기반 모델 검사(Model Checking)의 핵심 역량인 '시스템 코드 모델링' 과제에서 LLM을 평가한 경험을 공유합니다. 이 글은 '시스템 인텔리전스의 다음 지평(The Next Horizon of System Intelligence)' 시리즈의 7번째 블로그 포스트입니다.

몇 달 전, 우리는 Claude에게 Etcd의 Raft 구현체에 대한 TLA+ 명세(Spec) 작성을 요청했습니다. 결과물은 구문 검사(Syntax check)를 통과했고, TLC 모델 검사기에서도 문제없이 실행되었으며, 언뜻 보기에는 완성도 높은 형식 모델처럼 보였습니다. 그러나 곧 한 가지 사실을 깨달았습니다. 그 결과물은 Raft 논문에 나오는 명세와 거의 동일했으며, Etcd 고유의 세부 구현 내용과는 거의 관련이 없었습니다. Claude가 작성한 것은 'Etcd를 위한 명세'가 아니라 'Raft 논문 부록의 명세'였던 것입니다.

이후 우리는 'AI가 컴퓨팅 시스템을 충실하게 모델링한 것인지, 아니면 단순히 해당 시스템의 참고 논문을 암기하여 재생산한 것인지 어떻게 구별할 수 있을까?'라는 질문에 계속해서 직면했습니다. 대형 언어 모델(LLM)이 발전을 거듭할수록 이 질문에 답하기는 더욱 어려워지고 있습니다.

LLM은 인터넷에 공개된 거의 모든 TLA+ 예시를 학습했습니다. 따라서 모델에게 'Raft 명세 작성'을 요청하는 것은 사실상 학습 데이터를 떠올리게 하는 것과 다름없습니다. 반면, Etcd가 실제로 원자적 동작을 분해하고 상태를 전이시키는 방식을 반영한 'Etcd 명세 작성'을 요청하는 것은 완전히 다른 차원의 문제입니다. 이는 LLM이 복잡한 구현 코드에서 논리를 추상화한 뒤, 그 추상화를 올바른 형식 모델로 변환할 수 있는지를 평가하는 과제입니다.

SysMoBench는 우리가 이 두 가지를 자동화된 벤치마크를 통해 구별하고자 만든 도구입니다.

SysMoBench란 무엇인가? SysMoBench는 LLM에게 11개의 시스템을 제시하고, 모델이 생성한 TLA+ 명세를 자동으로 평가합니다.

표 1: SysMoBench에서 아티팩트로 사용된 시스템들 이 11개의 시스템은 동시성 동기화부터 분산 프로토콜까지 다양한 영역을 아우릅니다. 각 과제(Task)에 대해 우리는 소스 코드, 실행 추적(Trace) 수집 도구, 그리고 불변 조건(Invariant) 템플릿을 제공합니다.

그림 1: SysMoBench 개요 SysMoBench의 평가는 다음 4단계로 진행됩니다:

  1. 구문 단계(Syntax phase): 작성된 명세가 컴파일되는지 확인합니다.
  2. 런타임 단계(Runtime phase): TLC 모델 검사기가 오류 없이 명세를 실행할 수 있는지 확인합니다.
  3. 적합성 단계(Conformance phase): 명세-코드 일치성을 확인하는 표준 방법인 '추적 검증(Trace validation)'을 수행합니다. 코드의 실제 실행 추적과 모델의 동작을 비교합니다.
  4. 불변성 단계(Invariant phase): 명세가 핵심 안전성(Safety) 및 활성(Liveness) 속성을 만족하는지 확인합니다.

이 네 단계를 종합적으로 거치면서, 교과서 지식을 단순히 암기해 적은 명세와 실제 시스템을 정확히 모델링한 명세 사이의 격차가 명확하게 드러납니다. 또한 각 단계에서는 단일 점수가 아닌 '동작(Action)'이나 '불변 조건(Invariant)' 단위의 진단 결과를 제공하므로, 명세가 구현체의 어느 부분에서 정확히 어긋나는지 파악할 수 있습니다.

LLM 모델링 패턴 우리는 현재의 주요 LLM(Claude, GPT, Gemini, DeepSeek, Kimi, Qwen 등)을 대상으로 SysMoBench를 실행했습니다. 그 결과, 일관되게 반복되는 패턴이 하나 발견되었습니다. 생성된 명세들은 처음 두 단계(구문 및 런타임)에서 매우 우수한 성능을 보였습니다. 대부분 컴파일 오류 없이 깔끔하게 실행되었고, TLC 모델 검사기에서도 오류 없이 통과하는 경우가 많았습니다.

하지만 평가가 적합성 단계에 도달하면 두 가지 체계적인 '교과서식 모델링(Textbook modeling)' 형태의 한계가 뚜렷하게 나타납니다. (1) 실제 시스템에서는 결코 도달할 수 없는 상태(State)를 명세가 허용하거나, (2) 실제 시스템이라면 항상 도달해야 하는 상태를 명세가 표현하지 못하는 경우입니다. 이 두 가지 실패 원인은 곧바로 적합성 및 불변성 점수로 직결됩니다. 최신 주도 LLM들의 경우 구문 단계에서는 거의 만점에 가까운 점수를 받지만, 적합성 점수는 평균 약 46%, 불변성 점수는 41% 수준에 그쳤습니다.

명세가 실제 시스템이 도달할 수 없는 상태를 허용하는 경우를 살펴보면, LLM이 시스템의 실제 데이터 구조와 일치하지 않는 '일반적인 형식화 템플릿'을 사용해 명세를 작성한 경우가 많습니다. 그 결과, 실제 시스템에서는 절대 발생하지 않는 상태를 만들어내는 상태 전이(Transitions)를 명세가 포함하게 됩니다.

이를 보여주는 구체적인 예시로 Claude Sonnet이 작성한 ZooKeeper Fast Leader Election(FLE) 명세가 있습니다.

그림 2: LLM이 생성한 ZooKeeper FLE 명세에서 나타난 두 가지 실패 양상 ZooKeeper 코드에서 각 서버의 recvset은 th(스레드)를 키(key)로 사용하는 맵(Map) 구조입니다...

원문 보기
원문 보기 (영어)
Editors’ note: AI has been actively pushing the frontier of applied formal methods for computing systems. In this article, the Specula team wrote about their experience of evaluating LLMs on modeling system code, the basic capability for agentic model checking, using TLA+, a specification language for concurrent and distributed systems. The article is the 7th blog in The Next Horizon of System Intelligence series. Several months ago, we asked Claude to write a TLA + specification (spec) for Etcd's Raft implementation . It passed syntax checks, ran through the TLC model checker, and at first glance looked like a polished formal model. Then we noticed something: this looks like the spec from the Raft paper, and it has very little to do with Etcd-specific details. What Claude produced was not a spec for Etcd. It was a spec from the appendix of the Raft paper . Later, we kept coming back to the question: how do we tell whether the AI is faithfully modeling a computing system, or just reciting the system's reference paper? This question gets harder to answer as Large Language Models (LLMs) keep improving. LLMs have seen nearly every TLA+ example online. Asking one to "write a Raft spec" is almost the same as asking it to recall something from training. Asking it to "write Etcd's spec", a spec that reflects how Etcd actually decomposes its atomic actions and evolves its state, is a different problem entirely. It tests whether the LLMs can abstract logic out of a complex implementation and turn that abstraction into a correct formal model. SysMoBench is our attempt to differentiate the two via an automated benchmark. What is SysMoBench? SysMoBench provides eleven systems to an LLM and automatically evaluates the TLA+ specs it generates. Table 1: Systems used as artifacts in SysMoBench The eleven systems span concurrent synchronization and distributed protocols. For each task, we provide source code, a trace-collection harness, and an invariant template. Figure 1: Overview of SysMoBench Evaluation runs in four phases: The syntax phase checks whether a spec compiles. The runtime phase checks whether TLC can execute it without error. The conformance phase uses trace validation, the common method for checking spec-code consistency: execution traces from code are compared against the model. The invariant phase checks whether the spec satisfies key safety and liveness properties. Together, these four phases expose gaps between a spec that just recites the textbook and one that actually models the system. Each phase produces action- or invariant-granular diagnostics rather than a single aggregate score, so we can see exactly on which action or invariant the spec misaligns with the implementation. LLM Modeling Patterns When we run today’s leading LLMs on SysMoBench — Claude, GPT, Gemini, DeepSeek, Kimi, Qwen, etc — a recurring pattern emerges. Their specs do quite well in the first two phases (syntax and runtime). Most compile cleanly, and many run through TLC without error. But, once evaluation reaches the conformance phase, two systematic forms of "textbook modeling" become apparent: (1) the spec enters states that a real system can never reach, and (2) the spec fails to reach states that a real system always reaches. These two failure modes show up directly in the conformance and invariant scores: even the latest leading LLMs average around 46% on conformance and 41% on invariant, compared to near-perfect scores on syntax. In the cases where the spec enters states the real-world systems never reach, the LLMs write the spec following a common formalization template that does not match the system's actual data structure, and as a result, the spec admits transitions that produce states that real systems would never produce. A concrete example comes from Claude Sonnet's spec for ZooKeeper Fast Leader Election (FLE). Figure 2: Two failure modes in an LLM-generated ZooKeeper FLE spec In ZooKeeper code, each server's recvset is a map keyed by the sender: when the same peer sends a new vote in a new round, it overwrites the peer's old vote. Sonnet wrote this as a set union, recvVotes' = recvVotes ∪ {newVote}, keeping both the old and the new vote. The "accumulate all votes as evidence" pattern appears in many formal-methods textbooks, but it does not match ZooKeeper's semantics. As a result, whenever a peer's vote changes across rounds (which happens routinely in ZooKeeper's elections), the spec's post-state contains both the old and the new vote while the real system retains only the new vote. Once a downstream quorum check depends on vote counts, the spec encounters a state real code never enters. In the case where the spec fails to reach states real systems reach, the LLM merges operations that span multiple steps in the code into a single atomic guard, making transitions impossible in the spec. In the same Sonnet’s ZooKeeper spec, the HandleNotification action carries a guard, m.electionEpoch <= logicalClock[s], that checks if the incoming message's epoch is higher than the local logicalClock. If so, the action is disabled. But, ZooKeeper’s code doesn’t work this way. When a server receives a message with a higher electionEpoch, it first increases its own logicalClock to match, then processes the message. These two steps happen in that order in the code. However, the LLM fused them into a single guard and, in doing so, erased states that code enters in every election round (local epoch=1, incoming epoch=2). The above two patterns share a common cause. The LLMs produce structurally complete, type-correct TLA+ modules, but these are written in textbook formalization templates rather than reflecting the actual implementation. The LLM knows what Raft and ZAB look like as protocols, but it does not know how Etcd or ZooKeeper splits a particular action across multiple steps. This is exactly why syntax and runtime evaluation criteria are not enough. The specs produced by an LLM all pass the SANY TLA+ parser, as they are structurally complete and syntactically clean. To distinguish "modeling Etcd" from "reciting the Raft paper," evaluation has to reach the conformance and invariant phases, asking each action whether its transition matches the state changes the system actually produces at runtime. Transition Validation: Reading Specs at Action Granularity All phases in SysMoBench produce per-action or per-invariant diagnostics beyond a single aggregate score. Instead of compiling the whole module, the syntax phase runs per-action decomposition to localize which action is malformed. The runtime phase analyzes per-action coverage of the state space rather than only whether TLC can execute the spec at all. The invariant phase verifies each invariant separately, with an LLM translating a fixed template into runnable invariants for each generated spec. The conformance phase uses what we call Transition Validation , and it most directly reveals the gap between "reciting the textbook protocol" and "modeling the system." The idea is simple. We collect execution traces from real runs of the system, then cut each trace into a series of “transition windows”. A window is a triple: (pre_state, action, post_state). Each window is fed independently to TLC, which checks whether the spec's action can move from pre_state to post_state. The output is not a single score but rather a per-action breakdown, with one pass rate per action. As one concrete example: when we run Transition Validation on Asterinas RwMutex, the output is a per-action scorecard, detailing one pass rate per action. Coarse-grained evaluation cannot provide this kind of diagnostics. Whereas a single score only tells you if the spec passed or failed, Transition Validation tells you not just which action misaligns, but which specific state transition fails, pinned to a particular window in the trace. Findings: Where the Scores Diverge Running leading LLMs across the eleven systems shows that LLMs are great at producing corre