Wednesday, May 13, 2026

Difference between Default CrewAI created by CrewAI and Handcoded CrewAI

 When we run the command 

!uv tool run crewai run

in google colab, a default working Hello World CrewAI project is created.

This project uses a proper folder structure, and creates 4-5 code files.

Hence I tried to imitate this project in a single colab notebook. But I had to make
certain changes in it to make it work.

I specifically faced two issues:

Attribute Error: ‘str’ object has no attribute ‘get’

And
'function' object has no attribute 'get'

 I had to change
  #agent="researcher"
to
  agent=self.researcher()            

and
            #agents=self.agents, # Automatically created by the @agent decorator
            #tasks=self.tasks, # Automatically created by the @task decorator
To
            agents=[self.researcher(), self.reporting_analyst()],
            tasks=[self.research_task(), self.reporting_task()],

I got the issue analysed by Gemini and got the following answer:


The reason the demo project works without those parentheses is due to how the @CrewBase decorator

and the class attributes agents_config and tasks_config work together.

In the official crewai create crew template, the code usually looks like this:

Python
@agent
def researcher(self) -> Agent:
    return Agent(
        config=self.agents_config['researcher'], # This is the magic line
        verbose=True
    )

Here is the breakdown of why the official version works differently than your manual version:

1. The @CrewBase "Auto-Loading"

When you use the @CrewBase decorator, it automatically looks for a config/ folder.

It loads agents.yaml into an attribute called self.agents_config and tasks.yaml into self.tasks_config.

In your specific error, the logs showed:

WARNING:root:Agent config file not found at /content/config/agents.yaml. Proceeding with empty agent configurations.


Because those files were missing, self.agents_config was empty.

2. Lazy Loading vs. Manual Instantiation

In the official template, the Task definition in tasks.yaml usually looks like this:

YAML
research_task:
  description: "Conduct research on {topic}"
  agent: researcher  # This is a STRING, not a function

When the CrewAI framework reads this YAML, it sees the string "researcher".

It then looks for a method decorated with @agent that has the name researcher.

It handles the "calling" of that function for you behind the scenes.

3. Why your code threw the AttributeError

In your manual code, you were doing this:

Python
@task
def research_task(self) -> Task:
    return Task(
        description="...",
        agent=self.researcher  # Passing the METHOD reference
    )

Because you weren't using the YAML configuration to link the two, you were passing the Python function object self.researcher directly into the agent parameter of the Task class.

The Task class expected an Agent object, but it received a function. When it tried to process that "agent," it tried to look for configuration data inside it, leading to the error:

AttributeError: 'function' object has no attribute 'get'

Summary Table

FeatureOfficial Demo (YAML)Your Manual Code
Agent SettingsDefined in agents.yamlDefined inside Agent(...)
Task ConnectionLinked via string name in tasks.yamlLinked via self.agent_name() in Python
Decorator RoleOrchestrates the YAML-to-Method mappingMostly unused since configs were missing
LogicFramework "calls" the methods for youYou must call the methods yourself

In short, the official demo uses declarative configuration (YAML strings), whereas your approach was programmatic (Python objects). When doing it programmatically, you have to execute the function to produce the object the Task needs.

How are you finding the transition from YAML-based configs to pure Python?

No comments:

Post a Comment

LanceDB, ONNX, Lance and Parquet

I n the context of modern AI development, LanceDB and ONNX represent two different but complementary parts of the "AI stack." Wh...