CAMEL AI Framework
Deep-dive source code analysis โ v0.2.91a4 ยท 502 Python modules ยท 155K LOC
Framework Overview
CAMEL (Communicative Agents for "Mind" Exploration of Large Language Model Society) is one of the earliest and most comprehensive multi-agent frameworks. Originating from the "AI Society" research paper (2023), it has evolved into a full-stack agent framework covering:
- Single-agent chat with tool use, memory, and streaming
- Multi-agent role-playing and collaborative societies
- Hierarchical workforce orchestration (Workforce)
- 80+ toolkits spanning search, browser, code exec, cloud services, messaging, etc.
- 40+ LLM provider backends
- RAG pipelines, embeddings, retrievers
- RL-style environments for agent evaluation
- Sandboxed code execution runtimes (Docker, Daytona, HTTP)
The framework is Apache-2.0 licensed, requires Python 3.10+, and is structured as a single monorepo package camel-ai.
By the Numbers
System Architecture
The architecture follows a layered design: Models at the bottom, Agents in the middle, Societies at the top. Toolkits, Memory, and Runtimes are cross-cutting concerns that agents compose.
Agent Hierarchy
Agent Types
| Agent | Purpose | Key Feature |
|---|---|---|
ChatAgent | General-purpose conversation | Tool use, memory, streaming, summarization |
CriticAgent | Evaluate & select proposals | Multiple critic strategies |
EmbodiedAgent | Physical world interaction | Action space, code execution |
SearchAgent | Information retrieval | Search tool integration |
MCPAgent | MCP protocol agent | Model Context Protocol |
RepoAgent | Repository analysis | Code understanding |
KnowledgeGraphAgent | KG operations | Graph-based reasoning |
TaskSpecifyAgent | Make tasks specific | Creative task expansion |
TaskPlannerAgent | Plan task decomposition | Sequential planning |
RoleAssignmentAgent | Assign roles to agents | Dynamic role allocation |
DeductiveReasonerAgent | Step-by-step reasoning | Chain-of-thought |
ChatAgent โ The Core
ChatAgent is the beating heart of CAMEL at 6444 lines. It handles the complete agent lifecycle:
Step Execution Flow
Key Design Decisions in ChatAgent
๐ Auto-Summarization
When context exceeds summarize_threshold % of token limit, old messages are compressed into a [CONTEXT_SUMMARY]. Progressive compression appends new summaries; full compression replaces all. Last user message is preserved with a prefix linking to the summary.
๐ก๏ธ Tool Output Masking
mask_tool_output=True returns a sanitized placeholder instead of raw tool output. Snapshot content from browser tools can be cleaned to remove verbose DOM markers via enable_snapshot_clean.
๐ Token Count Caching
ScoreBasedContextCreator caches token counts from LLM responses and uses character-based approximation (~2 chars/token) for new messages to avoid expensive re-counting.
โก Streaming
Both sync and async streaming are supported. StreamContentAccumulator manages content across streaming chunks. StreamingChatAgentResponse wraps generators to be compatible with non-streaming code.
ChatAgent Constructor Signature (Key Params)
class ChatAgent(BaseAgent):
def __init__(
self,
system_message: Optional[Union[BaseMessage, str]] = None,
model: Optional[Union[BaseModelBackend, ModelManager, ...]] = None,
memory: Optional[AgentMemory] = None,
message_window_size: Optional[int] = None,
summarize_threshold: Optional[int] = 50, # % of token limit
token_limit: Optional[int] = None,
output_language: Optional[str] = None,
tools: Optional[List[Union[FunctionTool, Callable]]] = None,
external_tools: Optional[List[...]] = None,
response_terminators: Optional[List[ResponseTerminator]] = None,
max_iteration: Optional[int] = None,
tool_execution_timeout: Optional[float] = 10,
mask_tool_output: bool = False,
prune_tool_calls_from_memory: bool = False,
retry_attempts: int = 3,
retry_delay: float = 1.0,
step_timeout: Optional[float] = 10,
stream_accumulate: Optional[bool] = None,
summary_window_ratio: float = 0.6,
)
Societies & Orchestration
RolePlaying
The original CAMEL pattern: two agents (assistant + user) converse in character to solve a task. A CriticAgent or Human can be added to the loop.
class RolePlaying:
def __init__(
self,
assistant_role_name: str,
user_role_name: str,
critic_role_name: str = "critic",
task_prompt: str = "",
with_task_specify: bool = True,
with_task_planner: bool = False,
with_critic_in_the_loop: bool = False,
task_type: TaskType = TaskType.AI_SOCIETY,
...
)
Workforce โ Hierarchical Multi-Agent
A 6239-line orchestrator for distributed task execution. Workers form a tree structure with a coordinator at the root.
Workforce manages task assignment, decomposition, and worker lifecycle. Key features:
- Task channel: Workers listen to a shared
TaskChannel - Dynamic creation: New workers can be spawned mid-run
- Failure handling: Configurable recovery strategies (retry, decompose, reassign)
- Pipeline mode: Sequential task chaining with
PipelineTaskBuilder - Quality evaluation: Built-in output quality scoring
- Metrics:
WorkforceMetricstracks task stats
Memory System
Memory Block Types
ChatHistoryBlock
Stores messages sequentially in BaseKeyValueStorage (default: in-memory). Supports window_size for sliding window retrieval. Can pop_records and remove_records_by_indices.
VectorDBBlock
Embeds messages and stores in BaseVectorStorage (default: Qdrant). Retrieves semantically similar messages by topic. Tracks current topic from last user message.
LongtermAgentMemory โ Hybrid
Combines both blocks. On retrieve():
def retrieve(self) -> List[ContextRecord]:
chat_history = self.chat_history_block.retrieve()
vector_db = self.vector_db_block.retrieve(
self._current_topic, self.retrieve_limit,
)
# Insert relevant history BETWEEN system msg and recent msgs
return chat_history[:1] + vector_db + chat_history[1:]
Context Creation Strategy
ScoreBasedContextCreator orders records chronologically and uses token counting with caching:
- Caches actual token counts from LLM response
usage - For new messages, estimates via ~2 chars/token heuristic
- Returns all records that fit within
token_limit
Messages & Prompts
BaseMessage
A @dataclass that serves as the universal message type. Not a Pydantic model โ it's a plain dataclass for performance.
@dataclass
class BaseMessage:
role_name: str
role_type: RoleType # USER | ASSISTANT | SYSTEM | CRITIC | EMBODIMENT
meta_dict: Optional[Dict]
content: str
video_bytes: Optional[bytes]
image_list: Optional[List[Union[Image.Image, str]]]
image_detail: Literal["auto", "low", "high"]
parsed: Optional[Union[BaseModel, dict]]
reasoning_content: Optional[str]
Key conversion methods:
to_openai_system/user/assistant_message()โ OpenAI API formatto_sharegpt()/from_sharegpt()โ ShareGPT formatextract_text_and_code_prompts()โ Parse out code blocks- Supports multimodal: images (PIL or URL), video bytes
FunctionCallingMessage
Extends BaseMessage for tool calls with func_name, args, and result fields.
Prompt System
TextPrompt extends str with template keyword extraction and partial formatting:
@wrap_prompt_functions
class TextPrompt(str):
@property
def key_words(self) -> Set[str]:
# Extracts {placeholder} patterns from the string
return get_prompt_template_key_words(self)
def format(self, *args, **kwargs) -> 'TextPrompt':
# Allows partial formatting โ unfilled keys stay as {key}
default_kwargs = {key: '{' + f'{key}' + '}' for key in self.key_words}
default_kwargs.update(kwargs)
return TextPrompt(super().format(*args, **default_kwargs))
CodePrompt extends TextPrompt with code_type and an execute() method that runs through an interpreter.
Tool System
FunctionTool
The core abstraction โ wraps any callable into an OpenAI-compatible tool:
class FunctionTool:
def __init__(
self,
func: Callable,
openai_tool_schema: Optional[Dict] = None,
synthesize_schema: bool = False, # Auto-generate schema via LLM
synthesize_output: bool = False, # Mock execution via LLM
...
)
Key features:
- Auto schema generation: Parses function signature + docstring โ OpenAI JSON schema
- Schema synthesis: If validation fails, can use an LLM to generate a compliant schema
- Output synthesis: Mock tool execution by having an LLM predict the output
- Pydantic coercion: Auto-converts JSON dict args to Pydantic models when the function signature expects them
- Async support:
async_call()for async functions; sync calls with async detection and warnings - @tool decorator: Converts any function to a
FunctionTool
BaseToolkit
class BaseToolkit(metaclass=AgentOpsMeta):
timeout: Optional[float] = Constants.TIMEOUT_THRESHOLD
def get_tools(self) -> List[FunctionTool]: # Abstract
...
def run_mcp_server(self, mode): # MCP protocol support
self.mcp.run(mode)
All toolkit methods auto-wrapped with with_timeout unless they have a timeout parameter or are marked @manual_timeout.
RegisteredAgentToolkit Mixin
Toolkits that need a reference to their owning ChatAgent can inherit this. The agent auto-registers itself via toolkits_to_register_agent.
Toolkit Ecosystem (80+)
๐ Search & Web
SearchToolkit, BrowserToolkit, WebFetchToolkit, HybridBrowserToolkit, HeadlessBrowserSearchToolkit, SearxNGToolkit, AskNewsToolkit, DappierToolkit
๐ป Code & Dev
CodeExecution, TerminalToolkit, GitHubToolkit, FileToolkit, ExcelToolkit, PPTXToolkit, MarkitdownToolkit, SQLToolkit, SympyToolkit
๐ง Communication
GmailToolkit, ImapMailToolkit, OutlookToolkit, SlackToolkit, DingTalkToolkit, LarkToolkit, WhatsAppToolkit, WeChatToolkit, LinkedInToolkit, TwitterToolkit, RedditToolkit, ResendToolkit
๐จ Media
ImageGenerationToolkit, ImageAnalysisToolkit, VideoAnalysisToolkit, VideoDownloadToolkit, AudioAnalysisToolkit, MeshyToolkit, VertexAIVeoToolkit
๐ Data & Research
ArxivToolkit, PubMedToolkit, SemanticScholarToolkit, GoogleScholarToolkit, OpenBBToolkit, DataCommonsToolkit, EarthScienceToolkit, WeatherToolkit
โ๏ธ Cloud & MCP
MCPToolkit, GoogleDriveMCPToolkit, NotionMCPToolkit, PlaywrightMCPToolkit, MinimaxMCPToolkit, EdgeOnePagesMCPToolkit, KlavisToolkit, ZapierToolkit, ACIToolkit
๐ง Agent Utilities
ThinkingToolkit, MemoryToolkit, ContextSummarizerToolkit, TodoToolkit, TaskPlanningToolkit, PlanningWorktreeToolkit, SkillToolkit, HumanToolkit, NoteTakingToolkit
๐บ๏ธ Other
GoogleMapsToolkit, StripeToolkit, WolframAlphaToolkit, MathToolkit, PyAutoGUIToolkit, ScreenshotToolkit, NetworkXToolkit, BohriumToolkit, MinerUToolkit, WebDeployToolkit
Model Layer
ModelFactory
Creates backend instances from (ModelPlatformType, ModelType) tuples:
class ModelFactory:
_MODEL_PLATFORM_TO_CLASS_MAP = {
ModelPlatformType.OPENAI: OpenAIModel,
ModelPlatformType.ANTHROPIC: AnthropicModel,
ModelPlatformType.GEMINI: GeminiModel,
ModelPlatformType.DEEPSEEK: DeepSeekModel,
ModelPlatformType.OLLAMA: OllamaModel,
ModelPlatformType.VLLM: VLLMModel,
ModelPlatformType.SGLANG: SGLangModel,
ModelPlatformType.LITELLM: LiteLLMModel,
# ... 40+ more
}
@classmethod
def create(cls, model_platform, model_type, **kwargs) -> BaseModelBackend
ModelManager
Manages multiple model backends with scheduling strategies:
round_robinโ rotate through models- Custom strategies possible
ModelBackendMeta (Metaclass)
Automatically wraps run() and arun() to:
preprocess_messages()โ Remove<think>tags, format parallel tool calls- Call original
_run() postprocess_response()โ Extract<think>content intoreasoning_content
Environments
CAMEL provides RL-style environments for agent evaluation:
class StepResult(BaseModel):
observation: Observation
reward: float
rewards_dict: Dict[str, float]
done: bool
info: Dict[str, Any]
Standard RL interface with reset() and step() methods. Used for benchmarking and evaluating agent performance on structured tasks.
Runtimes
Sandboxed execution environments for tool code:
class BaseRuntime(ABC):
def add(self, funcs: Union[FunctionTool, List[FunctionTool]]) -> BaseRuntime
def reset(self) -> Any
def cleanup(self) -> None
def get_tools(self) -> List[FunctionTool]
Runtimes can be used as context managers (with runtime:) for automatic cleanup.
Prompt Template Analysis
AI Society Prompts
The original CAMEL research prompts define a structured dialogue protocol:
ASSISTANT_PROMPT
===== RULES OF ASSISTANT =====
Never forget you are a {assistant_role} and I am a {user_role}.
Never flip roles! Never instruct me!
We share a common interest in collaborating to successfully complete a task.
You must help me to complete the task.
Here is the task: {task}. Never forget our task!
...
Always end <YOUR_SOLUTION> with: Next request.
USER_PROMPT
===== RULES OF USER =====
Never forget you are a {user_role} and I am a {assistant_role}.
Never flip roles! You will always instruct me.
...
Instruction: <YOUR_INSTRUCTION>
Input: <YOUR_INPUT>
...
When the task is completed, you must only reply with
a single word <CAMEL_TASK_DONE>.
Embodiment Prompt
You are the physical embodiment of the {role} who is working on
solving a task: {task}.
You can do things in the physical world including browsing the Internet,
reading documents, drawing images, creating videos, executing code and so on.
...
Here is your action space but it is not limited:
{action_space}
Prompt Design Patterns
| Pattern | Usage | Example |
|---|---|---|
| Role anchoring | "Never forget you are a {role}" | Prevents role confusion in multi-agent |
| Task fixation | "Never forget our task!" | Maintains focus across long conversations |
| Structured output format | "Instruction: / Input: / Solution:" | Parseable dialogue turns |
| Completion signal | <CAMEL_TASK_DONE> | Clear termination condition |
| Partial formatting | TextPrompt.format() with defaults | Templates work with unfilled keys |
Design Patterns
Patterns Used in CAMEL
| Pattern | Where | How |
|---|---|---|
| Abstract Factory | ModelFactory | Creates model backends from platform+type specs |
| Strategy | ModelManager scheduling | Pluggable scheduling strategies (round_robin, custom) |
| Template Method | BaseModelBackend | run() calls abstract _run() |
| Decorator / Metaclass | ModelBackendMeta | Auto-wraps run/arun with pre/post processing |
| Observer | Workforce task channel | Workers listen to shared TaskChannel |
| Composite | Workforce tree | Workforce contains Workers or sub-Workforces |
| Builder | PipelineTaskBuilder | Fluent API for constructing task pipelines |
| Wrapper / Adapter | StreamingChatAgentResponse | Wraps generator to be compatible with non-streaming |
| Mixin | RegisteredAgentToolkit | Adds agent registration to any toolkit |
| Value Object | TextPrompt(str) | Immutable string extension with template features |
Framework Comparison
| Feature | CAMEL | LangChain/LangGraph | CrewAI | AutoGen | PydanticAI |
|---|---|---|---|---|---|
| Core Paradigm | Role-playing agents | Chains & graphs | Crew of agents | Conversational agents | Type-safe agents |
| Agent Definition | Class inheritance | Runnable/LCEL | Decorator-based | Class-based | Decorator + generics |
| Memory | 3 types (chat, vector, hybrid) | Multiple backends | Short/long term | List-based | Minimal |
| Tool System | 80+ toolkits, auto-schema | Tool/Toolkit classes | Decorator-based | Function calling | Pydantic-validated |
| Multi-Agent | RolePlaying + Workforce | LangGraph state machines | Crew + Process | GroupChat | Manual composition |
| Model Support | 40+ backends | 100+ via integrations | Litellm-based | OpenAI-centric | Multiple providers |
| Streaming | Sync + async with accumulation | Full streaming support | Limited | Yes | Yes |
| MCP Support | Native (MCPAgent, MCPToolkit) | Plugin-based | Plugin | Plugin | Plugin |
| Sandboxed Execution | Docker, Daytona, HTTP runtimes | No native sandbox | No native sandbox | Docker executor | No native sandbox |
| RL Evaluation | Environments (gym-style) | No | No | No | No |
| Schema Synthesis | LLM-generates tool schemas | No | No | No | No |
| Output Synthesis | LLM-mocks tool execution | No | No | No | No |
| Auto-Summarization | Progressive + full | ConversationSummaryMemory | No | No | No |
| Type Safety | Moderate (Pydantic in tools) | Moderate | Low | Low | High (Pydantic-first) |
| Codebase Size | 155K LOC, 502 modules | ~300K+ LOC | ~30K LOC | ~50K LOC | ~10K LOC |
Strengths & Weaknesses
โ Strengths
- Massive toolkit ecosystem โ 80+ pre-built toolkits covering search, browser, cloud, communication, media, code exec, etc. Unmatched breadth.
- Model provider coverage โ 40+ backends with unified interface. Best-in-class provider diversity.
- Sophisticated memory system โ Three memory types including hybrid vector+chat. Auto-summarization with progressive compression is well-engineered.
- MCP native support โ First-class MCPAgent and MCPToolkit. MCP server capability built into BaseToolkit.
- Sandboxed runtimes โ Docker, Daytona, and HTTP runtimes for safe code execution. Rare in agent frameworks.
- RL evaluation โ Gym-style environments for agent benchmarking. Unique differentiator.
- Schema synthesis โ LLM can auto-generate or fix tool schemas. Novel feature.
- Workforce orchestration โ Hierarchical task decomposition with failure recovery, pipeline mode, and quality evaluation.
- Streaming maturity โ Full sync/async streaming with content accumulation, reasoning extraction, and compatible wrappers.
- Research heritage โ Born from academic research on multi-agent societies. Strong theoretical foundation.
โ Weaknesses
- ChatAgent bloat โ 6444 lines in a single class. Violates SRP. Hard to maintain, test, and extend. Should be decomposed.
- Workforce complexity โ 6239 lines in one file. Task assignment, decomposition, worker management, failure handling all tangled together.
- Tight coupling โ ChatAgent directly depends on memory, model, tools, summarization. Hard to swap or mock components.
- No graph-based orchestration โ Unlike LangGraph, there's no DAG/state-machine abstraction for complex workflows. RolePlaying is turn-based; Workforce is tree-based.
- Weak typing in core โ
BaseMessageis a dataclass, not Pydantic.OpenAIMessageisDict[str, Any]. Type safety is inconsistent. - Inconsistent abstractions โ Some agents inherit ChatAgent, others don't. Tool system has both
FunctionToolandBaseToolkitwith overlapping responsibilities. - Documentation gaps โ Many toolkits have minimal docs. Internal APIs are sparsely documented despite public visibility.
- Testing concerns โ 155K LOC with
synthesize_outputmode (mocking tool execution) suggests testing challenges. - Performance overhead โ Metaclass wrapping, auto-timeout decoration, and logging infrastructure add overhead to every model call.
- Configuration complexity โ ChatAgent has 25+ constructor parameters. Too many knobs for common use cases.
Verdict
CAMEL is the "Swiss Army Knife" of agent frameworks โ it has the broadest toolkit ecosystem and model coverage of any open-source agent framework. Its research roots give it unique features (RL environments, role-playing, schema synthesis) that no other framework offers.
However, this breadth comes at a cost: the core classes (ChatAgent, Workforce) are monolithic, making the framework harder to learn, extend, and maintain than more focused alternatives. The lack of a graph-based orchestration layer limits its applicability for complex, non-hierarchical workflows.
Best suited for: Research teams exploring multi-agent societies, developers needing maximum toolkit/model coverage out-of-the-box, and teams working on agent evaluation with RL-style environments.
Less ideal for: Production systems needing clean separation of concerns, teams wanting a minimal learning curve, and applications requiring complex DAG-based workflow orchestration.
Analysis generated from source code of camel-ai v0.2.91a4 ยท Repository: github.com/camel-ai/camel
502 Python modules analyzed ยท Key files: chat_agent.py (6444 LOC), workforce.py (6239 LOC), base_model.py (1049 LOC), function_tool.py (1197 LOC)