
AI 전용 개발 환경「sunaba」를 통해 배우는: LLM의 편집 실수와 조급함 루프를 방지하는 AI 네이티브 툴 설계
요약
AI 에이전트가 코드 편집 시 발생하는 전체 덮어쓰기 실수와 조급함 루프(Panic Loop)를 방지하기 위한 'sunaba'의 툴 설계 방식을 다룹니다. 의도에 따라 write_file과 edit_file을 물리적으로 분리하여 시스템 차원에서 오류를 차단하는 AX(Agent Experience) 설계 사상을 공유합니다.
핵심 포인트
- AI의 코드 편집 실수인 'Silent Overwrite'와 'Panic Loop' 문제 분석
- 의도(Intent)에 따른 write_file과 edit_file의 물리적 분리 설계
- 툴 인자(Schema) 레벨에서 AI의 잘못된 편집 동작을 원천 차단
- AST 분석을 통한 스마트 치환 및 구문 오류 즉시 감지 기능
서론
지난 기사 「Claude Code 시대의 AI 에이전트 전용 『샌드박스(Sandbox)』 설계론」에서는 로컬 개발 환경의 파괴 방지나 Egress Proxy를 통한 물리적 보안, 그리고 검증 게이트를 통합한 개발 플로우의 필요성에 대해 해설했다.
이 보안 기반 위에서, AI 에이전트가 가장 스트레스 없이 안전하게 코드를 편집할 수 있도록 하는 「편집 툴(File Edit Tools)」 설계가 최근 몇 달 사이 결정적인 진화를 이루었다.
AI에게 코드를 작성하게 할 때, 많은 시스템이 직면하는 문제는 "사소한 치환 실수로 코드가 전부 손실된다", "구문 오류(Syntax Error)가 발생하면 AI가 패닉에 빠져 잘못된 패치를 덧씌우며 늪에 빠진다 (조급함 루프(Panic Loop))"라는 과제다.
본고에서는 자율 개발용 MCP 서버 「sunaba」가 이러한 과제들을 툴 디자인(AX: Agent Experience)의 힘으로 어떻게 해결했는지, 구체적인 구현과 설계 사상을 공유한다.
1. LLM에 의한 코드 편집에서의 「2대 트러블」
LLM에게 파일을 편집하게 할 때, 인간용 에디터나 일반적인 Git의 차분 패치(Unified Diff)를 그대로 사용하게 하면, AI 특유의 인지 한계로 인해 다음과 같은 두 가지 문제가 빈번하게 발생한다.
① 「부분 수정」을 하려다 「전체 덮어쓰기」(Silent Overwrite)
AI에게 "이 함수의 이 부분만 고쳐줘"라고 지시했을 때, AI가 편집 툴을 호출하는 인자에 파일 내용 전체를 덮어쓰는 듯한 데이터를 전달하여, 무관한 수백 줄의 코드가 조용히 삭제되는 사고가 끊이지 않는다.
특히 토큰(Token) 절감을 위해 차분 적용 툴을 병용하고 있는 경우, 툴 선택의 혼동(Intent의 혼동)으로 인해 이 문제가 발생한다.
② 조급함에 의한 자기 파괴 루프 (Edit Loop / Spiral)
AI는 한 번 코드를 망가뜨려 테스트 결과가 빨간색(Failed)이 되면, 거기서 조급해져 "망가진 상태의 파일"에 대해 더욱 엉뚱한 수정 패치를 적용하려 한다.
인간이라면 "일단 git restore를 해서 냉정하게 다시 시작하자"라는 판단을 할 수 있지만, 자율 루프에 진입한 AI는 과거의 정상적인 상태를 잊고, 불완전한 패치를 덧씌움으로써 파일을 완전히 복구 불가능한 상태까지 파괴해 버린다.
이러한 문제들은 프롬프트에 "덮어쓰지 마라", "망가뜨리면 되돌려라"라고 아무리 다정하게 타이르더라도 AI의 거동을 100% 제어할 수 없다. 시스템의 「툴 경계(Tool Boundary)」 그 자체로 물리적으로 방지해야 한다.
write_file
과 edit_file
- 의도(Intent)에 따른 툴의 완전 분리: sunaba의 이전 버전에서는 신규 파일 생성도 부분적인 문자열 치환도
write_file_sandbox라는 하나의 만능 툴로 담당하고 있었다. 하지만 이것이 AI의 「전체 덮어쓰기」를 유발하고 있었다.
최신 업데이트에서는 AI가 편집을 수행할 때의 「의도(Intent)」에 주목하여, 툴을 물리적으로 두 개로 분할했다.
write_file: 부분 치환용 파라미터(old_str등)를 일절 갖지 않고, 파일을 통째로 생성 또는 덮어쓰는 기능으로 한정.edit_file: 파일이 존재하지 않을 경우 에러를 반환하며, 반드시 「edit_file(기존 수정)old_str(이전 문자열 치환)」, 「start_line/end_line(행 범위 지정)」, 「append(추가)」 중 하나 이상의 편집 모드를 지정하게 함.
이를 통해 AI가 「부분 수정을 하려다 실수로 파일 전체를 날려버리는」 사태를 스키마 정의(툴 인자) 레벨에서 미연에 방지하고 있다.
또한, edit_file의 old_str 모드(AST 분석을 이용한 스마트 치환)에서 치환 시 구문 오류를 일으키는 불완전한 편집이 감지될 경우, 테스트 검증을 기다리지 않고 즉시 「편집 실패 경고」를 반환하는 가드레일(Guardrail)도 구현했다.
이미 적용된 패치를 몇 번이고 다시 적용하려는 AI 특유의 「중복 적용 루프」에 대해서도, "이미 적용되었을 가능성이 있습니다"라는 에러를 반환함으로써 무의미한 재시도를 즉시 차단한다.
📝 편집 전의 docstring을 자동으로 계승 (Docstring Preservation)
LLM이 기존의 함수나 클래스를 부분적으로 다시 쓸 때, 기존의 docstring(설명 주석)을 실수로 삭제하거나 새 코드에서 쓰는 것을 잊어버리는 경우가 매우 자주 발생한다.
sunaba의 edit_file
(AST 치환 모드)에서는 「편집 전의 코드가 가지고 있던 docstring을 해석하여, 편집 후의 새로운 코드에 자동으로 계승하여 머지(merge)하는」 기능이 갖춰져 있다.
단순히 문자열을 바꿔 쓰는 것이 아니라, AST (추상 구문 트리, Abstract Syntax Tree) 레벨에서 「새로운 함수의 바디(body)의 올바른 위치」를 특정하여 삽입하고, 여러 줄 주석의 인덴트(indent) 어긋남(상대 인덴트)도 자동으로 보정한다.
이를 통해 AI에게 「기존의 문서를 파괴하지 않도록 주의하라」고 프롬프트로 엄격하게 제약할 필요가 없어지며, 코드의 완전성과 문서화(documentation) 품질을 시스템 측에서 자동으로 보장할 수 있게 된다.
undo_file_edit
과 git apply --recount
- 타임머신과 허용력: 자폭 스파이럴에 빠진 AI를 구하기 위해 도입된 것이 자동 백업과 되돌리기(rollback) 메커니즘이다.
undo_file_edit
자동 롤백(Automatic Rollback): sunaba 상의 모든 편집(write_file, edit_file, transform_file)이 실행될 때마다, 편집 전의 스냅샷이 호스트 측(~/.sunaba/undo/)에 자동으로 저장된다 (파일당 최대 10세대).
AI가 코드를 망가뜨려 컴파일 에러를 발생시켰을 때, 에러 핸들러는 AI에게 「추가적인 수정 패치를 적용하여 앞으로 나아가라」고 하는 대신, **「 undo_file_edit 를 사용하여 일단 한 단계 되돌릴 것」**을 제1의 권장 액션으로 제시한다.
사실, 이 **「AI에게 명시적인 Undo 수단(타임머신)을 제공한다」**는 접근 방식은 기존의 코딩 어시스턴트 툴(예를 들어, 호스트 머신에서 직접 동작하는 Claude Code 등)을 살펴보아도 좀처럼 찾아보기 힘든 접근 방식이라 생각된다.
많은 툴은 「AI 스스로 git restore 를 실행하게 하거나」, 「AI가 끈질기게 수정 코드를 계속 내놓는 것」에 의존하고 있다. 하지만 AI가 혼란스러운 컨텍스트 속에서 올바른 명령어를 다시 선택하거나, 망가진 상태의 코드로부터 정합성을 유지한 코드를 재생해내는 것은 난이도가 높다.
시스템 측이 「확실하게 안전했던 한 단계 전의 상태」를 스냅샷으로 쥐고 있고, 그것을 전용 MCP 툴(undo_file_edit)로서 제공해 줌으로써, AI는 불필요한 명령어 사용이나 컨텍스트 오염을 신경 쓰지 않고 즉시 클린한 상태에서 재시도할 수 있게 된다.
Undo 이력 자체도 스냅샷으로 저장되기 때문에, 필요하다면 Redo(되돌린 것을 다시 진행)도 가능하다. 이를 통해 AI의 패닉 루프를 시스템 측에서 강제 종료할 수 있다.
apply_patch 의 위임
off-by-one을 허용한다. 기존의 apply_patch (Unified Diff 적용 툴)는 LLM이 자주 저지르는 「@@ 헤더의 행 수 계산 실수 (off-by-one)」로 인해 빈번하게 실패하곤 했다.
최신 구현에서 apply_patch는 권장되지 않는(Deprecated) 얇은 래퍼(wrapper)가 되었으며, 실제 처리는 컨테이너 내부의 git apply 명령어로 위임되었다.
git apply --recount -p1 --ignore-whitespace
여기서 효과를 발휘하는 것이 --recount 옵션이다.
LLM이 헤더의 행 수 메타데이터를 틀리게 작성하더라도, Git 측에서 자동으로 재계산하여 패치를 적용해 준다. LLM의 미세한 「계산 실수」를 툴 측에서 관대하게 받아들임으로써 리트라이(retry) 횟수를 대폭 줄이고 있다.
transform_file
- AI의 장점을 살리는 「명령적」 편집: AI에게 「엄격한 문자열 치환」을 시키려고 하면 미세한 공백이나 인덴트 어긋남으로 인해 실패한다. 반면, AI는 「코드(프로그램)를 생성하는 것」에 관해서는 매우 뛰어나다.
이 비대칭적인 능력을 활용하기 위해 설계된 것이 **transform_file**이다.
AI에게 「이 텍스트를 어떻게 편집할지」라는 차분(diff) 그 자체를 쓰게 하는 것이 아니라, **「편집을 수행하기 위한 Python 스크립트 (transform(text: str) -> str)」**를 쓰게 한다.
# LLM이 생성하는 transform 코드의 이미지
def transform(text: str) -> str:
# 클래스나 메서드의 위치를 AST나 단순 행 검색으로 특정하여, 안전하게 삭제/변경한다
...
이 Python 스크립트는 Base64로 전송되며, Docker 컨테이너 내부의 격리된 환경에서 안전하게 실행된다.
- JSON 이스케이프(JSON escape)나 셸의 인용부호(shell quote) 문제로부터 완전히 해방된다.
- 실행 후에는 unified diff가 AI에게 자동으로 반환되므로, AI는 "올바르게 수정되었는지" 확인하기 위해 파일 전체를 다시 읽을 필요가 없다 (컨텍스트(Context) 절약).
요약: AX(Agent Experience)의 모색
이번 sunaba 편집 도구의 업데이트를 통해 알 수 있는 점은, "AI를 위한 개발 환경 설계는 인간을 향한 설계(UX)와는 또 다른 관점이 필요할지도 모른다"는 것이다.
인간이라면 에러 메시지를 읽고, 자신의 의도를 정리하여 "일단 냉정해져서 git restore를 하자"라고 자발적으로 멈출 수 있다.
하지만 미리 정해진 자율 루프(autonomous loop) 속에서 묵묵히 움직이는 AI 에이전트에게는, 그러한 "분위기를 읽는" 듯한 유연한 대처를 기대하기에는 아직 현실적인 어려움이 있다.
인간을 위한 도구: 자유도가 높고 다기능이며, 사용자 자신의 유연한 판단에 맡겨진다.
AI를 위한 도구: 조작의 "의도(Intent)"가 처음부터 명확하게 구분되어 있고, 실패했을 때의 안전한 되돌리기(Undo)가 표준 장착되어 있으며, 가장 잘하는 접근 방식(코드 실행 등)으로 목적을 달성할 수 있다.
AI 에이전트가 원활하고 안전하게 개발을 진행할 수 있도록 하기 위해서는, 프롬프트(Prompt)를 통한 지시와 같은 불확실한 방벽에만 의존하는 것이 아니라, AI의 행동 특성이나 약점을 도구 설계 단계에서 보완하는 "AX(Agent Experience) 설계"의 관점을 도입하는 것이 향후 더욱 중요해지지 않을까.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기