Skip to content

Commit fe58156

Browse files
hesamsheikha7m-1stWendong-Fan
authored
feat: Workforce Workflow (#3180)
Co-authored-by: a7m-1st <Ahmed.jimi.awelkeir500@gmail.com> Co-authored-by: Wendong-Fan <133094783+Wendong-Fan@users.noreply.github.com>
1 parent 5af13b4 commit fe58156

File tree

12 files changed

+2401
-213
lines changed

12 files changed

+2401
-213
lines changed

camel/agents/chat_agent.py

Lines changed: 278 additions & 154 deletions
Large diffs are not rendered by default.

camel/societies/workforce/single_agent_worker.py

Lines changed: 390 additions & 11 deletions
Large diffs are not rendered by default.

camel/societies/workforce/workforce.py

Lines changed: 273 additions & 10 deletions
Large diffs are not rendered by default.

camel/toolkits/context_summarizer_toolkit.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ def _save_summary(self, summary: str) -> str:
191191
summary (str): The summary text to save.
192192
193193
Returns:
194-
str: Success message or error message.
194+
str: "success" or error message starting with "Error:".
195195
"""
196196
try:
197197
# prepare metadata for unified markdown saving
@@ -221,7 +221,7 @@ def _save_history(self, memory_records: List["MemoryRecord"]) -> str:
221221
to save.
222222
223223
Returns:
224-
str: Success message or error message.
224+
str: "success" or error message starting with "Error:".
225225
"""
226226
try:
227227
# prepare metadata for markdown saving

camel/types/enums.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class RoleType(Enum):
3030

3131

3232
class ModelType(UnifiedModelType, Enum):
33-
DEFAULT = os.getenv("DEFAULT_MODEL_TYPE", "gpt-5-mini")
33+
DEFAULT = os.getenv("DEFAULT_MODEL_TYPE", "gpt-4.1-mini-2025-04-14")
3434

3535
GPT_3_5_TURBO = "gpt-3.5-turbo"
3636
GPT_4 = "gpt-4"

camel/utils/context_utils.py

Lines changed: 379 additions & 22 deletions
Large diffs are not rendered by default.
Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
#!/usr/bin/env python3
2+
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
15+
16+
17+
import os
18+
19+
from dotenv import load_dotenv
20+
21+
from camel.agents import ChatAgent
22+
from camel.configs import ChatGPTConfig
23+
from camel.logger import get_logger
24+
from camel.messages import BaseMessage
25+
from camel.models import ModelFactory
26+
from camel.societies.workforce import Workforce
27+
from camel.tasks.task import Task
28+
from camel.toolkits import MathToolkit
29+
from camel.types import ModelPlatformType, ModelType
30+
31+
load_dotenv()
32+
33+
logger = get_logger(__name__)
34+
35+
36+
def create_math_agent() -> ChatAgent:
37+
r"""Create a math agent with math tools.
38+
39+
Returns:
40+
ChatAgent: A configured math expert agent with MathToolkit tools.
41+
"""
42+
math_msg = BaseMessage.make_assistant_message(
43+
role_name="Math Expert",
44+
content=(
45+
"You are a math expert specialized in solving "
46+
"mathematical problems.You can perform calculations, "
47+
"solve equations, and work with various "
48+
"mathematical concepts. Use the math tools available to you."
49+
),
50+
)
51+
model = ModelFactory.create(
52+
model_platform=ModelPlatformType.OPENAI,
53+
model_type=ModelType.DEFAULT,
54+
model_config_dict=ChatGPTConfig().as_dict(),
55+
)
56+
return ChatAgent(
57+
system_message=math_msg,
58+
model=model,
59+
tools=MathToolkit().get_tools(),
60+
)
61+
62+
63+
def create_writer_agent() -> ChatAgent:
64+
r"""Create a writer agent without tools.
65+
66+
Returns:
67+
ChatAgent: A configured content writer agent without additional tools.
68+
"""
69+
writer_msg = BaseMessage.make_assistant_message(
70+
role_name="Content Writer",
71+
content=(
72+
"You are a skilled content writer who specializes in creating "
73+
"clear, engaging, and well-structured written content. You excel "
74+
"at storytelling, technical writing, and adapting your tone to "
75+
"different audiences."
76+
),
77+
)
78+
model = ModelFactory.create(
79+
model_platform=ModelPlatformType.DEFAULT,
80+
model_type=ModelType.DEFAULT,
81+
model_config_dict=ChatGPTConfig().as_dict(),
82+
)
83+
return ChatAgent(system_message=writer_msg, model=model)
84+
85+
86+
def demonstrate_first_session():
87+
r"""Demonstrate first workforce session with workflow saving.
88+
89+
Creates a workforce with math and writer agents, processes tasks,
90+
and saves the resulting workflows for future use.
91+
92+
Returns:
93+
Dict[str, str]: Results of the workflow saving operation.
94+
"""
95+
# Create workforce with two specialized agents
96+
workforce = Workforce("Simple Demo Team")
97+
98+
# Add math agent with math tools
99+
math_agent = create_math_agent()
100+
workforce.add_single_agent_worker(
101+
description="math_expert",
102+
worker=math_agent,
103+
enable_workflow_memory=True, # Enable to save workflows later
104+
)
105+
106+
# Add writer agent without tools
107+
writer_agent = create_writer_agent()
108+
workforce.add_single_agent_worker(
109+
description="content_writer",
110+
worker=writer_agent,
111+
enable_workflow_memory=True, # Enable to save workflows later
112+
)
113+
114+
# Assign one task to each agent
115+
tasks = [
116+
Task(
117+
content="Calculate the compound interest on $1000 invested at 5% "
118+
"annual rate for 3 years",
119+
id="math_task",
120+
),
121+
Task(
122+
content="Write a one-paragraph creative story about a robot "
123+
"learning to paint",
124+
id="writing_task",
125+
),
126+
]
127+
128+
for task in tasks:
129+
try:
130+
workforce.process_task(task)
131+
except Exception as e:
132+
logger.warning(f"Failed to process task {task.id}: {e}")
133+
134+
# Save workflows after completing tasks
135+
saved_workflows = workforce.save_workflow_memories()
136+
137+
return saved_workflows
138+
139+
140+
def demonstrate_second_session():
141+
r"""Demonstrate second workforce session with workflow loading.
142+
143+
Creates a new workforce instance, loads previously saved workflows,
144+
and processes new tasks with the loaded workflow context.
145+
146+
Returns:
147+
Dict[str, bool]: Results of the workflow loading operation.
148+
"""
149+
# Create new workforce (simulating new session/process)
150+
workforce = Workforce("Simple Demo Team - Session 2")
151+
152+
# Add workers with same descriptive names as before
153+
math_agent = create_math_agent()
154+
workforce.add_single_agent_worker(
155+
description="math_expert", # Same description = loads matching
156+
# workflow
157+
worker=math_agent,
158+
enable_workflow_memory=False, # Not saving in this session
159+
)
160+
161+
writer_agent = create_writer_agent()
162+
workforce.add_single_agent_worker(
163+
description="content_writer", # Same description = loads
164+
# matching workflow
165+
worker=writer_agent,
166+
enable_workflow_memory=False, # Not saving in this session
167+
)
168+
169+
# Load previous workflows
170+
loaded_workflows = workforce.load_workflow_memories()
171+
172+
# Process new tasks with loaded workflow context
173+
new_tasks = [
174+
Task(
175+
content="Calculate the area of a circle with radius 7.5 meters",
176+
id="new_math_task",
177+
),
178+
Task(
179+
content="Write a brief technical explanation of machine "
180+
"learning for beginners",
181+
id="new_writing_task",
182+
),
183+
]
184+
185+
for task in new_tasks:
186+
try:
187+
workforce.process_task(task)
188+
except Exception as e:
189+
logger.warning(f"Failed to process task {task.id}: {e}")
190+
191+
return loaded_workflows
192+
193+
194+
def demonstrate_workflow_file_management() -> None:
195+
r"""Demonstrate workflow file management and inspection.
196+
197+
Shows information about where workflow files are stored and how
198+
to inspect the saved workflow data.
199+
"""
200+
201+
# Get the base directory for workforce workflows
202+
camel_workdir = os.environ.get("CAMEL_WORKDIR")
203+
if camel_workdir:
204+
base_dir = os.path.join(camel_workdir, "workforce_workflows")
205+
else:
206+
base_dir = "workforce_workflows"
207+
208+
logger.info(f"Workforce workflows are stored in: {base_dir}")
209+
210+
# Show how to check for existing workflow files
211+
if os.path.exists(base_dir):
212+
session_dirs = [
213+
d
214+
for d in os.listdir(base_dir)
215+
if os.path.isdir(os.path.join(base_dir, d))
216+
]
217+
logger.info(f"Found {len(session_dirs)} session directories")
218+
219+
for session_dir in session_dirs[:3]: # Show first 3 sessions
220+
session_path = os.path.join(base_dir, session_dir)
221+
workflow_files = [
222+
f for f in os.listdir(session_path) if f.endswith('.md')
223+
]
224+
logger.info(
225+
f"Session {session_dir}: {len(workflow_files)} workflow files"
226+
)
227+
else:
228+
logger.info("No workflow directory found yet")
229+
230+
231+
def main() -> None:
232+
try:
233+
# First session to execute tasks and save workflows
234+
saved_results = demonstrate_first_session()
235+
print(f"Workflow save results: {saved_results}")
236+
237+
# Second session to load workflows and execute new tasks
238+
loaded_results = demonstrate_second_session()
239+
print(f"Workflow load results: {loaded_results}")
240+
241+
except Exception as e:
242+
logger.error(f"Demonstration failed: {e}")
243+
raise
244+
245+
246+
if __name__ == "__main__":
247+
main()
248+
249+
"""
250+
===============================================================================
251+
Workflows saved:
252+
workforce_workflows/session_20250925_150330_302341/content_writer_workflow.md
253+
workforce_workflows/session_20250925_150330_302341/math_expert_workflow.md
254+
255+
Saved workflow in math_expert_workflow.md
256+
<MARKDOWN CONTENT>
257+
## Metadata
258+
259+
- session_id: session_20250925_150330_302341
260+
- working_directory: .../camel/workforce_workflows/
261+
session_20250925_150330_302341
262+
- created_at: ...
263+
- base_directory: .../Codebase/camel/workforce_workflows
264+
- agent_id: 687b2e57-11ce-4138-9990-4d5ab58d2acc
265+
- message_count: 3
266+
267+
## WorkflowSummary
268+
269+
### Task Title
270+
Calculate compound interest
271+
272+
### Task Description
273+
Calculate the future value of $1000 at 5% annual interest compounded
274+
once per year for 3 years, and produce a plain-text result containing:
275+
the compound interest formula with symbol definitions and numeric
276+
substitution, balances at end of Years 1-3, the final amount,
277+
total interest earned (all rounded to 2 decimals), and a final
278+
one-line JSON object with numeric values to two decimals.
279+
280+
281+
### Tools
282+
[Bullet point list of tools used]
283+
284+
### Steps
285+
1. Identify the required formula: A = P*(1 + r)^n.
286+
2. Substitute given values into the formula: P = 1000, r = 0.05, n = 3.
287+
3. Compute the sum inside parentheses: calculate 1 + r = 1.05.
288+
4. Compute the exponentiation: calculate 1.05^3 = 1.157625.
289+
5. Multiply by principal: compute A = 1000 * 1.157625 = 1157.625 (unrounded).
290+
6. Compute total interest unrounded: Interest = A - P =
291+
1157.625 - 1000 = 157.625.
292+
7. Round results to 2 decimals and format as USD: A → $1157.63;
293+
Interest → $157.63.
294+
8. Return deliverables: formula, each arithmetic step with numbers substituted,
295+
unrounded A, rounded A in USD, and rounded interest in USD.
296+
297+
### Failure And Recovery Strategies
298+
(No failure and recovery strategies recorded)
299+
300+
### Notes And Observations
301+
No errors encountered.
302+
</MARKDOWN CONTENT>
303+
304+
System message of math agent after loading workflow:
305+
You are a math expert specialized in solving mathematical problems.
306+
You can perform calculations, solve equations, and work with various
307+
mathematical concepts.
308+
Use the math tools available to you.
309+
310+
--- Workflow Memory ---
311+
The following is the context from a previous session or workflow which might be
312+
useful for to the current task. This information might help you understand the
313+
background, choose which tools to use, and plan your next steps.
314+
315+
## WorkflowSummary
316+
[The Workflow Summary from the above]
317+
"""

test/agents/test_chat_agent.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
[
8484
ModelFactory.create(
8585
model_platform=ModelPlatformType.OPENAI,
86-
model_type=ModelType.GPT_5_MINI,
86+
model_type=ModelType.DEFAULT,
8787
),
8888
pytest.param(None, marks=pytest.mark.model_backend),
8989
],
@@ -103,11 +103,11 @@ def test_chat_agent(model, step_call_count=3):
103103
assistant_without_sys_msg = ChatAgent(model=model)
104104

105105
assert str(assistant_with_sys_msg) == (
106-
"ChatAgent(doctor, " f"RoleType.ASSISTANT, {ModelType.GPT_5_MINI})"
106+
"ChatAgent(doctor, " f"RoleType.ASSISTANT, {ModelType.DEFAULT})"
107107
)
108108
assert str(assistant_without_sys_msg) == (
109109
"ChatAgent(assistant, "
110-
f"RoleType.ASSISTANT, {UnifiedModelType(ModelType.GPT_5_MINI)})"
110+
f"RoleType.ASSISTANT, {UnifiedModelType(ModelType.DEFAULT)})"
111111
)
112112

113113
for assistant in [assistant_with_sys_msg, assistant_without_sys_msg]:

0 commit comments

Comments
 (0)