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:
|
||||
"""Clawdbot AI 호출 (HTTP API)"""
|
||||
import requests as http_requests
|
||||
"""Clawdbot AI 호출 (WebSocket Gateway)"""
|
||||
import json
|
||||
import re
|
||||
from services.clawdbot_client import ask_clawdbot
|
||||
|
||||
PAAI_SYSTEM_PROMPT = """당신은 경험 많은 약사입니다.
|
||||
처방 데이터를 분석하여 약사에게 유용한 정보를 제공합니다.
|
||||
반드시 요청된 JSON 형식으로만 응답하세요."""
|
||||
|
||||
try:
|
||||
# Clawdbot Gateway API 호출
|
||||
response = http_requests.post(
|
||||
'http://localhost:8765/api/chat',
|
||||
json={
|
||||
'message': prompt,
|
||||
'session': 'paai-analysis',
|
||||
'timeout': 60
|
||||
},
|
||||
timeout=65
|
||||
# Clawdbot Gateway WebSocket API 호출
|
||||
ai_text = ask_clawdbot(
|
||||
message=prompt,
|
||||
session_id='paai-analysis',
|
||||
system_prompt=PAAI_SYSTEM_PROMPT,
|
||||
timeout=60,
|
||||
model='anthropic/claude-sonnet-4-5' # 빠른 Sonnet 사용
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
# AI 응답에서 JSON 파싱 시도
|
||||
ai_text = result.get('response', '')
|
||||
if not ai_text:
|
||||
logging.warning("[PAAI] Clawdbot 응답 없음")
|
||||
return generate_fallback_response(prompt)
|
||||
|
||||
# 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 블록 추출
|
||||
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[:200] if ai_text else '분석 결과 없음',
|
||||
'kims_analysis': '',
|
||||
'cautions': [],
|
||||
'otc_recommendations': [],
|
||||
'counseling_points': []
|
||||
}
|
||||
else:
|
||||
raise Exception(f"Clawdbot API 오류: {response.status_code}")
|
||||
# JSON 파싱 실패 시 텍스트 그대로 반환
|
||||
return {
|
||||
'prescription_insight': ai_text[:500] if ai_text else '분석 결과 없음',
|
||||
'kims_analysis': '',
|
||||
'cautions': [],
|
||||
'otc_recommendations': [],
|
||||
'counseling_points': []
|
||||
}
|
||||
|
||||
except http_requests.exceptions.ConnectionError:
|
||||
# Clawdbot 연결 안됨 - 기본 응답 생성
|
||||
return generate_fallback_response(prompt)
|
||||
except Exception as e:
|
||||
logging.error(f"Clawdbot AI 호출 오류: {e}")
|
||||
logging.error(f"[PAAI] Clawdbot AI 호출 오류: {e}")
|
||||
return generate_fallback_response(prompt)
|
||||
|
||||
|
||||
@ -1193,3 +1189,132 @@ def paai_feedback():
|
||||
except Exception as e:
|
||||
logging.error(f"PAAI 피드백 저장 오류: {e}")
|
||||
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