This commit is contained in:
hiondal 2025-06-15 21:34:07 +00:00
parent c737cd8210
commit c06d453cf4
2 changed files with 50 additions and 93 deletions

View File

@ -633,116 +633,67 @@ def _extract_store_id_from_place_url(place_url: str) -> Optional[str]:
return None return None
@app.post( @app.post(
"/action-recommendation", "/action-recommendation-simple",
response_model=ActionRecommendationResponse, response_model=ActionRecommendationSimpleResponse,
summary="액션 추천 요청", summary="간소화된 액션 추천 요청",
description="점주가 Claude AI에게 액션 추천을 요청합니다." description="JSON 추천 결과만 반환하는 최적화된 엔드포인트"
) )
async def action_recommendation( async def action_recommendation_simple(
request: ActionRecommendationRequest, request: ActionRecommendationRequest,
claude_service: ClaudeService = Depends(get_claude_service), claude_service: ClaudeService = Depends(get_claude_service),
vector_service: VectorService = Depends(get_vector_service) vector_service: VectorService = Depends(get_vector_service)
): ):
"""🧠 Claude AI 액션 추천 API""" """🧠 최적화된 Claude AI 액션 추천 API - JSON만 반환"""
try: try:
logger.info(f"액션 추천 요청: store_id={request.store_id}") logger.info(f"간소화된 액션 추천 요청: store_id={request.store_id}")
start_time = datetime.now() # 1단계: Vector DB에서 최적화된 컨텍스트 조회
context_data = None
# 1단계: Vector DB에서 컨텍스트 조회
try: try:
db_status = vector_service.get_db_status() # 개선된 검색 메소드 사용
context_data = vector_service.search_similar_cases_improved(
if db_status.get('total_documents', 0) == 0: request.store_id,
raise HTTPException( request.context
status_code=404, )
detail={
"success": False,
"error": "NO_VECTOR_DATA",
"message": "Vector DB에 데이터가 없습니다. 먼저 /build-vector API를 호출하여 데이터를 구축해주세요.",
"timestamp": datetime.now().isoformat()
}
)
# Vector DB에서 유사한 케이스 검색
context_data = vector_service.search_similar_cases(request.store_id, request.context)
except HTTPException:
raise
except Exception as e: except Exception as e:
logger.error(f"Vector DB 조회 실패: {e}") logger.warning(f"Vector DB 조회 실패 (계속 진행): {e}")
# Vector DB 조회 실패해도 일반적인 추천은 제공
context_data = None
# 2단계: Claude AI 호출 (프롬프트 구성부터 파싱까지 모두 포함) # 2단계: Claude AI 호출 - JSON만 추출
try: try:
# 컨텍스트 구성 # 컨텍스트 구성
full_context = f"가게 ID: {request.store_id}\n점주 요청: {request.context}" full_context = f"가게 ID: {request.store_id}\n점주 요청: {request.context}"
additional_context = context_data if context_data else None
# Claude AI 액션 추천 생성 (완전한 처리) # Claude AI 호출
claude_response, parsed_response = await claude_service.generate_action_recommendations( claude_response, parsed_json = await claude_service.generate_action_recommendations(
context=full_context, context=full_context,
additional_context=additional_context additional_context=context_data
) )
if not claude_response: if not parsed_json:
raise Exception("Claude AI로부터 응답을 받지 못했습니다") # JSON 파싱 실패시 재시도
logger.warning("JSON 파싱 실패 - 재시도")
parsed_json = claude_service.parse_recommendation_response(claude_response)
logger.info(f"Claude 응답 길이: {len(claude_response)} 문자") if not parsed_json:
json_parse_success = parsed_response is not None raise Exception("Claude AI 응답을 JSON으로 파싱할 수 없습니다")
return ActionRecommendationSimpleResponse(
success=True,
recommendation=parsed_json
)
except Exception as e: except Exception as e:
logger.error(f"Claude AI 호출 실패: {e}") logger.error(f"Claude AI 호출 실패: {e}")
raise HTTPException( return ActionRecommendationSimpleResponse(
status_code=500, success=False,
detail=f"AI 추천 생성 중 오류: {str(e)}" error_message=f"AI 추천 생성 중 오류: {str(e)}"
) )
# 3단계: 응답 구성
claude_execution_time = (datetime.now() - start_time).total_seconds()
# 가게 정보 추출 (Vector DB에서)
store_name = request.store_id # 기본값
food_category = "기타" # 기본값
try:
store_context = vector_service.get_store_context(request.store_id)
if store_context:
store_name = store_context.get('store_name', request.store_id)
food_category = store_context.get('food_category', '기타')
except Exception as e:
logger.warning(f"가게 정보 추출 실패: {e}")
response = ActionRecommendationResponse(
success=True,
message=f"액션 추천이 완료되었습니다. (실행시간: {claude_execution_time:.1f}초, JSON 파싱: {'성공' if json_parse_success else '실패'})",
claude_input=full_context + (f"\n--- 동종 업체 분석 데이터 ---\n{additional_context}" if additional_context else ""),
claude_response=claude_response,
parsed_response=parsed_response,
store_name=store_name,
food_category=food_category,
similar_stores_count=len(context_data.split("---")) if context_data else 0,
execution_time=claude_execution_time,
json_parse_success=json_parse_success
)
logger.info(f"✅ 액션 추천 완료: Claude 응답 {len(claude_response) if claude_response else 0} 문자, JSON 파싱 {'성공' if json_parse_success else '실패'}, {claude_execution_time:.1f}초 소요")
return response
except HTTPException:
raise
except Exception as e: except Exception as e:
logger.error(f"❌ 액션 추천 요청 실패: {str(e)}") logger.error(f"액션 추천 처리 실패: {e}")
return ActionRecommendationSimpleResponse(
raise HTTPException( success=False,
status_code=500, error_message=f"서버 내부 오류: {str(e)}"
detail={
"success": False,
"error": "RECOMMENDATION_FAILED",
"message": f"액션 추천 중 오류가 발생했습니다: {str(e)}",
"timestamp": datetime.now().isoformat()
}
) )
@app.get( @app.get(

View File

@ -57,6 +57,12 @@ class ActionRecommendationResponse(BaseModel):
execution_time: Optional[float] = Field(None, description="Claude API 응답 시간(초)") execution_time: Optional[float] = Field(None, description="Claude API 응답 시간(초)")
json_parse_success: Optional[bool] = Field(None, description="JSON 파싱 성공 여부") # 새로 추가 json_parse_success: Optional[bool] = Field(None, description="JSON 파싱 성공 여부") # 새로 추가
class ActionRecommendationSimpleResponse(BaseModel):
"""단순화된 액션 추천 응답 - JSON만 반환"""
success: bool = Field(description="추천 성공 여부")
recommendation: Optional[Dict[str, Any]] = Field(None, description="추천 결과 JSON")
error_message: Optional[str] = Field(None, description="오류 메시지 (실패시에만)")
class VectorStoreDocument(BaseModel): class VectorStoreDocument(BaseModel):
"""Vector Store에 저장될 문서 구조""" """Vector Store에 저장될 문서 구조"""
store_id: str = Field(description="가게 ID") store_id: str = Field(description="가게 ID")