diff --git a/lib/crewai/src/crewai/agent/core.py b/lib/crewai/src/crewai/agent/core.py index 0e246daf0..2355a586f 100644 --- a/lib/crewai/src/crewai/agent/core.py +++ b/lib/crewai/src/crewai/agent/core.py @@ -325,19 +325,29 @@ class Agent(BaseAgent): except (TypeError, ValueError) as e: raise ValueError(f"Invalid Knowledge Configuration: {e!s}") from e - def set_skills(self) -> None: + def set_skills( + self, + resolved_crew_skills: list[SkillModel] | None = None, + ) -> None: """Resolve skill paths and activate skills to INSTRUCTIONS level. Path entries trigger discovery and activation. Pre-loaded Skill objects below INSTRUCTIONS level are activated. Crew-level skills are merged in. + + Args: + resolved_crew_skills: Pre-resolved crew skills (already discovered + and activated). When provided, avoids redundant discovery per agent. """ from crewai.crew import Crew - crew_skills: list[Path | SkillModel] | None = ( - self.crew.skills - if isinstance(self.crew, Crew) and isinstance(self.crew.skills, list) - else None - ) + if resolved_crew_skills is None: + crew_skills: list[Path | SkillModel] | None = ( + self.crew.skills + if isinstance(self.crew, Crew) and isinstance(self.crew.skills, list) + else None + ) + else: + crew_skills = list(resolved_crew_skills) if not self.skills and not crew_skills: return diff --git a/lib/crewai/src/crewai/agents/agent_builder/base_agent.py b/lib/crewai/src/crewai/agents/agent_builder/base_agent.py index aee89cc66..3b2ddc0a9 100644 --- a/lib/crewai/src/crewai/agents/agent_builder/base_agent.py +++ b/lib/crewai/src/crewai/agents/agent_builder/base_agent.py @@ -504,5 +504,5 @@ class BaseAgent(BaseModel, ABC, metaclass=AgentMeta): def set_knowledge(self, crew_embedder: EmbedderConfig | None = None) -> None: pass - def set_skills(self) -> None: + def set_skills(self, resolved_crew_skills: list[Any] | None = None) -> None: pass diff --git a/lib/crewai/src/crewai/crews/utils.py b/lib/crewai/src/crewai/crews/utils.py index 3e683adeb..a896441a6 100644 --- a/lib/crewai/src/crewai/crews/utils.py +++ b/lib/crewai/src/crewai/crews/utils.py @@ -4,6 +4,7 @@ from __future__ import annotations import asyncio from collections.abc import Callable, Coroutine, Iterable, Mapping +from pathlib import Path from typing import TYPE_CHECKING, Any from opentelemetry import baggage @@ -11,6 +12,8 @@ from opentelemetry import baggage from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.crews.crew_output import CrewOutput from crewai.rag.embeddings.types import EmbedderConfig +from crewai.skills.loader import activate_skill, discover_skills +from crewai.skills.models import INSTRUCTIONS, Skill as SkillModel from crewai.types.streaming import CrewStreamingOutput, FlowStreamingOutput from crewai.utilities.file_store import store_files from crewai.utilities.streaming import ( @@ -51,6 +54,30 @@ def enable_agent_streaming(agents: Iterable[BaseAgent]) -> None: agent.llm.stream = True +def _resolve_crew_skills(crew: Crew) -> list[SkillModel] | None: + """Resolve crew-level skill paths once so agents don't repeat the work.""" + if not isinstance(crew.skills, list) or not crew.skills: + return None + + resolved: list[SkillModel] = [] + seen: set[str] = set() + for item in crew.skills: + if isinstance(item, Path): + for skill in discover_skills(item): + if skill.name not in seen: + seen.add(skill.name) + resolved.append(activate_skill(skill)) + elif isinstance(item, SkillModel): + if item.name not in seen: + seen.add(item.name) + resolved.append( + activate_skill(item) + if item.disclosure_level < INSTRUCTIONS + else item + ) + return resolved or None + + def setup_agents( crew: Crew, agents: Iterable[BaseAgent], @@ -67,10 +94,12 @@ def setup_agents( function_calling_llm: Default function calling LLM for agents. step_callback: Default step callback for agents. """ + resolved_crew_skills = _resolve_crew_skills(crew) + for agent in agents: agent.crew = crew agent.set_knowledge(crew_embedder=embedder) - agent.set_skills() + agent.set_skills(resolved_crew_skills=resolved_crew_skills) if not agent.function_calling_llm: # type: ignore[attr-defined] agent.function_calling_llm = function_calling_llm # type: ignore[attr-defined] if not agent.step_callback: # type: ignore[attr-defined]