AI 에이전트는 코드베이스를 이해하지 못하고 단순히 검색(grep)만 합니다.
요약
기존 AI 에이전트는 코드베이스의 의존성을 파악하기 위해 단순히 텍스트 검색(grep)에 의존하여, 실제로는 광범위한 컨텍스트를 읽어 비용과 속도를 저하시킵니다. 필자는 프로젝트 그래프를 유지하는 프레임워크 Semitexa를 구축하고, 에이전트에게 클래스 사용처 및 영향 범위(blast radius)를 데이터 기반으로 제공함으로써 이 문제를 해결했습니다.
핵심 포인트
- AI 에이전트는 단순 검색만으로는 코드 의존성을 파악하기 어렵습니다.
- 프로젝트 그래프는 런타임의 실제 연결 관계를 추적합니다.
- 에이전트에게 영향 범위(impact list)를 제공하면 정확도가 높아집니다.
- 그래프 시스템은 콘텐츠 해시로 최신 상태를 유지해야 합니다.
클래스를 건드리기 전에 신중한 개발자는 한 가지 질문을 던집니다. '누가 이것에 의존하는가?'
에이전트는 할 수 있는 유일한 방식으로 이에 답합니다. 텍스트 검색을 실행하고, 돌아오는 모든 것을 읽은 다음, 변경 사항이 국지적이라고 결정합니다. 대부분의 경우 괜찮습니다. 그렇지 않은 경우가 비싼 비용을 초래합니다.
grep이 볼 수 없는 것들
저는 18년 넘게 PHP를 작성해 왔고, 저에게 가장 큰 손실을 입힌 에이전트가 만든 버그들은 같은 형태를 가지고 있었습니다. 수정된 부분은 국지적으로 보였고, 근처의 테스트는 통과했습니다. 하지만 세 모듈 떨어진 곳에서 문제가 발생했습니다.
현대적인 PHP 애플리케이션에서는 검색할 수 있는 문자열 형태로 나타나지 않는 연결(wiring)이 많습니다:
- 핸들러가 호출(call)이 아닌 속성(attribute)으로 페이로드에 연결됩니다.
- 이벤트 리스너는 부팅 시 스캔을 통해 발견되므로, 아무것도 그것을 '호출'하지 않습니다.
- 템플릿은 이름으로 필드를 읽으므로, 속성을 변경하면 클래스를 가져오지 않은 페이지가 깨집니다.
Grep은 문자열을 찾습니다. 엣지(edge)를 찾지는 못합니다. 따라서 에이전트는 어두운 창고에서 손전등을 들고 조심하는 사람이 하는 것처럼 행동합니다: 더 많이 읽어냅니다. 프로젝트의 절반이 '만약을 대비하여' 컨텍스트에 포함되고, 답변은 느려지고 비용이 커지며, 여전히 추측일 뿐입니다.
에이전트에게 지도를 제공하세요
제 PHP 프레임워크인 Semitexa를 구축하는 동안, 저는 에이전트들에게 모든 작업마다 텍스트로부터 구조를 재구축하도록 요청하는 것을 멈췄습니다. 이 프레임워크는 프로젝트 그래프를 유지합니다: 런타임 자체가 사용하는 것과 동일한 속성 및 관습으로부터 수집된 클래스 간의 실제 엣지입니다. implements, handles, serves_route, returns와 같은 엣지들입니다.
수정 전에 에이전트는 두 가지 질문을 합니다:
bin/semitexa ai:review-graph:query --usages=<Class>
bin/semitexa ai:review-graph:impact <Class>
첫 번째는 클래스를 가리키는 지점을 나열합니다. 두 번째는 바깥으로 이동하여 영향 범위(blast radius)를 반환합니다: 변경 사항의 다운스트림에 있는 라우트, 핸들러 및 템플릿입니다. 이는 데이터로 돌아오기 때문에 에이전트는 추측하는 대신 그 데이터를 기반으로 행동합니다.
무엇이 바뀌었나
가장 마음에 들었던 부작용은 정확도가 아니었습니다. 에이전트가 프로젝트의 절반을 읽는 것을 멈췄다는 점입니다. 영향도 목록(impact list)을 가지고 있으면 중요한 파일을 열고 나머지는 건드리지 않습니다. 컨텍스트가 적어지니 결정이 더 좋아졌습니다.
코드 리뷰도 달라졌습니다. '이게 또 뭘 건드렸지?'라는 질문은 예전에는 제가 diff를 읽고 코드베이스를 기억해서 답해야 하는 것이었습니다. 이제는 단순한 질의(query)입니다.
트레이드오프 (The trade-offs)
지도(map) 역시 자체적인 실패 모드(failure modes)가 있으며, 이에 대해 솔직하게 이야기할 가치가 있습니다.
낡게 됩니다. 한 번 구축된 그래프는 5번의 수정이 일어난 후에는 존재하지 않는 코드를 설명합니다. 저희 시스템은 질의가 실행될 때 콘텐츠 해시(content hash)를 통해 새로 고치고, 할 수 없을 때는 그렇게 알려줍니다.
사각지대가 있습니다. 문자열로부터 생성되는 클래스 이름처럼 동적으로 연결된 것은 정적 스캔(static scan)에는 보이지 않습니다. 유용한 동작은 그래프가 완벽한 커버리지라고 가장하기보다 자신이 볼 수 없는 것을 말해주는 것입니다. '알 수 없음(unknown)' 섹션이 있는 영향 보고서가 깨끗한 보고서보다 더 솔직합니다.
관례에 따라 성능이 결정됩니다. 이 그래프는 프레임워크가 핸들러를 연결하는 한 가지 방법과 라우트를 선언하는 한 가지 방법을 가지고 있기 때문에 작동합니다. 만약 코드베이스에서 라우트가 월요일에는 YAML에서, 금요일에는 어트리뷰트(attribute)에서 온다면, 수집할 깨끗한 엣지(edge)가 없습니다. 이것이 제가 프레임워크가 각 작업을 수행하는 단 하나의 방법을 갖는 것에 신경 쓰는 큰 이유입니다.
grep은 여전히 역할이 있습니다
저는 매일 grep을 사용하고, 에이전트도 마찬가지입니다. 텍스트를 찾는 데는 올바른 도구입니다. 하지만 시스템을 이해하는 데는 잘못된 도구입니다. 왜냐하면 시스템은 관계(relationship)로 이루어져 있고, 관계야말로 텍스트 검색이 버려버리는 것이기 때문입니다.
만약 대규모 코드베이스에 에이전트를 실행한다면, 프레임워크 없이도 시도해 볼 수 있는 저렴한 방법이 있습니다. 변경되는 영역에 대한 대략적인 의존성 목록(dependency list)을 생성하여 에이전트가 편집을 시작하기 전에 앞에 넣어주세요. 얼마나 적게 읽는지 지켜보십시오.
오늘 여러분의 에이전트는 어떤 방식으로 변경의 영향을 판단하나요: 테스트, grep, 지도, 아니면 신뢰에 의존하나요?
저는 오픈 소스로 Semitexa를 구축하고 있습니다. 프로젝트 그래프는 여기서 더 자세히 설명되어 있습니다: Project Graph in Semitexa.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기