pharmacy-pos-qr-system/docs/PAAI_TROUBLESHOOTING_2026-03-07.md

10 KiB

PAAI 시스템 트러블슈팅 기록

날짜: 2026-03-07
작성자: 용림 (AI 디지털 직원)
상태: 해결 완료


📋 목차

  1. 시스템 아키텍처
  2. 모델 전략 (Opus vs Sonnet)
  3. 발생한 문제들
  4. 해결 과정
  5. 수정된 코드
  6. 서브에이전트 활용
  7. 교훈 및 권장사항

시스템 아키텍처

전체 흐름

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  PIT3000 POS    │────▶│  prescription   │────▶│     Flask       │
│  (MSSQL)        │     │  _trigger.py    │     │   (pmr_api)     │
└─────────────────┘     └─────────────────┘     └─────────────────┘
                              │ WebSocket            │ HTTP
                              ▼ (ws://8765)          ▼
                        ┌─────────────────┐     ┌─────────────────┐
                        │   PMR 화면      │     │  KIMS API       │
                        │   (실시간)      │     │  (약물 상호작용) │
                        └─────────────────┘     └─────────────────┘
                                                     │
                                                     ▼
                                              ┌─────────────────┐
                                              │   Clawdbot      │
                                              │   Gateway       │
                                              │ (ws://18789)    │
                                              └─────────────────┘
                                                     │
                                                     ▼
                                              ┌─────────────────┐
                                              │  Claude API     │
                                              │  (Sonnet 4)     │
                                              └─────────────────┘

PM2 서비스 구성

서비스 PM2 이름 포트 역할
Flask 웹서버 flask-pharmacy 7001 API 엔드포인트, PMR 화면
처방 감지 websocket-rx 8765 DB 폴링 → 실시간 알림
Clawdbot clawdbot-gateway 18789 AI 게이트웨이

모델 전략 (Opus vs Sonnet)

Gateway 기본 설정

// ~/.clawdbot/clawdbot.json
{
  "agents": {
    "defaults": {
      "model": {
        "primary": "anthropic/claude-opus-4-5"  // 기본 모델
      },
      "models": {
        "anthropic/claude-opus-4-5": { "alias": "opus" },
        "github-copilot/gpt-5": {},
        "openai-codex/gpt-5.2-codex": {}
      }
    }
  }
}

PAAI 전용 모델 (비용 절감)

PAAI 분석은 빠른 응답비용 절감이 중요하므로 Sonnet을 사용.

# pmr_api.py - Line 1417
ai_text = ask_clawdbot(
    message=prompt,
    session_id='paai-analysis',
    system_prompt=PAAI_SYSTEM_PROMPT,
    timeout=60,
    model='anthropic/claude-sonnet-4-5'  # Sonnet 지정!
)

sessions.patch 메커니즘

Gateway에서 모델이 allowlist에 없어도, sessions.patch로 세션별 모델 오버라이드 가능:

# clawdbot_client.py - _ask_gateway()

# 4. 모델 오버라이드 (sessions.patch)
if model:
    patch_frame = {
        'type': 'req',
        'method': 'sessions.patch',
        'params': {
            'key': session_id,
            'model': model,  # 'anthropic/claude-sonnet-4-5'
        }
    }
    await ws.send(json.dumps(patch_frame))
    # patch 실패해도 agent 요청은 계속 진행됨

모델 비용 비교

모델 입력 (1M) 출력 (1M) 용도
Claude Opus 4.5 $15 $75 메인 세션, 복잡한 작업
Claude Sonnet 4.5 $3 $15 PAAI 분석, 빠른 응답 필요

절감 효과: PAAI를 Sonnet으로 돌리면 비용 80% 절감!


발생한 문제들

문제 1: 처방 분석이 안 됨 (임명옥 환자)

증상:

  • 처방 감지는 되나 분석 결과가 안 나옴
  • DB에 generating 상태로 멈춤

원인:

  1. PM2 watch 모드로 인한 과도한 재시작 (30회 이상)
  2. 재시작 과정에서 분석 요청이 중단됨

문제 2: Flask 연결 끊김

증상:

ConnectionResetError: [WinError 10054] 
현재 연결은 원격 호스트에 의해 강제로 끊겼습니다

원인:

  • watch 모드가 파일 변경 감지 → Flask 재시작
  • 진행 중인 HTTP 요청이 끊김

문제 3: 모델 허용 오류 (경고)

증상:

WARNING: [Clawdbot] sessions.patch 실패: model not allowed: anthropic/claude-sonnet-4-5

분석:

  • Gateway allowlist에 claude-sonnet-4-5 없음
  • 하지만 sessions.patch 실패해도 agent 요청은 진행됨
  • 세션의 기존 모델 또는 기본 모델로 폴백
  • 실제 분석에는 영향 없음 (비용만 더 나갈 수 있음)

해결 과정

Step 1: 상황 파악

# PM2 상태 확인
pm2 list
# → flask-pharmacy: ↺ 17, websocket-rx: ↺ 30 (과도한 재시작)

# 최근 성공한 분석 확인
sqlite3 db/paai_logs.db "SELECT * FROM paai_logs WHERE status='success' ORDER BY id DESC LIMIT 5"
# → 00:24:32까지 성공, 이후 실패

Step 2: 원인 분석

# Flask 로그 확인
pm2 logs flask-pharmacy --lines 50

# watch 모드가 문제임을 확인
# uptime이 7초, 계속 재시작 중

Step 3: watch 모드 비활성화

# 기존 서비스 삭제
pm2 stop flask-pharmacy websocket-rx
pm2 delete flask-pharmacy websocket-rx

# watch 없이 재등록
pm2 start app.py --name "flask-pharmacy" --interpreter python \
    --cwd "c:\Users\청춘약국\source\pharmacy-pos-qr-system\backend"

pm2 start prescription_trigger.py --name "websocket-rx" --interpreter python \
    --cwd "c:\Users\청춘약국\source\prescription-trigger"

# 저장
pm2 save

Step 4: 밀린 처방 수동 분석

# 임명옥 처방 수동 분석 요청
curl -X POST http://localhost:7001/pmr/api/paai/analyze \
    -H "Content-Type: application/json" \
    -d '{"pre_serial": "20260307000059"}'

# → 성공!

수정된 코드

ThreadPoolExecutor 적용 (동시성 제한)

파일: prescription-trigger/prescription_trigger.py

Before (문제):

# 무제한 스레드 생성 → 세션 과부하
def _request_analysis(self, pre_serial, patient_name):
    thread = threading.Thread(target=self._analyze_worker, args=(...))
    thread.start()
    self._worker_threads.append(thread)

After (해결):

from concurrent.futures import ThreadPoolExecutor

class PrescriptionTrigger:
    def __init__(self):
        # 최대 3개 동시 처리
        self._executor = ThreadPoolExecutor(max_workers=3)
    
    def _request_analysis(self, pre_serial, patient_name):
        self._executor.submit(self._analyze_worker, pre_serial, patient_name)
    
    def stop(self):
        self._executor.shutdown(wait=True)  # graceful shutdown

커밋: feat: ThreadPoolExecutor로 동시 처리 제한 (max_workers=3)

OTC 라벨 import 수정

파일: backend/app.py

문제: PM2 환경에서 상대 import 실패

ModuleNotFoundError: No module named 'utils'

해결:

# app.py 상단에서 미리 import
from utils.otc_label_printer import generate_label_commands

서브에이전트 활용

오늘 트러블슈팅에 3개의 서브에이전트를 활용:

1. paai-investigation

  • 목적: PAAI 아키텍처 분석
  • 결과: Clawdbot Gateway WebSocket 사용 확인

2. paai-queue-analysis

  • 목적: 큐 처리 로직 분석
  • 결과: 날짜 필터로 어제 것은 무시, 오늘 것은 일괄 처리

3. paai-concurrency-design

  • 목적: 동시성 제어 방안 설계
  • 결과: ThreadPoolExecutor 방식 제안
# 서브에이전트 호출 예시
sessions_spawn(
    task="처방 분석 시스템의 동시성 제어 방안을 설계해주세요",
    label="paai-concurrency-design"
)

교훈 및 권장사항

PM2 watch 모드 사용 시 주의

# 개발 중에만 watch 사용
pm2 start app.py --watch  # 개발용

# 운영 환경에서는 watch 끄기
pm2 start app.py  # 운영용

# 코드 수정 후 수동 반영
pm2 restart flask-pharmacy

모델 비용 최적화

용도 권장 모델 이유
메인 대화 Opus 복잡한 작업, 높은 품질
PAAI 분석 Sonnet 빠른 응답, 정형화된 출력
업셀링 추천 Sonnet 간단한 추천

동시성 제어

# ThreadPoolExecutor 사용
executor = ThreadPoolExecutor(max_workers=3)

# 이점:
# 1. 리소스 제한 (CPU, 메모리)
# 2. Gateway 세션 과부하 방지
# 3. 순차적 처리보다 빠름

트러블슈팅 체크리스트

  1. pm2 list - 재시작 횟수 확인 (↺)
  2. pm2 logs <name> - 에러 로그 확인
  3. paai_logs.db - 분석 상태 확인
  4. trigger_state.db - 작업 큐 상태 확인

결론

항목 Before After
안정성 30회 재시작 안정적
동시성 무제한 스레드 3개 제한
비용 Opus (고비용) Sonnet (저비용)
Watch 모드 활성화 (불안정) 비활성화 (안정)

시스템 정상화 완료! 🎉