코딩 에이전트: "Bash 모방자"에서 "AST 조작자"로의 전환
요약
기존 코딩 에이전트의 텍스트 기반 Diff 방식이 가진 환각과 비용 문제를 해결하기 위해, AST를 인식하고 프로그래밍 언어를 직접 사용하는 새로운 접근법을 제시합니다. Elixir와 같은 실행 환경을 활용하여 모델이 직접 코드를 파이핑함으로써 컨텍스트 재읽기 없이 정밀한 리팩터링을 수행합니다.
핵심 포인트
- 텍스트 기반 Diff 방식의 환각 및 공백 민감도 문제 지적
- 프로그래밍 언어 자체를 편집 도구로 사용하는 순수 함수형 파이핑 도입
- 컨텍스트 재읽기(Context Re-Reads)를 제거하여 토큰 비용 절감
- BEAM 런타임을 활용한 정밀한 코드 조작 및 로직 국소화
지난 포스트에서 우리는 50개의 취약한 JSON-RPC 래퍼(wrapper)를 단일 eeva 프로세스(BEAM 상의 Elixir)로 교체함으로써 "도구 추상화 (Tool Abstraction)" 계층을 제거했습니다.
우리가 어떻게 에이전트를 텍스트 기반의 해킹 방식에서 실제 AST(Abstract Syntax Tree, 추상 구문 트리)를 인식하는 리팩터링 방식으로 전환했는지 소개합니다.
문제점: "Diff"의 환상
표준 에이전트들은 검색 블록(search blocks)을 출력함으로써 파일을 "편집"합니다:
Plaintext
<<<< SEARCH
def hello_world, do: "hi"
...
이 방식은 근본적으로 결함이 있습니다. 이는 모델이 들여쓰기, 숨겨진 줄바꿈, 컨텍스트를 포함하여 파일의 정확한 상태를 완벽하게 환각(hallucination)해낼 것이라는 가정에 의존합니다. 만약 마지막으로 읽은 이후 파일이 단 한 칸의 공백이라도 바뀌었다면, 전체 작업은 실패합니다. 이는 취약하고, 상태를 인식하지 못하며(state-blind), 비용이 많이 듭니다. 코딩 에이전트들은 다중 편집(multi-edits)을 위해 쉽지 않은 알고리즘을 도입하며 이 문제를 해결하려 노력하고 있습니다. 모델이 파일을 변경할 때마다, 다음 편집을 위해 파일을 다시 읽어야 합니다. 이것이 표준 흐름이 된다면, 이를 올바르게 수행하기 위해 엄청난 양의 토큰 비용이 발생할 것입니다. 코딩 에이전트들은 마지막 편집 내용을 메모리에 유지하고 다음 편집을 이동시키는 몇 가지 닌자 기술(ninja tricks)로 이를 해결하려 합니다. 하지만 모델은 이 사실을 알지 못합니다. 결국, 파일을 편집하는 단순한 작업이 모델과 하네스(harness) 모두에게 추측 게임이 되어버립니다.
해결책: 순수 함수형 파이핑 (Pure Functional Piping)
우리는 커스텀 "편집 프리미티브 (edit primitives)"나 특화된 편집 도구를 도입하여 이 문제를 해결하지 않았습니다. 독특한 도구 API를 작성하는 것은 당신을 다시 프롬프트 엔지니어링 지옥으로 몰아넣을 뿐입니다.
대신, 우리는 모델에게 파일을 직접 편집할 수 있도록 완전한 Elixir 실행 권한을 부여합니다. 코드가 곧 도구이기 때문에, 모델은 표준 언어 기능을 사용하여 단일 원자적 작업(atomic operation) 내에서 여러 파일에 걸친 완전한 다중 편집 체인을 실행할 수 있습니다.
모델은 가공되지 않은 diff 블록을 출력하지 않습니다. 대신 순수 함수형 변환(pure functional transformations)을 통해 파일을 파이핑(piping)합니다.
config_path = "config/config.exs"
...
이것이 루프를 깨는 이유
가공되지 않은 프로그래밍 언어를 편집 엔진으로 사용함으로써, 우리는 몇 가지 거대한 아키텍처적 이점을 확보할 수 있습니다:
-
Zero Context Re-Reads (컨텍스트 재읽기 제로)
모델은 편집을 실행하고, 하네스(harness)가 업데이트되기를 기다렸다가, 파일을 다시 읽고, 두 번째 diff를 포맷팅할 필요가 없습니다. 상태는 Elixir 평가 스트림(evaluation stream) 내부에 존재합니다. 하나의 파일 읽기 출력을 다른 파일의 수정으로 직접 파이프(pipe)할 수 있어, 단 한 번의 API 호출로 복잡한 리팩터링(refactor)을 완료할 수 있습니다. -
Localized Logic, Less Hallucinations (국소화된 로직, 적은 환각)
모델은 줄 번호나 공백을 추측할 필요가 없습니다. 모델은 자신이 변경하고자 하는 대상을 정확히 찾기 위해 네이티브 Elixir 필터, 정규 표현식(regex) 매치 또는 맵(map) 함수를 작성합니다. 검색 로직은 BEAM 런타임에 의해 실시간으로 실행되므로, 정밀한 공백 레이아웃을 맞추어야 하는 취약한 의존성을 완전히 제거합니다. -
Native Multitasking (네이티브 멀티태스킹)
eeva는 슈퍼바이저 트리(supervisor tree) 내부에서 실행되기 때문에, 이 전체 멀티 편집 코드는 격리된 일시적 BEAM 프로세스 내에서 실행됩니다. 만약 모델이 스트림 중간에 논리적 오류를 범하면, 프로세스는 안전하게 충돌(crash)하며 컴파일러는 정확한 구조적 실패 내용을 컨텍스트(context)로 다시 전달합니다. -
Dynamic Introspection (동적 자기 성찰)
표준적인 텍스트 기반 설정에서는 모델이 파일을 편집하고, 결과가 잘 나오기를 바라며, 그 후에 테스트를 실행하거나 구문을 확인하기 위해 별도의 도구를 호출해야 합니다. 만약 편집이 세 폴더 떨어진 곳의 의존성을 깨뜨렸다면, 에이전트는 전체 실행이 충돌할 때까지 그 사실을 알 수 없습니다.
순수한 Elixir 실행을 통해, 모델은 작업을 마치기 전에 인라인(inline)으로 자신의 편집 내용을 자기 성찰(introspect)할 수 있습니다. 모델은 파일 변이(mutation)를 라이브 컴파일러로 직접 파이프하여 변경 사항이 시스템을 깨뜨리지 않는지 확인할 수 있습니다:
# 파일을 편집한 후, 동일한 패스(pass) 내에서 모듈이 여전히 컴파일되는지 확인합니다
File.read!("lib/math.ex")
...
직접 시도해 보세요: https://github.com/beamcore/agent
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기