"""Requirement Analysis Service. This module provides the RequirementService class for analyzing user requirements using Claude AI with RAG knowledge retrieval. """ from typing import Optional, Dict, Any import uuid from loguru import logger from app.core.ai_engine import ClaudeEngine from app.core.rag_engine import RAGEngine from app.core.prompts import SYSTEM_PROMPT, ANALYZE_PROMPT_TEMPLATE from app.core.db_engine import DatabaseEngine class RequirementService: """Service for analyzing user requirements with AI assistance. This service integrates Claude AI, RAG knowledge retrieval, and database metadata to provide comprehensive requirement analysis. """ def __init__(self) -> None: """Initialize requirement service with required engines.""" self.ai_engine = ClaudeEngine() self.rag_engine = RAGEngine() self.db_engine = DatabaseEngine() logger.info("RequirementService initialized") async def analyze( self, user_input: str, session_id: Optional[str] = None ) -> Dict[str, Any]: """Analyze user requirement and generate structured specification. Args: user_input: Natural language requirement from user session_id: Session ID for context management (auto-generated if None) Returns: Structured requirement document as dictionary Raises: ValueError: If user_input is empty Exception: If AI analysis fails """ # Validate input if not user_input or not user_input.strip(): raise ValueError("User input cannot be empty") # Generate session ID if not provided session_id = session_id or str(uuid.uuid4()) logger.info(f"[{session_id}] Starting requirement analysis: {user_input[:50]}...") try: # Step 1: Retrieve relevant knowledge from RAG logger.debug(f"[{session_id}] Searching knowledge base") knowledge_results = self.rag_engine.search(user_input, top_k=3) knowledge_context = self._format_knowledge_context(knowledge_results) logger.info(f"[{session_id}] Retrieved {len(knowledge_results)} knowledge chunks") # Step 2: Query existing database tables logger.debug(f"[{session_id}] Querying existing tables") existing_tables = self._get_existing_tables(user_input) logger.info(f"[{session_id}] Retrieved existing table information") # Step 3: Build prompt prompt = ANALYZE_PROMPT_TEMPLATE.format( user_input=user_input, knowledge_context=knowledge_context, existing_tables=existing_tables ) messages = [ {"role": "user", "content": SYSTEM_PROMPT}, {"role": "assistant", "content": "我已了解平台配置规范,请告诉我您的需求。"}, {"role": "user", "content": prompt} ] # Step 4: Call Claude API logger.debug(f"[{session_id}] Calling Claude API") response = await self.ai_engine.call_claude(messages, temperature=0.7) # Step 5: Parse JSON response result = self.ai_engine.parse_json_response(response) function_name = result.get("功能名称", "Unknown") logger.success(f"[{session_id}] Requirement analysis completed: {function_name}") return result except Exception as e: logger.error(f"[{session_id}] Requirement analysis failed: {e}") raise def _format_knowledge_context(self, knowledge_results: list) -> str: """Format knowledge search results into context string. Args: knowledge_results: List of knowledge search results Returns: Formatted knowledge context string """ if not knowledge_results: return "未找到相关知识库内容" context_parts = [] for result in knowledge_results: source = result.get("metadata", {}).get("source", "文档") content = result.get("content", "") if content: context_parts.append(f"【{source}】\n{content}") return "\n\n".join(context_parts) if context_parts else "未找到相关知识库内容" def _get_existing_tables(self, user_input: str) -> str: """Query existing database tables relevant to user input. Args: user_input: User requirement text Returns: Formatted string listing existing tables """ try: # Query top 10 tables (simplified version - could be enhanced with relevance matching) sql = """ SELECT TOP 10 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME """ tables = self.db_engine.execute_sql(sql) if not tables: return "未找到现有数据表" table_list = [f"- {t[0]}" for t in tables] return "\n".join(table_list) except Exception as e: logger.warning(f"Failed to query existing tables: {e}") return "无法获取现有表信息"