from sqlalchemy import or_, select from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession from app.models.skill import Skill from app.models.user import User from app.services.auth_service import get_password_hash BUILTIN_SKILLS = [ { 'name': '今日重点拆解', 'description': '帮助日程规划师从上下文中提炼今天最值得推进的事项。', 'instructions': '优先识别今天最关键的 1-3 个重点,说明原因,并给出可执行顺序。', 'agent_type': 'schedule_planner', 'tools': ['calendar', 'tasks'], 'visibility': 'market', }, { 'name': '周计划编排', 'description': '把本周目标整理成可落地的节奏与时间块。', 'instructions': '将目标拆成周内节奏安排,明确先后顺序、时间块与缓冲。', 'agent_type': 'schedule_planner', 'tools': ['calendar'], 'visibility': 'market', }, { 'name': '时间冲突分析', 'description': '识别任务、日程与优先级之间的冲突。', 'instructions': '分析冲突来源、影响和推荐取舍,必要时给出替代方案。', 'agent_type': 'schedule_planner', 'tools': ['calendar', 'tasks'], 'visibility': 'market', }, { 'name': '任务执行 SOP', 'description': '为执行角色提供标准执行步骤和结果回报格式。', 'instructions': '执行前先确认目标与边界,执行中记录关键动作,执行后输出结果、风险与下一步。', 'agent_type': 'executor', 'tools': ['shell', 'api_calls'], 'visibility': 'market', }, { 'name': '外部交互推进', 'description': '支持论坛、外部接口或内容发布类动作。', 'instructions': '围绕外部交互任务,优先保证动作完整、结果清晰、反馈及时。', 'agent_type': 'executor', 'tools': ['api_calls', 'git'], 'visibility': 'market', }, { 'name': '知识检索摘要', 'description': '从知识中枢中提炼与当前问题最相关的信息。', 'instructions': '检索后只保留当前决策需要的内容,输出摘要、来源与缺口。', 'agent_type': 'librarian', 'tools': ['web_search', 'database'], 'visibility': 'market', }, { 'name': '图谱沉淀策略', 'description': '帮助知识管理员把零散信息沉淀为结构化关系。', 'instructions': '识别应沉淀的实体、关系与后续可检索维度。', 'agent_type': 'librarian', 'tools': ['database'], 'visibility': 'market', }, { 'name': '风险识别模板', 'description': '帮助分析师快速识别当前推进中的风险点。', 'instructions': '从进度、依赖、资源与外部信号中提炼风险,并按严重度排序。', 'agent_type': 'analyst', 'tools': ['database', 'api_calls'], 'visibility': 'market', }, { 'name': '趋势洞察模板', 'description': '把多源状态汇总为趋势与判断。', 'instructions': '对比近期变化,输出趋势、证据、判断与建议动作。', 'agent_type': 'analyst', 'tools': ['database', 'code_execution'], 'visibility': 'market', }, ] def _is_bootstrap_enabled(settings) -> bool: return bool(settings.ADMIN.strip() and settings.ADMIN_EMAIL.strip() and settings.ADMIN_PASSWORD.strip()) async def ensure_admin_user(db: AsyncSession, settings) -> None: if not _is_bootstrap_enabled(settings): return result = await db.execute( select(User).where( or_(User.username == settings.ADMIN.strip(), User.email == settings.ADMIN_EMAIL.strip()) ) ) existing_user = result.scalar_one_or_none() if existing_user: if ( existing_user.username == settings.ADMIN.strip() and existing_user.email == settings.ADMIN_EMAIL.strip() and existing_user.is_superuser ): return raise RuntimeError('admin bootstrap identity conflict') admin_user = User( username=settings.ADMIN.strip(), email=settings.ADMIN_EMAIL.strip(), hashed_password=get_password_hash(settings.ADMIN_PASSWORD), full_name=settings.ADMIN_FULL_NAME or None, is_active=True, is_superuser=True, ) db.add(admin_user) try: await db.commit() except IntegrityError: await db.rollback() result = await db.execute( select(User).where( or_(User.username == settings.ADMIN.strip(), User.email == settings.ADMIN_EMAIL.strip()) ) ) existing_user = result.scalar_one_or_none() if ( existing_user and existing_user.username == settings.ADMIN.strip() and existing_user.email == settings.ADMIN_EMAIL.strip() and existing_user.is_superuser ): return raise await db.refresh(admin_user) async def ensure_builtin_skills(db: AsyncSession, preferred_owner_id: str | None = None) -> None: owner = None if preferred_owner_id: owner_result = await db.execute( select(User).where(User.id == preferred_owner_id, User.is_active == True) ) owner = owner_result.scalar_one_or_none() if not owner: owner_result = await db.execute( select(User).where(User.is_active == True).order_by(User.is_superuser.desc(), User.created_at.asc()) ) owner = owner_result.scalars().first() if not owner: return existing_result = await db.execute(select(Skill.name)) existing_names = set(existing_result.scalars().all()) missing_skills = [ Skill( owner_id=owner.id, name=item['name'], description=item['description'], instructions=item['instructions'], agent_type=item['agent_type'], tools=item['tools'], required_context=[], output_format=None, visibility=item['visibility'], is_builtin=True, team_id=None, is_active=True, ) for item in BUILTIN_SKILLS if item['name'] not in existing_names ] if not missing_skills: return db.add_all(missing_skills) await db.commit()