2024-10-21
Forget LangChain, CrewAI and AutoGen — Try This Framework and Never Look Back

Photo by Xavi Cabrera on Unsplash
Frameworks like LangChain, CrewAI, and AutoGen have gained popularity by offering high-level abstractions to build AI systems. However, many developers, myself included, have found these tools to be more of a hindrance than a help, often introducing unnecessary complexity and frustration into the development process.
Enter Atomic Agents — a modular, streamlined framework designed to eliminate the headaches associated with existing AI development tools. Built on solid programming paradigms like the Input–Process–Output (IPO) model and the concept of atomicity, Atomic Agents offers a fresh approach that prioritizes simplicity, flexibility, and developer control.
In this article, we’ll delve deep into why Atomic Agents was created, the programming paradigms it embraces, and how it stands out from the crowd. We’ll also share code samples and practical examples to illustrate its effectiveness. If you’ve ever felt overwhelmed or constrained by the complexities of frameworks like LangChain, read on — you might just find the solution you’ve been searching for.
The Problem with Existing AI Frameworks
When I first started experimenting with LangChain, I was excited by its promise to simplify the development of AI agents. However, it didn’t take long for the excitement to wane. The framework is riddled with layers upon layers of abstraction, making it incredibly challenging to understand what’s happening under the hood.
Simple tasks that should have been straightforward became convoluted nightmares. I found myself navigating through a maze of classes, chains, and utilities, often without clear documentation to guide me. Debugging issues felt like trying to untangle a ball of yarn — pulling on one thread just tightened the knots elsewhere.
Moreover, LangChain seemed to be built not by developers who understand the practical challenges of AI development, but perhaps by those more interested in theoretical elegance over real-world usability. The excessive abstraction didn’t just make development harder; it made it frustratingly opaque.
The Illusion of Magic in CrewAI and AutoGen
Similarly, frameworks like CrewAI and AutoGen attempt to offer “magic” solutions by automating complex tasks. The idea of deploying swarms of AI agents to handle everything autonomously is alluring, but in practice, these tools often don’t work right half the time. They overpromise and underdeliver, leaving developers to grapple with unpredictable behaviors and a lack of control.
These frameworks obscure the underlying processes, making it difficult to debug or customize functionalities. The result is a tool that’s more of a black box than a helpful framework — a situation that’s less than ideal when developing applications that need to be reliable and maintainable.
Overpromising Capabilities: A Common Theme
A recurring issue with these frameworks is the tendency to overpromise capabilities. Companies and tools claim to offer near-AGI (Artificial General Intelligence) solutions, but as someone with 15 years of experience in AI development, I know we’re just not there yet. The hype often leads to unrealistic expectations, and when the tools inevitably fall short, developers are left dealing with the fallout.
The Need for a Better Approach
After wrestling with these frustrations, it became clear that there was a need for a framework that:
- Eliminates unnecessary complexity and abstraction layers.
- Provides full control to developers without hiding critical functionalities behind opaque interfaces.
- Follows solid, time-tested programming paradigms that promote maintainability and scalability.
- Is built by developers, for developers, understanding the real challenges faced in AI development.
This realization led to the creation of Atomic Agents.
Introducing Atomic Agents

Atomic Agents is an open-source framework designed to be as lightweight, modular, and composable as possible. It embraces the principles of the Input–Process–Output (IPO) model and atomicity, ensuring that every component is single-purpose, reusable, and interchangeable.
If you just want to dive into the code straight away, I suggest checking out the quickstart video on YouTube.
Why Does Atomic Agents Exist?
Atomic Agents was born out of the necessity to address the shortcomings of existing frameworks. It aims to:
- Streamline AI development by providing clear, manageable components.
- Eliminate redundant complexity and unnecessary abstractions that plague other frameworks.
- Promote flexibility and consistency, allowing developers to focus on building effective AI applications rather than wrestling with the framework itself.
- Encourage best practices by gently nudging developers toward modular, maintainable code structures.
By adhering to these principles, Atomic Agents empowers developers to build AI agents and applications that are both powerful and manageable.
The Programming Paradigms Behind Atomic Agents
The Input–Process–Output (IPO) Model
At the core of Atomic Agents lies the Input–Process–Output (IPO) model, a fundamental programming paradigm that structures programs into three distinct phases:
- Input: Data is received from the user or another system.
- Process: The data is manipulated or transformed.
- Output: The processed data is presented as a result.
This model promotes clarity and simplicity, making it easier to understand and manage the flow of data through an application.
In Atomic Agents, this translates to:
- Input Schemas: Define the structure and validation rules for incoming data using Pydantic.
- Processing Components: Agents and tools perform operations on the data.
- Output Schemas: Ensure that the results are structured and validated before being returned.
Atomicity: Building Blocks of Functionality
The concept of atomicity involves breaking down complex systems into their smallest functional parts, or “atoms.” Each atom:
- Has a single responsibility, making it easier to understand and maintain.
- Is reusable, allowing for components to be used across different parts of an application or even in different projects.
- Can be combined with other atoms to build more complex functionalities.
By focusing on atomic components, Atomic Agents promotes a modular architecture that enhances flexibility and scalability.
How Atomic Agents Works
The Anatomy of an Agent
In Atomic Agents, an AI agent is composed of several key components:
- System Prompt: Defines the agent’s behavior and purpose.
- Input Schema: Specifies the expected structure of input data.
- Output Schema: Defines the structure of the output data.
- Memory: Stores conversation history or state information.
- Context Providers: Inject dynamic context into the system prompt at runtime.
- Tools: External functions or APIs the agent can utilize.
Each component is designed to be modular and interchangeable, adhering to the principles of separation of concerns and single responsibility.
Modularity and Composability
Modularity is at the heart of Atomic Agents. By designing components to be self-contained and focused on a single task, developers can:
- Swap out tools or agents without affecting the rest of the system.
- Fine-tune individual components, such as system prompts or schemas, without unintended side effects.
- Chain agents and tools seamlessly by aligning their input and output schemas.
This modular approach not only makes development more manageable but also enhances the maintainability and scalability of AI applications.
Context Providers: Enhancing Flexibility
Context Providers allow agents to include dynamic data in their system prompts, enhancing their responses based on the latest information.
Example:
from atomic_agents.lib.components.system_prompt_generator import SystemPromptContextProviderBase
class SearchResultsProvider(SystemPromptContextProviderBase):
def __init__(self, title: str, search_results: List[str]):
super().__init__(title=title)
self.search_results = search_results
def get_info(self) -> str:
return "\n".join(self.search_results)
# Register the context provider with the agent
agent.register_context_provider("search_results", search_results_provider) By injecting real-time data into the agent’s context, you can create more dynamic and responsive AI applications.
Chaining Schemas and Agents
Atomic Agents simplifies the process of chaining agents and tools by aligning their input and output schemas.
Example: Suppose you have a query generation agent and a web search tool. By setting the output schema of the query agent to match the input schema of the search tool, you can directly chain them.
from web_search_agent.tools.searxng_search import SearxNGSearchTool
# Initialize the query agent
query_agent = BaseAgent(
BaseAgentConfig(
# ... other configurations ...
output_schema=SearxNGSearchTool.input_schema, # Align output schema
)
) This design promotes reusability and flexibility, allowing you to easily switch out components or extend functionality.
Why Atomic Agents Is Better Than the Rest
Eliminating Unnecessary Complexity
Unlike frameworks that introduce multiple layers of abstraction, Atomic Agents keeps things straightforward. Each component serves a clear purpose, and there’s no hidden magic to decipher.
- Transparent Architecture: You have full visibility into how data flows through your application.
- Easier Debugging: With less complexity, identifying and fixing issues becomes more manageable.
- Reduced Learning Curve: Developers can get up to speed quickly without needing to understand convoluted abstractions.
Built by Developers, for Developers
Atomic Agents is designed with real-world development challenges in mind. It embraces time-tested programming paradigms and prioritizes developer experience.
- Solid Programming Foundations: By following the IPO model and atomicity, the framework encourages best practices.
- Flexibility and Control: Developers have the freedom to customize and extend components as needed.
- Community-Driven: As an open-source project, it invites contributions and collaboration from the developer community.
Standalone and Reusable Components
Each part of Atomic Agents can be run independently, promoting reusability and modularity.
- Testable in Isolation: Components can be individually tested, ensuring reliability before integration.
- Reusable Across Projects: Atomic components can be used in different applications, saving development time.
- Easier Maintenance: Isolating functionality reduces the impact of changes and simplifies updates.
Building a Simple AI Agent
We’ll build an AI agent that responds to user queries and suggests follow-up questions.
Step 1: Define Custom Input and Output Schemas
from pydantic import BaseModel, Field
from typing import List
from atomic_agents.agents.base_agent import BaseIOSchema
class CustomInputSchema(BaseIOSchema):
chat_message: str = Field(..., description="The user's input message.")
class CustomOutputSchema(BaseIOSchema):
chat_message: str = Field(..., description="The agent's response message.")
suggested_questions: List[str] = Field(..., description="Suggested follow-up questions.") Step 2: Set Up the System Prompt
from atomic_agents.lib.components.system_prompt_generator import SystemPromptGenerator
system_prompt_generator = SystemPromptGenerator(
background=[
"You are a knowledgeable assistant that provides helpful information and suggests follow-up questions."
],
steps=[
"Analyze the user's input to understand the context and intent.",
"Provide a relevant and informative response.",
"Generate 3 suggested follow-up questions."
],
output_instructions=[
"Ensure clarity and conciseness in your response.",
"Conclude with 3 relevant suggested questions."
]
) Step 3: Initialize the Agent
from atomic_agents.agents.base_agent import BaseAgent, BaseAgentConfig
import instructor
import openai
# Initialize the agent
agent = BaseAgent(
config=BaseAgentConfig(
client=instructor.from_openai(openai.OpenAI(api_key='YOUR_OPENAI_API_KEY')),
model="gpt-4",
system_prompt_generator=system_prompt_generator,
input_schema=CustomInputSchema,
output_schema=CustomOutputSchema
)
) Step 4: Use the Agent
user_input = "Can you explain the benefits of using Atomic Agents?"
input_data = CustomInputSchema(chat_message=user_input)
response = agent.run(input_data)
print(f"Agent: {response.chat_message}")
print("Suggested questions:")
for question in response.suggested_questions:
print(f"- {question}") Expected Output:
Agent: Atomic Agents simplifies AI development by providing modular, reusable components based on solid programming paradigms like the IPO model and atomicity.
Suggested questions:
- How does Atomic Agents compare to other AI frameworks?
- Can you provide an example of building an agent with Atomic Agents?
- What are the key features of Atomic Agents that enhance productivity? Integrating Tools and Context Providers
Suppose we want our agent to perform web searches based on user queries.
Step 1: Download and Set Up the SearxNGSearchTool
Using the Atomic Assembler CLI, we can download the search tool:
atomic Select the SearxNGSearchTool from the menu and follow the instructions to install dependencies.
Step 2: Integrate the Search Tool
from web_search_agent.tools.searxng_search import SearxNGSearchTool, SearxNGSearchToolConfig
# Initialize the search tool
search_tool = SearxNGSearchTool(config=SearxNGSearchToolConfig(base_url="http://localhost:8080")) Step 3: Update the Agent to Use the Tool
We can modify the agent to decide when to use the search tool based on the user’s input.
from typing import Union
class OrchestratorOutputSchema(BaseModel):
tool: str = Field(..., description="The tool to use: 'search' or 'chat'")
parameters: Union[SearxNGSearchTool.input_schema, CustomInputSchema] = Field(..., description="Parameters for the selected tool.")
# Modify the agent's logic to output the OrchestratorOutputSchema
# ...
# Execute the selected tool
if response.tool == "search":
search_results = search_tool.run(response.parameters)
# Process search results...
else:
# Use the chat agent as before
pass Step 4: Use Context Providers to Include Search Results
class SearchResultsProvider(SystemPromptContextProviderBase):
def __init__(self, search_results):
super().__init__(title="Search Results")
self.search_results = search_results
def get_info(self) -> str:
return "\n".join(self.search_results)
# After obtaining search results
context_provider = SearchResultsProvider(search_results)
agent.register_context_provider("search_results", context_provider) This integration allows the agent to provide responses based on real-time web search data.
The Atomic Assembler CLI: Managing Tools Made Easy
One of the standout features of Atomic Agents is the Atomic Assembler CLI, a command-line tool that simplifies the management of tools and agents.
Inspired by some modern tailwind libraries such as shadcn, where you don’t install the components as dependencies, but rather take ownership over them in your own source code, we do something similar for tools.
What this means is that we will not install the tools as dependencies, using pip, but rather copy them into our project. This can be done in one of two ways:
- Manually download the tools or copy/paste their source code from the Atomic Agents GitHub repository and place them in the
atomic-forgefolder. - The option we will use, the Atomic Assembler CLI to download the tools.

Key Features
- Download and Manage Tools: Easily add new tools to your project without manual copying or dependency issues.
- Avoid Dependency Clutter: Install only the tools you need, keeping your project lean.
- Modify Tools Effortlessly: Each tool is self-contained with its own tests and documentation.
- Access Tools Directly: If you prefer, you can manage tools manually by accessing their folders.


Conclusion
Atomic Agents brings a much-needed shift in the AI development landscape by prioritizing simplicity, modularity, and developer control. By embracing solid programming paradigms like the Input–Process–Output model and atomicity, it addresses the frustrations many developers face with existing frameworks like LangChain, CrewAI, and AutoGen.
With Atomic Agents, you can:
- Eliminate unnecessary complexity and focus on building effective AI applications.
- Gain full control over every component of your system.
- Easily swap out or modify components without disrupting the entire application.
- Leverage modularity and reusability to enhance productivity and maintainability.
If you’re tired of wrestling with overcomplicated frameworks that overpromise and underdeliver, it’s time to give Atomic Agents a try.
Get Started Today
- GitHub Repository: eigenwise/atomic-agents
- API Documentation: Atomic Agents API Docs
- Examples Directory: Atomic Examples