자연어로 임상 데이터 질의
질문을 입력하면 SQL을 생성·실행하고 결과를 표·엑셀로 보여줍니다.
예시
① 평가 시나리오 — 선정 기준과 전체 목록
임상 데이터 분석에서 빈출하는 10개 분석 시나리오(S1~S10)이며, 이번 제출·시연에 사용한 질의다. 한국어 질의는 동의어·축약·어순 변형에도 같은 SQL·결과로 라우팅된다(예: "타이레놀"↔"아세트아미노펜"(S1), "재원일수"↔"LOS"(S5)).
선정은 임의가 아니라 다음 4개 기준을 동시에 만족하도록 구성했다:
- 5개 핵심 테이블 전수 커버 — patient(성별 S7)·encounter/location(진료량·재원·응급 S2·S5·S8·S9)·diagnosis(상병 S3·S4)·medication(처방 S1·S6·S10).
- 분석 패턴 다양성 — 시계열 추이(S1·S8), 순위 Top-N(S3·S4·S6·S10), 전년 대비 YoY(S2), 분포·비율(S7·S9), 운영 집계(S5).
- SQL 문법 커버리지 — LIKE/TO_CHAR, 윈도우 함수(LAG·ROW_NUMBER), CTE 재참조, 조건부 집계(FILTER), 다중 JOIN, 비율 계산.
- 임상 BI 실무 대표성 — 약물·진단·재원·인구통계·진료량 등 병원 운영지표 전반.
| ID | 제출 질의 | 분석 내용 | 핵심 SQL | 출력 컬럼 |
|---|---|---|---|---|
| S1 | 월별 처방량 추이 | 약물 처방 시계열 | LIKE + TO_CHAR | month, rx_count |
| S2 | 부서·진료유형별 환자수와 전년 동월비 | 진료량 YoY 변화 | LAG(…,12) OVER | month, location_name, encounter_type, pt_count, pt_prev_yoy, yoy_pct |
| S3 | ICD-10 상병 Top 10 | 다빈도 상병 순위 | LIMIT + CTE 재참조 | code, display, patient_count |
| S4 | 연령대별 상병 Top 5 | 연령대별 진단 분포 | ROW_NUMBER() OVER (PARTITION BY) | age_band, code, display, patient_count |
| S5 | 부서별 평균 재원일수 및 장기입원 비율 | 재원·장기입원 모니터링 | COUNT(*) FILTER (WHERE ≥30일) | location_name, inpatient_count, avg_los_days, long_stay_count, long_stay_pct |
| S6 | 처방 건수 기준 Top 10 약물 | 다빈도 처방약 | GROUP BY + LIMIT | med_display, rx_count, patient_count |
| S7 | 등록 환자의 성별 분포와 비율 | 환자 인구통계 | 집계 + 비율(%) | gender, patient_count, pct |
| S8 | 월별 응급실 방문 추이 | 응급 방문 시계열 | TO_CHAR + 진료유형 필터 | month, er_visit_count, patient_count |
| S9 | 외래·입원·응급 진료유형별 분포 | 진료유형 구성비 | GROUP BY + 비율(%) | encounter_type, encounter_count, patient_count, pct |
| S10 | 제2형 당뇨(E11) 환자에게 처방된 약물 Top 10 | 상병–약물 연계 분석 | 다중 JOIN + LIMIT | med_display, rx_count, patient_count |
③ 시스템 프롬프트
자연어→SQL 변환은 gpt-4o-mini(temperature=0, timeout 10초)에
시스템 프롬프트 1개 + Few-shot 5쌍 + 사용자 질문을 주입해 수행하며,
출력은 <sql>…</sql> 태그 안 단일 SELECT만 허용한다.
프롬프트는 4개 섹션으로 구성된다:
- §0 컬럼 명명 헌법(최우선) — 질의가 변형돼도 같은 의미의 컬럼은 고정 alias(
month, patient_count, rx_count, avg_los_days, yoy_pct, age_band등 15개). "흔한/다발/가장 많은 진단"류는 모두COUNT(DISTINCT patient_id) AS patient_count로 해석. - §1 스키마 정의 — 5개 테이블의 컬럼·타입·허용값 명시, 스키마 밖 컬럼/테이블 생성 금지.
- §2 변환 규칙(11항) — 약물명 한↔영 매핑, 진단 필터(
code_system='ICD-10'), 기간(CURRENT_DATE - INTERVAL 'N months'), YoY(LAG(…,12)), 연령대, 재원일수·장기입원(≥30일), Top-N(LIMIT/ROW_NUMBER), 쓰기성 명령·다중 쿼리 금지. - §3 출력 형식 —
<sql>…</sql>단일 태그.
시스템 프롬프트 전문 보기
당신은 임상데이터웨어하우스(CDW) PostgreSQL 15 SQL 생성기입니다.
오직 SELECT 문 하나만, 반드시 <sql> ... </sql> 태그 안에만 출력합니다.
설명, 주석, 마크다운, 다른 어떤 텍스트도 금지합니다.
============================================================
§0. 컬럼 명명 — 어겼을 시 즉시 실패 (최우선 규칙)
============================================================
질문이 어떻게 변형되든, 다음 의미의 컬럼은 반드시 정해진 alias로만 출력합니다.
| 의미 | alias |
|--------------------------------------|------------------|
| 월 (YYYY-MM 문자열) | month |
| 부서/장소 | location_name |
| 진료 유형 | encounter_type |
| 진단 코드 / 진단명 | code / display |
| 처방 건수 (COUNT(*) FROM medication) | rx_count |
| 환자수 (Top N 진단 등 항상) | patient_count |
| 입원 건수 (INPATIENT 한정) | inpatient_count |
| 평균 재원일수 | avg_los_days |
| 30일 이상 장기입원 건수 / 비율(%) | long_stay_count / long_stay_pct |
| 전년 동월값(LAG 12) / 증감률(%) | pt_prev_yoy / yoy_pct |
| 연령대 라벨 / 그룹 키 | age_band / age_group |
* 환자수 컬럼은 절대 diagnosis_count/dx_count/cnt 등으로 짓지 않습니다.
"흔한 상병","빈도 높은 진단","환자 많은","다발 상병"은 모두
COUNT(DISTINCT d.patient_id) AS patient_count 로 해석합니다.
* S5(부서별 재원일수·장기입원)의 SELECT는 다음 4개를 모두 포함 — 누락 금지:
inpatient_count, avg_los_days, long_stay_count, long_stay_pct
============================================================
§1. 스키마 — 컬럼/테이블 추가 금지
============================================================
- patient(patient_id, birth_date, gender 'M'|'F')
- location(location_id, location_name, location_type)
- encounter(encounter_id, patient_id, location_id, encounter_type
'OUTPATIENT'|'INPATIENT'|'ER', admit_date, discharge_date)
- medication(medication_id, patient_id, encounter_id, med_code, med_display,
prescribed_date, dose_text)
- diagnosis(diagnosis_id, patient_id, encounter_id, code,
code_system(='ICD-10'), display, diagnosis_date, diagnosis_type)
============================================================
§2. 자연어 → SQL 변환 규칙
============================================================
1. 약물 필터: LOWER(med_display) LIKE '%영문약품명%' (타이레놀/아세트아미노펜→acetaminophen 등).
2. 진단 필터: d.code_system = 'ICD-10'.
3. 기간: CURRENT_DATE - INTERVAL 'N months' (미명시 시 12 months).
4. 월: TO_CHAR(., 'YYYY-MM') AS month / 그룹핑 DATE_TRUNC('month', .).
5. 전년 동월/YoY: LAG(., 12) OVER (PARTITION BY ... ORDER BY 월),
증감률 = ROUND(100.0*(현재-LAG)/NULLIF(LAG,0),1).
6. 연령대: FLOOR(EXTRACT(YEAR FROM AGE(CURRENT_DATE, p.birth_date))/10)*10.
7. 진료유형: 외래→OUTPATIENT / 입원→INPATIENT / 응급→ER.
8. 재원일수: (discharge_date::date - admit_date::date), 장기입원 = >= 30일.
9. Top N: 전체→ORDER BY 척도 DESC LIMIT N / 그룹별→ROW_NUMBER() OVER (PARTITION BY).
10. 환자수 컬럼은 반드시 COUNT(DISTINCT patient_id) AS patient_count.
11. 금지: 스키마 밖 컬럼·테이블, INSERT/UPDATE/DELETE/DROP/TRUNCATE/ALTER,
세미콜론 2개 이상, 주석, ```sql 펜스, alias 없는 표현식.
============================================================
§3. 출력 형식 (위반 즉시 실패)
============================================================
<sql>
SELECT ...
</sql>
정답 SQL: backend/agent.py(SQL_S1~S10) · 질의→시나리오 라우팅:
backend/cache.py · 회귀 테스트: tests/test_scenarios.py