# -*- coding: utf-8 -*- """ PAAI 피드백 루프 시스템 - 피드백 수집, AI 정제, 프롬프트 인젝션 """ import sqlite3 import os import json from datetime import datetime from flask import Blueprint, request, jsonify paai_feedback_bp = Blueprint('paai_feedback', __name__) # DB 경로 DB_PATH = os.path.join(os.path.dirname(__file__), 'db', 'paai_feedback.db') def get_db(): """DB 연결""" os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row return conn def init_db(): """테이블 초기화""" conn = get_db() conn.execute(''' CREATE TABLE IF NOT EXISTS paai_feedback ( id INTEGER PRIMARY KEY AUTOINCREMENT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 컨텍스트 prescription_id TEXT, patient_name TEXT, patient_context TEXT, -- PAAI 응답 paai_request TEXT, paai_response TEXT, -- 피드백 rating TEXT, category TEXT, pharmacist_comment TEXT, -- AI 정제 결과 refined_rule TEXT, confidence REAL, -- 적용 상태 applied_to_prompt INTEGER DEFAULT 0, applied_to_training INTEGER DEFAULT 0 ) ''') conn.commit() conn.close() # 앱 시작 시 테이블 생성 init_db() @paai_feedback_bp.route('/api/paai/feedback', methods=['POST']) def submit_feedback(): """피드백 제출""" try: data = request.json conn = get_db() cursor = conn.cursor() cursor.execute(''' INSERT INTO paai_feedback ( prescription_id, patient_name, patient_context, paai_request, paai_response, rating, category, pharmacist_comment ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ''', ( data.get('prescription_id'), data.get('patient_name'), json.dumps(data.get('patient_context', {}), ensure_ascii=False), data.get('paai_request'), data.get('paai_response'), data.get('rating'), # 'good' or 'bad' data.get('category'), # 'interaction', 'indication', 'dosage', 'other' data.get('pharmacist_comment') )) feedback_id = cursor.lastrowid conn.commit() # bad 피드백이고 코멘트가 있으면 AI 정제 시도 if data.get('rating') == 'bad' and data.get('pharmacist_comment'): refined = refine_feedback_async(feedback_id, data) if refined: cursor.execute(''' UPDATE paai_feedback SET refined_rule = ?, confidence = ? WHERE id = ? ''', (refined['rule'], refined['confidence'], feedback_id)) conn.commit() conn.close() return jsonify({ 'success': True, 'feedback_id': feedback_id, 'message': '피드백이 저장되었습니다.' }) except Exception as e: return jsonify({'success': False, 'error': str(e)}), 500 def refine_feedback_async(feedback_id, data): """피드백을 규칙으로 정제 (동기 버전 - 나중에 비동기로 변경 가능)""" try: # TODO: AI 호출로 정제 # 지금은 간단히 코멘트를 규칙 형태로 저장 comment = data.get('pharmacist_comment', '') if not comment: return None # 간단한 규칙 형태로 변환 category = data.get('category', 'other') rule = f"[{category}] {comment}" return { 'rule': rule, 'confidence': 0.8 # 기본 신뢰도 } except: return None @paai_feedback_bp.route('/api/paai/feedback/rules', methods=['GET']) def get_feedback_rules(): """축적된 피드백 규칙 조회 (프롬프트 인젝션용)""" try: conn = get_db() cursor = conn.cursor() # bad 피드백 중 정제된 규칙만 cursor.execute(''' SELECT refined_rule, category, created_at FROM paai_feedback WHERE rating = 'bad' AND refined_rule IS NOT NULL ORDER BY created_at DESC LIMIT 20 ''') rules = [] for row in cursor.fetchall(): rules.append({ 'rule': row['refined_rule'], 'category': row['category'], 'created_at': row['created_at'] }) conn.close() return jsonify({ 'success': True, 'rules': rules, 'count': len(rules) }) except Exception as e: return jsonify({'success': False, 'error': str(e)}), 500 @paai_feedback_bp.route('/api/paai/feedback/stats', methods=['GET']) def get_feedback_stats(): """피드백 통계""" try: conn = get_db() cursor = conn.cursor() cursor.execute(''' SELECT COUNT(*) as total, SUM(CASE WHEN rating = 'good' THEN 1 ELSE 0 END) as good, SUM(CASE WHEN rating = 'bad' THEN 1 ELSE 0 END) as bad FROM paai_feedback ''') row = cursor.fetchone() conn.close() return jsonify({ 'success': True, 'stats': { 'total': row['total'] or 0, 'good': row['good'] or 0, 'bad': row['bad'] or 0 } }) except Exception as e: return jsonify({'success': False, 'error': str(e)}), 500 def get_rules_for_prompt(patient_context=None): """프롬프트에 주입할 규칙 목록 반환""" try: conn = get_db() cursor = conn.cursor() # 최근 규칙 20개 cursor.execute(''' SELECT refined_rule FROM paai_feedback WHERE rating = 'bad' AND refined_rule IS NOT NULL ORDER BY created_at DESC LIMIT 20 ''') rules = [row['refined_rule'] for row in cursor.fetchall()] conn.close() return rules except: return []