""" AI Steward Service - 主动式 AI 管家核心服务 核心功能: 1. 零配置项目创建 - 从一句话生成完整 SSTC 链路 2. 主动信号捕获 - 从多个源自动抓取并评估 3. 认知冲突检测 - 自动识别并消解团队认知差异 4. 阻塞自动解决 - 主动识别和解决开发阻塞 5. 个性化进度简报 - 为每个角色生成定制化简报 """ import asyncio import json from datetime import datetime, timedelta, timezone from typing import Any, Dict, List, Optional from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select from sqlalchemy import text from ..database import get_db from ..models import ( User, Project, Signal, TruthNode, Task, ProjectDomain, CognitiveConflict, UserSkillBinding ) from .llm import LLMService class AIStewardService: """主动式 AI 管家服务""" def __init__(self, db: AsyncSession): self.db = db self.llm = LLMService() async def create_project_from_natural_language( self, input_text: str, creator_id: int, tenant_id: int = 1 ) -> Dict[str, Any]: """ 零配置项目创建 - 从一句话生成完整 SSTC 链路 输入示例: "我们要做一个 AI 客服工具,基于 GPT-4o mini,对标 XX 竞品,3 周上线" 输出:完整的 SSTC 链路 - 竞品信号分析 - 宪章(TruthNode) - 任务拆解(Task) - 依赖关系 - 风险预警 """ # Step 0: 获取下一个可用的项目 ID(SQLite 兼容) from sqlalchemy import text next_id = (await self.db.execute(text("SELECT COALESCE(MAX(id),0)+1 FROM projects"))).scalar() or 1 # Step 1: AI 解析用户输入,提取关键信息 analysis = await self._analyze_project_input(input_text, tenant_id) # Step 2: 自动生成项目 project = Project( id=next_id, name=analysis.get('project_name', '未命名项目')[:50], title=analysis.get('project_title', input_text[:50]), description=input_text, owner_id=creator_id, status='active', domain_id=analysis.get('domain_id'), assigned_role=analysis.get('primary_role', 'backend'), signal_count=0, tenant_id=tenant_id ) self.db.add(project) await self.db.flush() # Step 3: 生成信号(竞品分析) signal = await self._generate_competitive_signal( project.id, analysis, tenant_id ) # Step 4: 生成宪章(核心真理节点) charter = await self._generate_charter( project.id, analysis, creator_id, tenant_id ) charter_id = int(charter['id']) # Convert string ID to int # Step 5: 生成任务链 tasks = await self._generate_task_chain( project.id, charter_id, analysis, tenant_id ) # Step 6: 生成风险预警规则 risks = await self._generate_risk_alert_rules( project.id, charter_id, analysis, tenant_id ) await self.db.commit() return { 'project': { 'id': str(project.id), 'name': project.name, 'title': project.title, 'description': project.description, 'status': project.status }, 'signal': signal, 'charter': charter, 'tasks': tasks, 'risks': risks, 'analysis': analysis } async def _analyze_project_input(self, input_text: str, tenant_id: int) -> Dict[str, Any]: """使用 AI 分析项目输入,提取关键信息""" prompt = f"""分析以下项目需求,提取关键信息并以 JSON 格式返回: 用户输入:{input_text} 请提取以下信息: {{ "project_name": "项目名称(简短)", "project_title": "项目标题", "tech_stack": ["技术栈列表"], "target_audience": "目标用户", "timeline": "时间周期", "primary_role": "主要角色类型(ai_engineer/backend/frontend)", "domain_id": 1, "key_features": ["核心功能列表"], "competitors": ["竞品列表"], "success_metrics": ["成功指标"] }} 只返回 JSON,不要其他内容。""" response = await self.llm.generate( prompt, db=self.db, tenant_id=tenant_id, ) try: # 清理可能的 markdown 代码块标记 response = response.strip() if response.startswith('```'): response = response.split('```')[1] if response.startswith('json'): response = response[4:] analysis = json.loads(response.strip()) except: # 降级处理 analysis = { 'project_name': input_text[:20], 'project_title': input_text[:50], 'tech_stack': ['GPT-4o-mini'], 'target_audience': '通用', 'timeline': '3周', 'primary_role': 'ai_engineer', 'domain_id': 1, 'key_features': [], 'competitors': [], 'success_metrics': [] } return analysis async def _generate_competitive_signal( self, project_id: int, analysis: Dict[str, Any], tenant_id: int ) -> Dict[str, Any]: """生成竞品分析信号""" from sqlalchemy import text # 获取下一个可用的 signal ID next_signal_id = (await self.db.execute(text("SELECT COALESCE(MAX(id),0)+1 FROM signals"))).scalar() or 1 competitors = analysis.get('competitors', []) if not competitors: competitors = ['行业同类产品'] prompt = f"""基于以下项目信息,生成竞品分析信号: 项目:{analysis.get('project_title')} 技术栈:{', '.join(analysis.get('tech_stack', []))} 竞品:{', '.join(competitors)} 请生成: 1. 竞品现状分析 2. 差异化机会 3. 技术风险评估 以 JSON 格式返回: {{ "content": "信号内容(200字以内)", "impact_score": 0.9, "priority": "high", "strategic_analysis": {{"is_strategic": true, "threat_level": "MEDIUM"}} }}""" response = await self.llm.generate( prompt, db=self.db, tenant_id=tenant_id, project_id=project_id, ) try: response = response.strip() if response.startswith('```'): response = response.split('```')[1] if response.startswith('json'): response = response[4:] signal_data = json.loads(response.strip()) except: signal_data = { 'content': f'竞品分析:{", ".join(competitors)} 的技术现状和差异化机会', 'impact_score': 0.8, 'priority': 'high', 'strategic_analysis': {'is_strategic': True} } signal = Signal( id=next_signal_id, source='AI-Steward', content=signal_data['content'], impact_score=signal_data.get('impact_score', 0.8), priority=signal_data.get('priority', 'high'), processed=True, strategic_analysis=json.dumps(signal_data.get('strategic_analysis', {})), tasks_generated=json.dumps([]), tenant_id=tenant_id ) self.db.add(signal) await self.db.flush() # 创建信号与项目的关联 from ..models import ProjectSignalLink next_link_id = (await self.db.execute(text("SELECT COALESCE(MAX(id),0)+1 FROM project_signal_links"))).scalar() or 1 link = ProjectSignalLink( id=next_link_id, project_id=project_id, signal_id=signal.id, link_type='competitive_analysis', relevance_score=0.9, tenant_id=tenant_id ) self.db.add(link) await self.db.flush() return { 'id': str(signal.id), 'content': signal.content, 'impact_score': signal.impact_score } async def _generate_charter( self, project_id: int, analysis: Dict[str, Any], creator_id: int, tenant_id: int ) -> Dict[str, Any]: """生成项目宪章(核心真理节点)""" prompt = f"""基于以下项目信息,生成项目宪章: 项目:{analysis.get('project_title')} 描述:{analysis.get('project_title')} 技术栈:{', '.join(analysis.get('tech_stack', []))} 核心功能:{', '.join(analysis.get('key_features', []))} 成功指标:{', '.join(analysis.get('success_metrics', []))} 请生成包含以下内容的宪章: 1. 项目目标(明确、可衡量) 2. 成功标准(具体指标) 3. 技术约束 4. 时间节点 以 JSON 格式返回: {{ "title": "宪章标题", "content": "宪章完整内容(包含上述4个方面)", "success_criteria": ["标准1", "标准2"], "constraints": ["约束1", "约束2"] }}""" response = await self.llm.generate( prompt, db=self.db, tenant_id=tenant_id, project_id=project_id, ) try: response = response.strip() if response.startswith('```'): response = response.split('```')[1] if response.startswith('json'): response = response[4:] charter_data = json.loads(response.strip()) except: charter_data = { 'title': f'{analysis.get("project_name")}项目宪章', 'content': f'项目目标:{analysis.get("project_title")}\n成功标准:按时上线', 'success_criteria': ['按时交付', '功能完整'], 'constraints': ['时间限制'] } raw_content = charter_data.get('content', '') if isinstance(raw_content, dict): charter_content = dict(raw_content) else: charter_content = { 'rationale': str(raw_content), 'success_criteria': charter_data.get('success_criteria', []), 'constraints': charter_data.get('constraints', []), 'signal_ids': [], } # 获取下一个可用的 truth_node ID from sqlalchemy import text next_charter_id = (await self.db.execute(text("SELECT COALESCE(MAX(id),0)+1 FROM truth_nodes"))).scalar() or 1 charter = TruthNode( id=next_charter_id, node_type='charter', project_id=project_id, created_by=creator_id, title=charter_data.get('title', '项目宪章'), content=charter_content, version=1, status='active', tenant_id=tenant_id ) self.db.add(charter) await self.db.flush() return { 'id': str(charter.id), 'title': charter.title, 'content': charter_content } async def _generate_task_chain( self, project_id: int, charter_id: int, analysis: Dict[str, Any], tenant_id: int ) -> List[Dict[str, Any]]: """生成任务链""" prompt = f"""基于以下信息,生成完整的任务链: 项目:{analysis.get('project_title')} 技术栈:{', '.join(analysis.get('tech_stack', []))} 时间周期:{analysis.get('timeline', '3周')} 请生成: 1. 任务列表(按优先级排序) 2. 每个任务包含:名称、类型、预估工时、依赖关系、技能要求 以 JSON 格式返回: {{ "tasks": [ {{ "title": "任务名称", "type": "backend/frontend/ai_engineer", "estimated_hours": 8, "description": "任务描述", "priority": "high/medium/low", "required_skills": ["技能1", "技能2"] }} ] }}""" response = await self.llm.generate( prompt, db=self.db, tenant_id=tenant_id, project_id=project_id, ) try: response = response.strip() if response.startswith('```'): response = response.split('```')[1] if response.startswith('json'): response = response[4:] tasks_data = json.loads(response.strip()) tasks_list = tasks_data.get('tasks', []) except: # 降级生成基础任务 tasks_list = [ { 'title': '技术方案设计', 'type': analysis.get('primary_role', 'backend'), 'estimated_hours': 16, 'description': '完成项目技术方案设计', 'priority': 'high', 'required_skills': ['系统设计'] }, { 'title': '核心功能开发', 'type': analysis.get('primary_role', 'backend'), 'estimated_hours': 40, 'description': '完成核心功能开发', 'priority': 'high', 'required_skills': ['开发'] } ] tasks = [] from sqlalchemy import text next_task_id = (await self.db.execute(text("SELECT COALESCE(MAX(id),0)+1 FROM tasks"))).scalar() or 1 for i, task_data in enumerate(tasks_list): task = Task( id=next_task_id, truth_node_id=charter_id, project_id=project_id, assignee_id=None, # AI 推荐稍后分配 title=task_data.get('title'), description=task_data.get('description', ''), required_skill_tag_ids=task_data.get('required_skills', []), # TypeDecorator 会自动处理 status='pending', priority=task_data.get('priority', 'medium').upper(), estimated_hours=task_data.get('estimated_hours', 8), actual_hours=0, quality_score=0, tenant_id=tenant_id ) self.db.add(task) await self.db.flush() next_task_id += 1 tasks.append({ 'id': str(task.id), 'title': task.title, 'type': task_data.get('type'), 'estimated_hours': task.estimated_hours, 'priority': task.priority }) return tasks async def _generate_risk_alert_rules( self, project_id: int, charter_id: int, analysis: Dict[str, Any], tenant_id: int ) -> List[Dict[str, Any]]: """生成风险预警规则""" risks = [ { 'type': 'schedule_delay', 'description': '任务超时预警', 'threshold_hours': analysis.get('estimated_hours', 40) * 1.2, 'severity': 'medium' }, { 'type': 'quality_drop', 'description': '质量下降预警', 'threshold': 0.85, 'severity': 'high' }, { 'type': 'skill_mismatch', 'description': '技能匹配度不足', 'threshold': 0.6, 'severity': 'medium' } ] # 获取下一个可用的 cognitive_conflict ID from sqlalchemy import text next_conflict_id = (await self.db.execute(text("SELECT COALESCE(MAX(id),0)+1 FROM cognitive_conflicts"))).scalar() or 1 # 创建认知冲突记录作为风险预警 for risk in risks: conflict = CognitiveConflict( id=next_conflict_id, truth_node_id=charter_id, # 关联到宪章 conflict_type=risk['type'], description=risk['description'], involved_user_ids=json.dumps([]), status='active', tenant_id=tenant_id ) self.db.add(conflict) next_conflict_id += 1 return risks # 全局服务实例 def get_ai_steward_service(db: AsyncSession) -> AIStewardService: """获取 AI Steward 服务实例""" return AIStewardService(db)