refactor: PAAI Clawdbot 호출 방식 개선
- HTTP API → WebSocket Gateway 방식으로 변경 - clawdbot_client.py의 ask_clawdbot() 함수 활용 - 시스템 프롬프트 분리
This commit is contained in:
parent
4275689c29
commit
59a55d6b22
@ -1111,52 +1111,48 @@ def build_paai_prompt(
|
|||||||
|
|
||||||
|
|
||||||
def call_clawdbot_ai(prompt: str) -> dict:
|
def call_clawdbot_ai(prompt: str) -> dict:
|
||||||
"""Clawdbot AI 호출 (HTTP API)"""
|
"""Clawdbot AI 호출 (WebSocket Gateway)"""
|
||||||
import requests as http_requests
|
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
|
from services.clawdbot_client import ask_clawdbot
|
||||||
|
|
||||||
|
PAAI_SYSTEM_PROMPT = """당신은 경험 많은 약사입니다.
|
||||||
|
처방 데이터를 분석하여 약사에게 유용한 정보를 제공합니다.
|
||||||
|
반드시 요청된 JSON 형식으로만 응답하세요."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Clawdbot Gateway API 호출
|
# Clawdbot Gateway WebSocket API 호출
|
||||||
response = http_requests.post(
|
ai_text = ask_clawdbot(
|
||||||
'http://localhost:8765/api/chat',
|
message=prompt,
|
||||||
json={
|
session_id='paai-analysis',
|
||||||
'message': prompt,
|
system_prompt=PAAI_SYSTEM_PROMPT,
|
||||||
'session': 'paai-analysis',
|
timeout=60,
|
||||||
'timeout': 60
|
model='anthropic/claude-sonnet-4-5' # 빠른 Sonnet 사용
|
||||||
},
|
|
||||||
timeout=65
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if response.status_code == 200:
|
if not ai_text:
|
||||||
result = response.json()
|
logging.warning("[PAAI] Clawdbot 응답 없음")
|
||||||
# AI 응답에서 JSON 파싱 시도
|
return generate_fallback_response(prompt)
|
||||||
ai_text = result.get('response', '')
|
|
||||||
|
# JSON 블록 추출
|
||||||
|
try:
|
||||||
|
json_match = re.search(r'\{[\s\S]*\}', ai_text)
|
||||||
|
if json_match:
|
||||||
|
return json.loads(json_match.group())
|
||||||
|
except Exception as parse_err:
|
||||||
|
logging.warning(f"[PAAI] JSON 파싱 실패: {parse_err}")
|
||||||
|
|
||||||
|
# JSON 파싱 실패 시 텍스트 그대로 반환
|
||||||
|
return {
|
||||||
|
'prescription_insight': ai_text[:500] if ai_text else '분석 결과 없음',
|
||||||
|
'kims_analysis': '',
|
||||||
|
'cautions': [],
|
||||||
|
'otc_recommendations': [],
|
||||||
|
'counseling_points': []
|
||||||
|
}
|
||||||
|
|
||||||
# JSON 블록 추출
|
|
||||||
try:
|
|
||||||
import re
|
|
||||||
json_match = re.search(r'\{[\s\S]*\}', ai_text)
|
|
||||||
if json_match:
|
|
||||||
return json.loads(json_match.group())
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# JSON 파싱 실패 시 텍스트 그대로 반환
|
|
||||||
return {
|
|
||||||
'prescription_insight': ai_text[:200] if ai_text else '분석 결과 없음',
|
|
||||||
'kims_analysis': '',
|
|
||||||
'cautions': [],
|
|
||||||
'otc_recommendations': [],
|
|
||||||
'counseling_points': []
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
raise Exception(f"Clawdbot API 오류: {response.status_code}")
|
|
||||||
|
|
||||||
except http_requests.exceptions.ConnectionError:
|
|
||||||
# Clawdbot 연결 안됨 - 기본 응답 생성
|
|
||||||
return generate_fallback_response(prompt)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Clawdbot AI 호출 오류: {e}")
|
logging.error(f"[PAAI] Clawdbot AI 호출 오류: {e}")
|
||||||
return generate_fallback_response(prompt)
|
return generate_fallback_response(prompt)
|
||||||
|
|
||||||
|
|
||||||
@ -1193,3 +1189,132 @@ def paai_feedback():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"PAAI 피드백 저장 오류: {e}")
|
logging.error(f"PAAI 피드백 저장 오류: {e}")
|
||||||
return jsonify({'success': False, 'error': str(e)}), 500
|
return jsonify({'success': False, 'error': str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
# ────────────────────────────────────────────────────────────────────────────────
|
||||||
|
# PAAI 어드민 페이지
|
||||||
|
# ────────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@pmr_bp.route('/admin')
|
||||||
|
def paai_admin_page():
|
||||||
|
"""PAAI 어드민 대시보드 페이지"""
|
||||||
|
return render_template('pmr_admin.html')
|
||||||
|
|
||||||
|
|
||||||
|
@pmr_bp.route('/api/admin/stats')
|
||||||
|
def paai_admin_stats():
|
||||||
|
"""PAAI 통계 API"""
|
||||||
|
from db.paai_logger import get_stats
|
||||||
|
|
||||||
|
try:
|
||||||
|
stats = get_stats()
|
||||||
|
return jsonify({'success': True, 'stats': stats})
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"PAAI 통계 조회 오류: {e}")
|
||||||
|
return jsonify({'success': False, 'error': str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@pmr_bp.route('/api/admin/logs')
|
||||||
|
def paai_admin_logs():
|
||||||
|
"""PAAI 로그 목록 API"""
|
||||||
|
from db.paai_logger import get_recent_logs
|
||||||
|
|
||||||
|
try:
|
||||||
|
limit = request.args.get('limit', 50, type=int)
|
||||||
|
status = request.args.get('status')
|
||||||
|
has_severe = request.args.get('has_severe')
|
||||||
|
date = request.args.get('date')
|
||||||
|
patient_name = request.args.get('patient_name')
|
||||||
|
|
||||||
|
# has_severe 파싱
|
||||||
|
if has_severe == 'true':
|
||||||
|
has_severe = True
|
||||||
|
elif has_severe == 'false':
|
||||||
|
has_severe = False
|
||||||
|
else:
|
||||||
|
has_severe = None
|
||||||
|
|
||||||
|
logs = get_recent_logs(
|
||||||
|
limit=limit,
|
||||||
|
status=status,
|
||||||
|
has_severe=has_severe,
|
||||||
|
date=date
|
||||||
|
)
|
||||||
|
|
||||||
|
# 환자명 필터링 (클라이언트 사이드에서 하기엔 데이터가 많을 수 있음)
|
||||||
|
if patient_name:
|
||||||
|
logs = [log for log in logs if patient_name.lower() in (log.get('patient_name') or '').lower()]
|
||||||
|
|
||||||
|
return jsonify({'success': True, 'logs': logs, 'count': len(logs)})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"PAAI 로그 조회 오류: {e}")
|
||||||
|
return jsonify({'success': False, 'error': str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@pmr_bp.route('/api/admin/log/<int:log_id>')
|
||||||
|
def paai_admin_log_detail(log_id):
|
||||||
|
"""PAAI 로그 상세 API"""
|
||||||
|
from db.paai_logger import get_log_detail
|
||||||
|
|
||||||
|
try:
|
||||||
|
log = get_log_detail(log_id)
|
||||||
|
if not log:
|
||||||
|
return jsonify({'success': False, 'error': '로그를 찾을 수 없습니다'}), 404
|
||||||
|
|
||||||
|
return jsonify({'success': True, 'log': log})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"PAAI 로그 상세 조회 오류: {e}")
|
||||||
|
return jsonify({'success': False, 'error': str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@pmr_bp.route('/api/admin/feedback-stats')
|
||||||
|
def paai_admin_feedback_stats():
|
||||||
|
"""피드백 통계 API (일별)"""
|
||||||
|
from db.paai_logger import DB_PATH
|
||||||
|
import sqlite3
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not DB_PATH.exists():
|
||||||
|
return jsonify({'success': True, 'stats': []})
|
||||||
|
|
||||||
|
conn = sqlite3.connect(str(DB_PATH))
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# 최근 30일 일별 통계
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT
|
||||||
|
DATE(created_at) as date,
|
||||||
|
COUNT(*) as total,
|
||||||
|
SUM(CASE WHEN feedback_useful = 1 THEN 1 ELSE 0 END) as useful,
|
||||||
|
SUM(CASE WHEN feedback_useful = 0 THEN 1 ELSE 0 END) as not_useful,
|
||||||
|
SUM(CASE WHEN feedback_useful IS NULL THEN 1 ELSE 0 END) as no_feedback,
|
||||||
|
SUM(CASE WHEN kims_has_severe = 1 THEN 1 ELSE 0 END) as severe,
|
||||||
|
AVG(ai_response_time_ms) as avg_ai_time
|
||||||
|
FROM paai_logs
|
||||||
|
WHERE created_at >= date('now', '-30 days')
|
||||||
|
GROUP BY DATE(created_at)
|
||||||
|
ORDER BY date DESC
|
||||||
|
""")
|
||||||
|
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
stats = []
|
||||||
|
for row in rows:
|
||||||
|
stats.append({
|
||||||
|
'date': row[0],
|
||||||
|
'total': row[1],
|
||||||
|
'useful': row[2] or 0,
|
||||||
|
'not_useful': row[3] or 0,
|
||||||
|
'no_feedback': row[4] or 0,
|
||||||
|
'severe': row[5] or 0,
|
||||||
|
'avg_ai_time': int(row[6]) if row[6] else 0
|
||||||
|
})
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
return jsonify({'success': True, 'stats': stats})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"피드백 통계 조회 오류: {e}")
|
||||||
|
return jsonify({'success': False, 'error': str(e)}), 500
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user