Skip to content

Commit 2a80bdf

Browse files
author
Davidson Gomes
committed
feat(agent): add Task Agent for structured task execution and improve context management
1 parent 198eb57 commit 2a80bdf

File tree

9 files changed

+375
-12
lines changed

9 files changed

+375
-12
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Add CrewAI agents
13+
- Add Task Agent for structured single-task execution
14+
- Improve context management in agent execution
1315

1416
## [0.0.9] - 2025-05-13
1517

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,39 @@ Allows organizing agents into a "crew" with specific tasks assigned to each agen
187187
}
188188
```
189189

190+
### 8. Task Agent
191+
192+
Executes a specific task using a target agent. Task Agent provides a streamlined approach for structured task execution, where the agent_id specifies which agent will process the task, and the task description can include dynamic content placeholders.
193+
194+
```json
195+
{
196+
"client_id": "{{client_id}}",
197+
"name": "web_search_task",
198+
"type": "task",
199+
"folder_id": "folder_id (optional)",
200+
"config": {
201+
"tasks": [
202+
{
203+
"agent_id": "search-agent-uuid",
204+
"description": "Search the web for information about {content}",
205+
"expected_output": "Comprehensive search results with relevant information"
206+
}
207+
],
208+
"sub_agents": ["post-processing-agent-uuid"]
209+
}
210+
}
211+
```
212+
213+
Key features of Task Agent:
214+
215+
- Passes structured task instructions to the designated agent
216+
- Supports variable content using {content} placeholder in the task description
217+
- Provides clear task definition with instructions and expected output format
218+
- Can execute sub-agents after the main task is completed
219+
- Simplifies orchestration for single-focused task execution
220+
221+
Task Agent is ideal for scenarios where you need to execute a specific, well-defined task with clear instructions and expectations.
222+
190223
### Common Characteristics
191224

192225
- All agent types can have sub-agents
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""add_task_agent_type_agents_table
2+
3+
Revision ID: 2df073c7b564
4+
Revises: 611d84e70bb2
5+
Create Date: 2025-05-14 11:46:39.573247
6+
7+
"""
8+
9+
from typing import Sequence, Union
10+
11+
from alembic import op
12+
import sqlalchemy as sa
13+
14+
15+
# revision identifiers, used by Alembic.
16+
revision: str = "2df073c7b564"
17+
down_revision: Union[str, None] = "611d84e70bb2"
18+
branch_labels: Union[str, Sequence[str], None] = None
19+
depends_on: Union[str, Sequence[str], None] = None
20+
21+
22+
def upgrade() -> None:
23+
"""Upgrade schema."""
24+
# ### commands auto generated by Alembic - please adjust! ###
25+
op.drop_constraint("check_agent_type", "agents", type_="check")
26+
op.create_check_constraint(
27+
"check_agent_type",
28+
"agents",
29+
"type IN ('llm', 'sequential', 'parallel', 'loop', 'a2a', 'workflow', 'crew_ai', 'task')",
30+
)
31+
# ### end Alembic commands ###
32+
33+
34+
def downgrade() -> None:
35+
"""Downgrade schema."""
36+
# ### commands auto generated by Alembic - please adjust! ###
37+
op.drop_constraint("check_agent_type", "agents", type_="check")
38+
op.create_check_constraint(
39+
"check_agent_type",
40+
"agents",
41+
"type IN ('llm', 'sequential', 'parallel', 'loop', 'a2a', 'workflow', 'crew_ai')",
42+
)
43+
# ### end Alembic commands ###

src/models/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ class Agent(Base):
123123

124124
__table_args__ = (
125125
CheckConstraint(
126-
"type IN ('llm', 'sequential', 'parallel', 'loop', 'a2a', 'workflow', 'crew_ai')",
126+
"type IN ('llm', 'sequential', 'parallel', 'loop', 'a2a', 'workflow', 'crew_ai', 'task')",
127127
name="check_agent_type",
128128
),
129129
)

src/schemas/schemas.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class AgentBase(BaseModel):
9898
goal: Optional[str] = Field(None, description="Agent goal or objective")
9999
type: str = Field(
100100
...,
101-
description="Agent type (llm, sequential, parallel, loop, a2a, workflow, crew_ai)",
101+
description="Agent type (llm, sequential, parallel, loop, a2a, workflow, crew_ai, task)",
102102
)
103103
model: Optional[str] = Field(
104104
None, description="Agent model (required only for llm type)"
@@ -137,9 +137,10 @@ def validate_type(cls, v):
137137
"a2a",
138138
"workflow",
139139
"crew_ai",
140+
"task",
140141
]:
141142
raise ValueError(
142-
"Invalid agent type. Must be: llm, sequential, parallel, loop, a2a, workflow or crew_ai"
143+
"Invalid agent type. Must be: llm, sequential, parallel, loop, a2a, workflow, crew_ai or task"
143144
)
144145
return v
145146

@@ -199,7 +200,7 @@ def validate_config(cls, v, values):
199200
raise ValueError(
200201
f'Agent {values["type"]} must have at least one sub-agent'
201202
)
202-
elif values["type"] == "crew_ai":
203+
elif values["type"] == "crew_ai" or values["type"] == "task":
203204
if not isinstance(v, dict):
204205
raise ValueError(f'Invalid configuration for agent {values["type"]}')
205206
if "tasks" not in v:

src/services/agent_builder.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from src.services.custom_agents.a2a_agent import A2ACustomAgent
4242
from src.services.custom_agents.workflow_agent import WorkflowAgent
4343
from src.services.custom_agents.crew_ai_agent import CrewAIAgent
44+
from src.services.custom_agents.task_agent import TaskAgent
4445
from src.services.apikey_service import get_decrypted_api_key
4546
from sqlalchemy.orm import Session
4647
from contextlib import AsyncExitStack
@@ -314,6 +315,55 @@ async def build_workflow_agent(
314315
logger.error(f"Error building Workflow agent: {str(e)}")
315316
raise ValueError(f"Error building Workflow agent: {str(e)}")
316317

318+
async def build_task_agent(
319+
self, root_agent
320+
) -> Tuple[TaskAgent, Optional[AsyncExitStack]]:
321+
"""Build a task agent with its sub-agents."""
322+
logger.info(f"Creating Task agent: {root_agent.name}")
323+
324+
agent_config = root_agent.config or {}
325+
326+
if not agent_config.get("tasks"):
327+
raise ValueError("tasks are required for Task agents")
328+
329+
try:
330+
# Get sub-agents if there are any
331+
sub_agents = []
332+
if root_agent.config.get("sub_agents"):
333+
sub_agents_with_stacks = await self._get_sub_agents(
334+
root_agent.config.get("sub_agents")
335+
)
336+
sub_agents = [agent for agent, _ in sub_agents_with_stacks]
337+
338+
# Additional configurations
339+
config = root_agent.config or {}
340+
341+
# Convert tasks to the expected format by TaskAgent
342+
tasks = []
343+
for task_config in config.get("tasks", []):
344+
task = CrewAITask(
345+
agent_id=task_config.get("agent_id"),
346+
description=task_config.get("description", ""),
347+
expected_output=task_config.get("expected_output", ""),
348+
)
349+
tasks.append(task)
350+
351+
# Create the Task agent
352+
task_agent = TaskAgent(
353+
name=root_agent.name,
354+
tasks=tasks,
355+
db=self.db,
356+
sub_agents=sub_agents,
357+
)
358+
359+
logger.info(f"Task agent created successfully: {root_agent.name}")
360+
361+
return task_agent, None
362+
363+
except Exception as e:
364+
logger.error(f"Error building Task agent: {str(e)}")
365+
raise ValueError(f"Error building CrewAI agent: {str(e)}")
366+
317367
async def build_crew_ai_agent(
318368
self, root_agent: Agent
319369
) -> Tuple[CrewAIAgent, Optional[AsyncExitStack]]:
@@ -434,7 +484,8 @@ async def build_agent(self, root_agent) -> Tuple[
434484
| LoopAgent
435485
| A2ACustomAgent
436486
| WorkflowAgent
437-
| CrewAIAgent,
487+
| CrewAIAgent
488+
| TaskAgent,
438489
Optional[AsyncExitStack],
439490
]:
440491
"""Build the appropriate agent based on the type of the root agent."""
@@ -446,5 +497,7 @@ async def build_agent(self, root_agent) -> Tuple[
446497
return await self.build_workflow_agent(root_agent)
447498
elif root_agent.type == "crew_ai":
448499
return await self.build_crew_ai_agent(root_agent)
500+
elif root_agent.type == "task":
501+
return await self.build_task_agent(root_agent)
449502
else:
450503
return await self.build_composite_agent(root_agent)

src/services/agent_service.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ async def create_agent(db: Session, agent: AgentCreate) -> Agent:
202202
if "api_key" not in agent.config or not agent.config["api_key"]:
203203
agent.config["api_key"] = generate_api_key()
204204

205-
elif agent.type == "crew_ai":
205+
elif agent.type == "crew_ai" or agent.type == "task":
206206
if not isinstance(agent.config, dict):
207207
agent.config = {}
208208
raise HTTPException(
@@ -213,7 +213,7 @@ async def create_agent(db: Session, agent: AgentCreate) -> Agent:
213213
if "tasks" not in agent.config:
214214
raise HTTPException(
215215
status_code=status.HTTP_400_BAD_REQUEST,
216-
detail="Invalid configuration: tasks is required for crew_ai agents",
216+
detail=f"Invalid configuration: tasks is required for {agent.type} agents",
217217
)
218218

219219
if not agent.config["tasks"]:
@@ -682,14 +682,14 @@ async def update_agent(
682682
if "config" not in agent_data:
683683
agent_data["config"] = agent_config
684684

685-
if ("type" in agent_data and agent_data["type"] == "crew_ai") or (
686-
agent.type == "crew_ai" and "config" in agent_data
685+
if ("type" in agent_data and agent_data["type"] in ["crew_ai", "task"]) or (
686+
agent.type in ["crew_ai", "task"] and "config" in agent_data
687687
):
688688
config = agent_data.get("config", {})
689689
if "tasks" not in config:
690690
raise HTTPException(
691691
status_code=status.HTTP_400_BAD_REQUEST,
692-
detail="Invalid configuration: tasks is required for crew_ai agents",
692+
detail=f"Invalid configuration: tasks is required for {agent_data.get('type', agent.type)} agents",
693693
)
694694

695695
if not config["tasks"]:

src/services/custom_agents/crew_ai_agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"""
22
┌──────────────────────────────────────────────────────────────────────────────┐
33
│ @author: Davidson Gomes │
4-
│ @file: a2a_agent.py
4+
│ @file: crew_ai_agent.py │
55
│ Developed by: Davidson Gomes │
6-
│ Creation date: May 13, 2025 │
6+
│ Creation date: May 14, 2025 │
77
│ Contact: [email protected]
88
├──────────────────────────────────────────────────────────────────────────────┤
99
│ @copyright © Evolution API 2025. All rights reserved. │

0 commit comments

Comments
 (0)