From 2ccc0be41f4d0fd8543430a20001ccbd0f9eca78 Mon Sep 17 00:00:00 2001 From: Gojko Cutura Date: Wed, 3 Dec 2025 12:51:11 +0100 Subject: [PATCH 1/4] added the agentspec adapter --- .../agent_adapters/agentspec/__init__.py | 0 .../agentspec/agentspec_adapter.py | 82 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 lib/crewai/src/crewai/agents/agent_adapters/agentspec/__init__.py create mode 100644 lib/crewai/src/crewai/agents/agent_adapters/agentspec/agentspec_adapter.py diff --git a/lib/crewai/src/crewai/agents/agent_adapters/agentspec/__init__.py b/lib/crewai/src/crewai/agents/agent_adapters/agentspec/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/crewai/src/crewai/agents/agent_adapters/agentspec/agentspec_adapter.py b/lib/crewai/src/crewai/agents/agent_adapters/agentspec/agentspec_adapter.py new file mode 100644 index 0000000000..75bb77cdef --- /dev/null +++ b/lib/crewai/src/crewai/agents/agent_adapters/agentspec/agentspec_adapter.py @@ -0,0 +1,82 @@ +from pydantic import Field, PrivateAttr +from typing import Any, Optional, Dict, Union, Callable, List + +from crewai.agents.agent_builder.base_agent import BaseAgent +from crewai.agents.agent_adapters.base_agent_adapter import BaseAgentAdapter +from crewai import Agent as CrewAIAgent +from crewai.tools.base_tool import BaseTool, Tool as CrewAITool +from crewai.utilities.import_utils import import_and_validate_definition +from crewai.utilities.types import LLMMessage + + +class AgentSpecAgentAdapter(BaseAgentAdapter): + + _crewai_agent: CrewAIAgent = PrivateAttr() + function_calling_llm: Any = Field(default=None) + step_callback: Any = Field(default=None) + + def __init__( + self, + agentspec_agent_json: str, + tool_registry: Optional[Dict[str, Union[Callable, CrewAITool]]] = None, + **kwargs: Any, + ): + agent_spec_loader: type[Any] = import_and_validate_definition( + "crewai_agentspec_adapter.AgentSpecLoader" + ) + loader = agent_spec_loader(tool_registry=tool_registry) + crewai_agent = loader.load_json(agentspec_agent_json) + + init_kwargs = { + "role": getattr(crewai_agent, "role", "AgentSpec Agent"), + "goal": getattr(crewai_agent, "goal", "Execute tasks defined by AgentSpec"), + "backstory": getattr( + crewai_agent, "backstory", "Adapter wrapper around AgentSpec-generated CrewAI agent" + ), + "llm": getattr(crewai_agent, "llm", None), + "function_calling_llm": getattr(crewai_agent, "llm", None), + "tools": getattr(crewai_agent, "tools", None), + "verbose": getattr(crewai_agent, "verbose", False), + "max_iter": getattr(crewai_agent, "max_iter", 25), + } + init_kwargs.update(kwargs or {}) + super().__init__(**{k: v for k, v in init_kwargs.items() if v is not None}) + + self.function_calling_llm = getattr(crewai_agent, "llm", None) + self._crewai_agent = crewai_agent + + + # --- Abstract methods of BaseAgentAdapter --- + + def configure_tools(self, tools: list[BaseTool] | None = None) -> None: + # Nothing to do, tools were already converted by AgentSpecLoader + pass + + @property + def last_messages(self) -> list[LLMMessage]: + return self._crewai_agent.last_messages + + + # --- Abstract methods of BaseAgent --- + # We just delegate to the underlying agent's methods, since it's all already + # created by AgentSpecLoader (the output is crewai.Agent which is derived from BaseAgent) + + def execute_task( + self, + task: Any, + context: Optional[str] = None, + tools: Optional[List[Any]] = None, + ) -> Any: + return self._crewai_agent.execute_task(task, context=context, tools=tools) + + def create_agent_executor(self, tools: Optional[List[Any]] = None) -> None: + self._crewai_agent.create_agent_executor(tools=tools) + + def get_delegation_tools(self, agents: List[BaseAgent]) -> List[Any]: + return self._crewai_agent.get_delegation_tools(agents) + + def get_platform_tools(self, apps: List[Any]) -> List[Any]: + return self._crewai_agent.get_platform_tools(apps) + + def get_mcp_tools(self, mcps: List[Any]) -> List[Any]: + return self._crewai_agent.get_mcp_tools(mcps) From 9ff650a9af733f4ab5da0c21cf3a44edbaea5b11 Mon Sep 17 00:00:00 2001 From: Gojko Cutura Date: Wed, 3 Dec 2025 16:21:16 +0100 Subject: [PATCH 2/4] added a docstring for the adapter --- .../agentspec/agentspec_adapter.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lib/crewai/src/crewai/agents/agent_adapters/agentspec/agentspec_adapter.py b/lib/crewai/src/crewai/agents/agent_adapters/agentspec/agentspec_adapter.py index 75bb77cdef..8137cab5f6 100644 --- a/lib/crewai/src/crewai/agents/agent_adapters/agentspec/agentspec_adapter.py +++ b/lib/crewai/src/crewai/agents/agent_adapters/agentspec/agentspec_adapter.py @@ -10,6 +10,31 @@ class AgentSpecAgentAdapter(BaseAgentAdapter): + """ + Adapter that lets CrewAI import agents defined using Oracle's AgentSpec specification language. + (https://github.com/oracle/agent-spec.git) + + This adapter wraps around the crewaiagentspecadapter which provides all required + conversion methods for loading an AgentSpec representation into a CrewAI Agent. + (https://github.com/oracle/agent-spec/tree/main/adapters/crewaiagentspecadapter) + + When the conversion is done, this adapter delegates required methods to corresponding + methods of the underlying converted agent. + + Supported features: + - ReAct-style agents + - Tools + + Not currently supported: + - Flows + - Multi-agent patterns + + Installation: + 1) git clone https://github.com/oracle/agent-spec.git + 2) cd agent-spec + 3) pip install pyagentspec + 4) pip install adapters/crewaiagentspecadapter + """ _crewai_agent: CrewAIAgent = PrivateAttr() function_calling_llm: Any = Field(default=None) From 52f47eb3681f59e9dab0ab0a32f1dda249c623a2 Mon Sep 17 00:00:00 2001 From: Gojko Cutura Date: Fri, 5 Dec 2025 02:17:09 +0100 Subject: [PATCH 3/4] updated bring-your-own-agent tutorial with agentspec adapter --- docs/en/learn/bring-your-own-agent.mdx | 40 ++++++++++++++++++++------ 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/docs/en/learn/bring-your-own-agent.mdx b/docs/en/learn/bring-your-own-agent.mdx index fb7b22dc21..52fdad4e79 100644 --- a/docs/en/learn/bring-your-own-agent.mdx +++ b/docs/en/learn/bring-your-own-agent.mdx @@ -322,6 +322,8 @@ By implementing these methods, your `MyCustomConverterAdapter` ensures that stru We provide out of the box adapters for the following frameworks: 1. LangGraph 2. OpenAI Agents +3. [AgentSpec](https://github.com/oracle/agent-spec) + - AgentSpec is a framework-agnostic declarative language for defining agentic systems. It defines building blocks for standalone agents and structured agentic workflows as well as common ways of composing them into multi-agent systems. ## Kicking off a crew with adapted agents: @@ -331,14 +333,16 @@ import os from typing import List from crewai_tools import SerperDevTool -from src.crewai import Agent, Crew, Task +from crewai import Agent, Crew, Task from langchain_openai import ChatOpenAI +from pyagentspec.agent import Agent +from pyagentspec.llms import OpenAiConfig +from pyagentspec.serialization import AgentSpecSerializer from pydantic import BaseModel -from crewai.agents.agent_adapters.langgraph.langgraph_adapter import ( - LangGraphAgentAdapter, -) +from crewai.agents.agent_adapters.langgraph.langgraph_adapter import LangGraphAgentAdapter from crewai.agents.agent_adapters.openai_agents.openai_adapter import OpenAIAgentAdapter +from crewai.agents.agent_adapters.agentspec.agentspec_adapter import AgentSpecAgentAdapter # CrewAI Agent code_helper_agent = Agent( @@ -348,6 +352,18 @@ code_helper_agent = Agent( allow_delegation=False, verbose=True, ) + +# AgentSpec Agent Adapter +code_reviewer_agent = AgentSpecAgentAdapter( + agentspec_agent_json=AgentSpecSerializer().to_json( + Agent( + name="Code Reviewer", + system_prompt="""You are an experienced programmer who performs thorough code review. Given a code snippet, suggest helpful improvements.""", + llm_config=OpenAiConfig(name="openai-gpt-4o", model_id="gpt-4o"), + ) + ) +) + # OpenAI Agent Adapter link_finder_agent = OpenAIAgentAdapter( role="Link Finder", @@ -373,13 +389,20 @@ class Code(BaseModel): code: str -task = Task( +task_write_code = Task( description="Give an answer to the coding question: {task}", expected_output="A thorough answer to the coding question: {task}", agent=code_helper_agent, output_json=Code, ) -task2 = Task( + +task_review_code = Task( + description="Provide thorough helpful review for the generated code", + expected_output="A thorough review for the generated code", + agent=code_reviewer_agent, +) + +task_find_links = Task( description="Find links to resources that can help with coding tasks. Use the serper tool to find resources that can help.", expected_output="A list of links to resources that can help with coding tasks", agent=link_finder_agent, @@ -391,16 +414,17 @@ class Report(BaseModel): links: List[str] -task3 = Task( +task_generate_report = Task( description="Report the results of the tasks.", expected_output="A report of the results of the tasks. this is the code produced and then the links to the resources that can help with the coding task.", agent=reporter_agent, output_json=Report, ) + # Use in CrewAI crew = Crew( agents=[code_helper_agent, link_finder_agent, reporter_agent], - tasks=[task, task2, task3], + tasks=[task_write_code, task_review_code, task_find_links, task_generate_report], verbose=True, ) From e38b7c62f86f0702606ed95b9ebac63b3785c7ed Mon Sep 17 00:00:00 2001 From: Gojko Cutura Date: Fri, 5 Dec 2025 17:23:18 +0100 Subject: [PATCH 4/4] minor docs fix --- docs/en/learn/bring-your-own-agent.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/learn/bring-your-own-agent.mdx b/docs/en/learn/bring-your-own-agent.mdx index 52fdad4e79..03044bfad5 100644 --- a/docs/en/learn/bring-your-own-agent.mdx +++ b/docs/en/learn/bring-your-own-agent.mdx @@ -423,7 +423,7 @@ task_generate_report = Task( # Use in CrewAI crew = Crew( - agents=[code_helper_agent, link_finder_agent, reporter_agent], + agents=[code_helper_agent, code_reviewer_agent, link_finder_agent, reporter_agent], tasks=[task_write_code, task_review_code, task_find_links, task_generate_report], verbose=True, )