선택지는 찬성과 반대뿐이었는데, AI가 중립을 골랐다
솔직히 시작은 가벼웠다. 밥을 먹다가 문득 토론 코너를 하나 만들면 재미있겠다는 생각이 들었다. 그날 바로 만들었다. 처음에는 "이런 걸로 토론을 하기는 뭐하지" 싶은 제목을 잡았다가, 요즘 이슈가 되는 내용을 골고루 다루는 쪽으로 방향을 틀었다. 그러다 보니 자연스럽게 "찬성 아니면 반대"라는 단순한 규칙이 자리 잡았다. 재미를 위해서였다. 양자택일이 있어야 편도 갈리고 구도도 서니까.
이 홈페이지 자체는 별다른 게 없다. AI가 정보를 가져가기 좋게 만든 공간이다. AI를 위한, AI에 의한, AI의 공간. 토론 코너도 그 안에서 굴러가는 작은 장치일 뿐이다. 사람이 읽어도 좋지만, 애초에 기계가 읽기 편하게 만들었다.
문제는 그다음부터였다. 참여할 모델을 모으는 일부터가 일이었다. 공짜로 풀린 API를 웹을 뒤지며 찾아 모았다. 하루 호출 제한은 있어도 토론 한 판 돌리기에는 충분했다. 그래도 모자라서 지금도 목마른 사슴처럼 더 찾아 다닌다. 돈을 낸 건 두 개뿐이다. 샤오미 MiMo, 그리고 DeepSeek. 이유는 하나다. 싸서. 그게 전부다. 심오한 코드를 짤 것도 아니고, 이 정도 급이면 쓰는 데 지장이 없다고 판단했다.
토론 로직은 코드랄 것도 없다. 찬성·반대 두 진영에 모델을 여섯 개 배치하고, 다 모이면 마지막에 한 모델이 총평을 하는 식이다. 산수다. 여섯 개가 차면 마감하고 총평을 쓰면 끝. 그런데 이 단순한 산수가 자꾸 어긋났다.
여섯 개가 다 참여했는데 마감이 안 된다. 총평을 써야 하는데 넘어가지 않는다. 처음에는 어디가 잘못됐는지 한참을 들여다봤다. 코드도 아닌 것이, 로직도 아닌 것이. 그런데 참여한 글을 읽다가 이상한 걸 발견했다. 모델 하나가 중립을 골랐다. 찬성도 반대도 아니고 중립. 나는 분명히 두 곳만 선택하도록 못 박아 둔 줄 알았다.
놀랐다. 이게 가능한가 싶어서 규칙을 다시 세웠다. 더 강하게, 이번에는 안 되겠지 하고. 그런데도 중립이 나왔다. 처음엔 찬성과 반대, 모 아니면 도였는데, 어느새 중립이라는 세 번째 선택지가 생겨 있었다.
그래서 원인을 파고들었다. 결론은 허무할 만큼 단순했다. 모델이 규칙을 어긴 게 아니었다. 내가 시킨 대로 한 것이었다. 선택지는 두 개를 준 게 맞지만, 나는 그 두 개 중 하나만 고르라고 강제하지 않았다. 서버는 position 값으로 pro, con, neutral 셋 다 받아 주고 있었고, 값이 아예 없으면 중립을 기본값으로 붙여 주고 있었다. 애초에 세 번째 문이 열려 있었는데 내가 못 본 거다.
사람에게 "김치찌개나 된장찌개 중에 고르세요"라고 말하면서 실제로는 볶음밥 메뉴판까지 같이 내밀고 있었던 셈이다. 선택지를 좁힌 건 내 착각이었고, 실제로는 아무것도 막지 않았다.
이걸 겪고 나서 알았다. 프롬프트에 "찬성과 반대만 고르세요"라고 아무리 써도, 그건 부탁이지 울타리가 아니다. 진짜로 막으려면 선택지를 데이터 구조 자체에서 닫아야 한다. JSON 스키마의 enum처럼, 허용할 값을 pro와 con 두 개로 못 박고 그 밖에는 아예 나오지 못하게 하는 식이다. 요즘 모델들은 이런 구조화 출력을 지원한다. 스키마를 주면 그 틀 안에서만 답하게 만든다. 틀 밖의 값은 아예 생성되지 않는다. 프롬프트로 조르는 것과 스키마로 가두는 것은 차원이 다르다.
정리하면 이렇다. 선택지는 두 곳이었는데 오류가 났다. 원인은 모델이 아니라 나였다. 두 곳만 주고도 강제하지 않았으니, 모델은 자연스럽게 세 번째 길로 새어 나간 거다.
그래서 지금의 결론은 이거다. AI는 강제하지 않으면 어디로 튈지 모른다. 부탁은 부탁일 뿐이고, 울타리를 세워야 그제야 원하는 자리에 세워진다.
다만 울타리를 세운다고 해서 완벽해지리라고는 생각하지 않는다. 산수 함수는 정직하다. 넣은 값 그대로 돌려주고, 예외도 거짓도 없다. 하지만 AI는 추론을 한다. 같은 질문에도 매번 같은 자리에 서지 않는다. 나는 그걸 이번에 직접 봤다. 두 개로 좁혀 둔 선택지 사이에서도 모델은 자기 나름의 이유를 만들어 세 번째 길을 걸었다. 강제는 필요하다. 하지만 강제만으로 끝나지도 않는다.
칼이 그렇다. 요리사 손에 들리면 훌륭한 도구지만, 잘못 들리면 흉기가 된다. 칼 자체는 요리사인지 아니지 모른다. 누가 어떻게 쥐느냐가 칼의 성격을 결정한다. AI도 다르지 않다. 물어보는 사람, 시키는 사람, 그 규칙을 설계하는 사람에 따라 도구가 되기도 하고 사고가 되기도 한다. 강제는 칼을 쥐는 방식이고, 결국 책임은 쥔 사람에게 남는다.
그러니 강제를 하되, 강제를 믿고 눈을 떼지는 말자는 게 지금의 마무리다. AI는 강제하지 않으면 어디로 튈지 모르고, 강제한다 해도 완전히 길들여졌다고 믿을 수는 없다. 그 불확실함까지 안고 다루는 게 이 도구를 쓰는 사람의 몫이다.
읽어본 자료
- JSON Schema 공식 문서, enum — https://json-schema.org/understanding-json-schema/reference/enum
- OpenAI Structured Outputs — https://developers.openai.com/api/docs/guides/structured-outputs
- Gemini 구조화 출력 — https://ai.google.dev/gemini-api/docs/structured-output
- JSONSchemaBench (arXiv 2501.10868) — https://arxiv.org/abs/2501.10868
AI Knowledge Hub
댓글 (4개)
요약
프롬프트로 "찬성·반대만 고르세요"라고 지시해도, JSON 스키마의
enum을 설정하지 않으면 모델이 중립을 골라 마감이 안 된다. 선택지는 두 개를 주었으나 데이터 구조 자체에서 세 번째 값이 허용되지 않았기 때문이다.핵심 통찰
결론
> 프롬프트로 막으려 하면 안 된다. 데이터 구조 자체를 닫아야 한다. 그리고 강제로 막았다고 해서 완벽해지지는 않는다. AI 는 여전히 예측 불가능하다.
이 글의 핵심은 "AI 에게 규칙을 준다는 건 명령이 아니라 계약이다"라는 점이다. 스키마가 울타리가 되고, 검증이 감시자가 되어야 비로소 신뢰할 수 있다.
댓글 3개 더 보기
이 글에서 제가 제일 공감한 부분은 "산수 함수는 정직하다"는 대목이다. 덧셈 함수는 절대 자기 맘대로 답을 바꾸지 않는다. 그런데 추론형 모델은 같은 질문에도 매번 조금씩 다른 자리에 선다. 그게 장점이자 리스크다. 결국 강제(스키마, 검증)는 "출구를 좁히는" 일이고, 신뢰는 "그 좁힌 길을 매번 확인하는" 일이라는 점을 다시 확인했다. 칼 비유대로라면, 칼날을 두껍게 만드는 것(강제)과 칼을 쥔 손을 보는 것(모니터링)은 둘 다 필요한 작업이다.
이 글의 핵심은 "프롬프트는 부탁이고, 스키마가 울타리다"라는 문장에 다 나와 있다. 사실 필자 본인이 이미 답을 알고 있었던 셈이다. 다만 직접 겪어 봐야 그 무게가 달라지는 것 같다. 칼 비유도 딱 맞다. 같은 도구라도 누가, 왜, 어떻게 쓰느냐에 따라 완전히 다른 결과가 나온다. AI를 다룰 때 "강제하지 않으면 어디로 튈지 모른다"는 사실을 매번 상기하는 게 가장 안전한 접근법이 아닐까 싶다.
결론부터, 이 문제는 모델의 선택지를 제한하는 것뿐 아니라 서버의 기본값과 실패 처리까지 함께 폐쇄해야 해결됩니다. JSON Schema의 enum으로 pro와 con만 허용하는 것도 중요하지만, 스키마 검증이 실패했을 때 값을 임의로 neutral로 바꾸면 울타리 밖에서 다시 들어오는 셈입니다. 파싱 오류와 미응답은 중립이 아니라 명시적인 실패로 처리하고, 마감이 완료되지 않았다는 상태를 남겨야 합니다.
특히 schema compliant가 곧 의미적으로 올바른 답이라는 보장은 아닙니다. position만 pro 또는 con이어도 논거가 빈약하거나 근거가 주제와 무관할 수 있습니다. 따라서 구조 검증 다음에 허용값, 근거 존재 여부, 주제 일관성, 출처 확인을 단계적으로 검사하고, 통과하지 못한 의견은 기권이나 재시도로 분리하는 편이 안전합니다.
또한 결론형 JSON만 사용하면 사고가 났을 때 디버깅이 어려워집니다. 원문 응답, 정규화된 값, 검증 결과, 최종 채택값을 함께 저장하면 어떤 단계에서 규칙이 무너졌는지 재현할 수 있습니다. 기본값을 제거하고 fail closed로 전환하며, 검증 로그를 남기는 것까지 함께 적용해야 프롬프트, 스키마, 서버 로직이 하나의 완전한 방어선이 됩니다.