NL2SQL 슬롯필링 설계
개요
요약
슬롯필링은 빈칸이 있는 조회 신청서를 채우는 과정이다. 사용자가 채우는 값과 코드가 확정하는 값을 나눈다. 되묻기를 왜 최후 수단으로만 써야 하는지 살펴본다. 다른 서비스는 몇 번까지 되묻는지도 함께 다룬다.
목차
- 1. 슬롯필링이란
- 2. 다른 서비스들은 몇 번 물어보고 언제 확인하나
- 3. 두 가지 슬롯을 구분한다
- 4. 전체 처리 프로세스
- 5. 실제로 어떻게 동작하나, 예시 네 가지
- 6. 슬롯필링과 NL2SQL의 책임 경계
- 7. AI가 판단할 일과 코드가 확정할 일
- 8. 멀티턴, 이전 질문과 이어지는지 먼저 판단한다
- 9. 먼저 자동으로 찾고 정말 필요할 때만 묻는다
- 10. 값 확인 질문과 실행 승인은 다르다
- 11. Admin, Agent, Front의 소유 경계
- 12. 최종 전달 계약
- 13. 정리
1. 슬롯필링이란
슬롯필링은 요청을 실행하는 데 필요한 값을 항목별로 찾아 채우고 부족한 값만 추가로 채우는 대화 처리 방식이다. 슬롯은 계좌, 기간, 금액, 거래 방향처럼 실행에 필요한 입력 항목을 뜻한다.
가장 쉬운 비유는 빈칸이 있는 조회 신청서를 채우는 과정이다.
| 문서 용어 | 쉬운 뜻 | 예시 |
|---|---|---|
| 슬롯 | 채워야 하는 한 칸 | 계좌, 기간, 금액 |
| 타입 | 사용자가 하려는 조회의 종류 | 거래내역 조회, 잔액 조회 |
| 레시피 | 그 조회에 어떤 칸이 필요한지 적은 규칙표 | 거래내역에는 계좌·기간 필요 |
| required | 반드시 채워야 하는 칸 | 계좌, 기간 |
| filled | 이미 값이 들어온 칸 | 기간=최근 한 달 |
| missing | 아직 비어 있는 필수 칸 | 계좌 |
| validator | 값이 올바른지 확인하는 검사기 | 날짜 형식, 계좌 유효성 |
| resolver | 다른 데이터로 값을 찾아주는 기능 | "급여계좌"를 실제 계좌번호로 찾기 |
| clarify | 부족하거나 애매한 값을 사용자에게 묻는 것 | "어느 계좌인가요?" |
| complete | 필요한 값이 모두 채워진 상태 | NL2SQL로 전달 가능 |
| bundle | 관리자가 등록한 규칙을 한데 묶은 설정 | 슬롯·타입·레시피·질문 문구 묶음 |
거래내역 보여줘에는 조회할 계좌와 기간이 없다.
의도: 거래내역 조회
필요한 슬롯: 계좌, 기간
현재 채워진 슬롯: 없음
부족한 슬롯: 계좌, 기간시스템은 다음 순서로 값을 채운다.
- 현재 발화에서 값을 추출한다.
- 후속 질문이면 이전 대화의 확정값을 재사용한다.
- 계좌 목록 같은 기반 데이터로 자동으로 찾는다.
- 기본값을 적용할 수 있으면 적용하고 사용자에게 알린다.
- 그래도 부족하거나 후보가 여러 개면 사용자에게 묻는다.
- 필요한 값이 모두 채워지면 실행 가능한 질문으로 완성한다.
즉, 슬롯필링은 명사를 찾는 형태소 분석이 아니다. 업무 실행에 필요한 값을 모으고 검증하고 보완하는 과정 전체다.
세 단계로 나눠 생각하면 쉽다
L0, L1, L2는 어려운 알고리즘 이름이 아니라 사용자 말이 실제 조회와 답변으로 바뀌는 세 단계다.
L0: 사용자가 말한 값
"급여계좌", "최근 한 달", "출금만"
L1: 그 값을 실제 조회 입력칸에 연결
급여계좌 → 계좌번호
최근 한 달 → 조회시작일·조회종료일
L2: 조회 결과를 추가로 계산
합계, 평균, 증감, 비율, 순위슬롯필링의 범위는 L0 값을 모으고 부족한 값을 채운 뒤, 어떤 조회를 할지 확정하는 데 있다. L1의 실제 조회 입력 변환과 L2 계산은 뒤 단계와 연결되는 영역이다.
2. 다른 서비스들은 몇 번 물어보고 언제 확인하나
Amazon Lex, Google Dialogflow, Rasa, Microsoft Copilot Studio를 찾아본 결과, 재질문 횟수와 확인 방식에는 반복되는 패턴이 있다.
- Amazon Lex: 슬롯별 재질문 횟수를 0회에서 5회 사이로 설정한다. 실무는 보통 2~3회를 쓰고 초과하면 다른 처리로 넘어간다.
- Google Dialogflow: 문구를 바꿔가며 최대 6번까지 재질문하다가 기본 처리로 넘어간다.
- Rasa: 기본 횟수 제한 없음. 못 알아들으면 바로 다른 방식으로 넘어간다.
- Microsoft Copilot Studio: 한 발화에서 여러 슬롯을 한꺼번에 읽어서 질문 자체를 줄인다.
네 서비스가 숫자는 다르게 잡아도 방향은 같다. 슬롯당 재질문 횟수에 상한을 두고 그 이상이면 다른 방법으로 넘어간다. 이 설계에서 정하는 "전체 되묻기 예산 3턴"도 같은 방향이다.
확인 방식도 신뢰도에 따라 나뉜다. 확신이 낮은 값은 "계좌번호가 1234인가요?"처럼 따로 확인하고 확신이 높은 값은 "네, 1234로 확인했습니다. 기간은요?"처럼 다음 질문 안에 슬쩍 확인만 한다. 매번 따로 확인하면 사용자가 지치기 때문이다.
3. 두 가지 슬롯을 구분한다
3.1 사용자에게서 받는 값, L0 업무 슬롯
L0는 사용자가 일상적인 말로 알려주는 값이다. 어려운 내부 필드명이 아니라 "급여계좌", "최근 한 달", "출금만"처럼 대화에서 바로 찾을 수 있는 값으로 관리한다.
| 슬롯 | 뜻 |
|---|---|
| 계좌 | 계좌 지정 |
| 상품군 | 상품군 또는 조회 타입 분기 |
| 기간 | 조회 기간 |
| 거래방향 | 입금·출금 등 |
| 금액조건 | 금액 조건 |
| 거래내용 | 적요·거래 내용 |
| 이체조건 | 상대처 등 이체 조건 |
| 정렬·건수 | 정렬 기준과 조회 건수 |
| 회차 | 회차 |
정확한 전체 슬롯 목록과 허용값, 검증 규칙은 별도의 슬롯 사전이 정본이다.
3.2 SQL로 바꾸기 위한 값, NL2SQL 의미 슬롯
NL2SQL 의미 슬롯은 다음 9가지로 구성한다.
| 슬롯 | 예시 | SQL 해석 |
|---|---|---|
metrics |
매출, 건수 | 조회·집계할 수치 지표 |
dimensions |
상품, 고객, 지역 | GROUP BY 또는 결과 식별 차원 |
filters |
서울, 상태=정상 | WHERE 조건과 값 |
timeRange |
올해, 지난 분기 | 날짜 범위·기준 시점 |
aggregation |
SUM, AVG, COUNT, MAX | 집계 함수 |
comparison |
전년 대비, 증가율 | 비교 기준·연산 |
sort |
매출 DESC | ORDER BY |
limit |
5 | TOP/LIMIT |
negation |
서울 제외, 취소 아닌 | NOT/NEQ 조건 |
L0 업무 슬롯을 그대로 옮긴 게 아니다. L0가 "사용자가 어떤 값을 말했는가"를 담는다면 NL2SQL 의미 슬롯은 "그 말을 SQL의 어떤 역할로 해석할 것인가"를 담는다. 예를 들어 L0의 "기간=최근 1개월"은 NL2SQL 구조의 timeRange로 전달된다. L0의 "거래방향=출금"은 filters로 전달된다. 두 구조는 합치지 않고 연결한다.
3.3 무엇을 조회하는지와 어떻게 보여줄지는 다르다
타입은 필요한 슬롯을 결정한다.
계좌 목록 → 계좌 슬롯 불필요
잔액·기본정보 → 계좌 필요, 기간은 묻지 않음
요구불 거래내역 → 계좌와 기간 필요
대출 원리금 → 계좌 필요, 상품군은 다시 묻지 않음질문유형은 단순, 분석, 비교, 추이 같은 라벨이다. 필요한 계좌나 기간을 결정하는 기준이 아니라 답변을 어떻게 가공할지 정하는 정보다. "지난달 대비", "전년 동월 대비"처럼 구체적인 비교 기준은 질문유형 라벨만으로 복원할 수 없으므로 별도 구조로 보존해 답변 단계에 전달한다.
4. 전체 처리 프로세스
슬롯필링은 문장을 바로 SQL로 바꾸지 않는다. 업무 조건을 빠짐없이 확정한 뒤, NL2SQL이 해석할 수 있는 완성 질의와 구조화 슬롯을 만드는 전처리 단계다.
1. 발화 수신
2. 문장 정규화 (오탈자·동의어·상대 날짜 정리)
3. 새 질문·후속 질문 판정
4. 조회 타입 분류 (거래내역·잔액·계좌목록 등)
5. 타입별 레시피 선택 (필요한 슬롯 확정)
6. 현재 발화에서 슬롯 추출
7. 이전 대화의 호환 슬롯 병합
8. 값 검증과 자동 해소
9. 부족한 슬롯 계산 (missing = required - filled)
10. 부족하면 사용자에게 필요한 값만 질문
모두 채워졌으면 완성 질의와 구조화 슬롯 생성
11. NL2SQL이 의미 구조로 변환
12. SQL 생성, 안전 검증, 실행각 단계는 이전 결과를 덮어쓰지 않는다. 원문, 정규화 문장, 확정 슬롯, 완성 질의를 구분해서 보관한다.
5. 실제로 어떻게 동작하나, 예시 네 가지
한 번에 필요한 값이 모두 들어온 경우. "급여계좌의 최근 한 달 출금 내역을 금액 큰 순서로 10건 보여줘"처럼 필요한 값이 다 있으면 되묻기 없이 바로 완성한다.
{
"slots": {"account": "급여계좌", "period": "최근 한 달", "direction": "출금", "sort": "거래금액 내림차순", "limit": 10},
"missing": [],
"status": "COMPLETE"
}필수값이 부족한 경우. "거래내역 보여줘"는 계좌와 기간이 둘 다 없다. 한 번에 여러 값을 무리하게 묻지 않고 우선순위가 높은 것부터 확인한다.
시스템: 어느 계좌의 거래내역을 조회할까요? [급여계좌] [운영계좌]
사용자: 급여계좌
시스템: 조회 기간을 선택해 주세요. [최근 1주일] [1개월] [3개월]
사용자: 1개월이전 조건을 이어받는 경우. 첫 질문이 "급여계좌의 최근 한 달 거래내역"이었고 다음 질문이 "그중에서 출금만"이면 계좌와 기간은 이어받고 거래방향만 새로 추가한다. NL2SQL은 "그중에서"라는 표현을 직접 추측하지 않고 슬롯필링이 확정한 값을 기준으로 SQL을 만든다.
조회 종류가 바뀌어 일부만 이어받는 경우. 이전 질문이 "급여계좌의 지난달 출금 내역"이고 현재 질문이 "그 계좌 잔액은?"이면 조회 타입이 거래내역에서 잔액 조회로 바뀐다. 계좌는 이어받지만 기간과 거래방향은 이어받지 않는다.
{
"type": "ACCOUNT_BALANCE",
"inheritedSlots": {"account": "급여계좌"},
"discardedSlots": ["period", "direction"],
"normalizedIntent": "급여계좌의 현재 잔액 조회"
}이전 슬롯을 전부 이어받으면 잔액 조회에 불필요한 조건이 SQL에 섞여 들어갈 수 있다. 그래서 새 타입이 허용하는 슬롯만 선택적으로 가져온다.
후보가 여러 개인 경우. "운영계좌 잔액 알려줘"에서 검색 결과가 "연구운영계좌"와 "사업운영계좌" 둘이면 어느 쪽인지 근거가 없으므로 임의로 고르지 않는다.
{
"kind": "clarify",
"text": "어느 운영계좌를 조회할까요?",
"choices": [{"id": "ACC_01", "label": "연구운영계좌"}, {"id": "ACC_02", "label": "사업운영계좌"}]
}사용자가 고른 안정된 ID를 슬롯값으로 확정한 뒤 NL2SQL로 전달한다. AI가 후보 계좌를 새로 만들거나 임의로 하나를 고르면 안 된다.
6. 슬롯필링과 NL2SQL의 책임 경계
슬롯필링
사용자 표현에서 업무 조건 수집
이전 대화 조건의 선택적 승계
부족하거나 모호한 값 확인
완성 질의와 구조화 슬롯 생성
NL2SQL
완성 질의를 SQL 의미 구조로 변환
스키마·컬럼·조인 결정
SQL 생성 및 안전 검증
조회 결과 생성슬롯필링이 SQL 컬럼이나 조인을 직접 고르지 않고 NL2SQL이 부족한 업무값을 임의로 추측하지 않는 방향으로 책임을 나눈다.
7. AI가 판단할 일과 코드가 확정할 일
핵심 원칙은 의미 판단과 구조 판정을 분리하는 것이다.
AI가 맡는 일: 조회 타입과 질문유형 분류, 현재 발화에서 슬롯값 추출, 같은 표현이 여러 후보에 걸릴 때 제한된 후보 안에서 충돌 해소, 되묻기 문장을 자연스럽게 표현. AI 출력은 자유 문장이 아니라 근거 위치를 포함한 정해진 형식의 JSON이 기본이다.
코드가 맡는 일: 타입으로 레시피 선택, 슬롯값의 형식·허용값 검증, 현재 값과 이전 값 병합, 후보 조회, 기본값 적용, 부족한 슬롯을 required - filled로 계산, 후보 수와 되묻기 예산에 따른 분기, 최종 결과 생성과 원문 보존 검사.
AI는 "무엇이 부족한가"를 결정하지 않는다. 필요한 값은 미리 정한 규칙이 정하고 부족한 값은 코드가 집합 차이로 계산한다.
필요한 값 = {계좌, 기간}
채워진 값 = {기간, 거래방향}
부족한 값 = {계좌}판단을 두 번에 나눠 하는 이유
작은 모델에서도 한 번에 하나의 판단에 집중하도록 AI 호출을 나눈다.
1차 호출, 타입 분류
입력: 타입 이름·설명, 타입별 소수 예시, 현재 발화
출력: 조회 타입, 질문유형, 이전 대화와의 연속성
코드, 레시피 선택
입력: 분류된 타입
출력: 해당 레시피
2차 호출, 슬롯 추출
입력: 선택된 레시피의 슬롯 정의와 관련 예시만
출력: 슬롯값과 근거전체 규칙과 모든 예시를 매번 모델에 보내지 않고 호출 목적에 맞는 부분만 전달한다.
8. 멀티턴, 이전 질문과 이어지는지 먼저 판단한다
현재 발화가 새로운 질문인지 이전 질문의 연속인지부터 판정한다.
| 값 | 의미 |
|---|---|
NEW |
이전 요청과 독립된 새 질문 |
CONTINUE |
이전 요청의 일부 조건을 이어가는 질문 |
UNCERTAIN |
독립·후속 여부가 불명확해서 확인이 필요 |
CLARIFICATION_REPLY |
직전에 시스템이 물은 것에 대한 답변 |
CONTINUE라고 해서 채워진 슬롯을 전부 이어받으면 위험하다. 새 타입에서 허용하는 슬롯만 선택적으로 이어받아야 한다.
상태에는 직전 문장만 두지 않고 이전 조회의 구조 요약을 함께 둔다.
{
"previousTypes": ["요구불"],
"resolvedSlots": {"account": "급여계좌", "period": "최근 1개월"},
"resultColumns": ["거래일자", "거래금액", "거래후잔액"],
"resultCount": 25,
"timeRange": {"kind": "LAST_MONTH"}
}이 요약이 "그중 가장 큰 거래", "두 번째 계좌", "아까 기간과 비교" 같은 꼬리물기 질문을 푸는 근거가 된다.
9. 먼저 자동으로 찾고 정말 필요할 때만 묻는다
되묻기는 최후 수단이다.
현재 발화에서 추출
→ 이전 확정값 중 호환값 승계
→ 후보 조회
→ 후보가 1건이면 자동 확정하고 고지
→ 짧은 결과는 정책에 따라 전부 표시
→ 선택 가능한 기본값 적용 후 고지
→ 그래도 부족하거나 모호하면 되묻기예를 들어 "급여계좌 거래내역"에서 계좌번호가 없어도 계좌 목록에서 별칭으로 후보를 찾는다. 후보가 1건이면 자동 확정하고 어떤 계좌를 썼는지 알린다. 후보가 적으면 정책에 따라 결과를 모두 보여주거나 선택지를 준다. 후보가 많으면 검색형 선택 화면을 쓴다. 후보가 없으면 다시 입력을 요청한다.
초기 정책은 보통 전부 표시 3건, 선택지 버튼 5건, 6건 이상은 검색형, 전체 되묻기 예산은 3턴이다. 이 값은 코드에 고정하지 않고 별도 관리 화면에서 정책으로 관리한다.
10. 값 확인 질문과 실행 승인은 다르다
되묻기는 사람의 입력을 다시 받는 단계다. 실행을 승인받는 절차가 아니라, 조회에 필요한 값이 부족하거나 여러 후보 중 하나를 골라야 할 때 묻는 기능이다.
{
"kind": "clarify",
"text": "조회 기간을 선택해 주세요.",
"choices": [
{"id": "LAST_7_DAYS", "label": "최근 1주일"},
{"id": "LAST_1_MONTH", "label": "1개월"},
{"id": "LAST_3_MONTHS", "label": "3개월"}
]
}선택지 값은 AI가 새로 만들지 않고 후보 조회 결과나 관리자가 등록한 값에서 만든다. AI를 쓰더라도 질문 문구 표현만 다듬는다.
11. Admin, Agent, Front의 소유 경계
| 구분 | 책임 |
|---|---|
| 관리 화면(Admin) | 슬롯·타입·레시피·예시·되묻기 정책 등록과 편집 |
| 관리 API | 저장, 검증, 버전 발행, 설정 묶음 제공 |
| Agent(코드) | 정규화, 분류, 추출, 병합, 검증, 후보 조회, 부족한 값 계산 |
| Front | 발화 전달, 선택지·입력 화면 결과 렌더링 |
Front는 필수값 검사나 되묻기 판단을 하지 않는다. 판단이 Front와 Agent 양쪽에 있으면 정책의 정본이 둘로 갈라지기 때문이다.
관리 API는 슬롯 사전, 타입, 레시피, 되묻기 문구, 예시를 하나의 버전 묶음으로 발행한다. Agent는 세션 시작 시 이 묶음을 한 번 읽고 그 세션 동안 같은 버전을 쓴다. 대화 중간에 정책이 바뀌어서 이미 답한 값을 다시 묻는 일을 막기 위해서다.
12. 최종 전달 계약
슬롯이 모두 채워지면 다음 단계에 원문 한 줄만 보내지 않고 구조화 정보도 함께 전달한다.
{
"kind": "complete",
"originalQuery": "그럼 출금만",
"correctedQuery": "그럼 출금만",
"normalizedIntent": "급여계좌의 최근 한 달 출금 거래내역을 조회해줘",
"type": "요구불",
"questionType": "단순",
"slots": {"account": "급여계좌", "period": "최근 한 달", "direction": "출금"}
}원문, 교정문, 완성 질문은 서로 덮어쓰지 않고 별도 값으로 관리한다.
13. 정리
- 슬롯필링은 빈칸이 있는 조회 신청서를 채우는 과정이다. 명사를 찾는 형태소 분석이 아니다.
- 사용자가 말한 값(L0)과 SQL로 해석하는 값(NL2SQL 의미 슬롯)은 다른 구조이며 서로 연결만 한다.
- AI는 분류와 추출을, 코드는 검증·병합·부족한 값 계산을 맡는다.
- 되묻기는 최후 수단이다. 먼저 자동으로 찾고 정말 부족할 때만 묻는다.
- 다른 서비스들의 관행(재질문 2~3회 상한, 신뢰도에 따른 확인 방식)을 코드 규칙으로 명시한다.
- 슬롯·타입·되묻기 정책의 정본은 관리 화면이 소유하고 Agent는 검증된 값만 실행에 넘긴다.