출마자가 공약을 입력하면 당 방향 부합 여부, 중복·유사 여부, 보완점을 즉시 제공하는 AI 기반 공약 멘토링 환경입니다.
- 출마자: 당 정강·정책에 맞는 공약을 스스로 점검하고, 타 후보와의 차별화 포인트를 참고
- 정책국: 기초 검토 자동화로 전략·메시지·공약 완성도에 집중
| 기능 | 설명 |
|---|---|
| 당 방향 부합 점검 | 정강·정책·과거 공약 기준으로 부합 여부 점검, 수정·보완 체크리스트 제공 |
| 벡터 검색 기반 검증 | FAISS 인덱스를 활용한 정확한 근거 인용 및 리포트 생성 (POST /api/pledge/verify) |
| 중복·유사 탐지 | 후보 공약 DB와 비교해 유사 공약 제시, 차별화 포인트 제안 |
- 본 시스템: 즉각 피드백 및 당 정체성 가이드에 집중
- 지방의회 회의록 기반 지역 현안 발굴/데이터 분석(참치상사 등)과는 역할 분리
| 항목 | 결정 |
|---|---|
| 데이터 | PDF 방식으로 제공 (정강·정책·과거 공약) |
| 분석 엔진 | GPT API 이용 |
| 우선 기능 | 당 부합 점검 먼저 구현 → 이후 중복·유사 탐지 확장 |
- 요구사항 명세서 — 추진배경, 목적, 범위, 핵심 기능
- 기술 명세 및 구현 방향 — PDF 처리, GPT API, 당 부합 점검 플로우
Policy/
├── README.md
├── docs/
│ ├── 요구사항_명세서.md
│ └── 기술_명세_구현방향.md
├── data/
│ ├── pdf/ # 정강·정책·과거 공약 PDF 원본
│ │ ├── 정강정책/ # 정강정책 PDF들
│ │ ├── 공약/ # 우리당 공약 PDF들
│ │ └── 지역별 공약/ # 타지역 공약 PDF들
│ └── index_cache/ # FAISS 인덱스 캐시 (자동 생성)
├── backend/ # PDF 파싱, GPT API 호출, 당 부합 점검 API
│ ├── pdf_loader.py # PDF 텍스트 추출
│ ├── pdf_loader_chunks.py # PDF 청크 분할
│ ├── chunking.py # 텍스트 청킹 로직
│ ├── embeddings.py # OpenAI 임베딩 생성
│ ├── vector_index.py # FAISS 벡터 인덱스
│ ├── index_builder.py # 인덱스 빌드 및 캐시 관리
│ ├── report.py # 검색 결과 기반 리포트 생성
│ └── main.py # FastAPI 앱
├── frontend/ # 출마자 공약 입력·결과 표시 UI (추후)
└── prompts/ # GPT 시스템/유저 프롬프트
공개 시 API 비용 폭증을 막기 위해 회원가입·관리자 승인·쿼터·레이트리밋이 적용됩니다.
| 항목 | 설명 |
|---|---|
| 회원가입 | /signup에서 이메일·비밀번호로 가입. ADMIN_EMAILS에 해당 이메일이 있으면 자동 승인. |
| 관리자 승인 | /admin/users에서 PENDING 사용자를 APPROVED/REJECTED/SUSPENDED로 변경 |
| 쿼터 | 일일(QUOTA_DAILY, 기본 30회), 월간(QUOTA_MONTHLY, 기본 300회). .env에서 변경 가능 |
| 레이트리밋 | IP당 분당(RATE_LIMIT_IP_PER_MIN, 기본 30회), 사용자당 분당(RATE_LIMIT_USER_PER_MIN, 기본 10회) |
| 캐싱 | 동일 입력 24시간 내 재호출 시 OpenAI 미호출, 저장된 결과 반환 |
최초 실행 흐름
python scripts/init_db.py— DB 초기화.env에[email protected]설정 (관리자 이메일)- 회원가입 → ADMIN_EMAILS에 있으면 자동 승인, 없으면 관리자가 /admin/users에서 승인
- 승인 후 /pledge에서 분석 가능
쿼터/레이트리밋 변경: .env에 QUOTA_DAILY=50, QUOTA_MONTHLY=500 등 추가
이메일 인증 (회원가입 시 인증 메일 발송)
인증 메일을 쓰려면 .env에 아래를 설정한 뒤 서버를 재시작하세요.
| 변수 | 설명 | 예시 |
|---|---|---|
EMAIL_VERIFICATION_ENABLED |
1이면 가입 후 이메일 인증 필요 | 1 |
SMTP_HOST |
SMTP 서버 주소 | smtp.gmail.com |
SMTP_PORT |
포트 (TLS 587) | 587 |
SMTP_USER |
발송 계정 이메일 | [email protected] |
SMTP_PASS |
발송 계정 비밀번호(또는 앱 비밀번호) | — |
FROM_EMAIL |
(선택) 발신 주소, 없으면 SMTP_USER 사용 | — |
APP_BASE_URL |
인증 링크에 들어갈 사이트 주소 | https://your-domain.com |
- Gmail: 앱 비밀번호 사용 권장.
- 설정이 없거나 잘못되면 가입 시 "이메일 발송 실패. 관리자에게 문의하세요." 메시지가 나올 수 있습니다.
-
가상환경 및 패키지
python -m venv .venv .venv\Scripts\activate # Windows pip install -r requirements.txt
-
API 키 설정
.env.example을 복사해.env를 만들고OPENAI_API_KEY에 키를 넣는다. -
문서 배치
다음 폴더 구조로 PDF 또는 TXT를 배치한다:data/pdf/ ├── 정강정책/ # 정강정책 문서 (.pdf, .txt) ├── 공약/ # 우리당 공약 문서 (.pdf, .txt) └── 지역별 공약/ # 타지역 공약 문서 (.pdf, .txt).txt는 UTF-8로 읽는다. 같은 이름의 .pdf와 .txt가 있으면 .pdf 우선. (폴더가 없어도 API는 동작하며, 해당 섹션은 비어있음으로 표시된다.) -
DB 초기화 (최초 1회)
python scripts/init_db.py
-
후보
region_code정규화 (기존 데이터 보정)# 분석만 python scripts/normalize_candidate_regions.py --dry-run # 실제 업데이트 python scripts/normalize_candidate_regions.py
- 매핑 테이블:
data/region_map.json기준으로region_codes테이블 동기화 - 결과 리포트:
data/reports/region_normalization_report.json - 미매핑 목록:
data/reports/region_unmapped_items.json(각 항목에suggested_region_codes자동 추천 포함)
- 매핑 테이블:
5-1. 선거구(시군구/선거구) 단위 조회
- 후보 데이터는
region_code(시·도) +district_code(선거구 코드)를 함께 사용할 수 있습니다. - 표준 선거구 코드 동기화:
python scripts/sync_district_codes.py
- 기준 파일: `data/district_map.json` (전국 시군구 전체 목록, 17개 시도)
- `district_map.json`은 `districts`(평탄화 포맷) 또는 `data`(시도별 배열 포맷) 둘 다 지원
- 지방선거 세부선거구(가나다):
data/district_sub_map.json. 공공데이터 API로 채우려면.env에DATA_GO_KR_API_KEY설정 후python scripts/build_district_sub_from_api.py실행 (활용신청: 코드정보 API). - 당선인 정보/선거공약 API(당 부합 점검 시 2022 당선인 이름 보강): 동일 키 사용. 당선인 정보, 선거공약 정보. 키 동작 확인:
python3 scripts/test_nec_api.py. 파이프라인 검증(서울/시도지사 시 타지역 미포함):python3 scripts/verify_winners_api.py. - API:
GET /api/regionsGET /api/districts?region_code=41&election_type=localGET /api/candidates?region_code=41&district_code=41:수원시장안구&election_type=local
district_code가 비어 있는 기존 데이터는region_code:district_name형태로 자동 파생되어 조회됩니다.
-
서버 실행 (프로젝트 루트에서)
uvicorn backend.main:app --reload
브라우저: http://127.0.0.1:8000
API 문서: http://127.0.0.1:8000/docs참고:
- FAISS 모드: 서버 시작 시 PDF 스캔 → 청크 분할 → 임베딩 → 인덱스 구축. 첫 실행 시 시간이 걸릴 수 있음.
- Vector Store 모드: 서버 시작 시 ingest 없음. 반드시
scripts/ingest_vector_store.py로 사전 인덱싱 후 서버를 실행.
-
API 호출 예시 (로그인·승인 필요)
기존 방식 (전체 컨텍스트 사용):
curl -X POST http://127.0.0.1:8000/check \ -H "Content-Type: application/json" \ -d '{"pledge": "지역 청년 일자리 1000개 창출"}'
벡터 검색 기반 검증 (근거 인용):
curl -X POST http://127.0.0.1:8000/api/pledge/verify \ -H "Content-Type: application/json" \ -d '{ "text": "지역 청년 일자리 1000개 창출", "top_k_platform": 6, "top_k_pledge": 6, "top_k_regional": 8 }'
디버그 검색 (GET, query params) —
source,q필수,top_k선택(기본 10, 1~50):curl -G "http://localhost:8000/api/debug/search" --data-urlencode "source=pledge" --data-urlencode "q=신구연금 분리" --data-urlencode "top_k=10"
디버그 검색 (POST, JSON 바디):
curl -X POST "http://localhost:8000/api/debug/search" -H "Content-Type: application/json" -d "{\"source\":\"pledge\",\"q\":\"신구연금 분리\",\"top_k\":10}"
(Windows PowerShell에서는
-d '{"source":"pledge","q":"신구연금 분리","top_k":10}'형태로 사용 가능.)응답은 JSON 형식으로 다음을 포함합니다:
summary: 적합도 점수 및 판정platform: 정강정책 근거 스니펫 (인용 포함)pledges: 우리당 공약 근거 스니펫 (인용 포함)regional_similarity: 타지역 공약 유사성 분석conflicts: 상충 이슈 및 제안improvements: 개선 제안
FAISS 대신 OpenAI File Search를 쓰면 인덱스 경로·EBS/EFS 등 AWS 설정이 필요 없습니다.
USE_OPENAI_VECTOR_STORE=1
OPENAI_VECTOR_STORE_ID=vs_xxx
OPENAI_REGIONAL_VECTOR_STORE_ID=vs_yyy
FILE_SEARCH_MAX_RESULTS=6서버 시작 시 ingest가 실행되지 않도록 분리되어 있습니다. 아래 스크립트로 PDF를 업로드·인덱싱합니다:
# 프로젝트 루트에서 실행
python scripts/ingest_vector_store.pydata/pdf/정강정책/,data/pdf/공약/→ 정강·공약 Vector Store 생성data/pdf/지역별 공약/→ 지역별 공약 Vector Store 생성- 중복 판정: sha256(file bytes) 기반으로 동일 파일은 재업로드하지 않음
- Vector Store ID는
.rag/registry.json및.rag/vector_store_id*.txt에 저장
자세한 사용법: docs/Vector_Store_업로드_스크립트.md
phase=quick: file_search 결과 3~5개, 짧은 판정(JSON)만 반환phase=full: 결과 6~8개, 근거 인용·상충 분석 포함
push to main 시 GitHub Actions가 scripts/sync_vector_store.py를 실행해 변경된 PDF만 Vector Store에 반영합니다.
초기 설정: docs/Vector_Store_업로드_스크립트.md §8 참고.
.env 파일에 다음 변수를 설정할 수 있습니다:
OPENAI_API_KEY=your_api_key_here
OPENAI_MODEL=gpt-4o-mini
EMBEDDING_MODEL=text-embedding-3-large
CHAT_MODEL=gpt-4o-mini
CHUNK_SIZE=1200
CHUNK_OVERLAP=200
MAX_CHUNKS_PER_FILE=120
EMBEDDING_BATCH_SIZE=64- 인덱스는
INDEX_CACHE_DIR에 저장됩니다 (Windows:data/index_cache, Linux:/tmp/index_cache). - AWS 배포 시: Linux 기본값
/tmp는 재시작 시 삭제되므로.env에INDEX_CACHE_DIR=/app/data/index_cache등 영구 경로를 지정하세요. - PDF 파일이 변경되면 (파일명, 수정시간, 크기 기준) 자동으로 재빌드됩니다.
- 강제 재빌드가 필요한 경우 캐시 파일을 삭제하거나
REBUILD_INDEX=1로 서버를 재시작하세요. - RAG 검색 시 멀티워커는 인덱스 미공유 이슈가 있으므로
uvicorn --workers 1권장.
- 프롬프트 정교화: 실제 PDF로 테스트하며 부합/부분부합/미부합·체크리스트 형식 조정
- 프론트/챗: 웹 폼 또는 챗봇으로 연동
- 중복·유사 탐지: 후보 공약 DB 구축 후 2차 기능 추가