# PostgreSQL Apache AGE 기반 약국 GraphRAG 추천 시스템 기획서

**작성일**: 2026-01-24
**프로젝트**: 약국 POS QR 마일리지 시스템 → 근거 기반 AI 추천 시스템 전환

---

## 1. 프로젝트 개요

### 목표
기존 SQLite 기반 약국 시스템을 **PostgreSQL + Apache AGE**로 확장하여, **PubMed 논문 근거 기반 GraphRAG 제품 추천 시스템** 구축

### 핵심 가치
- ✅ **근거 기반 추천**: "왜 이 제품을 추천하는가?" → 논문(PMID) 근거 제시
- ✅ **복합 증상 매칭**: "피로 + 소화불량 + 스트레스" → 최적 복합제 추천
- ✅ **추론 경로 시각화**: Statin → 근육약화 → CoQ10 추천 경로
- ✅ **AI 카테고리 관리**: 무분별한 증가 방지, 23개 기본 카테고리 유지

---

## 2. 현황 분석

### 현재 시스템
- **MSSQL (PIT3000)**: 판매 거래 데이터
- **SQLite (mileage.db)**: 제품 마스터, 카테고리, 질병 코드
- **PubMed 파이프라인**: 완성 (여러 연구 스크립트로 근거 수집 중)

### 주요 데이터
- 제품 마스터: 바코드 기준 (동일 제품 다른 이름 가능)
- 카테고리: 23개 기본 (진통제, 소화제, 복합비타민 등)
- 질병 코드: ICD-10 기반
- 제품-카테고리 매핑: 다대다 (relevance_score 0.0~1.0)

### 현재 한계
- ❌ 복잡한 관계 표현 불가 (약물 → 부작용 → 예방 관계)
- ❌ 추론 경로 제시 불가
- ❌ PubMed 근거와 제품 연결 미흡
- ❌ 성분 간 시너지 표현 불가

---

## 3. 기술 아키텍처

### 3.1 시스템 구조

```
┌─────────────────────────────────────┐
│  웹앱 (Flask) + 관리자 대시보드     │
└───────────────┬─────────────────────┘
                ↓ REST API
┌───────────────────────────────────────┐
│   Flask Backend (Python)              │
│   - GraphRAG Service (Cypher 쿼리)   │
│   - OpenAI (카테고리 태깅)           │
│   - PubMed (Biopython)                │
└───────────────┬───────────────────────┘
                ↓
┌───────────────────────────────────────┐
│ PostgreSQL 15 + Apache AGE            │
├───────────────────────────────────────┤
│ SQL 테이블:                           │
│  - products (제품 마스터)             │
│  - categories (카테고리)              │
│  - evidence (PubMed 논문)             │
├───────────────────────────────────────┤
│ 그래프 (Cypher):                      │
│  노드: Product, Ingredient,           │
│       Symptom, Disease, Evidence      │
│  엣지: CONTAINS, TREATS, CAUSES,      │
│       PREVENTS, SYNERGY_WITH          │
└───────────────────────────────────────┘
```

### 3.2 기술 스택

| 레이어 | 기술 |
|--------|------|
| **DB** | PostgreSQL 15 + Apache AGE 확장 |
| **Backend** | Python 3.11 + Flask |
| **그래프 쿼리** | Cypher (Apache AGE) |
| **AI** | OpenAI GPT-4o (카테고리 태깅, 추천 설명) |
| **PubMed** | Biopython (논문 검색 및 근거 수집) |
| **캐싱** | Redis (추천 결과 캐싱) |
| **라이브러리** | psycopg2, apache-age-python |

---

## 4. 데이터 모델 설계

### 4.1 그래프 노드 타입

#### Product (제품)
```cypher
(:Product {
    barcode: '8806436016712',
    name: '탁센캡슐',
    manufacturer: '동아제약',
    price: 8000
})
```

#### Ingredient (성분)
```cypher
(:Ingredient {
    id: 'naproxen',
    name: '나프록센',
    category: 'NSAID'
})
```

#### Symptom (증상)
```cypher
(:Symptom {
    id: 'headache',
    name: '두통',
    severity: 'moderate'
})
```

#### Disease (질병)
```cypher
(:Disease {
    icd_code: 'K30',
    name: '소화불량',
    category: '소화기질환'
})
```

#### Evidence (PubMed 논문)
```cypher
(:Evidence {
    pmid: '27959716',
    title: 'CV Safety of Naproxen',
    journal: 'NEJM',
    year: 2016,
    reliability: 0.99
})
```

### 4.2 그래프 관계 타입

| 관계 | From → To | 속성 | 의미 |
|------|-----------|------|------|
| **CONTAINS** | Product → Ingredient | amount, role | 제품이 성분 포함 |
| **TREATS** | Ingredient → Symptom/Disease | efficacy, evidence_pmid | 성분이 증상 치료 |
| **CAUSES** | Ingredient → Symptom | probability, severity | 성분이 부작용 유발 |
| **PREVENTS** | Ingredient → Symptom | efficacy, dosage | 성분이 부작용 예방 |
| **SYNERGY_WITH** | Ingredient ↔ Ingredient | synergy_score, reason | 성분 간 시너지 |
| **SAFER_THAN** | Product → Product | aspect, confidence | 제품 안전성 비교 |
| **BELONGS_TO** | Product → Category | relevance_score | 제품-카테고리 매핑 |

### 4.3 PostgreSQL 테이블

```sql
-- 제품 마스터
CREATE TABLE products (
    barcode TEXT PRIMARY KEY,
    product_name TEXT NOT NULL,
    manufacturer TEXT,
    price INTEGER,
    stock INTEGER,
    ingredients_json JSONB,
    search_vector tsvector
);

-- 카테고리 (계층 구조)
CREATE TABLE categories (
    category_id SERIAL PRIMARY KEY,
    category_name TEXT UNIQUE NOT NULL,
    parent_id INTEGER REFERENCES categories(category_id),
    level INTEGER DEFAULT 1,  -- 1: 기본 23개, 2-3: 서브
    description TEXT
);

-- PubMed 논문
CREATE TABLE evidence (
    pmid TEXT PRIMARY KEY,
    title TEXT NOT NULL,
    journal TEXT,
    year INTEGER,
    study_type TEXT,
    abstract TEXT,
    reliability REAL
);
```

---

## 5. 핵심 기능

### 5.1 근거 기반 제품 추천

**입력**:
- 증상: ["두통", "피로"]
- 환자 프로필: 65세, 고혈압, Statin 복용 중
- 예산: 20,000원

**Cypher 쿼리**:
```cypher
MATCH (p:Product)-[:CONTAINS]->(i:Ingredient)-[t:TREATS]->(s:Symptom)
WHERE s.name IN ['두통', '피로']
  AND p.price <= 20000
OPTIONAL MATCH (e:Evidence {pmid: t.evidence_pmid})
WHERE e.reliability > 0.8
RETURN p.name, p.price,
       COLLECT(s.name) AS symptoms,
       e.pmid, e.title, e.reliability
ORDER BY e.reliability DESC, COUNT(DISTINCT s) DESC
LIMIT 5
```

**출력**:
```json
{
    "recommendations": [
        {
            "name": "비맥스제트",
            "price": 15000,
            "symptoms_covered": ["피로", "소화불량"],
            "evidence": {
                "pmid": "12345678",
                "title": "Vitamin B Complex for Fatigue",
                "reliability": 0.85
            },
            "reasoning": "비타민 B복합체가 피로 회복에 효과적입니다. (PMID:12345678, 신뢰도: 85%)"
        }
    ]
}
```

### 5.2 복합 증상 매칭 + 시너지

**Cypher 쿼리**:
```cypher
-- 피로 + 근육통 + 스트레스를 동시에 치료하면서 시너지 있는 제품
MATCH (p:Product)-[:CONTAINS]->(i1:Ingredient)-[:TREATS]->(s:Symptom)
WHERE s.name IN ['피로', '근육통', '스트레스']
WITH p, COUNT(DISTINCT s) AS coverage
WHERE coverage >= 2

MATCH (p)-[:CONTAINS]->(i1:Ingredient)-[:TREATS]->(:Symptom {name: '피로'})
MATCH (p)-[:CONTAINS]->(i2:Ingredient)-[:TREATS]->(:Symptom {name: '근육통'})
OPTIONAL MATCH (i1)-[syn:SYNERGY_WITH]->(i2)

RETURN p.name, coverage,
       i1.name + ' + ' + i2.name AS combo,
       syn.synergy_score AS synergy,
       p.price
ORDER BY coverage DESC, synergy DESC
```

**예시 결과**: 비맥스제트 (비타민B1 + 마그네슘, 시너지 0.9)

### 5.3 추론 경로 시각화

**쿼리**: Statin → 근육약화 → CoQ10 추천 경로

```cypher
MATCH path = (drug:Ingredient {id: 'statin'})
             -[:CAUSES]->(side:Symptom {name: 'muscle_weakness'})
             <-[:PREVENTS]-(supplement:Ingredient {id: 'coq10'})
RETURN [node IN nodes(path) | node.name] AS reasoning_path,
       [rel IN relationships(path) | type(rel)] AS relationships
```

**결과**:
```json
{
    "reasoning_path": ["Statin", "근육약화", "CoQ10"],
    "relationships": ["CAUSES", "PREVENTS"],
    "explanation": "Statin은 근육약화를 유발할 수 있으며, CoQ10이 이를 예방합니다."
}
```

### 5.4 AI 카테고리 자동 태깅

**프로세스**:
1. 제품명 + 성분 정보 → OpenAI GPT-4o
2. 기존 23개 카테고리에서 1~3개 선택
3. relevance_score (0.0~1.0) 자동 계산
4. 그래프에 BELONGS_TO 관계 생성

**예시**:
```python
# 입력
product = {
    "name": "탁센캡슐",
    "ingredients": ["나프록센 250mg"]
}

# AI 응답
{
    "categories": [
        {"name": "진통소염제", "score": 1.0, "reason": "주성분이 NSAID"},
        {"name": "진통제", "score": 0.9, "reason": "진통 효과"}
    ]
}

# 그래프 저장
CREATE (p:Product {barcode: '8806436016712'})
       -[:BELONGS_TO {relevance_score: 1.0, tagged_by: 'AI'}]
       ->(c:Category {name: '진통소염제'})
```

---

## 6. 카테고리 관리 전략

### 6.1 3단계 계층 구조

```
Level 1 (고정 23개 - 기본 카테고리)
├─ 진통소염제
│  ├─ Level 2 (서브 카테고리)
│  │  ├─ NSAID
│  │  └─ 아세트아미노펜
│  └─ Level 3 (세분화)
│     └─ 심혈관 안전 NSAID
├─ 소화제
│  ├─ 위장약
│  └─ 정장제
└─ 복합비타민
   ├─ B복합체
   └─ 종합비타민
```

### 6.2 신규 카테고리 추가 규칙

**원칙**:
- Level 1 (23개) = **절대 불변**
- Level 2-3 = 약사/AI 제안 → 검토 후 승인
- 월 최대 2개 신규 허용
- 최소 10개 제품 예상 시에만 추가

**워크플로우**:
1. 약사/AI가 신규 카테고리 제안
2. AI 검증 (필요성, 중복 여부)
3. 영향도 분석 (기존 제품 재분류 필요성)
4. 승인 시 → 기존 전체 제품 재검토 (AI 일괄 태깅)

---

## 7. 마이그레이션 계획

### Phase 1: 환경 설정 (1주)

```bash
# PostgreSQL + AGE 설치
sudo apt install postgresql-15 postgresql-15-age

# 그래프 생성
CREATE EXTENSION age;
SELECT create_graph('pharmacy_graph');

# Python 라이브러리
pip install psycopg2-binary apache-age-python biopython
```

### Phase 2: 데이터 마이그레이션 (2주)

**Step 1**: SQLite → PostgreSQL (SQL 테이블)
- products, categories, evidence

**Step 2**: 그래프 노드 생성
- Product, Ingredient, Symptom, Disease 노드

**Step 3**: 관계 생성
- CONTAINS, TREATS, BELONGS_TO 엣지

**Step 4**: PubMed 근거 통합
- 주요 약물-증상 쌍 100개 논문 수집
- Evidence 노드 + TREATS 관계에 pmid 연결

### Phase 3: 검증 및 테스트 (1주)

- 노드/엣지 개수 검증
- Cypher 쿼리 성능 테스트
- SQLite vs PostgreSQL+AGE 병렬 운영

### Phase 4: API 개발 (2주)

- `/api/recommend` - GraphRAG 추천
- `/api/graph/path` - 추론 경로
- `/api/categories/tag` - AI 자동 태깅

---

## 8. API 설계

### 8.1 POST `/api/recommend`

**Request**:
```json
{
    "symptoms": ["두통", "피로"],
    "patient_profile": {
        "age": 65,
        "conditions": ["HTN"],
        "medications": ["Atorvastatin 10mg"]
    },
    "budget": 20000
}
```

**Response**:
```json
{
    "recommendations": [
        {
            "barcode": "8806436016712",
            "name": "탁센캡슐",
            "price": 8000,
            "score": 0.95,
            "reasoning": {
                "path": ["Naproxen", "TREATS", "두통"],
                "evidence": {
                    "pmid": "27959716",
                    "title": "CV Safety of Naproxen",
                    "reliability": 0.99
                }
            }
        }
    ]
}
```

### 8.2 GET `/api/products/<barcode>/graph`

제품의 전체 관계 그래프 반환 (성분, 증상, 근거 논문)

### 8.3 POST `/api/categories/tag-batch`

AI 일괄 카테고리 태깅

---

## 9. 성능 최적화

### 인덱스
```sql
CREATE INDEX idx_products_barcode ON products(barcode);
CREATE INDEX idx_products_search ON products USING GIN(search_vector);
CREATE INDEX idx_evidence_reliability ON evidence(reliability DESC);
```

### 캐싱 (Redis)
- 추천 결과: TTL 1시간
- 그래프 경로: TTL 24시간
- 카테고리 목록: TTL 7일

### Cypher 최적화
- 증상부터 역방향 탐색 (인덱스 활용)
- OPTIONAL MATCH 최소화
- WITH 절로 중간 필터링

---

## 10. 구현 우선순위

### 우선순위 1 (필수, 2주)
- ✅ PostgreSQL + AGE 설치
- ✅ 기본 스키마 생성
- ✅ SQLite 데이터 마이그레이션
- ✅ Product, Ingredient 노드 생성
- ✅ CONTAINS, TREATS 관계

### 우선순위 2 (핵심, 2주)
- ✅ `/api/recommend` API
- ✅ PubMed 근거 통합 (100개 논문)
- ✅ 복합 증상 매칭 Cypher 쿼리
- ✅ 추론 경로 생성

### 우선순위 3 (AI, 1주)
- ✅ AI 카테고리 자동 태깅
- ✅ 신규 카테고리 제안 워크플로우
- ✅ 카테고리 계층 관리

### 우선순위 4 (최적화, 1주)
- ✅ 인덱스 최적화
- ✅ Redis 캐싱
- ✅ 성능 벤치마크

---

## 11. 핵심 파일 구조

```
backend/
├─ migration/
│  ├─ migrate_to_postgres.py      # SQLite → PostgreSQL
│  ├─ build_graph.py               # 그래프 노드 생성
│  └─ integrate_pubmed.py          # PubMed 근거 통합
├─ services/
│  ├─ graphrag_service.py          # GraphRAG 추천 로직
│  ├─ ai_tagging.py                # AI 카테고리 태깅
│  └─ category_service.py          # 카테고리 관리
├─ api/
│  ├─ recommend.py                 # 추천 API
│  └─ categories.py                # 카테고리 API
├─ db/
│  ├─ age_connector.py             # Apache AGE 연결
│  └─ schema.sql                   # PostgreSQL 스키마
└─ pubmed_search.py                # PubMed 검색 (재사용)
```

---

## 12. 예상 효과

### 비즈니스 가치
- **업셀링 증가**: 복합제 추천으로 객단가 +50%
- **고객 신뢰**: 논문 근거 제시로 재방문율 +30%
- **전문성 강화**: 약국 브랜드 이미지 향상

### 기술적 이점
- **확장성**: 수만 개 제품 처리 가능
- **유연성**: 새 관계 타입 쉽게 추가
- **추론 가능**: 다단계 복합 쿼리

### 차별화
- 타 약국 대비 **과학적 근거 기반 추천**
- AI + GraphRAG 결합한 **최초 약국 시스템**

---

## 13. 리스크 및 대응

| 리스크 | 대응 방안 |
|--------|----------|
| PostgreSQL 학습 곡선 | 단계적 마이그레이션, SQLite 병렬 운영 |
| Apache AGE 초기 버전 | 핵심 기능만 사용, SQL Fallback |
| PubMed API 제한 | API 키 발급, 캐싱, 배치 처리 |
| AI 비용 | GPT-4o-mini 사용, 결과 캐싱 |

---

## 14. 다음 단계

### 즉시 시작 (이번 주)
1. PostgreSQL 15 + AGE 설치 테스트
2. 샘플 그래프 생성 (10개 제품)
3. 기본 Cypher 쿼리 실습

### 다음 주
1. SQLite → PostgreSQL 마이그레이션 스크립트
2. PubMed 100개 논문 수집
3. `/api/recommend` 프로토타입

### 다음 달
1. 전체 시스템 통합
2. 웹앱 UI 추가
3. 성능 최적화

---

**작성자**: Claude Sonnet 4.5
**검토 필요 사항**:
- PostgreSQL 서버 사양 확인
- PubMed API 키 발급
- 카테고리 계층 최종 승인
