애플리케이션 계약으로서의 구조화된 출력
요약
LLM 출력을 단순한 텍스트가 아닌, JSON Schema와 타입 지정 TypeScript 인터페이스를 따르는 '애플리케이션 계약'으로 다루는 것이 중요합니다. 구조화된 출력은 정규식보다 안정적이며, 데이터의 신뢰성을 높여 다운스트림 서비스의 오류율을 크게 낮춥니다.
핵심 포인트
- 구조화된 출력을 사용하면 LLM이 예측 불가능한 텍스트를 생성하는 것을 방지할 수 있습니다.
- JSON Schema는 응답의 형태와 타입을 강제하여 데이터 신뢰성을 보장합니다.
- 거절(refusal) 응답 자체를 안전 장치로 활용하여 시스템 안정성을 높일 수 있습니다.
- 스키마 버전 관리를 통해 다운스트림 서비스의 호환성 문제를 해결할 수 있습니다.
LLM 출력이 '느낌'이 아닌 '계약'을 가져야 하는 이유
저는 한 핀테크 스타트업의 AI 엔지니어였는데, GPT-4 채팅 기능을 신용 점수 제안 엔진으로 바꾸려고 시도하고 있었습니다. 사용자가 “승진했는데, 얼마 정도 대출받아야 할까요?”라고 입력하자 모델은 마치 시처럼 보이는 단락을 내뱉었습니다: “축하드립니다! $5k~$10k 정도가 괜찮을 수 있지만, 부채 대비 소득 비율(debt-to-income ratio)은 낮게 유지하세요…” 저는 정규식(regex) /\$(\d+)-(\d+)k/를 사용해서 숫자들을 추출하려고 했습니다. 스포일러: 공백 문자나 달러 기호 없이 “5-10k”라고 입력하는 등 누군가 다르게 입력하는 순간 작동이 멈췄습니다. 하이픈을 잘못 치는 것만으로도 제가 만든 다운스트림 서비스가 충돌했고, 안전한 신청자를 위험하다고 오분류했습니다. 마치 체로 돼지(greased pig)를 잡으려는 느낌이었습니다.
그때 저는 애플리케이션 계약으로서의 구조화된 출력(structured outputs as application contracts)의 힘을 발견했습니다. 모델에게 단순히 “내가 필요한 것을 주기를” 기대하는 대신, 응답이 가져야 하는 모양—JSON Schema와 타입 지정 TypeScript 인터페이스를 따르는 JSON 객체—을 정확하게 알려준 것입니다. 프롬프트는 다음과 같이 바뀌었습니다:
{
"loan_range": { "min": int, "max": int },
"confidence": "high" | "medium" | "low"
...
이제 모델은 실수로 시를 뿌릴 수 없습니다. 오직 지키거나 “죄송하지만 준수할 수 없습니다”라고 말합니다. 이 거절(refusal) 자체가 기능입니다: 제 코드는 if (response.refusal) …을 확인하고 안전한 기본값으로 폴백(fall back)합니다.
이것이 왜 정규식보다 나은가요? 정규식은 자유 텍스트에서 패턴을 찾는 부서진 탐정과 같습니다. 패턴 밖에 있는 모든 것—추가 공백, 이모지, 다른 구문—은 그것을 막다른 길로 보냅니다. 반면에 스키마 검증기(schema validator)는 올바른 데이터 구조를 파싱하고 누락된 필드나 잘못된 유형의 필드가 무엇인지 즉시 알려줍니다. 이것은 얼굴을 대충 보는 것 대신 여권을 확인하는 것과 같습니다.
제 에듀테크 사이드 프로젝트에서는 “question”, “options”, 그리고 “answer”를 포함하는 퀴즈 질문 목록이 필요했습니다. 스키마를 버전 관리("$schema": "http://json-schema.org/draft-07/schema#", "title": "QuizV2")함으로써, 이전 클라이언트를 망가뜨리지 않으면서 새로운 필드인 “explanation”을 배포할 수 있었습니다. 다운스트림 안전 검사(downstream safety checks) – 예를 들어, “옵션 문자열 길이가 200자를 넘지 않도록” – 는 스키마에 내장되므로, LLM이 UI를 망가뜨리는 거대한 답변을 우리에게 줄 수 없습니다.
간단한 사례 연구: 구조화된 출력(structured outputs)으로 전환하고 거절(refusals) 기능을 활성화한 후, 저희 핀테크 대출 승인 파이프라인의 오류율은 12%에서 1% 미만으로 떨어졌습니다. 우리가 거절 응답을 받은 유일한 경우는 사용자가 불법 활동에 대한 조언을 요청했을 때였고, 이때 우리는 정중한 오류 메시지를 반환했습니다.
결론: LLM을 당신의 기분에 맞춰 분위기를 타는 시인처럼 대하지 말고, 계약을 따르는 주니어 개발자처럼 다루세요. JSON 스키마를 정의하고, 타입이 지정된 인터페이스(typed interfaces)를 생성하며, 유효성 검사(validate)하고, 버전 관리(version)하며, 거절 응답을 안전망으로 활용하세요.
재미있는 정규 표현식(regex) 이야기나 스키마 관련 성공 사례가 있나요? 댓글로 알려주세요. 여러분이 LLM을 어떻게 길들이고 있는지 듣고 싶습니다!
만약 제가 공유한 기술적인 작업 및 아키텍처 디자인에 관심이 있다면, 제 경험을 바탕으로 더 자세한 내용을 다음에서 확인하실 수 있습니다: [https://github.com/SalmonJoy/My_guide_for_building_AI_systems/blob/main/Structured_outputs_as_application_contracts.md]
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기