
AI 에이전트에게 '사양대로'를 보장하게 만들기 — SoT Chain 설계
요약
AI 에이전트가 작성한 코드가 사양(Specification)을 준수하도록 보장하는 SoT Chain 설계 방식을 소개합니다. 설계 판단부터 구현까지를 일방향 참조 체인으로 연결하고 CI 게이트를 통해 기계적으로 검증하는 SoTOHE 프레임워크를 제안합니다.
핵심 포인트
- AI 에이전트의 빠른 코드 생성 속도에 대응하는 기계적 검증 필요성
- 사양과 구현의 정합성을 보장하기 위한 SoT Chain 구조 제안
- 설계, 사양, 타입 계약, 구현을 잇는 4단계 계층 구조
- 프롬프트가 아닌 CI 게이트와 스키마 검증을 통한 강제적 규칙 적용
본 기사는 Zenn에도 동일한 내용이 공개되어 있습니다: https://zenn.dev/flip451/articles/sotohe-sot-chain-overview
AI 코딩 에이전트는 코드를 작성하는 속도가 빠르다. 하지만 "작성된 코드가 사양(Specification)대로인가"에 대해서는 사실 아무도 보장하지 않는다. 이 문제를 해결하는 한 가지 방법으로, 사양이나 설계를 문서 형태 그대로 두지 않고, 기계가 강제하는 제약(Constraint)으로 다루는 기구를 제안한다. 그 기구는 설계 판단, 사양, 타입 계약(Type Contract), 구현을 일방향 참조로 잇는 체인(Chain)과 각 링크를 검증하는 CI 게이트(Gate)로 구성된다.
본 기사는 시리즈 제1회이다. 먼저 문제 설정과 전체상을 보여주고, 개별적인 메커니즘은 제2회 이후에서 다룬다.
Claude Code나 Codex와 같은 에이전트에게 기능 추가를 요청하면, 몇 분 만에 컴파일이 통과되고 테스트도 green(통과) 상태인 코드가 돌아온다. 그 자체는 이제 놀라운 일이 아니다. 문제는 그 너머에 있다.
질문 1: 그 코드는 합의된 사양을 구현하고 있는가? 사양에 적히지 않은 동작을 마음대로 추가하지 않았는가?
질문 2: 사양 측면은 설계 판단과 모순되지 않는가?
질문 3: 테스트는 사양의 약속을 검증하고 있는가, 아니면 단순히 "테스트가 존재"할 뿐인가?
인간으로만 구성된 팀이라면, 이러한 정합성은 리뷰와 기억을 통해 유지되어 왔다. 하지만 AI 에이전트가 구현의 주력이 되면, 생산되는 코드의 양과 속도에 대해 인간의 리뷰가 구조적으로 따라잡을 수 없게 된다. 그리고 리뷰가 따라잡은 범위 내에서도, 사양과 구현의 일치를 기계가 검사하고 있는 것은 아니다. 수작업에 의한 확인인 이상, 결과는 담당자나 당시의 상황에 좌우된다.
그렇다면 보장은 사람의 주의력 밖에 두어야 한다. 그렇다고 규약이나 프롬프트(Prompt)에 적어서 지키게 하는 것만으로는 부족하다. 지시는 읽히지 않고 넘어갈 수 있으며, 읽더라도 지켜진다는 보장이 없다. 따라서 규칙은 우회할 수 없는 기구(CI 게이트, 훅(Hook), 스키마 검증)로서 구현해야 한다. 필자가 개발하고 있는 SoTOHE(Source of Truth Oriented Harness Engine)는 이러한 사고방식을 일관되게 적용한 템플릿이다.
SoTOHE는 AI 에이전트에 의한 사양 주도 개발(SDD: Spec-Driven Development)을 관리하는 Rust제 CLI (sotp) + 에이전트 하네스(Harness) 템플릿이다. Claude Code / Codex CLI를 조작면으로 사용한다. 개발의 전 과정(설계 판단 기록, 사양서 작성, 타입 계약 선언, 구현, 리뷰, 커밋, PR)을 기계 판독 가능한 산출물과 검증 게이트의 연속으로서 관리한다.
SSoT(Single Source of Truth, 해당 정보에 대해 신뢰할 수 있는 유일한 원천)라는 용어는 널리 알려져 있다. SoTOHE는 이를 단 한 장의 문서에 요구하는 것이 아니라, 계층(Layer)마다 배치한다. 핵심 아이디어는 단 하나다.
설계부터 구현까지를 4개의 계층으로 나누고, 각 계층에 SSoT를 각각 하나의 독립된 파일로 배치하며, 하류(Downstream)가 상류(Upstream)를 참조하는 일방향 체인을 기계적으로 검증한다.
계층별로 배치된 SSoT를 하류에서 상류로의 참조를 통해 하나의 사슬로 연결한 것. 그것을 SoTOHE에서는 SoT Chain이라고 부른다. 각 계층과 그 SSoT가 되는 파일은 다음과 같다.
| 계층 | SSoT 파일 | 라이프사이클 |
|---|---|---|
| ADR | knowledge/adr/*.md | 개발 단위를 넘나드는 영구적인 설계 판단 |
| 사양서 | spec.json | 개발 단위별 요구사항 (목적 / 스코프 / 제약 / 수락 기준) |
| 타입 계약 | <layer>-types.json | 개발 단위별 타입 선언 (타입 레벨 테스트) |
| 구현 | libs/* / apps/* 소스 | 영구적인 코드 |
최상류의 ADR (Architecture Decision Record)은 "왜 이 설계로 했는가"라는 결정을 이유와 함께 건당 하나의 짧은 문서로 남기는 기록 형식이다. SoTOHE 고유의 발명은 아니며, 널리 사용되는 이 관행을 체인의 기점으로 삼고 있다.
이 참조에는 방향이 있다. 하류는 반드시 상류를 참조한다. 사양서의 각 항목은 근거가 되는 ADR을 인용하고, 타입 계약의 각 타입 선언은 근거가 되는 사양 항목을 인용하며, 구현은 타입 계약과 대조된다. 참조가 끊기면(상류에 근거가 없는 기술이 하류에 나타나면) CI가 중단된다.
「문서와 코드를 동기화하자」라는 구호와 이 메커니즘의 차이점은, 참조가 기계 판독 가능 (Machine-readable) 하다는 것이다. 인용은 JSON 필드로 작성되며, 체인의 평가는 명령어 한 번으로 재현할 수 있다. 구현과 타입 계약 (Type contract)의 일치도 기계적으로 검증 가능하다.
각 링크의 평가 결과는 신호등으로 표현된다2.
| 참조 | 🔵 Blue | 🟡 Yellow | 🔴 Red |
|---|---|---|---|
| 구현 → 타입 계약 | 구현과 계약이 일치 | 미구현 | 계약 위반 |
| ... |
색상은 검사 결과일 뿐, 작업을 중단하라는 지시가 아니다. 중단 여부를 결정하는 것은 색상을 읽는 쪽의 '게이트 (Gate)'이다3. 세 가지 색상의 취급은 다음과 같다.
- 🔵 은 근거가 연결되어 있다. 그대로 진행해도 좋다.
- 🟡 은 연결되어 있지만, 해결해야 할 과제가 남아 있다. 중단 여부는 게이트 설정에 달려 있지만, 개발 단위의 완료 시점까지는 반드시 해결해야 한다.
- 🔴 은 참조가 끊겨 있다. 어떤 게이트에서도 중단되며, 즉시 수정해야 한다.
참고로, 이 세 가지 색상은 CI 자체의 green / red와는 별개의 레이어(Layer) 용어이다. 신호의 평가 결과는 커밋 게이트(Commit gate)나 CI의 pass / fail로 번역된다. 🔴 가 남아 있는 한, 커밋은 물리적으로 불가능하다. 인간의 주의력에 의존하지 않는다.
지금까지의 검증은 결정론적인 구조 검사이다. 하지만 구조 검사로 알 수 있는 것은 '참조가 있다'는 것까지이며, '참조가 의미론적으로 올바른가'는 알 수 없다. ADR을 인용한 사양서가 해당 ADR과 모순되는 내용을 작성하는 것은 얼마든지 가능하다.
따라서 SoTOHE는 각 링크를 두 개의 층으로 보호한다.
- 구조 검증 (Structural Verification) (결정론적): 참조의 존재와 정합성을 기계가 검사한다.
- 의미론 검증 (Semantic Verification) (LLM 판정): "이 인용이 의미론적으로 성립하는가"를 LLM이 판정한다.
의미론 판정 결과는 신호등의 세 가지 색상에 섞지 않고, 별도의 레인(Lane) 결과로 보유한다 (그 이유는 제4회에서 다룬다).
"LLM에게 판정하게 한다"라고 하면 불안할 수도 있을 것이다. SoTOHE는 이 판정을 CI 게이트로서 신뢰할 수 있는 것으로 만들기 위해 몇 가지 규율을 부과하고 있다. 합격 판정에는 근거 위치의 인용이 필수이다 ("LGTM"만 있는 합격은 존재하지 않는다). 판정 결과는 입력값의 hash에 동결되며, 상류(Upstream)나 하류(Downstream)에서 단 한 글자라도 바뀌면 실효된다. 실효된 상태를 회복하는 수단은 hash의 재계산이 아니라, 재판정의 합격뿐이다. 이는 "변경 사항을 읽고 정합성을 확인했다"는 것과 "맹목적으로 재발행했다"는 것을 구분하기 위함이다. 자세한 내용은 제4회에서 다룬다.
나아가, 마지막 링크(구현 → 타입 계약)에는 한 단계 더 높은 의미론 검증이 있다. 타입 선언으로부터 테스트 의무를 기계적으로 도출한다. 그 의무가 이행되었는지를 검증하는 게이트다. "작성해야 할 테스트가 작성되지 않았음"을 CI에서 검출하는 메커니즘이며, 이는 제5회의 주제이다.
SoTOHE는 모든 작업을 track이라는 단위로 관리한다. 1 track은 1개의 기능 추가 또는 1개의 버그 수정에 해당한다. 각 track은 전용 브랜치(Branch)에서 진행된다. 사양서, 타입 계약, 구현 계획, 리뷰 결과는 track 디렉토리에 독립된 파일로 남는다.
정규 플로우는 두 개의 명령어로 집약되어 있다.
/adr:add <slug> # 설계 판단을 ADR로 기록한다 (대화형 히어링)
/track:adr2pr # 해당 ADR을 기점으로, 사양 ⇒ 타입 계약 ⇒ 구현 계획 ⇒ 구현
# ⇒ 리뷰 ⇒ 커밋 ⇒ PR까지 자율 주행한다
이 하나의 명령어 내부에서는 사양을 작성하는 에이전트, 타입을 설계하는 에이전트, 구현하는 에이전트, 리뷰하는 에이전트가 역할을 분담하여 움직인다. 역할마다 담당 프로바이더 (Claude / Codex / Gemini)를 설정 파일로 할당할 수 있다. 예를 들어 리뷰는, 구현한 에이전트와는 별도로 기동되는 독립된 리뷰어가 수행하며, 지적 사항이 0이 될 때까지 커밋할 수 없다. git commit의 직접 실행은 훅(Hook)에 의해 차단되며, 커밋은 가드(Guard)가 있는 경로로만 존재한다.
이렇게 공정의 대부분이 자동으로 진행되므로, 인간이 판단하는 지점은 두 곳으로 압축된다. 입구에서 ADR을 작성하여 확정하는 것과, 출구에서 PR을 머지(Merge)하는 것이다. 워크플로우의 상세 내용은 제2회에서 다룬다.
이 메커니즘은 탁상공론상의 프레임워크가 아니다. SoTOHE 자체(sotp CLI)가 SoTOHE의 워크플로우를 통해 개발되고 있다. 본 원고 집필 시점을 기준으로 200개 이상의 track이 이 파이프라인을 통과하고 있다. 이 기사에서 소개한 신호등(traffic light)과 의미론적 검증(semantic verification) 또한 그 자체가 하나의 track으로서 사양화되고, 타입 계약(type contract)이 선언되었으며, 리뷰 게이트(review gate)를 통과하여 구현된 것이다.
자체 제작한 메커니즘을 자신의 개발에 사용하는 것(dogfooding)은 품질 보증인 동시에 설계에 대한 강제력이기도 하다. 게이트를 하나 늘릴 때마다 그 번거로움에 따른 비용을 가장 먼저 지불하는 것은 필자 자신이다³.
좋은 점만 있는 것은 아니다. 이 메커니즘에는 실비가 발생한다.
- 의미론적 검증의 LLM 판정에는 토큰 비용이 발생한다 (hash 동결 캐시(hash frozen cache)를 통해 변경이 없으면 0이지만, 최초 실행 및 변경 시에는 실비 발생)
- 구현에 착수하기 전에 ADR, 사양서, 타입 계약을 작성해야 하므로, 착수까지의 리드 타임(lead time)이 "즉시 작성하기"보다 길다
- 결과물의 스키마(schema)나 규약이라는 학습 비용이 존재한다
그럼에도 필자는 이 비용을 개발을 인간에서 AI로 넘겨주기 위한 대가로 받아들이고 있다. 인간이 모든 결과물을 계속해서 리뷰하는 한, 권한의 이양은 일어나지 않는다. AI가 작성하는 양이 늘어나면 인간의 리뷰로 품질을 지탱하는 노선은 결국 파탄에 이른다. 따라서 "사양대로"를 보장하는 업무를 인간의 주의력에서 기구(mechanism)와 결과물 측으로 옮긴다. 구현 전에 작성하는 ADR, 사양서, 타입 계약, 그리고 LLM 판정에 드는 실비 모두가 그 이전을 위한 지불이다.
마지막으로 시리즈 전체의 지도를 남겨둔다. 본 기사에서 본 SoT Chain(📍)이 중심에 있으며, 이어지는 각 회차는 그 주변을 하나씩 파고든다.
이후 회차에서 각 계층을 순차적으로 다룬다. 서두의 질문 1부터 질문 3에 답하는 것은 제3회부터 제5회이다. 질문 1은 구조에 관한 이야기와 동작에 관한 이야기로 나뉘므로 담당도 2회에 걸쳐 진행된다.
-
제2회: ADR부터 PR까지 자율 주행하는 track 워크플로우와 멀티 에이전트 분업
-
제3회: 타입 계약서를 SSoT로 만드는 TDDD (타입 정의 주도 개발) (질문 1)
-
제4회: hash 동결 verdict와 인용 의무로 LLM 판정을 CI 게이트로 만들기 (질문 2)
-
제5회: "작성해야 할 테스트가 작성되지 않음"을 검출하는 테스트 의무 게이트 (질문 1 및 질문 3)
-
제6회: SoTOHE를 사용하기 위한 템플릿 export 및 신규 프로젝트 실전 기록
-
제7회: SoTOHE를 뒷받침하는 설계 원칙
-
AI 에이전트에게 "사양대로"를 보장하게 만들기 — SoT Chain이라는 설계 (본 기사)
-
ADR부터 PR까지 자율 주행하는 track 워크플로우와 멀티 에이전트 분업 (공개 예정)
-
타입 계약서를 SSoT로 만드는 — TDDD (공개 예정)
-
LLM 판정을 CI 게이트로 만드는 — hash 동결 verdict와 인용 의무 (공개 예정)
-
"작성해야 할 테스트가 작성되지 않음"을 검출하는 — 테스트 의무 게이트 (공개 예정)
-
SoTOHE를 사용하기 — 템플릿 export 및 신규 프로젝트 실전 기록 (공개 예정)
-
SoTOHE를 뒷받침하는 설계 원칙 (공개 예정)
리포지토리: https://github.com/Flip451/SoTOHE-core
본 시리즈에서는 화살표를 구분하여 사용한다. → 는 참조의 방향으로, "참조하는 쪽 → 참조되는 쪽", 즉 하류에서 상류를 가리킨다 (구현 → 타입 계약 등). ← 는 그 반대로 쓴 표기로, 체인 전체의 열거 (ADR ← 사양서 ← 타입 계약 ← 구현)에 사용한다. ⇒ 는 공정의 순서로, 시간의 전후를 나타낸다 (사양화 ⇒ 구현 ⇒ 리뷰 등). ↩
원래 클래스메소드(Classmethod)사의 AI 주도 개발 프레임워크 Tsumiki에서 착안하여 작성한 기능이었으나, 정신을 차려보니 별개의 것이 되어 있었다. ↩
실제로 지불한 사례가 있다. 코드의 의미적 중복을 LLM으로 검사하는 게이트는 과거 모든 커밋에서 필수였다. 하지만 자신의 개발을 측정해 보니, 이 검사와 관련된 작업이 개발 시간의 약 15%를 차지하고 있었다. 게다가 재검사 58회 중 84%는 지적 사항이 없는 헛수고였다. 현재는 기본적으로 비활성화해 두고, 필요할 때만 활성화하는 위치로 낮추어 두었다. ↩
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기