# NBS 페르소나 설문조사 시스템 구축 가이드 이 문서는 전국지표조사(NBS) 원본 데이터를 활용하여 16만 건의 응답자를 아바타로 구현하고, 새로운 질문에 대해 시뮬레이션할 수 있는 시스템을 구축하기까지의 전 과정을 상세히 기록하고 있습니다. --- ## 1. 프로젝트 개요 본 프로젝트의 목표는 2020년부터 축적된 160회 이상의 전국지표조사 데이터를 통합하여, 특정 인구통계학적 특성(성별, 연령, 지역, 직업)과 과거 가치관을 가진 '페르소나 아바타' 1,000명을 샘플링하고 이들이 새로운 사회적 이슈에 대해 어떻게 답변할지 예측하는 시뮬레이션 환경을 구축하는 것입니다. --- ## 2. 데이터 처리 과정 (Data Pipeline) ### 단계 1: 원본 데이터 변환 (`전국지표조사_원본` → `전국지표조사_json`) - **대상**: 163개의 조사 폴더 내 Stata(`.dta`) 및 SPSS(`.sav`) 파일. - **처리 내용**: - 각 폴더의 `README.txt` 및 PDF 보고서를 분석하여 설문지 문항 매칭. - Stata/SPSS의 수치형 코드 데이터에 변수 라벨(Value Labels)을 입혀 텍스트 기반의 JSON 포맷으로 변환. - **결과**: `전국지표조사_json` 폴더 내 163개의 표준화된 JSON 파일 생성. ### 단계 2: 데이터 통합 및 정규화 (`전국지표조사_json` → `consolidated_nbs_data.parquet`) - **대상**: 163개의 JSON 파일 (총 **166,721건**의 개별 응답자 데이터). - **Parquet 파일의 구조 (Wide Table & Sparse Matrix)**: - **엑셀과의 비교**: 엑셀이 하나의 시트에 행과 열을 맞추는 것과 유사하지만, Parquet는 훨씬 거대한 **'초광대역 테이블(Wide Table)'** 구조입니다. - **통합 원리**: 163회의 설문조사는 매번 질문이 다릅니다. 이 시스템은 `Pandas.concat` 기술을 사용하여, **동일한 질문은 같은 열(Column)에 합치고, 새로운 질문은 새로운 열로 추가**하며 옆으로 길게 붙여나가는 방식을 취합니다. - **희소 행렬(Sparse Matrix)**: 특정 회차에 없던 질문 칸은 비어있는(NaN) 상태로 저장됩니다. Parquet 포맷은 이런 빈 공간을 매우 효율적으로 압축하여 저장하므로, 수천 개의 열이 있어도 파일 용량이 매우 작게 유지됩니다. - **응답자 식별 체계 (Unique Identification)**: - **survey_round**: 전체 160여 회차 중 해당 데이터가 수집된 특정 조사 회차 (예: 100회 NBS). - **respondent_id**: 해당 회차 내에서 응답자에게 부여된 고유 번호. - **식별 방식**: `survey_round`와 `respondent_id`의 조합(복합 키)을 통해 16만여 명의 전체 응답자 중 특정 개인을 유일하게 찾아내고 관리합니다. - **주요 인구통계 통계**: - **총 데이터 건수**: 166,721명 (응답자 단위) - **성별 분포**: 남자 (50.1%), 여자 (49.9%) - **연령 통계**: 평균 50.0세 (표준편차 17.4), 최소 18세 ~ 최대 99세 - **지역 분포 (Top 5)**: 경기(25.5%), 서울(18.5%), 부산(6.9%), 경남(6.5%), 인천(5.7%) - **주요 직업군**: 사무/기술직(25.2%), 주부(18.2%), 자영업(15.7%), 무직/퇴직/기타(14.7%) - **전체 질문 항목 확인**: [NBS 고유 설문 문항 리스트(1,219건)](file:///c:/vscode/crawling_chatbot/페르소나_설문조사_전국지표조사/NBS_Question_List.md) - **처리 내용**: - **통합**: 163개의 서로 다른 설문 파일을 하나의 거대 데이터셋으로 병합. - **값 표준화**: "남/여", "남성/여성" 등 다양한 표기를 "남자/여자"로 통일. ### 단계 3: 질문 의미론적 인덱싱 (`index_questions.py` → `nbs_questions_index.parquet`) - **고유 문항 추출 및 유니피케이션(Unification) 기술**: 163회의 설문조사에서 파편화된 질문들을 **1,219개**의 고유 지식으로 통합하기 위해 다음 기술이 적용되었습니다. 1. **Preprocessing (지능형 매핑)**: 원본 데이터의 변수명(`q1`)을 PDF를 통해 **전체 문장**으로 복원하여 통합의 기초를 마련했습니다. 2. **Noise Reduction (메타데이터 제외)**: 아이디, 가중치 등 비질문 데이터를 제외하여 순수 질문 **1,219건**을 확보했습니다. 3. **Stable Vector Search (고성능 선형 검색)**: - **변경 사항**: 기존의 외부 데이터베이스(ChromaDB) 대신, **Pandas + NumPy 기반의 선형 벡터 검색** 방식을 채택했습니다. - **이유**: Windows 환경에서의 라이브러리 호환성(HNSW 오류)을 완벽히 해결하고, 1,219건 정도의 데이터 규모에서는 별도 DB 엔진 없이도 소수 밀리초(ms) 단위의 초고속 검색이 가능하기 때문입니다. 4. **Virtual Unification (의미론적 보정)**: 미세하게 다른 질문들은 SBERT 임베딩과 코사인 유사도(Cosine Similarity) 계산을 통해 검색 시점에 지능적으로 통합 참조됩니다. - **임베딩 모델**: `snunlp/KR-SBERT-V40K-klueNLI-augSTS` - **인덱스 데이터 구조 (`nbs_questions_index.parquet`)**: - **question**: 질문 원문 (예: "대통령 국정운영 평가") - **vector**: SBERT가 생성한 768차원의 수치 벡터 리스트. - **검색 알고리즘 (Cosine Similarity)**: - NumPy를 사용하여 사용자의 질문과 인덱스된 1,219개 질문 사이의 각도를 계산, 가장 의미가 가까운 질문들을 추출합니다. --- ## 3. 시뮬레이션 시스템 작동 원리 (Technical Deep-Dive) ### 1) 하이브리드 검색 프로세스 (Data Flow Diagram) 시스템은 '누구인지(인구통계)'와 '어떤 성향인지(과거 답변)'를 결합하여 답변을 생성합니다. ```mermaid graph TD A["사용자 입력 (질문 + 조건)"] --> B{"1단계: 인구통계 필터링\n(Parquet/Hard Filter)"} B -- "일치자 발견" --> C["응답자 후보군 (Candidates)"] B -- "일치자 없음" --> B_Fail["경고 후 종료 (조건 완화 권장)"] A --> D{"2단계: 의미 검색\n(Vector DB/Soft Filter)"} D --> E["유사 질문 Top-5 추출"] C --> F{"3단계: 컨텍스트 추출\n(Grounding)"} E --> F F --> G["A. 페르소나 핵심 질문 스캔\n(정치/이념/경제 등)"] F --> H["B. 유사 질문 답변 매칭"] G --> I["최종 컨텍스트 (지식 베이스)"] H --> I I --> J["4단계: LLM 답변 생성\n(Gemini 2.5 Flash)"] J --> K["시뮬레이션 완료 (JSON 저장)"] ``` ### 2) 예외 상황 및 작동 원리 (FAQ) - **Q: 인구통계 조건(성별/지역 등)에 맞는 응답자가 없으면 어떻게 되나요?** - 현재 시스템은 **'조건 완화'**를 위해 빈 결과를 반환합니다. 예를 들어 45세 응답자가 없다면 40대 전체로 범위를 넓히거나 특정 조건을 생략하여 다시 시뮬레이션해야 합니다. - **Q: 조건은 맞는데, 유사한 과거 질문에 답한 기록이 없는 응답자라면요?** - 시스템의 **'이중 안전장치(Safety Net)'**가 작동합니다. 질문과 직접적인 연관이 없더라도 해당 응답자가 과거에 답했던 **정치적 성향, 이념, 지지 정당, 경제적 계층 의식** 등 페르소나를 결정짓는 핵심 답변을 우선적으로 추출합니다. - 만약 이마저도 없다면, LLM은 입력된 인구통계 정보(예: "서울 거주 30대 남성")만을 바탕으로 사회적 통념에 기반한 답변을 생성하게 됩니다. - **Q: `referenced_context`가 질문 주제와 직접적인 관련이 없어 보이는 이유는 무엇인가요?** - **이중 검색 로직**: 시스템은 **A. 핵심 페르소나 문항(가치관)**과 **B. 주제 유사 문항(기억)**을 동시에 추출합니다. - **희소성 대응**: 만약 해당 응답자가 참여한 회차에 질문 주제와 의미적으로 유사한 질문이 없었을 경우(희소 데이터), 시스템은 아바타의 답변 논리를 보강하기 위해 그 사람의 근본적인 가치관(정치/이념/계층 등)을 컨텍스트로 우선 제공합니다. 이는 LLM이 "이러한 성향의 사람이라면 새로운 이슈에 대해 이렇게 답할 것이다"라고 타당하게 추론하도록 돕는 장치입니다. - **Q: 인구통계 조건과 질문 유사도 사이의 관계 설정은 어떻게 하나요?** - **인구통계(Hard Filter)**는 아바타의 '신분'을 결정하고, **질문 유사도(Soft Filter)**는 그 아바타의 '기억'을 소환합니다. - 신분이 결정된 응답자의 행(Row)에서 의미적으로 가장 가까운 기억(답변)을 찾아 LLM에게 전달함으로써, "이 사회적 위상을 가진 사람은 이런 질문에 이렇게 답할 것이다"라는 논리적 타당성을 확보합니다. - **Q: 조건에 맞는 후보자가 1,000명이라면 최종 10명은 어떤 기준으로 선정되나요?** - **무작위 샘플링(Random Sampling)** 방식을 사용합니다. 1차 필터링(인구통계)을 통과한 후보군이 설정한 샘플 수(`--sample`)보다 많을 경우, 시스템은 집단 내 편향을 방지하기 위해 그들 중 무작위로 최종 대상자를 추출합니다. - **Q: 왜 질문과 가장 유사한 답변을 한 사람을 순서대로 뽑지 않고 무작위로 뽑나요?** - **선택 편향(Selection Bias) 방지**: 질문과 유사한 생각을 가진 사람만 골라낸다면, 해당 세대나 지역 내의 다양한 가치관이 소멸하고 단일한 목단만 남게 됩니다. - **통계적 다양성 보존**: 같은 '서울 30대 여성'이라도 진보/보수, 부유함/가난함 등 다양한 페르소나가 존재합니다. 무작위 샘플링을 통해 이러한 집단 내 **다양한 스펙트럼의 목소리**를 골고루 시뮬레이션에 반영하여 실제 여론 지형에 가까운 결과를 얻기 위함입니다. --- ## 4. 작동 사례 (Example Case Study) **[사용자 입력]** - 질문: "기본소득제 도입에 대해 어떻게 생각하십니까?" - 조건: 성별=남자, 지역=서울, 연령=40, 직업=자영업 **[시스템 내부 작동]** 1. **데이터 필터링**: `consolidated_nbs_data.parquet`에서 서울 거주 40세 남성 자영업자들을 추출. 2. **유사 질문 검색**: ChromaDB에서 "복지 정책 확대", "재정난 해결 방안" 등의 질문을 유사 문항으로 선정. 3. **컨텍스트 구축**: 추출된 응답자 중 한 명의 데이터를 보니: * 정치 성향: 보수 * 복지 정책에 대한 답변: "반대 (세금 부담 증대 우려)" * 부동산 가격에 대한 답변: "급등 반대" 4. **LLM 생성 (Gemini)**: * 위 정보들을 종합하여 "보수적 가치관을 가진 40대 자영업자로서, 세금 부담이 늘어날 수 있는 기본소득제에 대해서는 신중하거나 반대하는 입장"의 답변을 생성. * 결과 JSON의 `referenced_context`에 위의 참조된 과거 답변들을 노출. --- ## 5. 현재 사용 방법 (Usage Guide) 설문 시스템은 `페르소나_설문조사_전국지표조사` 폴더 내에서 실행됩니다. ### 옵션 A: 실제 응답자 샘플링 모드 (`avatar_actual.py`) 실제 과거 응답자들의 답변 이력을 바탕으로 가장 생생한 응답을 생성합니다. ```powershell # 예시 1: 단순 질문 형식 (경기도 거주 31~40세 남성 5명) ..\.venv\Scripts\python.exe avatar_actual.py --question "정년연장에 대해 어떻게 생각해?" --region "경기" --gender "남자" --age "31~40" --job "사무/기술직" --sample 5 # 예시 2: 세부 설명 및 5점 척도 포함 (최저임금 관련 심화 시뮬레이션) ..\.venv\Scripts\python.exe avatar_actual.py --question "최근 고물가와 저성장 기조 속에서 서민 경제의 안정을 위해 현재의 최저임금을 대폭 인상해야 한다는 주장이 있습니다. 이에 대해 얼마나 동의하십니까? (1점: 전혀 동의 안함 ~ 5점: 매우 동의)" --region "서울" --gender "여자" --age "25~35" --sample 5 ``` ### 옵션 B: 가상 통계 기반 모드 (`avatar_synthetic.py`) 특정 그룹의 통계적 답변 분포를 분석하여 가상의 페르소나 답변을 생성합니다. ```powershell # 예시: 서울 거주 20대 여성 학생 그룹의 통계 기반 10명 생성 ..\.venv\Scripts\python.exe avatar_synthetic.py --question "공유 경제 서비스 이용 의향은?" --region "서울" --gender "여자" --age "20" --job "학생" --sample 10 ``` ### 파라미터 상세 설명 - `--question`: 시뮬레이션할 새로운 질문 (필수) - **질문 설계 권장사항**: - **이슈 중심 질문**: 특정 인물에 대한 개인적 평가보다는 **사회적 이슈나 정책적 사안**에 대한 질문이 더 타당한 답변을 유도합니다. - **배경 설명 포함**: 단순한 질문보다는 이슈에 대한 맥락(배경 설명)을 함께 제공하는 것이 좋습니다. - *예시 (권장)*: "최근 고물가 상황에서 서민 경제 안정을 위해 최저임금을 인상해야 한다는 주장이 있습니다. 이에 대해..." - **응답 척도 명시**: 정량적인 분석이나 구조화된 답변이 필요하다면 "얼마나 동의하십니까? (1: 전혀 동의 안함 ~ 5: 매우 동의)"와 같이 **구체적인 척도**를 제시하는 것을 강력히 권장합니다. - `--region`: 지역 (입력 가능: 서울, 경기, 인천, 강원, 부산, 대구, 광주, 대전, 울산, 세종, 충북, 충남, 전북, 전남, 경북, 경남, 제주) - `--gender`: 성별 (입력 가능: 남자/남, 여자/여) - `--age`: 나이 또는 나이 범위 (33, 20~29 등 입력 가능) - `--job`: 직업 (입력 가능: 학생, 사무/기술직, 자영업, 주부, 경영/관리/전문직, 생산/기능/노무직, 농/림/수산업, 무직/퇴직/기타) - `--sample`: 생성할 아바타/응답 수 - **권장 사항 (Stable & Conservative)**: **10 ~ 50개** - **이유**: 시스템은 응답자별로 개별적인 LLM 호출을 수행합니다. Gemini 2.5 Flash Lite의 속도와 API 안정성(네트워크 타임아웃 및 속도 제한)을 고려할 때, 한 번의 실행에서 50개 내외를 처리하는 것이 가장 안정적이고 빠른 결과를 보장합니다. 대량 시나리오(100개 이상)가 필요한 경우 여러 번 나누어 실행하는 것을 권장합니다. --- ## 6. 시뮬레이션 모드 상세 비교 (Actual vs Synthetic) 시스템은 목적에 따라 '개별성'과 '통계성' 중 하나를 선택할 수 있도록 두 가지 실행 모드를 제공합니다. ### 1) 비교 요약 테이블 | 비교 항목 | 옵션 A: 실제 응답자 모드 (`actual`) | 옵션 B: 가상 통계 모드 (`synthetic`) | | :--- | :--- | :--- | | **핵심 철학** | "실제 존재했던 사람의 기억을 소환" | "특정 그룹의 통계적 경향성을 의인화" | | **컨텍스트 구성** | 특정 개인의 개별 답변 이력 (1:1 매칭) | 그룹 전체의 답변 분포 통계 (N:1 요약) | | **시뮬레이션 대상** | 데이터상의 실제 인물 (예: ID 6038번 응답자) | 통계를 바탕으로 생성된 가상의 페르소나 | | **답변의 특징** | 파편화되어 있으나 극도로 구체적이고 현실적임 | 그룹의 평균적인 가치관과 트렌드를 잘 반영함 | | **추천 용도** | 심층 인터뷰(FGI) 수준의 생생한 반응 필요 시 | 집단적 여론의 흐름이나 통계적 경향 파악 시 | --- ### 2) 사례를 통한 차이 설명 (서울 거주, 20대 여성, 학생 그룹) **질문: "대학 등록금 반값 인하 정책에 대해 어떻게 생각하십니까?"** #### [옵션 A: 실제 응답자 모드 (`avatar_actual.py`)] 1. **동작**: 서울 20대 여성 학생 데이터 중 5명을 무작위로 뽑습니다. 2. **컨텍스트**: - 아바타 1호: "과거에 장학금 혜택 부족에 불만이라고 답했음, 부모님 경제력 낮음" - 아바타 2호: "과거에 교육 질 저하 우려로 정부 개입 반대한다고 답했음" 3. **결과**: 아바타마다 본인의 **개인적 사정**이 듬뿍 담긴 제각각의 답변을 내놓습니다. (현실성 높음) #### [옵션 B: 가상 통계 모드 (`avatar_synthetic.py`)] 1. **동작**: 서울 20대 여성 학생 수천 명의 데이터를 통계로 냅니다. 2. **컨텍스트**: - "해당 그룹의 75%는 등록금 부담이 매우 높다고 답했으며, 15%는 대학의 재정 투명성을 강조했습니다." 3. **결과**: "우리 세대의 대다수가 등록금 부담으로 고통받고 있으며..."와 같이 **그룹의 대표성**을 띤 답변을 생성합니다. (경향성 파악 용이) --- ### 3) 가상 통계 모드의 상세 로직 (Deep-Dive into Synthetic Logic) 가상 통계 모드는 실제 개인이 아닌 **'집단의 지혜'**를 추출하기 위해 다음과 같은 고도화된 컨텍스트 추출 방식을 사용합니다. #### **① 통계적 분포(Distribution) 방식의 채택** 단순히 "가장 많은 답변" 하나만 전달하는 것이 아니라, `"A 답변(60%), B 답변(30%), 기타(10%)"`와 같이 실제 응답 비율을 수치로 계산하여 LLM에게 보고합니다. - **다양성 보존**: 집단 내의 지배적인 의견뿐만 아니라 소수 의견의 비중까지 학습시켜, 답변의 확신도나 신중함의 정도를 실제 여론 지형에 맞게 조절합니다. - **뉘앙스 구현**: 찬반이 팽팽한 이슈(51:49)와 압도적인 이슈(90:10)에 대해 LLM이 서로 다른 뉘앙스의 페르소나를 연기하도록 유도합니다. #### **② 지능적 문항 선별 (Top-5 Dense Columns)** 1,200여 개의 문항 중 해당 집단을 가장 잘 설명할 수 있는 데이터를 자동으로 선별합니다. - **데이터 밀도 검사**: 고유 페르소나 문항(정치/이념/계층 등) 중 결측치(NaN)가 가장 적은, 즉 **가장 많은 사람이 성실하게 응답한 상위 5개 문항**을 자동 추출합니다. - **질문 유사성 결합**: 사용자의 질문과 의미적으로 유사한 과거 문항들의 통계 분포를 결합하여, 집단의 '기초 체력(가치관)'과 '직접적 경험(유사 질문)'을 모두 반영합니다. #### **③ 결측치 극복 (Sparse Data Recovery)** 특정 개인의 응답이 비어 있어도 모집단 중 한 명이라도 답했다면 그 경향성이 통계에 반영되므로, 실제 응답자 모드보다 **데이터 공백에 훨씬 강하며 안정적인 시뮬레이션**이 가능합니다. --- ## 7. 결과 확인 및 검증 - 결과는 `survey_results_actual.json` 또는 `survey_results_synthetic.json`에 저장됩니다. - 각 결과에는 아바타의 답변뿐만 아니라, 답변의 근거가 된 **과거 응답 내용(`referenced_context`)**이 포함되어 있어 논리적 일관성을 확인할 수 있습니다. --- ## 8. 핵심 데이터 파일 상세 비교 시스템은 두 개의 핵심 Parquet 파일로 작동하며, 각각의 역할이 명확히 구분됩니다. ### 1) `nbs_questions_index.parquet` (6.2 MB) **목적**: 질문 검색 인덱스 (Question Semantic Search Index) **구조**: | 컬럼명 | 타입 | 설명 | 예시 | |--------|------|------|------| | `question` | string | 질문 원문 | "대통령 국정운영 평가" | | `vector` | list[float] | KR-SBERT 임베딩 (768차원) | [0.123, -0.456, 0.789, ...] | **데이터 규모**: 1,219개 고유 질문 **역할**: - 사용자가 입력한 새로운 질문과 **의미적으로 유사한 과거 질문**을 찾기 위한 벡터 검색 - Cosine Similarity 기반 Top-K 검색 - 예: "정년 연장 어떻게 생각하세요?" → 유사 질문: "정년퇴직 연령 조정", "고령자 고용 정책" 등 **생성 방법**: ```python # index_questions.py 실행으로 재생성 가능 (1분 소요) python index_questions.py ``` --- ### 2) `consolidated_nbs_data.parquet` (6.7 MB) **목적**: 응답자 데이터베이스 (Respondent Database) **구조** (Wide Table + Sparse Matrix): | 컬럼 유형 | 컬럼명 예시 | 타입 | 설명 | |-----------|-------------|------|------| | **메타데이터** | `survey_round` | int | 조사 회차 (1~163) | | | `respondent_id` | int | 응답자 고유 번호 | | **인구통계** | `gender` | string | 성별 (남자/여자) | | | `age` | int | 연령 (18~99세) | | | `region` | string | 거주 지역 (서울, 경기 등) | | | `job` | string | 직업 (사무/기술직, 주부 등) | | **응답 데이터** | `대통령_국정운영_평가` | string | 개별 질문에 대한 답변 | | | `정년연장_찬반` | string | 개별 질문에 대한 답변 | | | ... (1,219개 질문 컬럼) | ... | ... | **데이터 규모**: 166,721명 × 1,219개 질문 (희소 행렬) **역할**: 1. **응답자 필터링**: 인구통계 조건(성별/연령/지역/직업)으로 특정 응답자 추출 2. **과거 답변 참조**: 선택된 응답자의 유사 질문에 대한 실제 답변 이력을 LLM 컨텍스트로 제공 3. **통계 집계**: Synthetic 모드에서 그룹 전체의 답변 분포 통계 계산 **특징**: - **Sparse Matrix**: 각 응답자는 전체 1,219개 질문 중 평균 20~30개만 답변 (나머지는 NaN) - **압축 효율**: Parquet의 효율적인 압축으로 13만 개 이상의 셀 중 대부분이 비어있어도 6.7MB로 유지 --- ### 3) 두 파일의 작동 흐름 (Data Flow) ```mermaid graph LR A[사용자 질문] --> B[nbs_questions_index.parquet] B --> C[유사 질문 Top-5 추출] D[인구통계 조건] --> E[consolidated_nbs_data.parquet] E --> F[조건 맞는 응답자 필터링] C --> G[응답자의 유사 질문 답변 추출] F --> G G --> H[LLM 프롬프트 구성] H --> I[시뮬레이션 답변 생성] ``` **단계별 예시**: 1. **사용자 입력**: "기본소득제 도입에 대해 어떻게 생각하십니까?" (조건: 서울, 40대, 남성, 자영업) 2. **질문 검색** (`nbs_questions_index.parquet` 사용): - 유사 질문 발견: "복지 정책 확대", "재정 지출 증가", "소득 재분배" 등 3. **응답자 필터링** (`consolidated_nbs_data.parquet` 사용): - 서울 거주 + 40대 + 남성 + 자영업자 → 234명 추출 - 무작위 샘플링 → 5명 선정 4. **컨텍스트 구성**: - 각 응답자별로 위 유사 질문에 대한 과거 답변 조회 - 응답자 A: "복지 정책 확대 → 반대 (세금 부담)", "재정 지출 증가 → 신중해야" - 응답자 B: "복지 정책 확대 → 찬성 (서민 보호)", "소득 재분배 → 필요" 5. **LLM 생성**: 위 정보를 바탕으로 각 응답자가 "기본소득제"에 대해 답변할 내용 예측 --- ## 9. 문제 해결 (Troubleshooting) ### 인덱스 파일 유실 만약 `nbs_questions_index.parquet` 파일이 삭제되거나 손상되었다면, `index_questions.py`를 다시 실행하여 1분 내외로 복구할 수 있습니다. ```powershell cd 페르소나_설문조사_전국지표조사 ..\.venv\Scripts\python.exe index_questions.py ``` *(기존의 nbs_db_stable 폴더 및 ChromaDB 인덱스 로드 오류는 시스템이 Parquet 기반 선형 검색으로 전환됨에 따라 더 이상 발생하지 않습니다.)*