diff --git a/vector/app/main.py b/vector/app/main.py index 43267e0..2562feb 100644 --- a/vector/app/main.py +++ b/vector/app/main.py @@ -633,118 +633,69 @@ def _extract_store_id_from_place_url(place_url: str) -> Optional[str]: return None @app.post( - "/action-recommendation", - response_model=ActionRecommendationResponse, - summary="액션 추천 요청", - description="점주가 Claude AI에게 액션 추천을 요청합니다." + "/action-recommendation-simple", + response_model=ActionRecommendationSimpleResponse, + summary="간소화된 액션 추천 요청", + description="JSON 추천 결과만 반환하는 최적화된 엔드포인트" ) -async def action_recommendation( +async def action_recommendation_simple( request: ActionRecommendationRequest, claude_service: ClaudeService = Depends(get_claude_service), vector_service: VectorService = Depends(get_vector_service) ): - """🧠 Claude AI 액션 추천 API""" + """🧠 최적화된 Claude AI 액션 추천 API - JSON만 반환""" try: - logger.info(f"액션 추천 요청: store_id={request.store_id}") - - start_time = datetime.now() - - # 1단계: Vector DB에서 컨텍스트 조회 + logger.info(f"간소화된 액션 추천 요청: store_id={request.store_id}") + + # 1단계: Vector DB에서 최적화된 컨텍스트 조회 + context_data = None try: - db_status = vector_service.get_db_status() - - if db_status.get('total_documents', 0) == 0: - raise HTTPException( - 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 + # 개선된 검색 메소드 사용 + context_data = vector_service.search_similar_cases_improved( + request.store_id, + request.context + ) except Exception as e: - logger.error(f"Vector DB 조회 실패: {e}") - # Vector DB 조회 실패해도 일반적인 추천은 제공 - context_data = None - - # 2단계: Claude AI 호출 (프롬프트 구성부터 파싱까지 모두 포함) + logger.warning(f"Vector DB 조회 실패 (계속 진행): {e}") + + # 2단계: Claude AI 호출 - JSON만 추출 try: # 컨텍스트 구성 full_context = f"가게 ID: {request.store_id}\n점주 요청: {request.context}" - additional_context = context_data if context_data else None - - # Claude AI 액션 추천 생성 (완전한 처리) - claude_response, parsed_response = await claude_service.generate_action_recommendations( + + # Claude AI 호출 + claude_response, parsed_json = await claude_service.generate_action_recommendations( context=full_context, - additional_context=additional_context + additional_context=context_data ) - - if not claude_response: - raise Exception("Claude AI로부터 응답을 받지 못했습니다") - - logger.info(f"Claude 응답 길이: {len(claude_response)} 문자") - json_parse_success = parsed_response is not None - + + if not parsed_json: + # JSON 파싱 실패시 재시도 + logger.warning("JSON 파싱 실패 - 재시도") + parsed_json = claude_service.parse_recommendation_response(claude_response) + + if not parsed_json: + raise Exception("Claude AI 응답을 JSON으로 파싱할 수 없습니다") + + return ActionRecommendationSimpleResponse( + success=True, + recommendation=parsed_json + ) + except Exception as e: logger.error(f"Claude AI 호출 실패: {e}") - raise HTTPException( - status_code=500, - detail=f"AI 추천 생성 중 오류: {str(e)}" + return ActionRecommendationSimpleResponse( + success=False, + 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: - logger.error(f"❌ 액션 추천 요청 실패: {str(e)}") - - raise HTTPException( - status_code=500, - detail={ - "success": False, - "error": "RECOMMENDATION_FAILED", - "message": f"액션 추천 중 오류가 발생했습니다: {str(e)}", - "timestamp": datetime.now().isoformat() - } + logger.error(f"액션 추천 처리 실패: {e}") + return ActionRecommendationSimpleResponse( + success=False, + error_message=f"서버 내부 오류: {str(e)}" ) - + @app.get( "/vector-status", response_model=VectorDBStatusResponse, diff --git a/vector/app/models/vector_models.py b/vector/app/models/vector_models.py index 1cf028b..e74a6da 100644 --- a/vector/app/models/vector_models.py +++ b/vector/app/models/vector_models.py @@ -57,6 +57,12 @@ class ActionRecommendationResponse(BaseModel): execution_time: Optional[float] = Field(None, description="Claude API 응답 시간(초)") 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): """Vector Store에 저장될 문서 구조""" store_id: str = Field(description="가게 ID")