자체 30문항 기준 NL2SQL 정확도를 80%까지 올렸다(이전 글 ). 같은 모델을 공개 벤치마크(BIRD Mini-Dev)로 돌려보니 숫자가 완전히 달랐다. BIRD는 11개 도메인 DB에 걸쳐 500개 질문이 담긴 NL2SQL 표준 벤치마크다. 80% → 56% 기존에 학습한 유통 도메인에서는 잘 맞췄는데, 처음 보는 도메인으로 넘어가니 절반 수준이다. 이 56%를 넘겨보려고 9번 실험했다. (처음엔 솔직히 금방 될 줄 알았다.) 한 줄 결론 SQL 생성 시도 횟수를 늘린다고 될 문제가 아니라, 처음부터 한 번에 맞춰야 되는 문제였다. 왜 SQL을 여러 개 만들어 고르는 방식(multi-candidate)을 시도했나 SQL을 하나만 만들지 말고 여러 개를 만든 뒤에, 그중에서 가장 좋은 걸 고른다 NL2SQL에서 이미 널리 쓰이는 방식이다. self-consistency (같은 질문을 여러 번 생성해 다수결로 선택) execution-based selection (실행 결과로 후보를 거르는 방식) multi-agent pipeline (여러 에이전트가 분업해서 SQL 생성) BIRD 리더보드 상위 모델들도 대부분 이 방향이다. 대신, 두 가지를 가정했다. 한 번 생성에는 정확도 한계가 있다 여러 개 만들고 잘 고르면 그 한계를 넘을 수 있다 1. 힌트를 더 주면 좋아질까 (4번 실패) 결과: BIRD에서는 효과 없음. 오히려 정답을 바꿔버린 경우 까지도 발생했다. LLM이 더 잘 고르게 힌트를 주자 예를 들어: 어떤 집계 함수 써야 하는지 어떤 컬럼이 “매출"인지 관련 컬럼 후보 목록이 어떤게 있는지 하지만, 결과는: 자체 30문항: 유지 BIRD: 변화 없음 일부 케이스에서는 결과가 더 나빠졌다. 원래 products.price를 잘 고르던 질문에서 힌트를 주자 order_items.unit_price로 바뀌었다. 힌트가 정답을 바꿔버렸다. 60%는 컬럼 선택이 아니라 SQL “형태"의 문제였다. 예: SUM(CASE WHEN ...) COUNT(CASE WHEN ...) 둘 다 맞는 SQL이지만 NULL 처리 차이 때문에 결과가 달라진다. 힌트로 컬럼을 바꿀 수는 있지만, SQL 작성 방식을 바꾸지는 못한다. 2. 여러 개 만들고 고르면 되지 않을까 (3번 실패) 선택지를 여러 개 만들어도 답이 안 바뀌었다. 결국 첫 번째로 만든 SQL을 그대로 썼다. 설정: k=3 temperature=0.3 결과 기반 선택 정확도 56% (변화 없음) 지표 값 후보 3개가 같은 결과 92% 첫 번째 후보 채택 100% selector가 단 한 번도 결과를 바꾸지 않았다. “+8% 오른 것처럼 보였던” 착시 처음엔 48% → 56%로 보였다. 다시 채점해보니 원래도 56%였다. 채점 로직 변경으로 생긴 착시였다. (이걸 계기로 채점 드리프트 가드도 만들었다.) 다양성을 늘리면 될까? temperature ↑ 프롬프트 변형 5개 추가 하지만, 결과는: SQL 텍스트는 달라짐 실행 결과는 동일 62%의 질문에서 5개 후보가 전부 같은 결과 결론적으로, temperature를 올리고 프롬프트를 바꿔도 실행 결과는 달라지지 않았다. 3. 시스템이 강제로 다른 후보를 만들면? (3번 실패) 결과: 강제로 선택지를 여러개 만들어도 selector가 정답을 못 고른다. 실행 결과만으로는 판별 자체가 불가능하다. LLM이 다양성을 못 만들면 시스템이 강제로 만들어보자. 실험 1: 컬럼 강제 올바른 컬럼 강제 → 60% 성공 잘못된 컬럼 강제 → 0% 성공 schema binding이 정확도를 결정한다 실험 2: selector 검증 정답 / 오답 / 그럴듯한 오답 / 변형 → 4개 후보 중 고르게 함 28.6% (거의 랜덤) 가장 치명적인 사례 DB에 저장된 실제 값은 체코어 VYBER(현금 인출)였는데, LLM은 이걸 모르는 채로 SQL을 만들어서 후보 4개 모두 WHERE 조건에 영어(cash withdrawal)를 썼다. 당연히 매칭되는 행이 없다. 결과: 4개 모두 빈 결과 (0 row) selector는 4개가 같은 결과를 내자 “합의"로 판단 오답 선택 정답이 아니면, 합의를 해도 의미가 없다 실험 3: 점수 기반 selector 전략을 바꿨다. 실행 결과만 보는 게 아니라, 반환된 값의 분포, 컬럼 수, 행 수 등 여러 신호를 종합해 각 후보에 점수를 매기는 V2 selector를 만들었다. 결과: V1: 40% V2: 33% 더 정교하게 만들었는데 오히려 더 나빠졌다. 한 케이스(qid 819)가 이유를 잘 보여준다. V2가 정답(gold) SQL에 55점, 명백히 틀린(obvious_wrong) SQL에 75점을 매겼다. 정답보다 오답을 더 높이 평가한 셈이다. 실행 결과만으로는 어떤 SQL이 더 “맞는지” 판단할 수 없다 접은 방향과 남긴 방향 세 방향 모두 폐기: 힌트 기반 보정 → 실패 LLM multi-candidate → 실패 실행 결과 기반 선택 → 실패 공통 원인 문제는 “선택” 단계가 아니라 “생성하기 이전” 단계다. SQL을 실행한 뒤 고르는 방식으로는 안 되고, 실행 전에 이미 올바른 테이블/컬럼이 잡혀 있어야 한다. 그래서 방향을 바꿨다 Schema 이해를 강화해서 한 번에 맞추기 Schema Binding Plan SQL을 바로 만들지 않는다. 먼저 사용할 테이블 / 컬럼 / join 조건을 JSON으로 생성 시스템이 검증 그 다음 SQL 생성 3차 실험에서 확인했다: LLM은 binding만 맞으면 100% 따른다. 문제는 SQL 생성이 아니고, schema 해석 단계 문제였다. 9번 실패에서 남은 것 실험은 실패했지만 교훈은 남았다. 채점 드리프트 가드. 채점 로직이 조금씩 바뀌어도 이전 결과와 비교할 수 있도록 맞춰주는 코드. 이게 없었다면 이번 실험이 “+8%p 성공"으로 잘못 기록됐다. signal classifier. “정답을 고를 수 있는 실마리가 있는가"를 STRONG/MISLEADING 등 4단계로 분류하는 도구. “selector가 약한 건지, 아니면 판별 근거 자체가 없는 건지"를 숫자로 나눠볼 수 있게 만들었다. 강제 binding 검증 코드. LLM에게 “이 컬럼을 써라"고 지시했을 때, 실제로 생성된 SQL에 그 컬럼이 들어갔는지 자동으로 확인하는 코드(SQL 파서 sqlglot 사용). 다음 단계 schema grounding에도 그대로 쓸 수 있다. 중단 조건 / 실험 설계 체계. 실험 전에 “여기서 안 되면 접는다"는 기준을 미리 코드에 박아두고, 작은 스팟 체크로 빠르게 방향을 결정하는 방식. 그리고 하나 더. SOTA가 맞다고 해서, 내 문제에도 맞는 건 아니다 AI 3개에게 물었더니 10개 중 8개가 multi-candidate를 추천했다. 실험 데이터가 없었다면 그대로 따라갔을 것이다. 마무리 8번 글에서는 “80%는 시작"이라고 썼었는데, 그건 학습된 도메인 기준이었다. 처음 보는 도메인(Unseen Domain)에서는 56%다. 이게 진짜 시작점이다.