AI 에이전트가 일을 끝냈을 때, 무엇을 확인해야 할까
목차
코딩 에이전트가 구현을 끝내고 테스트도 통과했다고 보고하면, 개발자는 무엇을 더 확인해야 할까. 실행한 검사가 요구사항의 어느 부분을 확인했는지, 그리고 에이전트가 어떤 가정을 선택했는지를 살펴야 한다. 결과를 빠르게 만드는 도구일수록 이 두 질문을 작업의 시작과 끝에 남겨 둘 필요가 있다.
Addy Osmani의 Critical thinking when agents do the work는 AI에 작업을 맡기는 동안 비판적 사고를 어떻게 유지할지 다룬 글이다. 아래는 그 주장의 요약과 개발 작업에 적용해 본 해석이다. 정렬 예제와 요청문은 설명을 위해 독립적으로 만들었으며 원문의 사례가 아니다. 원문 본문은 공개 미러를 통해 확인했다.
위임한 결과가 내 판단이 되는 순간
Osmani는 AI에게 일을 맡기는 태도를 세 단계로 설명한다. 작업은 넘기되 결과에 대한 책임을 유지하는 위임, 도구의 답을 검토할 가설이 아닌 다음 작업의 전제로 사용하는 상태, 그리고 확인할 필요조차 느끼지 않고 그 답을 자신의 결론으로 받아들이는 상태다. 마지막을 인지적 항복(cognitive surrender)이라고 부른다. 원문은 이 용어를 Steven Shaw와 Gideon Nave에게 귀속한다.
저자가 경계하는 변화는 점진적이다. 앞선 답이 괜찮았으니 다음 답은 훑어보고, 그다음에는 구조만 본다. 그러다 누군가 변경 이유를 물으면 설명할 수 없게 된다. 모델의 답이 매끄러워질수록 검토를 시작하게 만드는 눈에 띄는 오류도 줄어든다는 것이 그의 문제의식이다. 이 글은 그런 경험과 실천 제안을 담은 에세이이며, 특정 모델의 오류율을 측정한 실험 보고서는 아니다. 원문
이 관점을 코드 리뷰에 적용하면, 검토할 대상에는 변경된 줄뿐 아니라 그 줄이 전제로 삼은 결정도 들어간다. 기본값을 무엇으로 골랐는지, 충돌하는 요구사항 중 무엇을 우선했는지, 어디까지를 이번 작업에서 제외했는지가 결과를 바꿀 수 있다.
테스트가 통과해도 틀릴 수 있는 작은 예제
다음은 숫자가 문자열로 들어오는 목록을 정렬하는 가상 작업이다. 요구사항은 “정수 값이 작은 순서대로 정렬하되 출력은 문자열로 유지한다”이다. 입력은 정수로 변환할 수 있는 문자열로 한정한다.
그런데 구현이 문자열 자체를 정렬했다고 하자. 아래 첫 검사는 통과한다. 한 자리 양수에서는 문자열 순서와 숫자 순서가 같기 때문이다.
def sort_values(values):
return sorted(values)
assert sort_values(["2", "1"]) == ["1", "2"]
actual = sort_values(["2", "10"])
print(actual)
print(actual == ["2", "10"])실행하면 다음 결과가 나온다.
['10', '2']
False문자열 비교에서는 첫 글자인 1이 2보다 앞서므로 "10"이 먼저 나온다. 첫 검사에서 입력의 자릿수만 바꾸었는데 요구사항과 구현의 차이가 드러난다. 기존 검사는 거짓말하지 않았다. 다만 숫자 정렬과 문자열 정렬을 구별할 수 없는 입력을 사용했다.
이 요구사항에 맞는 최소 수정은 비교에 사용할 값만 정수로 바꾸는 것이다. 결과에는 원래 문자열이 남는다.
def sort_values(values):
return sorted(values, key=int)
assert sort_values(["2", "1"]) == ["1", "2"]
assert sort_values(["2", "10"]) == ["2", "10"]
assert sort_values(["0", "-3", "2"]) == ["-3", "0", "2"]여기까지 확인해도 모든 입력을 지원하는 것은 아니다. "1.5"나 "unknown"을 허용할지는 별도 요구사항이다. 숫자 순서가 아니라 식별자의 문자 순서를 원했다면 처음 구현이 맞았을 수도 있다. 어느 동작이 옳은지는 테스트의 초록색 표시가 아니라 요구사항이 결정한다. 위 예제는 이 차이를 설명하며, 실제 에이전트의 실수 빈도나 자동 검증의 효과를 측정한 결과는 아니다.
작업 전에 기대를 적고, 끝나면 빠진 범위를 읽는다
Osmani는 질문하기 전에 자신의 가설을 세우고, 결과를 받은 뒤에는 그것을 틀리게 만들 조건을 찾아보라고 권한다. 검증의 완료 기준도 작업을 시작하기 전에 정하라는 제안이다. 원문
정렬 작업에 적용하면 긴 설계 문서까지 필요하지 않다. 입력의 의미와 기대 출력, 이번에 다루지 않을 범위를 먼저 적으면 된다. 다음은 이 글에서 만든 요청 예시다.
정수 문자열 목록을 숫자 값의 오름차순으로 정렬해줘.
출력은 원래 문자열을 유지해야 해.
["2", "10"]의 기대 결과는 ["2", "10"]이야.
소수나 숫자가 아닌 문자열은 이번 입력 범위에 없어.
작업 후에는 실행한 검사와 결과,
추가로 정한 가정, 아직 확인하지 않은 범위를 알려줘.이 요청을 받으면 구현자는 정렬 기준을 임의로 선택할 여지가 줄어든다. 검토자는 결과를 볼 때 “정렬했다”는 요약을 믿는 대신, 숫자와 문자열 순서가 다른 입력을 실제로 검사했는지 확인할 수 있다. 기대 출력을 미리 정한 덕분에 구현에 맞춰 테스트의 정답을 바꾸는 일도 알아차리기 쉽다.
검토용 질문도 구체적이어야 한다. “잘했는지 다시 봐줘”보다 “숫자 정렬과 문자열 정렬을 구별하지 못하는 검사가 있는지 찾아줘”가 어떤 근거를 봐야 하는지 알려 준다. 다른 에이전트의 검토 역시 도움이 될 수 있지만, 같은 잘못된 전제를 공유한다면 의견이 일치해도 오류는 남는다. 이 때문에 검토자의 수와 함께 서로 다른 해석을 구별할 입력을 준비하는 편이 중요하다.
자율적으로 진행하게 하면서 판단을 유지하기
이 글을 모든 변경 전에 사람의 승인을 받으라는 뜻으로 적용하면 작업은 쉽게 정체된다. 여기서 제안하는 방식은 작업 범위를 먼저 정하고, 그 안의 되돌릴 수 있는 실행은 에이전트가 이어 가게 하는 것이다. 사람은 결과를 이해하고 받아들일 근거를 남긴다.
| 상황 | 에이전트에게 맡길 실행 | 사람이 정하거나 확인할 것 |
|---|---|---|
| 오탈자와 문장 다듬기 | 문서 수정, 변경 비교 | 원래 의미가 유지됐는지 |
| 정렬 기능 구현 | 코드 작성, 사례 실행, 실패 수정 | 숫자와 문자 중 어떤 순서가 요구사항인지 |
| 파일 영구 삭제 | 삭제 후보 조사, 영향과 복구 가능성 정리 | 실제 삭제 범위와 실행 허가 |
이 표는 원문의 고정된 승인 체계가 아니라, 실패의 비용과 되돌릴 수 있는 정도에 따라 위임 범위를 정해 본 적용 예다. 정렬 구현처럼 목표가 정해진 작업에서 작은 수정마다 멈출 필요는 없다. 반면 자료를 영구 삭제하는 결정은 실행이 쉬워도 결과를 되돌리기 어렵다. 둘에 같은 자율성을 적용할 이유가 없다.
Osmani는 큰 코드베이스 전체를 계속 머릿속에 보관하라고 요구하지도 않는다. 필요할 때 해당 부분의 이해를 다시 구성할 수 있어야 한다고 설명한다. 그래서 모든 줄을 외우는 대신, 변경의 의도와 판단에 필요한 자료로 돌아갈 경로를 남기는 것이 현실적인 적용이다. 원문
다음에 완료 보고를 받으면 실행 결과 하나를 먼저 열어 보자. 정렬 예제라면 ["2", "10"]의 실제 출력이다. 그 결과가 왜 요구사항에 맞는지 설명하고, 검사하지 않은 입력도 말할 수 있다면 승인할 근거가 생긴다. 설명할 수 없다면 새 작업을 더 맡기기 전에 그 부분의 이해부터 복구하는 편이 낫다.