feat: implement ERP AI Assistant Phase 1
Backend (FastAPI + SQLAlchemy + Claude API + RAG): - Config management with Pydantic v2 - Database engine with connection pooling and SQL injection prevention - AI engine with Claude API integration (support custom base URL) - RAG engine with ChromaDB and sentence-transformers - Requirement analysis service - Config generation service - Executor engine with SQL validation - REST API endpoints: /analyze, /generate, /execute Frontend (Vue 3 + Element Plus + Pinia): - Complete 3-step workflow: analyze → generate → execute - Step indicator with progress visualization - Analysis result display with field table - SQL preview with monospace font - Execute confirmation dialog with safety warning - Execution result display - State management with Pinia - API service integration Security: - SQL injection prevention with parameterized queries - Dangerous SQL operation blocking - Database password URL encoding - Transaction auto-rollback - Pydantic config validation Features: - Natural language requirement analysis - Automated SQL configuration generation - Safe execution with human review - LAN access support - Custom Claude API endpoint support Documentation: - README with quick start guide - Quick start guide - LAN access configuration - Dependency fixes guide - Claude API configuration - Git operation guide - Implementation report Dependencies fixed: - numpy<2.0.0 for chromadb compatibility - sentence-transformers==2.7.0 for huggingface_hub compatibility Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1
backend/app/services/__init__.py
Normal file
1
backend/app/services/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Service modules for ERP AI Assistant."""
|
||||
135
backend/app/services/config_service.py
Normal file
135
backend/app/services/config_service.py
Normal file
@@ -0,0 +1,135 @@
|
||||
"""Config Generation Service.
|
||||
|
||||
This module provides the ConfigService class for generating ERP platform
|
||||
configuration SQL based on structured requirements.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any
|
||||
|
||||
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, GENERATE_PROMPT_TEMPLATE
|
||||
from app.core.db_engine import DatabaseEngine
|
||||
|
||||
|
||||
class ConfigService:
|
||||
"""Service for generating ERP platform configuration.
|
||||
|
||||
This service uses Claude AI with RAG knowledge retrieval to generate
|
||||
SQL configuration statements based on structured requirements.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize config service with required engines."""
|
||||
self.ai_engine = ClaudeEngine()
|
||||
self.rag_engine = RAGEngine()
|
||||
self.db_engine = DatabaseEngine()
|
||||
logger.info("ConfigService initialized")
|
||||
|
||||
async def generate(
|
||||
self,
|
||||
requirements: Dict[str, Any],
|
||||
session_id: str
|
||||
) -> Dict[str, Any]:
|
||||
"""Generate configuration SQL based on requirements.
|
||||
|
||||
Args:
|
||||
requirements: Structured requirement specification
|
||||
session_id: Session ID for tracking
|
||||
|
||||
Returns:
|
||||
Configuration plan with SQL statements
|
||||
|
||||
Raises:
|
||||
ValueError: If requirements are invalid
|
||||
Exception: If generation fails
|
||||
"""
|
||||
if not requirements:
|
||||
raise ValueError("Requirements cannot be empty")
|
||||
|
||||
function_name = requirements.get("功能名称", "Unknown")
|
||||
logger.info(f"[{session_id}] Starting config generation for: {function_name}")
|
||||
|
||||
try:
|
||||
# Step 1: Retrieve platform rules for form type
|
||||
form_type = requirements.get("窗体类型", "0")
|
||||
logger.debug(f"[{session_id}] Retrieving platform rules for form type: {form_type}")
|
||||
platform_rules = self._get_platform_rules(form_type)
|
||||
logger.info(f"[{session_id}] Retrieved platform rules")
|
||||
|
||||
# Step 2: Retrieve similar cases
|
||||
logger.debug(f"[{session_id}] Retrieving similar cases")
|
||||
similar_cases = self._get_similar_cases(function_name)
|
||||
logger.info(f"[{session_id}] Retrieved similar cases")
|
||||
|
||||
# Step 3: Build prompt
|
||||
prompt = GENERATE_PROMPT_TEMPLATE.format(
|
||||
requirements=str(requirements),
|
||||
platform_rules=platform_rules,
|
||||
similar_cases=similar_cases
|
||||
)
|
||||
|
||||
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 for config generation")
|
||||
response = await self.ai_engine.call_claude(messages, temperature=0.5)
|
||||
|
||||
# Step 5: Parse JSON response
|
||||
result = self.ai_engine.parse_json_response(response)
|
||||
|
||||
logger.success(f"[{session_id}] Config generation completed")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[{session_id}] Config generation failed: {e}")
|
||||
raise
|
||||
|
||||
def _get_platform_rules(self, form_type: str) -> str:
|
||||
"""Retrieve platform configuration rules for specific form type.
|
||||
|
||||
Args:
|
||||
form_type: Form type code
|
||||
|
||||
Returns:
|
||||
Platform rules text
|
||||
"""
|
||||
try:
|
||||
results = self.rag_engine.search(
|
||||
f"窗体类型{form_type}配置规则",
|
||||
top_k=2
|
||||
)
|
||||
if not results:
|
||||
return "未找到相关配置规则"
|
||||
|
||||
return "\n\n".join([r["content"] for r in results])
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to retrieve platform rules: {e}")
|
||||
return "无法获取平台配置规则"
|
||||
|
||||
def _get_similar_cases(self, keywords: str) -> str:
|
||||
"""Retrieve similar configuration cases from knowledge base.
|
||||
|
||||
Args:
|
||||
keywords: Search keywords
|
||||
|
||||
Returns:
|
||||
Similar cases text
|
||||
"""
|
||||
try:
|
||||
results = self.rag_engine.search(keywords, top_k=2)
|
||||
if not results:
|
||||
return "未找到相似案例"
|
||||
|
||||
return "\n\n".join([r["content"] for r in results])
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to retrieve similar cases: {e}")
|
||||
return "无法获取相似案例"
|
||||
147
backend/app/services/requirement_service.py
Normal file
147
backend/app/services/requirement_service.py
Normal file
@@ -0,0 +1,147 @@
|
||||
"""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 "无法获取现有表信息"
|
||||
Reference in New Issue
Block a user