매번 똑같은 방식으로 답하지 않는 컴포넌트 배포하기
요약
비결정론적인 언어 모델을 결정론적인 프로덕션 체인에 통합하기 위한 검증 및 수정 메커니즘을 다룹니다. 모델의 출력을 결과가 아닌 '제안'으로 취급하며, 계층적 검증 단계를 통해 비용과 효율성을 최적화하는 방법을 설명합니다.
핵심 포인트
- 모델의 출력을 신뢰하는 대신 기계적 검증기를 통해 사후 검토 수행
- 프롬프트에 규칙을 요청하는 대신 검증기(Validator)로 강제 적용
- 비용 절감을 위해 모델 재호출 전 기계적 수리(Mechanical repairs) 우선 실시
- 오류 발생 시 전체 재생성 대신 특정 블록에 대한 마이크로 호출 활용
우리의 프로덕션 체인(production chain)은 엔드 투 엔드(end to end)로 결정론적(deterministic)입니다. 동일한 설정, 동일한 빌드, 동일한 앱이 보장됩니다. 이것이 플랫폼이 약속하는 바입니다. 그리고 몇 달 전, 우리는 현존하는 가장 비결정론적인(least deterministic) 컴포넌트를 여기에 연결했습니다. 바로 고객의 앱에 설치될 코드를 생성하는 임무를 맡은 언어 모델(language model)입니다.
그 코드가 어떻게 작동하는지 — 어떻게 통합되고, 무엇을 건드릴 수 있는지 — 에 대해서는 다른 곳에서 이야기한 바 있습니다. 이번 이야기는 모델 주변의 메커니즘에 관한 것입니다. 즉, 매번 똑같은 방식으로 답하지 않는 컴포넌트가 변동이 허용되지 않는 체인 내부에서 살아남기 위해 무엇을 구축해야 하는지에 대한 이야기입니다.
출력물은 정답이 아니라 제안입니다
이것이 첫 번째 사고방식의 전환입니다. 모델이 작업물을 전달할 때, 그 무엇도 그것을 결과로 취급하지 않습니다. 그것은 하나의 제안(proposal)이며, 검사(inspected)를 거치게 됩니다.
규칙은 타협의 여지가 없습니다: 하나의 루트 컨테이너(root container); HTML에 몰래 삽입된 스크립트 금지; 외부 의존성(external dependencies)은 공개적으로 선언; 모든 곳에 HTTPS 적용; 승인된 도메인만 접근 가능. 이 중 그 어떤 것도 모델이 기억해주길 바라며 프롬프트(prompt)에 정중하게 요청하는 것이 아닙니다. 프롬프트는 교육을 담당하고, 검증기(validator)가 결정합니다. 모든 것은 생성될 때마다 사후에 기계적으로 재검토됩니다.
이것이 컴포넌트를 신뢰하는 것과 계약(contract) 아래 두는 것의 차이입니다. 컴포넌트에게 신뢰할 수 있게 해달라고 요청하는 것이 아니라, 그 비결정론적인 특성이 해롭지 않도록 만드는 것입니다.
루프에는 계층 구조가 있습니다
검증(validation) 단계에서 제안이 거부되었을 때, 우리는 즉시 모델을 다시 호출하지 않습니다. 응답은 계층화되어 있으며, 가장 저렴한 것부터 처리합니다. 프로덕션 환경에서는 모든 모델 호출이 시간과 비용을 소모하기 때문입니다. 또한, 수리 방법을 알고 있는 결정론적인 프로그램(deterministic program)이, 수리할 수도 있고 못 할 수도 있는 재생성(regeneration)보다 훨씬 낫습니다.
맨 아래에는 기계적 수리 (mechanical repairs)가 있습니다. 잘못된 형식의 JSON은 모델 없이도 수정됩니다. 수정본(revision)에서 누락된 필드는 부모(parent)와 비교됩니다. 만약 다른 모든 부분이 바이트 단위로 일치한다면, 누락된 필드는 상속됩니다. 모델이 해당 필드가 변경되지 않았다고 판단한 것이 맞았기 때문입니다. 그리고 특정 블록이 실제로 누락된 경우에는 섹션 전체가 아니라 해당 블록에 대해서만 마이크로 호출 (micro-call)을 수행합니다. 이는 전체 재생성 (regeneration)보다 훨씬 저렴합니다.
그다음에야 모델에 의한 수정 (correction by the model)이 이루어집니다. 정확한 검증 오류 (validation error)가 피드백으로서 모델에게 전달되며, 모델에게는 두 번의 시도가 주어집니다. 여기서 관료적으로 보이지만 실제로는 그렇지 않은 세부 사항이 하나 있습니다. 수정 모드 (correction mode)에서 모델은 섹션 이외의 다른 것으로는 답변할 수 없습니다. 이 잠금 장치가 없다면, "이것을 생성하는 데 실패했습니다"라는 답변이 "사실, 그것은 대화형 질문이었습니다"라는 식으로 변장할 수 있습니다. 즉, 실패가 옷을 갈아입게 되어 생성기 (generator)의 실제 퇴보 (regression)가 지표 (metrics)에서 보이지 않게 됩니다.
그리고 모든 것이 실패하면, 우리는 깔끔하게 실패 처리합니다. 생성은 원래의 오류와 함께 실패로 표시됩니다. 이때 오류는 재시도 더미의 마지막 오류가 아니라, 실제로 무슨 일이 일어났는지 알려주는 첫 번째 검사 단계의 오류입니다. 그리고 텔레메트리 (telemetry)의 카운터가 올라갑니다. 사용자는 간단한 메시지와 다음에 해야 할 일을 전달받습니다. 기술적인 세부 사항은 사용자가 아닌 우리를 위한 것입니다. 깔끔하고 계산 가능한 실패가 의심스러운 성공보다 낫습니다.
반복되지 않는 것을 테스트하기
미리 밝혀둡시다. 출력값 대신 속성 (properties)을 테스트하고, 평가 (evals)를 작성하며, 한 모델이 다른 모델을 채점하게 하는 것 — 이 중 어느 것도 독창적인 것은 아닙니다. 이는 이제 프로덕션 (production) 환경에서 LLM을 운영하는 사람들에게는 표준 장비와 같습니다. 정말 말할 가치가 있는 것은 이러한 도구들이 어디에 적용되는가 하는 점입니다. 왜냐하면 모든 것은 예산의 비대칭성 (asymmetry of budgets)에 달려 있기 때문입니다.
실시간 상황에서는 사용자가 지켜보고 있으므로, 매 초와 매 호출이 중요합니다. 즉, Best-of-N 방식이나 판사 (judge) 모델을 사용할 여유가 없습니다. 예산은 위에서 언급한 루프(loop)에 투입됩니다. 즉, 첫 번째로 기계적인 수리(mechanical repairs)를 하고, 두 번째로 타겟팅된 교정(targeted corrections)을 수행합니다. 반면 오프라인에서는 방정식이 뒤집힙니다. 미리 생성된 콘텐츠의 경우 비용이 분할 상환(amortize)되므로, 여러 후보를 생성한 뒤 판사 (judge) 모델이 검사를 통과한 첫 번째 결과가 아닌 가장 좋은 것을 선택하게 할 수 있습니다. 동일한 도구를 사용하지만, 두 개의 서로 다른 경제 체제가 존재합니다.
그리고 실제로 효과를 본 습관이 하나 있습니다. 실패 모드 (failure mode)가 계속 반복될 때, 그것을 단순히 종결해야 할 티켓(ticket)으로 취급하지 않고, 이름과 하네스 (harness) — 즉, 재현 가능한 케이스 세트 — 를 부여하는 것입니다. 가장 당혹스러운 사례는 동일한 조건에서 실패했다가 다시 성공하는 프롬프트 (prompts)입니다. 이것은 "수정"하는 것이 아닙니다. 그것은 버그가 아니라 컴포넌트 (component)의 본질이기 때문입니다. 이름을 붙이고 재현 가능하게 만들면, 그것은 팀의 미신이 아니라 엔지니어링 데이터 (engineering data)가 됩니다.
동일한 논리에 기반한 한 단계 더 깊은 층위는 다음과 같습니다. 모델이 존재하는 것이 아니라, 역할 (roles)이 존재합니다. 생성 (generating), 판정 (judging), 번역 (translating)은 동일한 모델에서 실행되지 않습니다. 각 역할은 자신만의 모델을 고정하며, 하나를 변경하는 것은 당신이 내리는 결정이지, 당신에게 일어나는 사건이 아닙니다. 각 역할의 프롬프트 (prompt)는 코드 내에 존재하며, 다른 모든 것과 마찬가지로 버전 관리(versioned)되고 리뷰됩니다. 그것은 가장 완전한 의미에서의 API 표면 (API surface)입니다.
뇌가 아닌, 공급업체 (vendor)
이 모든 것을 종합하면 매우 비낭만적인 결론에 도달합니다. 우리는 모델을 뇌로 취급하지 않습니다. 우리는 모델을 공급업체 (vendor)로 취급합니다. 재능은 있지만 예측 불가능하며, 제품을 선반에 바로 가져다 놓을 수 없는 공급업체 말입니다. 즉, 입고 검사 (goods-in inspection), 품질 관리 (quality control), 부적합 사항에 대한 납품서 반송, 그리고 배송이 도착하지 않을 때를 대비한 대비책 (fallback route)이 필요합니다.
"AI가 코드를 작성한다"는 말보다는 덜 흥미진진할 수 있습니다. 하지만 이것이 바로 두 번 다시 똑같이 답하지 않는 컴포넌트 (component)가 변동이 허용되지 않는 체인 (chain) 내부에서 살아남을 수 있게 해주는 방식입니다. 그리고 이것을 가능하게 만드는 것은 생성이 아니라, 바로 루프 (loop)입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기