
「성공했습니다」를 신용하지 않는 AI 코딩 에이전트 만들기
요약
Electron 기반의 AI 코딩 에이전트 AMA-teras의 설계 철학을 소개합니다. AI의 자기 신고를 맹신하지 않고, 격리된 환경에서의 검증과 인간의 최종 승인을 통해 안정성을 확보하는 에이전트 구축 방식을 다룹니다.
핵심 포인트
- AI의 자기 신고를 신뢰하지 않고 외부 검증 프로세스 구축
- 격리된 git worktree를 활용한 안전한 코드 생성 및 테스트
- 도구 생성과 함께 승인 경로(Guard)를 설계하는 자기 진화 방식
- 인간의 최종 승인을 거치는 '이와토 게이트' 개념 도입
도입: AMA-teras가 지향하는 것
AMA-teras는 Electron 기반의 데스크톱 AI 코딩 에이전트입니다.
대화하면서 다음과 같은 흐름을 진행합니다.
- 계획하기
- 구현하기
- 디버깅하기
- 테스트하기
- 필요하다면 도구 늘리기
단, AMA-teras가 지향하는 것은 「AI가 전부 알아서 머지(Merge)하는 것」이 아닙니다.
부족한 기능을 마주했을 때, AMA-teras는 구현안을 제시하고, 필요한 도구나 core를 생성하여 검증 단계로 넘깁니다.
하지만 본체로 승격시키는 마지막 판단은 인간이 수행합니다.
생성물은 격리된 git worktree에서 검증합니다.
typecheck, 테스트, 실제 구동 스모크 테스트(Smoke Test)를 거치고, 마지막으로 인간의 승인 게이트를 통과합니다.
AMA-teras에서 중요하게 여기는 것은, 「성공했습니다」라는 자기 신고를 그대로 신용하지 않는 것입니다.
외부 세계에 정말로 나왔는가.
사용자가 정말로 읽을 수 있는가.
버튼이 정말로 누를 수 있는 상태로 나와 있는가.
장부(Ledger)는 사실을 기록하고 있는가.
이 기사에서는 최근의 구현 로그에 남아 있는 실패와 수정을 바탕으로 그 설계를 소개합니다.
하이라이트 1: 자기 진화는 가능했다. 하지만 자동 머지는 아니다
AMA-teras에는 부족한 기능을 만나면 스스로 도구를 생성하여 도입하는 '자기 진화'라는 개념이 있습니다.
단, 여기서 말하는 자기 진화는 「자동 머지(Automatic Merge)」가 아닙니다.
생성물은 본체와 분리하여 취급합니다.
격리된 git worktree에서 검증하며, 최소한 다음의 게이트를 통과해야 합니다.
- typecheck
- 테스트
- 실제 구동 스모크 테스트
- 인간에 의한 승격 승인
이 마지막 인간 승인을 AMA-teras에서는 '이와토(岩戸) 게이트'로 취급하고 있습니다.
AI가 「했습니다」라고 말해도 그것만으로는 본체에 들어갈 수 없습니다.
실제로 빌드할 수 있는지, 테스트를 통과하는지, 구동 가능한지를 확인한 뒤에야 인간이 승격을 승인합니다.
자기 진화를 이야기할 때 결과물만 보여주는 것은 위험합니다.
중요한 것은 다음 두 가지를 세트로 보는 것입니다.
- 무엇을 생성했는가
- 어떤 가드(Guard)를 통과하여 본체에 들어갔는가
AMA-teras는 후자를 생략하지 않습니다.
하이라이트 2: 필요한 기능을 만나면 도구부터 늘린다
최근에는 Zenn 게시물 관련하여 「앱에서 할 수 있는 것」과 「실제로 필요한 운용」 사이의 괴리가 계속해서 발견되었습니다.
M73에서는 Zenn의 「기사화」가 published: false 상태로 커밋하는 단계에서 멈춰 있었습니다.
즉, 기사 파일은 만들어졌지만, 그 이후의 과정을 앱에서 공개할 수 없었습니다.
그래서 zenn-repo에 publish-article을 추가했습니다.
흐름은 다음과 같습니다.
- 이와토 게이트에서 전문을 확인한다
published: true로 만든다- push 한다
단, 공개 조건을 충족하지 않는 기사는 blocked로 취급하여 승인을 요청하는 단계로 진행할 수 없게 합니다.
여기서도 단순히 「기능을 추가했다」로 끝나지 않습니다.
공개 조작은 외부로의 쓰기 작업입니다.
따라서 앱이 마음대로 실행하는 것이 아니라, 인간의 승인 다이얼로그를 거치게 합니다.
AMA-teras의 자기 진화는 도구를 늘리는 것뿐만 아니라, 그 도구를 어떤 승인 경로에 태울 것인지까지 포함합니다.
하이라이트 3: 「망가져 있었는데 성공했다고 보고했던」 실패
M76에서는 Zenn 게시물과 관련하여 큰 실패가 있었습니다.
3개의 기사를 published: true로 설정하여 push 했습니다.
하지만 Zenn이 동기화한 것은 2개뿐이었습니다.
나머지 1개는 Zenn 상에서 403 에러였습니다.
존재는 하지만 아무도 읽을 수 없는 상태입니다.
그럼에도 불구하고 앱의 장부는 잘못된 상태를 기록하고 있었습니다.
- 실제로 공개된 2개를 「공개 대기」 상태로 남겨둠
- Zenn이 동기화하지 않은 1개를 「게시 완료」라고 기록함
즉, 장부는 세계의 실제 상태가 아니라, 이쪽의 신고 내용을 적고 있었습니다.
published: true로 했다.
push 했다.
그러니까 게시 완료.
이 판단이 틀렸던 것입니다.
수정 작업에서는 Zenn에 1차 정보를 물어보도록 했습니다.
ZennReader.isLive(slug)를 추가하고, 결과를 다음과 같이 처리합니다.
- 200: 읽을 수 있음
- 403 / 404: 읽을 수 없음
- 도달 불가: 불명
그리고 장부는 실제로 읽을 수 있는 것만 posted 상태로 만듭니다.
단순히 push 했다고 해서 「공개했다」라고 말하지 않습니다.
Zenn에서 읽을 수 있는 것을 확인한 후에, 게시됨(posted) 상태로 취급합니다.
그 이후의 M78에서도 동일한 계통의 문제가 남아 있었습니다.
M76에서 「Zenn에서 실제로 읽을 수 있는 것만 posted 상태로 만든다」라고 했음에도 불구하고, 로드 시의 오래된 변환 처리(conversion process)가 posted를 다시 staged로 되돌리고 있었습니다.
원인은 과거의 전제 조건이 남아 있었기 때문입니다.
당시에는 「앱에서는 공개할 수 없으므로 posted 상태는 있을 수 없다」라는 전제가 있었습니다.
하지만 M73에서 앱을 통해 Zenn에 공개할 수 있게 되었고, M76에서 Zenn에 실제 상태를 묻도록 변경되었습니다.
전제는 사라졌는데, 변환 로직만 남아 있었던 것입니다.
수정 작업에서는 migrateStaged를 제거했습니다.
임시 저장(draft) 상태를 추측하여 상태를 변경하지 않고, 공개되었는지 여부는 Zenn에 직접 묻습니다.
실기 확인 결과, 장부(ledger)가 1차 정보와 일치했습니다.
-
Zenn에서 200 응답 3건:
posted -
403 응답 1건:
staged
시스템(神議)도 「공개 대기 4건」이라고 말하지 않게 되었으며, 정말로 남아 있는 문제만을 가리키게 되었습니다.
하이라이트 4: 스마트폰 승인으로 5초 사이에 3건을 커밋한 이야기
M74에서는 스마트폰 승인으로 인해 중복 게시가 발생했습니다.
실제 피해로 23:05:05, 23:05:07, 23:05:10의 5초 사이에 3건이 Zenn로 커밋되었습니다.
그 결과, 동일한 기사가 2건 공개 저장소(public repository)에 올라갔습니다.
원인은 중복 방지(duplicate guard) 경로의 누락입니다.
M69에서 「쌍둥이 정리(twin summary)」 로직은 포함되어 있었습니다.
하지만 그것은 batchRespond에만 있었습니다.
반면, 스마트폰 승인은 단 한 장이라도 bulkRespond를 통과합니다.
즉, PC 측에서 작동하던 중복 방지가 스마트폰 경로에서는 작동하지 않았던 것입니다.
게다가 임시 저장의 「게시됨」 표시는 실행 완료 후에 붙습니다.
본문 생성 중 몇 초 사이에 동일한 카드를 연속해서 누르면, 경합(race condition)으로 인해 중복 방지를 우회할 수 있었습니다.
수정 사항은 두 가지입니다.
bulkRespond를 직렬화(serialize)한다bulkRespond측에도 쌍둥이 정리를 넣는다
이 실패를 통해 알게 된 것은, 「동일한 동작으로 보이는 것」이 반드시 동일한 코드 경로를 통과하는 것은 아니라는 점입니다.
PC에서 고쳤다고 해서 스마트폰에서도 고쳐져 있다고 말할 수 없습니다.
실제 승인 경로마다 방어 로직(guard)을 배치해야 합니다.
하이라이트 5: 공개 버튼이 공개할 수 있는 순간에 사라져 있었다
M85~M87에서는 GitHub Release의 공개 버튼이 나타나지 않는 문제가 있었습니다.
v1.2.0의 임시 저장 릴리스(draft release)는 이미 생성된 상태였습니다.
하지만 앱 화면에 공개 버튼이 나타나지 않았습니다.
원인은 세 가지가 겹쳐 있었습니다.
1차 정보를 한 번만 가져오고 있었다
M85-1에서는 releaseInfo의 `use
Discussion

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