본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 03. 04:18

코드 에이전트는 더 나은 프롬프트가 아니라 계약이 필요합니다

요약

AI 에이전트의 가장 위험한 실패 모드는 코드가 부러지는 버그가 아니라, 테스트를 통과하지만 승인되지 않은 방향으로 제품 범위를 조용히 확장하는 '드리프트'입니다. 이 문제를 해결하기 위해 단순히 프롬프트를 개선하는 것만으로는 부족하며, 대신 에이전트의 행동에 대한 명확한 '계약(Contract)'을 정의해야 합니다. 계약은 명령어, 출력 스키마, 결정론적 규칙 등 외부 소비자가 관찰하고 의존할 수 있는 경계를 기계적으로 검증 가능하게 만들어, 리포지토리가 승인하지 않은 모든 추가적인 행동을 거부하도록 강제합니다.

핵심 포인트

  • AI 에이전트의 주요 위험은 명시적인 버그보다 '조용한 드리프트'입니다. 이는 테스트를 통과하지만 사양 범위를 벗어나는 코드 확장을 의미합니다.
  • 문제 해결의 핵심은 프롬프트를 개선하는 것이 아니라, 시스템의 경계(Boundary)를 정의하고 강화하는 것입니다.
  • ‘계약’이란 법적 문서가 아닌, 명령어, 출력 스키마, 결정론 규칙 등 외부 소비자가 기계적으로 검증할 수 있는 명확한 행동 서술을 의미합니다.
  • 출력 스키마에 `additionalProperties: false`와 같은 제한을 적용하거나, 테스트 케이스를 통해 예상되는 키만 허용하도록 강제하는 것이 중요합니다.
  • 이러한 '폐쇄된 계약'은 에이전트가 승인되지 않은 행동(예: 추가 메타 데이터 필드)을 할 때 시스템이 이를 거부하게 만듭니다.

배포 전 AI 에이전트의 드리프트 (drift) 를 가시화하기 위해 리포지토리를 어떻게 구조화했는지. 제가 에이전트 코딩 워크플로우에서 본 가장 위험한 실패 모드는 부러진 코드가 아닙니다. 부러진 코드는 적어도 가시적입니다. 위험한 실패는 그럴듯한 코드입니다: 테스트를 통과하고 요청과 유사한 것을 구현하지만, 아무도 승인하지 않은 방향으로 제품 범위를 조용히 확장하는 코드입니다. 버그가 없습니다. 충돌이 없습니다. 그저 드리프트일 뿐입니다. 이 문제를 몇 달간 싸운 후 저는 더 나은 프롬프트를 작성하려고 시도하는 것을 멈췄습니다. 문제는 에이전트가 추가 지시를 필요로 했다는 것이 아니었습니다. 문제는 리포지토리가 권한을 충분히 명확하게 만들지 못했다는 것입니다. 행동이 암묵적일 때 에이전트는 간극을 채웁니다. 해결책은 그들이 그렇게 하지 않도록 요청하는 것이 아닙니다. 해결책은 간극을 제거하는 것입니다. "계약 (contract)"이라고 할 때 저는 법적 문서나 무거운 프레임워크를 의미하지 않습니다. 저는 관찰 가능한 행동의 서술, 즉 명령어, 출력, 종료 코드, 스키마, 결정론 규칙 및 구현이 변경할 수 있는 경계를 의미합니다. 이는 전체 설계가 아닙니다. 외부 소비자가 관찰하고 의존할 수 있는 부분입니다. 프롬프트는 에이전트가 어떻게 행동해야 하는지 알려줍니다. 계약은 리포지토리가 승인하지 않은 행동을 거부하도록 만듭니다.

조용한 드리프트가 실제로 어떻게 보이는지
저는 --json 명령어를 구현하고 있습니다. 스펙에 따르면 출력에는 세 개의 키가 있어야 합니다: { "anchors" : [], "mappings" : [], "findings" : [] } 제가 에이전트에게 이를 구현하도록 요청합니다. 에이전트는 올바르게 구현하지만, 디버깅에 유용해 보이기 때문에 런타임 진단을 포함하는 메타 (meta) 키도 추가합니다. 에이전트의 암묵적 추론은 이해할 수 있습니다: 스펙이 이를 금지하지 않았으며, 출력을 더 정보 풍부하게 만듭니다. 테스트는 통과합니다. 세 개의 예상된 키가 모두 존재하고 정확합니다. 추가 키의 부재를 확인하는 테스트를 작성한 사람은 아무도 없습니다. 이러한 관대한 테스트는 다음과 같이 통과합니다:

expect ( result . anchors ). toEqual ([]);
expect ( result . mappings ). toEqual ([]);
expect ( result . findings ). toEqual ([]);

실제 출력이 다음과 같더라도:
{ "anchors" : [], "mappings" : [], "findings" : [], "meta" : { "cwd" : "/Users/me/project" , "durationMs" : 42 } }

차이 (diff) 는 깔끔해 보입니다. 코드 리뷰에서도 승인됩니다. 기능이 배포됩니다. 세 주 후, 정확한 JSON 스키마를 예상하는 하스트림 소비자가 응답을 거부하기 시작합니다. 스키마에 예상치 못한 필드가 있기 때문입니다. 또는 더 나쁜 경우: 메타 키가 내부 경로 정보를 누출할 수 있는 곳에 정보를 누출합니다. 버그는 에이전트가 요청을 구현하지 못했다는 것이 아닙니다. 버그는 리포지토리가 경계를 기계적으로 검증 가능하게 만들지 못했다는 것입니다. 폐쇄된 계약 (closed contract) 이라면 이를 잡았을 것입니다:

expect ( Object . keys ( result )). toEqual ([ "anchors" , "mappings" , "findings" ]);

또는 JSON Schema 를 사용하면:
{ "type" : "object" , "required" : [ "anchors" , "mappings" , "findings" ], "properties" : { "anchors" : { "type" : "array" }, "mappings" : { "type" : "array" }, "findings" : { "type" : "array" } }, "additionalProperties" : false }

구조화된 문서 (docs/contract.md) 에 명시적으로 출력 스키마를 폐쇄된 것으로 나열합니다: 추가 속성은 허용되지 않습니다.

AI 자동 생성 콘텐츠

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

원문 바로가기
6

댓글

0