AI ERP 자동 주문 시스템 기획서

버전: 1.0
작성일: 2026-03-06
목표: 약국 재고 관리 및 주문을 AI가 학습하여 완전 자동화


📋 Executive Summary

비전

"약사님이 주문에 신경 쓰지 않아도 되는 약국"

AI가 사용량, 재고, 도매상 상황, 과거 주문 패턴을 학습하여: - 언제 주문할지 - 어느 도매상에 주문할지 - 어떤 규격으로 주문할지 - 얼마나 주문할지

모든 것을 자동으로 결정하고 실행합니다.

핵심 가치

AS-IS TO-BE
매일 재고 확인 AI가 자동 모니터링
수동으로 도매상 선택 AI가 최적 도매상 선택
경험에 의존한 주문량 데이터 기반 최적 주문량
주문 누락/지연 발생 선제적 자동 주문

🎯 시스템 목표

1차 목표 (자동화)

2차 목표 (최적화)

3차 목표 (예측)


🧠 AI 학습 요소

1. 주문 패턴 학습

1.1 규격 선택 패턴 (Spec Selection)

학습 데이터:
- 약품별 과거 주문 규격 (30T, 100T, 300T, 500T)
- 각 규격 선택 시점의 재고/사용량
- 선택 결과 (남은 재고, 다음 주문까지 기간)

학습 목표:
- 사용량 대비 최적 규격 예측
- 낭비 최소화 (유통기한 고려)
- 단가 최적화 (대용량 할인 vs 소량 회전)

예시 시나리오: | 사용량/월 | 학습된 최적 규격 | 이유 | |-----------|-----------------|------| | 50개 | 30T x 2 | 소량, 빠른 회전 | | 200개 | 100T x 2 | 중간, 적정 재고 | | 800개 | 300T x 3 | 대량, 단가 절감 |

1.2 재고 전략 학습 (Inventory Strategy)

학습 데이터:
- 주문 시점의 재고 수준
- 재고 소진까지 남은 일수
- 주문 후 입고까지 리드타임
- 품절 발생 이력

학습 목표:
- 약사님의 재고 선호도 파악
  - 타이트형: 최소 재고 유지 (현금 흐름 중시)
  - 여유형: 안전 재고 확보 (품절 방지 중시)

재고 전략 프로파일:

class InventoryStrategy:
    TIGHT = {
        'safety_days': 2,      # 안전 재고 2일치
        'reorder_point': 0.8,  # 80% 소진 시 주문
        'order_coverage': 7    # 7일치 주문
    }

    MODERATE = {
        'safety_days': 5,
        'reorder_point': 0.6,
        'order_coverage': 14
    }

    CONSERVATIVE = {
        'safety_days': 10,
        'reorder_point': 0.5,
        'order_coverage': 30
    }

1.3 주문량 전략 학습 (Order Quantity)

학습 데이터:
- 사용량 (일별, 주별, 월별)
- 주문량
- 주문 후 소진까지 기간
- 사용량 변동성 (표준편차)

학습 패턴:
1. 정확 매칭형: 사용량 = 주문량
2. 안전 마진형: 사용량 + α
3. 라운드업형: 규격 단위로 올림
4. 할인 최적형: MOQ(최소주문량) 충족

1.4 도매상 선택 학습 (Wholesaler Selection)

학습 데이터:
- 도매상별 주문 빈도
- 도매상별 가격
- 도매상별 재고 상황
- 도매상별 배송 속도
- 분할 주문 패턴

학습 목표:
- 기본 도매상 선호도
- 상황별 대체 도매상
- 분할 주문 조건

도매상 선택 로직:

def select_wholesaler(product, quantity, urgency):
    """
    AI가 학습한 도매상 선택 로직

    고려 요소:
    1. 재고 (있는 곳 우선)
    2. 가격 (저렴한 곳)
    3. 선호도 (과거 패턴)
    4. 긴급도 (배송 속도)
    """
    candidates = []

    for ws in wholesalers:
        score = 0

        # 재고 체크
        if ws.has_stock(product, quantity):
            score += 100

        # 가격 (낮을수록 높은 점수)
        score += (1 - ws.price_ratio) * 50

        # 학습된 선호도
        score += ai_model.preference_score(ws, product) * 30

        # 긴급도 반영
        if urgency == 'high':
            score += ws.delivery_speed * 20

        candidates.append((ws, score))

    return max(candidates, key=lambda x: x[1])

📊 데이터 모델

주문 컨텍스트 (AI 학습용)

CREATE TABLE order_context (
    id INTEGER PRIMARY KEY,
    order_item_id INTEGER,

    -- 약품 정보
    drug_code TEXT,
    product_name TEXT,

    -- 주문 시점 상황
    stock_at_order INTEGER,       -- 주문 시점 재고
    usage_1d INTEGER,             -- 최근 1일 사용량
    usage_7d INTEGER,             -- 최근 7일 사용량
    usage_30d INTEGER,            -- 최근 30일 사용량
    avg_daily_usage REAL,         -- 일평균 사용량
    usage_stddev REAL,            -- 사용량 변동성

    -- 주문 결정
    ordered_spec TEXT,            -- 선택한 규격 (30T, 300T)
    ordered_qty INTEGER,          -- 주문 수량
    ordered_dose INTEGER,         -- 총 정제수
    wholesaler_id TEXT,           -- 선택한 도매상

    -- 선택지 정보
    available_specs JSON,         -- 가능했던 규격들
    available_wholesalers JSON,   -- 가능했던 도매상들
    spec_stocks JSON,             -- 규격별 재고
    wholesaler_prices JSON,       -- 도매상별 가격

    -- 선택 이유 (AI 분석용)
    selection_reason TEXT,        -- 'price', 'stock', 'preference', 'urgency'

    -- 예측 vs 실제
    predicted_days_coverage REAL, -- 예상 커버 일수
    actual_days_to_reorder INT,   -- 실제 재주문까지 일수

    -- 결과 평가
    was_optimal BOOLEAN,          -- 최적 선택이었나
    waste_amount INTEGER,         -- 낭비량 (폐기, 유통기한)
    stockout_occurred BOOLEAN,    -- 품절 발생했나

    created_at TIMESTAMP
);

사용량 시계열

CREATE TABLE daily_usage (
    id INTEGER PRIMARY KEY,
    drug_code TEXT,
    usage_date DATE,

    -- 출처별 사용량
    rx_qty INTEGER,      -- 처방전 사용량
    pos_qty INTEGER,     -- POS 판매량
    return_qty INTEGER,  -- 반품량

    -- 집계
    net_usage INTEGER,   -- 순 사용량

    -- 재고 스냅샷
    stock_start INTEGER,
    stock_end INTEGER,

    -- 특이사항
    is_holiday BOOLEAN,
    is_event BOOLEAN,    -- 프로모션 등
    weather TEXT,        -- 날씨 (선택)

    UNIQUE(drug_code, usage_date)
);

AI 분석 결과

CREATE TABLE ai_recommendations (
    id INTEGER PRIMARY KEY,
    drug_code TEXT,
    analysis_date DATE,

    -- 현재 상황
    current_stock INTEGER,
    avg_daily_usage REAL,
    days_of_stock REAL,

    -- AI 추천
    should_order BOOLEAN,
    recommended_qty INTEGER,
    recommended_spec TEXT,
    recommended_wholesaler TEXT,
    urgency_level TEXT,         -- 'low', 'medium', 'high', 'critical'

    -- 추천 근거
    reasoning JSON,
    confidence_score REAL,

    -- 실행 상태
    auto_executed BOOLEAN,
    executed_at TIMESTAMP,
    execution_result TEXT,

    created_at TIMESTAMP
);

🔄 시스템 아키텍처

전체 흐름

┌─────────────────────────────────────────────────────────────────┐
│                        AI ERP 자동 주문 시스템                    │
└─────────────────────────────────────────────────────────────────┘
                                │
        ┌───────────────────────┼───────────────────────┐
        ▼                       ▼                       ▼
┌───────────────┐      ┌───────────────┐      ┌───────────────┐
│  데이터 수집   │      │   AI 분석     │      │   자동 실행    │
│               │      │               │      │               │
│ • POS 판매    │─────▶│ • 사용량 예측  │─────▶│ • 도매상 API  │
│ • 처방전 조제  │      │ • 재고 분석   │      │ • 주문 실행   │
│ • 현재 재고   │      │ • 주문 추천   │      │ • 결과 피드백  │
│ • 도매상 재고  │      │ • 패턴 학습   │      │               │
└───────────────┘      └───────────────┘      └───────────────┘
        │                       │                       │
        └───────────────────────┼───────────────────────┘
                                ▼
                    ┌───────────────────┐
                    │    학습 루프       │
                    │                   │
                    │  주문 결과 평가    │
                    │  → 모델 업데이트   │
                    │  → 전략 조정      │
                    └───────────────────┘

컴포넌트 상세

┌──────────────────────────────────────────────────────────────────┐
│                         데이터 레이어                             │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐  ┌────────────┐ │
│  │  PIT3000   │  │   SQLite   │  │   지오영    │  │   수인     │ │
│  │  (MSSQL)   │  │  Orders DB │  │    API     │  │   API      │ │
│  └─────┬──────┘  └─────┬──────┘  └─────┬──────┘  └─────┬──────┘ │
│        │               │               │               │        │
│        └───────────────┴───────────────┴───────────────┘        │
│                                │                                 │
└────────────────────────────────┼─────────────────────────────────┘
                                 ▼
┌──────────────────────────────────────────────────────────────────┐
│                         서비스 레이어                             │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐  │
│  │  InventorySync  │  │  UsageAnalyzer  │  │  OrderExecutor  │  │
│  │                 │  │                 │  │                 │  │
│  │ • 재고 동기화    │  │ • 사용량 집계   │  │ • 주문 실행     │  │
│  │ • 실시간 추적    │  │ • 트렌드 분석   │  │ • 결과 처리     │  │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘  │
│                                                                  │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐  │
│  │  AIPredictor    │  │  AIOptimizer    │  │  AILearner      │  │
│  │                 │  │                 │  │                 │  │
│  │ • 수요 예측     │  │ • 규격 최적화   │  │ • 패턴 학습     │  │
│  │ • 재고 예측     │  │ • 도매상 선택   │  │ • 모델 업데이트  │  │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘  │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘
                                 │
                                 ▼
┌──────────────────────────────────────────────────────────────────┐
│                         인터페이스 레이어                          │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐  │
│  │   웹 대시보드    │  │  알림 시스템    │  │   관리자 앱     │  │
│  │                 │  │                 │  │                 │  │
│  │ • 재고 현황     │  │ • 주문 알림     │  │ • 수동 개입     │  │
│  │ • 주문 이력     │  │ • 이상 감지     │  │ • 설정 조정     │  │
│  │ • AI 추천      │  │ • 승인 요청     │  │                 │  │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘  │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

🤖 AI 모델 설계

1. 수요 예측 모델

class DemandPredictor:
    """
    약품별 일간 수요 예측

    입력:
    - 과거 30일 사용량
    - 요일 (월~일)
    - 계절/월
    - 특수일 (공휴일, 이벤트)

    출력:
    - 향후 7일 예측 사용량
    - 예측 신뢰구간
    """

    def predict(self, drug_code: str, days: int = 7) -> dict:
        features = self._extract_features(drug_code)

        prediction = {
            'daily_forecast': [],  # 일별 예측
            'total_forecast': 0,   # 총 예측량
            'confidence': 0.0,     # 신뢰도
            'lower_bound': 0,      # 하한
            'upper_bound': 0       # 상한
        }

        return prediction

2. 재고 최적화 모델

class InventoryOptimizer:
    """
    최적 재고 수준 및 재주문점 계산

    입력:
    - 예측 수요
    - 리드타임 (주문~입고)
    - 서비스 수준 (품절 허용률)
    - 재고 유지 비용

    출력:
    - 재주문점 (Reorder Point)
    - 안전 재고 (Safety Stock)
    - 최적 주문량 (EOQ)
    """

    def calculate_reorder_point(self, drug_code: str) -> dict:
        demand = self.demand_predictor.predict(drug_code)
        lead_time = self._get_lead_time(drug_code)

        # 재주문점 = 리드타임 수요 + 안전재고
        lead_time_demand = demand['daily_avg'] * lead_time
        safety_stock = self._calculate_safety_stock(drug_code)

        return {
            'reorder_point': lead_time_demand + safety_stock,
            'safety_stock': safety_stock,
            'lead_time_days': lead_time
        }

3. 규격 선택 모델

class SpecSelector:
    """
    최적 규격 선택

    고려 요소:
    - 예상 사용량
    - 규격별 단가
    - 유통기한
    - 과거 선택 패턴
    """

    def select_spec(self, drug_code: str, needed_qty: int, 
                    available_specs: list) -> dict:

        candidates = []

        for spec in available_specs:
            spec_qty = self._parse_spec_qty(spec)  # "300T" → 300

            # 필요 단위 수 계산
            units_needed = math.ceil(needed_qty / spec_qty)
            total_qty = units_needed * spec_qty
            waste = total_qty - needed_qty

            # 비용 계산
            unit_price = self._get_unit_price(drug_code, spec)
            total_cost = units_needed * unit_price
            cost_per_dose = total_cost / total_qty

            # 학습된 선호도
            preference = self.ai_model.spec_preference(drug_code, spec)

            # 점수 계산
            score = self._calculate_score(
                waste_ratio=waste / total_qty,
                cost_efficiency=1 / cost_per_dose,
                preference=preference
            )

            candidates.append({
                'spec': spec,
                'units': units_needed,
                'total_qty': total_qty,
                'waste': waste,
                'cost': total_cost,
                'score': score
            })

        return max(candidates, key=lambda x: x['score'])

4. 도매상 선택 모델

class WholesalerSelector:
    """
    최적 도매상 선택 (다중 도매상 지원)

    고려 요소:
    - 재고 유무
    - 가격
    - 배송 속도
    - 과거 선호도
    - 최소 주문 금액
    """

    def select_wholesaler(self, drug_code: str, spec: str, 
                          quantity: int, urgency: str) -> dict:

        wholesalers = ['geoyoung', 'sooin', 'baekje']
        candidates = []

        for ws in wholesalers:
            # 재고 확인
            stock = self._check_stock(ws, drug_code, spec)
            if stock < quantity:
                continue

            # 가격 조회
            price = self._get_price(ws, drug_code, spec)

            # 배송 속도
            delivery_hours = self._get_delivery_time(ws)

            # AI 학습 선호도
            preference = self.ai_model.wholesaler_preference(
                drug_code, ws
            )

            # 종합 점수
            score = self._calculate_score(
                has_stock=True,
                price=price,
                delivery=delivery_hours,
                preference=preference,
                urgency=urgency
            )

            candidates.append({
                'wholesaler': ws,
                'stock': stock,
                'price': price,
                'delivery_hours': delivery_hours,
                'score': score
            })

        if not candidates:
            return self._handle_no_stock(drug_code, spec, quantity)

        return max(candidates, key=lambda x: x['score'])

    def _handle_no_stock(self, drug_code, spec, quantity):
        """재고 없을 때: 분할 주문 또는 대체품"""
        # 1. 다른 규격으로 분할
        # 2. 다중 도매상 분할
        # 3. 대체 약품 추천
        pass

5. 주문 결정 엔진

class OrderDecisionEngine:
    """
    종합 주문 결정

    매일 실행:
    1. 모든 약품 재고 스캔
    2. 재주문점 도달 품목 식별
    3. 각 품목별 최적 주문 계획 수립
    4. 자동 실행 또는 승인 요청
    """

    def daily_analysis(self) -> list:
        recommendations = []

        for drug in self._get_all_drugs():
            current_stock = self._get_stock(drug.code)
            reorder_point = self.inventory_optimizer.calculate_reorder_point(drug.code)

            if current_stock <= reorder_point['reorder_point']:
                # 주문 필요
                order_plan = self._create_order_plan(drug)
                recommendations.append(order_plan)

        return recommendations

    def _create_order_plan(self, drug) -> dict:
        # 1. 필요 수량 계산
        needed_qty = self._calculate_needed_qty(drug)

        # 2. 최적 규격 선택
        spec = self.spec_selector.select_spec(
            drug.code, needed_qty, drug.available_specs
        )

        # 3. 최적 도매상 선택
        wholesaler = self.wholesaler_selector.select_wholesaler(
            drug.code, spec['spec'], spec['units'], 
            urgency=self._determine_urgency(drug)
        )

        return {
            'drug_code': drug.code,
            'drug_name': drug.name,
            'current_stock': self._get_stock(drug.code),
            'needed_qty': needed_qty,
            'recommended_spec': spec['spec'],
            'recommended_units': spec['units'],
            'recommended_wholesaler': wholesaler['wholesaler'],
            'estimated_cost': wholesaler['price'] * spec['units'],
            'urgency': self._determine_urgency(drug),
            'confidence': self._calculate_confidence(),
            'auto_execute': self._should_auto_execute(drug)
        }

📈 학습 파이프라인

피드백 루프

주문 실행 → 결과 기록 → 평가 → 학습 → 모델 업데이트
    │                                        │
    └────────────────────────────────────────┘

평가 지표

class OrderEvaluator:
    """주문 결과 평가"""

    def evaluate(self, order_id: int) -> dict:
        order = self._get_order(order_id)

        # 1. 재고 효율성
        days_covered = self._calculate_days_covered(order)
        expected_days = order.expected_coverage
        coverage_accuracy = days_covered / expected_days

        # 2. 비용 효율성
        actual_cost_per_dose = order.total_cost / order.total_dose
        market_avg_cost = self._get_market_avg_cost(order.drug_code)
        cost_efficiency = market_avg_cost / actual_cost_per_dose

        # 3. 낭비율
        waste = self._calculate_waste(order)
        waste_ratio = waste / order.total_dose

        # 4. 품절 발생 여부
        stockout = self._check_stockout_before_next_order(order)

        return {
            'coverage_accuracy': coverage_accuracy,
            'cost_efficiency': cost_efficiency,
            'waste_ratio': waste_ratio,
            'stockout_occurred': stockout,
            'overall_score': self._calculate_overall_score(...)
        }

모델 업데이트

class AILearner:
    """주문 결과로부터 학습"""

    def learn_from_order(self, order_id: int):
        evaluation = self.evaluator.evaluate(order_id)
        context = self._get_order_context(order_id)

        # 1. 규격 선택 학습
        self.spec_model.update(
            drug_code=context.drug_code,
            chosen_spec=context.ordered_spec,
            was_optimal=evaluation['waste_ratio'] < 0.1
        )

        # 2. 재고 전략 학습
        self.inventory_model.update(
            drug_code=context.drug_code,
            reorder_point=context.stock_at_order,
            was_optimal=not evaluation['stockout_occurred']
        )

        # 3. 도매상 선호도 학습
        self.wholesaler_model.update(
            drug_code=context.drug_code,
            chosen_wholesaler=context.wholesaler_id,
            satisfaction=evaluation['cost_efficiency']
        )

⚙️ 자동화 레벨

Level 0: 수동

Level 1: 반자동

Level 2: 조건부 자동

Level 3: 완전 자동

class AutomationLevel:
    def should_auto_execute(self, order_plan: dict) -> bool:
        level = self.settings.automation_level

        if level == 0:
            return False

        if level == 1:
            return False  # 항상 승인 필요

        if level == 2:
            # 조건부 자동
            conditions = [
                order_plan['confidence'] > 0.9,
                order_plan['estimated_cost'] < 100000,
                order_plan['drug_code'] in self.trusted_drugs,
                order_plan['urgency'] != 'critical'
            ]
            return all(conditions)

        if level == 3:
            # 완전 자동 (이상 상황만 제외)
            return not self._is_anomaly(order_plan)

🔔 알림 시스템

알림 유형

유형 조건 채널
승인 요청 Level 1-2에서 자동 실행 안 되는 주문 카톡, 앱 푸시
주문 완료 자동 주문 실행됨 앱 푸시
재고 경고 안전 재고 이하 카톡
품절 긴급 재고 0, 당일 필요 전화, 카톡
이상 감지 비정상 사용량, 가격 급등 앱 푸시
일간 리포트 매일 오전 이메일

알림 메시지 예시

📦 주문 승인 요청

약품: 콩코르정 2.5mg
현재고: 45개 (3일치)
추천 주문: 300T x 2박스
도매상: 지오영
예상 금액: 72,000원

[승인] [수정] [거절]

📅 개발 로드맵

Phase 1: 기반 구축 (1-2주)

Phase 2: AI 기본 (2-3주)

Phase 3: 학습 시스템 (2-3주)

Phase 4: 자동화 (1-2주)

Phase 5: 고도화 (지속)


📊 성공 지표 (KPI)

지표 현재 목표
주문 소요 시간 30분/일 0분 (자동)
품절 발생률 5% <1%
재고 회전율 - +20%
주문 비용 절감 - 5-10%
폐기 손실 - -30%

🔐 보안 및 안전장치

자동 주문 제한

롤백 메커니즘

감사 로그


💡 핵심 인사이트

"AI는 약사님의 주문 습관을 학습합니다."

AI는 대체하는 것이 아니라, 약사님의 방식을 자동화합니다.


📚 참고 자료