Compare commits

...

103 Commits

Author SHA1 Message Date
Brandon Hancock
641921eb6c capture result from recursive call 2024-07-15 13:59:58 -04:00
Brandon Hancock
a02d2fb93e Add return statement to recursive call 2024-07-15 13:40:51 -04:00
Gui Vieira
b93632a53a [DO NOT MERGE] Provide inputs on crew creation (#898)
* Provide inputs on crew creation

* Better naming

* Add crew id and task index to tasks

* Fix type again
2024-07-15 09:00:02 -03:00
Eduardo Chiarotti
09938641cd feat: add max retry limit to agent execution (#899)
* feat: add max retry limit to agent execution

* feat: add test to max retry limit feature

* feat: add code execution docstring

---------

Co-authored-by: João Moura <joaomdmoura@gmail.com>
2024-07-15 08:58:50 -03:00
Brandon Hancock (bhancock_ai)
7acf0b2107 Feature/use converter instead of manually trimming (#894)
* Exploring output being passed to tool selector to see if we can better format data

* WIP. Adding JSON repair functionality

* Almost done implementing JSON repair. Testing fixes vs current base case.

* More action cleanup with additional tests

* WIP. Trying to figure out what is going on with tool descriptions

* Update tool description generation

* WIP. Trying to find out what is causing the tools to duplicate

* Replacing tools properly instead of duplicating them accidentally

* Fixing issues for MR

* Update dependencies for JSON_REPAIR

* More cleaning up pull request

* preppering for call

* Fix type-checking issues

---------

Co-authored-by: João Moura <joaomdmoura@gmail.com>
2024-07-15 08:53:41 -03:00
OP (oppenheimer)
4eb4073661 Add Groq - OpenAI Compatible API - details (#934) 2024-07-14 16:11:54 -03:00
Brandon Hancock (bhancock_ai)
7b53457ef3 Feature/kickoff consistent output (#847)
* Cleaned up task execution to now have separate paths for async and sync execution. Updating all kickoff functions to return CrewOutput. WIP. Waiting for Joao feedback on async task execution with task_output

* Consistently storing async and sync output for context

* outline tests I need to create going forward

* Major rehaul of TaskOutput and CrewOutput. Updated all tests to work with new change. Need to add in a few final tricky async tests and add a few more to verify output types on TaskOutput and CrewOutput.

* Encountering issues with callback. Need to test on main. WIP

* working on tests. WIP

* WIP. Figuring out disconnect issue.

* Cleaned up logs now that I've isolated the issue to the LLM

* more wip.

* WIP. It looks like usage metrics has always been broken for async

* Update parent crew who is managing for_each loop

* Merge in main to bugfix/kickoff-for-each-usage-metrics

* Clean up code for review

* Add new tests

* Final cleanup. Ready for review.

* Moving copy functionality from Agent to BaseAgent

* Fix renaming issue

* Fix linting errors

* use BaseAgent instead of Agent where applicable

* Fixing missing function. Working on tests.

* WIP. Needing team to review change

* Fixing issues brought about by merge

* WIP

* Implement major fixes from yesterdays group conversation. Now working on tests.

* The majority of tasks are working now. Need to fix converter class

* Fix final failing test

* Fix linting and type-checker issues

* Add more tests to fully test CrewOutput and TaskOutput changes

* Add in validation for async cannot depend on other async tasks.

* Update validators and tests
2024-07-11 00:35:02 -03:00
João Moura
691b094a40 adding new docs 2024-07-08 03:15:14 -04:00
prime-computing-lab
68e9e54c88 Update MDXSearchTool.md (#745)
description fixed to markdown language instead of marketing search
2024-07-08 02:21:00 -03:00
João Moura
d0d99125c4 updating crewAI-tools verison 2024-07-08 01:17:22 -04:00
Taleb
129000d01f Performed spell check across most of code base (#882)
* Performed spell check across the entire documentation

Thank you once again!

* Performed spell check across the most of code base
Folders been checked:
- agents
- cli
- memory
- project
- tasks
- telemetry
- tools
- translations
2024-07-07 13:00:05 -03:00
WellyngtonF
47f9d026dd passing cloned agents when copying context (#885) 2024-07-07 12:58:38 -03:00
Gui Vieira
b75b0b5552 Emit task created (#875)
* Emit task created

* Limit data to shared crews
2024-07-07 12:58:24 -03:00
João Moura
3dd6249f1e TYPO 2024-07-06 20:03:54 -04:00
João Moura
8451113039 new docs 2024-07-06 16:32:00 -04:00
João Moura
a79b216875 preparing new version 2024-07-06 12:26:41 -04:00
João Moura
52217c2f63 updating dependencies and fixing tests (#878) 2024-07-06 02:14:52 -03:00
Eelke van den Bos
7edacf6e24 Add converter_cls option to Task (#800)
* Add converter_cls option to Task

Fixes #799

* Update task_test.py

* Update task.py

* Update task.py

* Update task_test.py

* Update task.py

* Update task.py

* Update task.py

* Update task.py

---------

Co-authored-by: João Moura <joaomdmoura@gmail.com>
2024-07-06 02:01:39 -03:00
João Moura
58558a1950 TYPO 2024-07-06 00:34:50 -04:00
Ikko Eltociear Ashimine
1607c85ae5 chore: fix typo (#810)
* chore: update converter.py

attemps -> attempts

* chore: update tool_usage.py

attemps -> attempts
2024-07-06 01:33:48 -03:00
Alex Brinsmead
a6ff342948 Fix incorrect definition of RAG in GithubTool docs (#864) 2024-07-06 01:31:51 -03:00
Taleb
d2eb54ebf8 Performed spell check across the entire documentation (#872)
Thank you once again!
2024-07-06 01:30:40 -03:00
Eduardo Chiarotti
a41bd18599 Fix/async tasks (#877)
* fix: async tasks calls

* fix: some issue along with some type check errors

* fix: some issue along with some type check errors

* fix: async test
2024-07-06 01:30:07 -03:00
Eduardo Chiarotti
bb64c80964 fix: Fix tests (#873)
* fix: call asserts

* fix: test_increment_tool_errors

* fix: test_increment_delegations_for_sequential_process

* fix: test_increment_delegations_for_hierarchical_process

* fix: test_code_execution_flag_adds_code_tool_upon_kickoff

* fix: test_tool_usage_information_is_appended_to_agent

* fix: try to fix test_crew_full_output

* fix: try to fix test_crew_full_output

* fix: test remove vcr to test crew_test test

* fix: comment test to see if ci passes

* fix: comment test to see if ci passes

* fix: test changing prompt tokens to get error on CI

* fix: test changing prompt tokens to get error on CI

* fix: test changing prompt tokens to get error on CI

* fix: test changing prompt tokens to get error on CI

* fix: test new approach

* fix: comment funciont not working in CI

* fix: github python version

* fix: remove need of vcr

* fix: fix and add comments for all type checking errors
2024-07-05 09:06:56 -03:00
João Moura
2fb56f1f9f Adding support to force a tool return to be the final answer. (#867)
* Adding support to force a tool return to be the final answer.
This will at the end of the execution return the tool output.
It will return the output of the latest tool with the flag

* Update src/crewai/agent.py

Co-authored-by: Gui Vieira <guilherme_vieira@me.com>

* Update tests/agent_test.py

Co-authored-by: Gui Vieira <guilherme_vieira@me.com>

---------

Co-authored-by: Gui Vieira <guilherme_vieira@me.com>
2024-07-04 16:36:00 -03:00
MO Jr
35676fe2f5 Update Crews.md (#868)
Fix misspelling
2024-07-04 16:35:07 -03:00
Eduardo Chiarotti
81ed6f177e fix: file_handler issue (#869)
* fix: file_handler issue

* fix: add logic for the trained_agent data
2024-07-04 16:34:43 -03:00
João Moura
4bcd1df6bb TYPO 2024-07-03 18:41:52 -04:00
João Moura
6fae56dd60 TYPO 2024-07-03 18:41:52 -04:00
João Moura
430f0e9013 TYPO 2024-07-03 18:41:52 -04:00
João Moura
d7f080a978 fix agentops attribute 2024-07-03 18:41:52 -04:00
Lorenze Jay
5d18f73654 Lj/optional agent in task bug (#843)
* fixed bug for manager overriding task agent and then added pydanic valditors to sequential when no agent is added to task

* better test and fixed task.agent logic

* fixed tests and better validator message

* added validator for async_execution true in tasks whenever in hierarchical run
2024-07-03 18:45:53 -03:00
Brandon Hancock (bhancock_ai)
57fc079267 Bugfix/kickoff for each usage metrics (#844)
* WIP. Figuring out disconnect issue.

* Cleaned up logs now that I've isolated the issue to the LLM

* more wip.

* WIP. It looks like usage metrics has always been broken for async

* Update parent crew who is managing for_each loop

* Merge in main to bugfix/kickoff-for-each-usage-metrics

* Clean up code for review

* Add new tests

* Final cleanup. Ready for review.

* Moving copy functionality from Agent to BaseAgent

* Fix renaming issue

* Fix linting errors

* use BaseAgent instead of Agent where applicable
2024-07-03 15:30:53 -03:00
Alex Brinsmead
706f4cd74a Fix typos in EN "human_feedback" string (#859)
* Fix typo in EN "human_feedback" string

* Fix typos in EN "human_feedback" string
2024-07-03 15:26:58 -03:00
Taleb
2e3646cc96 Improved documentation for training module usage (#860)
- Added detailed steps for training the crew programmatically.
- Clarified the distinction between using the CLI and programmatic approaches.

This update makes it easier for users to understand how to train their crew both through the CLI and programmatically, whether using a UI or API endpoints.

Again Thank you to the author for the great project and the excellent foundation provided!
2024-07-03 15:26:32 -03:00
Brandon Hancock (bhancock_ai)
844cc515d5 Fix issue agentop poetry install issue (#863)
* Fix issue agentop poetry install issue

* Updated install requirements tests to fail if .lock becomes out of sync with poetry install. Cleaned up old issues that were merged back in.
2024-07-03 15:22:32 -03:00
Braelyn Boynton
f47904134b Add back AgentOps as Optional Dependency (#543)
* implements agentops with a langchain handler, agent tracking and tool call recording

* track tool usage

* end session after completion

* track tool usage time

* better tool and llm tracking

* code cleanup

* make agentops optional

* optional dependency usage

* remove telemetry code

* optional agentops

* agentops version bump

* remove org key

* true dependency

* add crew org key to agentops

* cleanup

* Update pyproject.toml

* Revert "true dependency"

This reverts commit e52e8e9568.

* Revert "cleanup"

This reverts commit 7f5635fb9e.

* optional parent key

* agentops 0.1.5

* Revert "Revert "cleanup""

This reverts commit cea33d9a5d.

* Revert "Revert "true dependency""

This reverts commit 4d1b460b

* cleanup

* Forcing version 0.1.5

* Update pyproject.toml

* agentops update

* noop

* add crew tag

* black formatting

* use langchain callback handler to support all LLMs

* agentops version bump

* track task evaluator

* merge upstream

* Fix typo in instruction en.json (#676)

* Enable search in docs (#663)

* Clarify text in docstring (#662)

* Update agent.py (#655)

Changed default model value from gpt-4 to gpt-4o.
Reasoning.
gpt-4 costs 30$ per million tokens while gpt-4o costs 5$.
This is more cost friendly for default option.

* Update README.md (#652)

Rework example so that if you use a custom LLM it doesn't throw code errors by uncommenting.

* Update BrowserbaseLoadTool.md (#647)

* Update crew.py (#644)

Fixed Type on line 53

* fixes #665 (#666)

* Added timestamp to logger (#646)

* Added timestamp to logger

Updated the logger.py file to include timestamps when logging output. For example:

 [2024-05-20 15:32:48][DEBUG]: == Working Agent: Researcher
 [2024-05-20 15:32:48][INFO]: == Starting Task: Research the topic
 [2024-05-20 15:33:22][DEBUG]: == [Researcher] Task output:

* Update tool_usage.py

* Revert "Update tool_usage.py"

This reverts commit 95d18d5b6f.

incorrect bramch for this commit

* support skip auto end session

* conditional protect agentops use

* fix crew logger bug

* fix crew logger bug

* Update crew.py

* Update tool_usage.py

---------

Co-authored-by: João Moura <joaomdmoura@gmail.com>
Co-authored-by: Howard Gil <howardbgil@gmail.com>
Co-authored-by: Olivier Roberdet <niox5199@gmail.com>
Co-authored-by: Paul Sanders <psanders1@gmail.com>
Co-authored-by: Anudeep Kolluri <50168940+Anudeep-Kolluri@users.noreply.github.com>
Co-authored-by: Mike Heavers <heaversm@users.noreply.github.com>
Co-authored-by: Mish Ushakov <10400064+mishushakov@users.noreply.github.com>
Co-authored-by: theCyberTech - Rip&Tear <84775494+theCyberTech@users.noreply.github.com>
Co-authored-by: Saif Mahmud <60409889+vmsaif@users.noreply.github.com>
2024-07-02 21:52:15 -03:00
Salman Faroz
d72b00af3c Update Sequential.md (#849)
To Resolve : 
pydantic_core._pydantic_core.ValidationError: 1 validation error for Task
expected_output
Field required [type=missing, input_value=, input_type=dict]
For further information visit https://errors.pydantic.dev/2.6/v/missing

"Expected Output" is mandatory now as it forces people to be specific about the expected result and get better result


refer : https://github.com/joaomdmoura/crewAI/issues/308
2024-07-02 21:17:53 -03:00
Taleb
bd053a98c7 Enhanced documentation for readability and clarity (#855)
- Added a "Parameters" column to attribute tables. Improved overall document formatting for enhanced readability and ease of use.

Thank you to the author for the great project and the excellent foundation provided!
2024-07-02 21:17:04 -03:00
Lorenze Jay
c18208ca59 fixed mixin (#831)
* fixed mixin

* WIP: fixing types

* type fixes on mixin
2024-07-02 21:16:26 -03:00
João Moura
acbe5af8ce preparing new version 2024-07-02 09:03:20 -07:00
Eduardo Chiarotti
c81146505a docs: Update training feature/code interpreter docs (#852)
* docs: remove training docs from README

* docs: add CodeinterpreterTool to docs and update docs

* docs: fix name of tool
2024-07-02 13:00:37 -03:00
João Moura
6b9a1d4040 adding link to docs 2024-07-01 18:41:31 -07:00
João Moura
508fbd49e9 preparing new version 2024-07-01 18:28:11 -07:00
João Moura
e18a6c6bb8 updatign tools 2024-07-01 15:25:29 -07:00
João Moura
16237ef393 rollback update to new version 2024-07-01 15:25:10 -07:00
João Moura
5332d02f36 preparing new version 2024-07-01 15:12:22 -07:00
João Moura
7258120a0d preparing new version 2024-07-01 15:10:13 -07:00
João Moura
8b7bc69ba1 preparing new version 2024-07-01 08:41:13 -07:00
João Moura
5a807eb93f preparing new version 2024-07-01 06:08:46 -07:00
João Moura
130682c93b preparing new version 2024-07-01 05:48:47 -07:00
João Moura
02e29e4681 new docs 2024-07-01 05:32:22 -07:00
João Moura
6943eb4463 small formatting details 2024-07-01 05:32:22 -07:00
João Moura
939a18a4d2 Updating docs 2024-07-01 05:32:22 -07:00
João Moura
ccbe415315 updating docs 2024-07-01 05:32:22 -07:00
João Moura
511af98dea small refractoring for new version 2024-07-01 05:32:22 -07:00
gpu7
a9d94112f5 bugfix in python script sample code (#787)
Add the line:

process = Process.sequential
2024-07-01 00:23:06 -03:00
JoePro
1bca6029fe Update LLM-Connections.md (#796)
Revised to utilize Ollama from langchain.llms instead as the functionality from the other method simply doesn't work when delegating.

Co-authored-by: João Moura <joaomdmoura@gmail.com>
2024-07-01 00:22:38 -03:00
Eelke van den Bos
c027aa8bf6 Set manager verbosity to crew verbosity by default (#797)
Fixes #793
2024-07-01 00:20:39 -03:00
finecwg
ce7d86e0df Update tool_usage.py (#828)
fixed error for some cases with Pandas DataFrame:

ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
2024-07-01 00:19:36 -03:00
Bruno Tanabe
5dfaf866c9 fix: Fix grammar error in documentation 'Crew Attributes' (#836)
Correction of grammar error in the CrewAI documentation, on the page 'https://docs.crewai.com/core-concepts/Crews/' it says 'ustom' instead of 'Custom'.
2024-07-01 00:16:06 -03:00
Gui Vieira
5b66e87621 Improve telemetry (#818)
* Improve telemetry

* Minor adjustments

* Try to fix typing error

* Try to fix typing error [2]
2024-06-28 20:05:47 -03:00
João Moura
851dd0f84f preparing new version 2024-06-27 11:04:08 -07:00
Eduardo Chiarotti
2188358f13 docs: add docs for training (#824) 2024-06-27 14:56:32 -03:00
Lorenze Jay
10997dd175 Lorenzejay/byoa (#776)
* better spacing

* works with llama index

* works on langchain custom just need delegation to work

* cleanup for custom_agent class

* works with different argument expectations for agent_executor

* cleanup for hierarchial process, better agent_executor args handler and added to the crew agent doc page

* removed code examples for langchain + llama index, added to docs instead

* added key output if return is not a str for and added some tests

* added hinting for CustomAgent class

* removed pass as it was not needed

* closer just need to figuire ou agentTools

* running agents - llamaindex and langchain with base agent

* some cleanup on baseAgent

* minimum for agent to run for base class and ensure it works with hierarchical process

* cleanup for original agent to take on BaseAgent class

* Agent takes on langchainagent and cleanup across

* token handling working for usage_metrics to continue working

* installed llama-index, updated docs and added better name

* fixed some type errors

* base agent holds token_process

* heirarchail process uses proper tools and no longer relies on hasattr for token_processes

* removal of test_custom_agent_executions

* this fixes copying agents

* leveraging an executor class for trigger llamaindex agent

* llama index now has ask_human

* executor mixins added

* added output converter base class

* type listed

* cleanup for output conversions and tokenprocess eliminated redundancy

* properly handling tokens

* simplified token calc handling

* original agent with base agent builder structure setup

* better docs

* no more llama-index dep

* cleaner docs

* test fixes

* poetry reverts and better docs

* base_agent_tools set for third party agents

* updated task and test fix
2024-06-27 14:56:08 -03:00
Eduardo Chiarotti
da9cc5f097 fix: fix trainig_data error (#820)
* fix: fix trainig_data error

* fix: fix lack crew on agent

* fix: fix lack crew on agent executor
2024-06-27 12:58:20 -03:00
Eduardo Chiarotti
c005ec3f78 fix: fix tests (#814) 2024-06-27 05:45:23 -03:00
Eduardo Chiarotti
6018fe5872 feat: add CodeInterpreterTool to run when enable code execution (#804)
* feat: add CodeInterpreterTool to run when enable code execution is allowed on agent

* feat: change to allow_code_execution

* feat: add readme for CodeInterpreterTool
2024-06-27 02:25:39 -03:00
Nuraly
bf0e70999e Update Agents.md (#816)
Made a space to ensure that Header formatting is displayed correctly on the website
2024-06-27 02:23:18 -03:00
Eduardo Chiarotti
175d5b3dd6 feat: Add Train feature for Crews (#686)
* feat: add training logic to agent and crew

* feat: add training logic to agent executor

* feat: add input parameter  to cli command

* feat: add utilities for the training logic

* feat: polish code, logic and add private variables

* feat: add docstring and type hinting to executor

* feat: add constant file, add constant to code

* feat: fix name of training handler function

* feat: remove unused var

* feat: change file handler file name

* feat: Add training handler file, class and change on the code

* feat: fix name error from file

* fix: change import to adapt to logic

* feat: add training handler test

* feat: add tests for file and training_handler

* feat: add test for task evaluator function

* feat: change text to fit in-screen

* feat: add test for train function

* feat: add test for agent training_handler function

* feat: add test for agent._use_trained_data
2024-06-27 02:22:34 -03:00
Bruno Tanabe
9e61b8325b fix: Fix grammar error in documentation in PDF Search Tool (#819)
Correction of grammar error in the CrewAI documentation, on the page 'https://docs.crewai.com/tools/PDFSearchTool/' it says 'Optinal' instead of 'Optional'.
2024-06-27 00:41:22 -03:00
João Moura
c4d76cde8f updating docs 2024-06-22 19:49:50 -03:00
João Moura
9c44fd8c4a preparing new version 2024-06-22 17:47:35 -03:00
João Moura
f9f8c8f336 Preparing new version 2024-06-22 17:01:22 -03:00
João Moura
0fb3ccb9e9 preapring to cut new version 2024-06-20 12:58:50 -03:00
João Moura
0e5fd0be2c addding new kickoff docs 2024-06-20 02:46:13 -03:00
João Moura
1b45daee49 adding new docs to the menu 2024-06-20 02:24:02 -03:00
João Moura
9f384e3fc1 Updating Docs 2024-06-20 02:19:35 -03:00
Brandon Hancock (bhancock_ai)
377f919d42 Resolved Merge Conflicts for PR #712: Remove Hyphen in co-workers (#786)
* removed hyphen in co-workers

* Fix issue with AgentTool agent selection. The LLM included double quotes in the agent name which messed up the string comparison. Added additional types. Cleaned up error messaging.

* Remove duplicate import

* Improve explanation

* Revert poetry.lock changes

* Fix missing line in poetry.lock

---------

Co-authored-by: madmag77 <goncharov.artemv@gmail.com>
2024-06-18 16:57:56 -03:00
João Moura
e6445afac5 fixing bug to multiple crews on yaml format in the same project 2024-06-18 02:32:53 -03:00
Lorenze Jay
095015d397 Lorenzejay/crew kickoff union type (#767)
* added extra parameter for kickoff to return token usage count after result

* added output_token_usage to class and in full_output

* logger duplicated

* added more types

* added usage_metrics to full output instead

* added more to the description on full_output

* possible mispacing

* updated kickoff return types to be either string or dict applicable when full_output is set

* removed duplicates
2024-06-14 14:23:55 -03:00
Lorenze Jay
614183cbb1 fixes crewai docs assembling crew code block example code (#768) 2024-06-14 14:23:30 -03:00
Dan McKinley
0bc92a284d updates instructor to the latest version. (#760)
* updates instructor to the latest version. adds jsonref, which instructor seems to depend on.

* updates embedchain reference, necessary for python 3.12
2024-06-14 01:57:40 -03:00
Lorenze Jay
d3b6640b4a added usage_metrics to full output (#756)
* added extra parameter for kickoff to return token usage count after result

* added output_token_usage to class and in full_output

* logger duplicated

* added more types

* added usage_metrics to full output instead

* added more to the description on full_output

* possible mispacing
2024-06-12 14:18:52 -03:00
Guangqiang Lu
a1a48888c3 add datetime import for logger.py (#702) 2024-06-11 16:43:15 -03:00
Matt Thompson
bb622bf747 fix: correct default model (gpt-4o), correct token counts, and correct TaskOutput attributes (added agent) (#749)
* fix: 'from datetime import datetime for logging' to print the timestamp

* fix: correct default model (gpt-4o), correct token counts, and correct TaskOutput attributes (added agent)

* test: verify Task callback data is an instance of TaskOutput
2024-06-11 15:29:22 -03:00
Brandon Hancock (bhancock_ai)
946c56494e Feature/kickoff for each sync (#680)
* Sync with deep copy working now

* async working!!

* Clean up code for review

* Fix naming

---------

Co-authored-by: João Moura <joaomdmoura@gmail.com>
2024-06-11 12:51:39 -03:00
Taradepan R
2a0e21ca76 updated the import for cohere llm (#696) 2024-06-04 03:32:23 -03:00
Karthik Kalyanaraman
ea893432e8 Add Langtrace to the "How to" docs for CrewAI Agent Observability (#634)
* Add files via upload

* Create Langtrace-Observability.md

* Rename crewai-agentops-stats.png to crewai-langtrace-stats.png
2024-05-29 02:14:29 -03:00
theCyberTech - Rip&Tear
bf40956491 Added timestamp to logger (#646)
* Added timestamp to logger

Updated the logger.py file to include timestamps when logging output. For example:

 [2024-05-20 15:32:48][DEBUG]: == Working Agent: Researcher
 [2024-05-20 15:32:48][INFO]: == Starting Task: Research the topic
 [2024-05-20 15:33:22][DEBUG]: == [Researcher] Task output:

* Update tool_usage.py

* Revert "Update tool_usage.py"

This reverts commit 95d18d5b6f.

incorrect bramch for this commit
2024-05-26 01:32:16 -03:00
Saif Mahmud
48948e1217 fixes #665 (#666) 2024-05-26 01:31:28 -03:00
theCyberTech - Rip&Tear
27412c89dd Update crew.py (#644)
Fixed Type on line 53
2024-05-24 00:06:27 -03:00
Mish Ushakov
56f1d24e9d Update BrowserbaseLoadTool.md (#647) 2024-05-24 00:05:52 -03:00
Mike Heavers
ab066a11a8 Update README.md (#652)
Rework example so that if you use a custom LLM it doesn't throw code errors by uncommenting.
2024-05-24 00:05:32 -03:00
Anudeep Kolluri
e35e81e554 Update agent.py (#655)
Changed default model value from gpt-4 to gpt-4o.
Reasoning.
gpt-4 costs 30$ per million tokens while gpt-4o costs 5$.
This is more cost friendly for default option.
2024-05-24 00:04:53 -03:00
Paul Sanders
551e48da4f Clarify text in docstring (#662) 2024-05-24 00:04:01 -03:00
Paul Sanders
21ce0aa17e Enable search in docs (#663) 2024-05-24 00:03:31 -03:00
Olivier Roberdet
2d6f2830e1 Fix typo in instruction en.json (#676) 2024-05-24 00:03:07 -03:00
Eduardo Chiarotti
24ed8a2549 feat: Add crew train cli (#624)
* fix: fix crewai-tools cli command

* feat: add crewai train CLI command

* feat: add the tests

* fix: fix typing hinting issue on code

* fix: test.yml

* fix: fix test

* fix: removed fix since it didnt changed the test
2024-05-23 18:46:45 -03:00
João Moura
a336381849 adding agent to task output 2024-05-16 05:12:32 -03:00
Jason Schrader
208c3a780c Add version command to CLI (#348)
* feat: add version command to cli with tools flag

* test: check output of version and tools flag

* fix: add version tool info to cli outputs
2024-05-15 19:50:49 -03:00
João Moura
1e112fa50a fixing crew base 2024-05-14 17:40:38 -03:00
João Moura
38fc5510ed ppreparing new version 0.30.9 2024-05-14 11:32:05 -03:00
146 changed files with 884286 additions and 15071 deletions

View File

@@ -19,13 +19,12 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: "3.10"
python-version: "3.11.9"
- name: Install Requirements
run: |
sudo apt-get update &&
pip install poetry &&
poetry lock &&
set -e
pip install poetry
poetry install
- name: Run tests

5
.gitignore vendored
View File

@@ -11,4 +11,7 @@ chroma.sqlite3
old_en.json
db/
test.py
rc-tests/*
rc-tests/*
*.pkl
temp/*
.vscode/*

View File

@@ -2,6 +2,8 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.4
hooks:
# Run the linter.
- id: ruff
args: [--fix]
args: ["--fix"]
exclude: "templates"
- id: ruff-format
exclude: "templates"

View File

@@ -70,6 +70,17 @@ os.environ["SERPER_API_KEY"] = "Your Key" # serper.dev API key
# os.environ["OPENAI_MODEL_NAME"] ='openhermes' # Adjust based on available model
# os.environ["OPENAI_API_KEY"] ='sk-111111111111111111111111111111111111111111111111'
# You can pass an optional llm attribute specifying what model you wanna use.
# It can be a local model through Ollama / LM Studio or a remote
# model like OpenAI, Mistral, Antrophic or others (https://docs.crewai.com/how-to/LLM-Connections/)
#
# import os
# os.environ['OPENAI_MODEL_NAME'] = 'gpt-3.5-turbo'
#
# OR
#
# from langchain_openai import ChatOpenAI
search_tool = SerperDevTool()
# Define your agents with roles and goals
@@ -81,18 +92,9 @@ researcher = Agent(
You have a knack for dissecting complex data and presenting actionable insights.""",
verbose=True,
allow_delegation=False,
tools=[search_tool]
# You can pass an optional llm attribute specifying what model you wanna use.
# It can be a local model through Ollama / LM Studio or a remote
# model like OpenAI, Mistral, Antrophic or others (https://docs.crewai.com/how-to/LLM-Connections/)
#
# import os
# os.environ['OPENAI_MODEL_NAME'] = 'gpt-3.5-turbo'
#
# OR
#
# from langchain_openai import ChatOpenAI
# llm=ChatOpenAI(model_name="gpt-3.5", temperature=0.7)
# llm=ChatOpenAI(model_name="gpt-3.5", temperature=0.7),
tools=[search_tool]
)
writer = Agent(
role='Tech Content Strategist',
@@ -125,6 +127,7 @@ crew = Crew(
agents=[researcher, writer],
tasks=[task1, task2],
verbose=2, # You can set it to 1 or 2 to different logging levels
process = Process.sequential
)
# Get your crew to work!
@@ -193,6 +196,7 @@ Please refer to the [Connect crewAI to LLMs](https://docs.crewai.com/how-to/LLM-
**CrewAI's Advantage**: CrewAI is built with production in mind. It offers the flexibility of Autogen's conversational agents and the structured process approach of ChatDev, but without the rigidity. CrewAI's processes are designed to be dynamic and adaptable, fitting seamlessly into both development and production workflows.
## Contribution
CrewAI is open-source and we welcome contributions. If you're looking to contribute, please:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 810 KiB

View File

@@ -16,21 +16,24 @@ description: What are crewAI Agents and how to use them.
## Agent Attributes
| Attribute | Description |
| :------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Role** | Defines the agent's function within the crew. It determines the kind of tasks the agent is best suited for. |
| **Goal** | The individual objective that the agent aims to achieve. It guides the agent's decision-making process. |
| **Backstory** | Provides context to the agent's role and goal, enriching the interaction and collaboration dynamics. |
| **LLM** *(optional)* | Represents the language model that will run the agent. It dynamically fetches the model name from the `OPENAI_MODEL_NAME` environment variable, defaulting to "gpt-4" if not specified. |
| **Tools** *(optional)* | Set of capabilities or functions that the agent can use to perform tasks. Expected to be instances of custom classes compatible with the agent's execution environment. Tools are initialized with a default value of an empty list. |
| **Function Calling LLM** *(optional)* | Specifies the language model that will handle the tool calling for this agent, overriding the crew function calling LLM if passed. Default is `None`. |
| **Max Iter** *(optional)* | The maximum number of iterations the agent can perform before being forced to give its best answer. Default is `25`. |
| **Max RPM** *(optional)* | The maximum number of requests per minute the agent can perform to avoid rate limits. It's optional and can be left unspecified, with a default value of `None`. |
| **max_execution_time** *(optional)* | Maximum execution time for an agent to execute a task It's optional and can be left unspecified, with a default value of `None`, menaning no max execution time |
| **Verbose** *(optional)* | Setting this to `True` configures the internal logger to provide detailed execution logs, aiding in debugging and monitoring. Default is `False`. |
| **Allow Delegation** *(optional)* | Agents can delegate tasks or questions to one another, ensuring that each task is handled by the most suitable agent. Default is `True`. |
| **Step Callback** *(optional)* | A function that is called after each step of the agent. This can be used to log the agent's actions or to perform other operations. It will overwrite the crew `step_callback`. |
| **Cache** *(optional)* | Indicates if the agent should use a cache for tool usage. Default is `True`. |
| Attribute | Parameter | Description |
| :------------------------- | :---- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Role** | `role` | Defines the agent's function within the crew. It determines the kind of tasks the agent is best suited for. |
| **Goal** | `goal` | The individual objective that the agent aims to achieve. It guides the agent's decision-making process. |
| **Backstory** | `backstory` | Provides context to the agent's role and goal, enriching the interaction and collaboration dynamics. |
| **LLM** *(optional)* | `llm` | Represents the language model that will run the agent. It dynamically fetches the model name from the `OPENAI_MODEL_NAME` environment variable, defaulting to "gpt-4" if not specified. |
| **Tools** *(optional)* | `tools` | Set of capabilities or functions that the agent can use to perform tasks. Expected to be instances of custom classes compatible with the agent's execution environment. Tools are initialized with a default value of an empty list. |
| **Function Calling LLM** *(optional)* | `function_calling_llm` | Specifies the language model that will handle the tool calling for this agent, overriding the crew function calling LLM if passed. Default is `None`. |
| **Max Iter** *(optional)* | `max_iter` | Max Iter is the maximum number of iterations the agent can perform before being forced to give its best answer. Default is `25`. |
| **Max RPM** *(optional)* | `max_rpm` | Max RPM is the maximum number of requests per minute the agent can perform to avoid rate limits. It's optional and can be left unspecified, with a default value of `None`. |
| **Max Execution Time** *(optional)* | `max_execution_time` | Max Execution Time is the Maximum execution time for an agent to execute a task. It's optional and can be left unspecified, with a default value of `None`, meaning no max execution time. |
| **Verbose** *(optional)* | `verbose` | Setting this to `True` configures the internal logger to provide detailed execution logs, aiding in debugging and monitoring. Default is `False`. |
| **Allow Delegation** *(optional)* | `allow_delegation` | Agents can delegate tasks or questions to one another, ensuring that each task is handled by the most suitable agent. Default is `True`. |
| **Step Callback** *(optional)* | `step_callback` | A function that is called after each step of the agent. This can be used to log the agent's actions or to perform other operations. It will overwrite the crew `step_callback`. |
| **Cache** *(optional)* | `cache` | Indicates if the agent should use a cache for tool usage. Default is `True`. |
| **System Template** *(optional)* | `system_template` | Specifies the system format for the agent. Default is `None`. |
| **Prompt Template** *(optional)* | `prompt_template` | Specifies the prompt format for the agent. Default is `None`. |
| **Response Template** *(optional)* | `response_template` | Specifies the response format for the agent. Default is `None`. |
## Creating an Agent
@@ -56,12 +59,91 @@ agent = Agent(
function_calling_llm=my_llm, # Optional
max_iter=15, # Optional
max_rpm=None, # Optional
max_execution_time=None, # Optional
verbose=True, # Optional
allow_delegation=True, # Optional
step_callback=my_intermediate_step_callback, # Optional
cache=True # Optional
cache=True, # Optional
system_template=my_system_template, # Optional
prompt_template=my_prompt_template, # Optional
response_template=my_response_template, # Optional
config=my_config, # Optional
crew=my_crew, # Optional
tools_handler=my_tools_handler, # Optional
cache_handler=my_cache_handler, # Optional
callbacks=[callback1, callback2], # Optional
agent_executor=my_agent_executor # Optional
)
```
## Setting prompt templates
Prompt templates are used to format the prompt for the agent. You can use to update the system, regular and response templates for the agent. Here's an example of how to set prompt templates:
```python
agent = Agent(
role="{topic} specialist",
goal="Figure {goal} out",
backstory="I am the master of {role}",
system_template="""<|start_header_id|>system<|end_header_id|>
{{ .System }}<|eot_id|>""",
prompt_template="""<|start_header_id|>user<|end_header_id|>
{{ .Prompt }}<|eot_id|>""",
response_template="""<|start_header_id|>assistant<|end_header_id|>
{{ .Response }}<|eot_id|>""",
)
```
## Bring your Third Party Agents
!!! note "Extend your Third Party Agents like LlamaIndex, Langchain, Autogen or fully custom agents using the the crewai's BaseAgent class."
BaseAgent includes attributes and methods required to integrate with your crews to run and delegate tasks to other agents within your own crew.
CrewAI is a universal multi agent framework that allows for all agents to work together to automate tasks and solve problems.
```py
from crewai import Agent, Task, Crew
from custom_agent import CustomAgent # You need to build and extend your own agent logic with the CrewAI BaseAgent class then import it here.
from langchain.agents import load_tools
langchain_tools = load_tools(["google-serper"], llm=llm)
agent1 = CustomAgent(
role="backstory agent",
goal="who is {input}?",
backstory="agent backstory",
verbose=True,
)
task1 = Task(
expected_output="a short biography of {input}",
description="a short biography of {input}",
agent=agent1,
)
agent2 = Agent(
role="bio agent",
goal="summarize the short bio for {input} and if needed do more research",
backstory="agent backstory",
verbose=True,
)
task2 = Task(
description="a tldr summary of the short biography",
expected_output="5 bullet point summary of the biography",
agent=agent2,
context=[task1],
)
my_crew = Crew(agents=[agent1, agent2], tasks=[task1, task2])
crew = my_crew.kickoff(inputs={"input": "Mark Twain"})
```
## Conclusion
Agents are the building blocks of the CrewAI framework. By understanding how to define and interact with agents, you can create sophisticated AI systems that leverage the power of collaborative intelligence.
Agents are the building blocks of the CrewAI framework. By understanding how to define and interact with agents, you can create sophisticated AI systems that leverage the power of collaborative intelligence.

View File

@@ -15,16 +15,19 @@ description: Exploring the dynamics of agent collaboration within the CrewAI fra
The `Crew` class has been enriched with several attributes to support advanced functionalities:
- **Language Model Management (`manager_llm`, `function_calling_llm`)**: Manages language models for executing tasks and tools, facilitating sophisticated agent-tool interactions. Note that while `manager_llm` is mandatory for hierarchical processes to ensure proper execution flow, `function_calling_llm` is optional, with a default value provided for streamlined tool interaction.
- **Custom Manager Agent (`manager_agent`)**: Allows specifying a custom agent as the manager instead of using the default manager provided by CrewAI.
- **Process Flow (`process`)**: Defines the execution logic (e.g., sequential, hierarchical) to streamline task distribution and execution.
- **Verbose Logging (`verbose`)**: Offers detailed logging capabilities for monitoring and debugging purposes. It supports both integer and boolean types to indicate the verbosity level. For example, setting `verbose` to 1 might enable basic logging, whereas setting it to True enables more detailed logs.
- **Rate Limiting (`max_rpm`)**: Ensures efficient utilization of resources by limiting requests per minute. Guidelines for setting `max_rpm` should consider the complexity of tasks and the expected load on resources.
- **Internationalization Support (`language`, `language_file`)**: Facilitates operation in multiple languages, enhancing global usability. Supported languages and the process for utilizing the `language_file` attribute for customization should be clearly documented.
- **Internationalization / Customization Support (`language`, `prompt_file`)**: Facilitates full customization of the inner prompts, enhancing global usability. Supported languages and the process for utilizing the `prompt_file` attribute for customization should be clearly documented. [Example of file](https://github.com/joaomdmoura/crewAI/blob/main/src/crewai/translations/en.json)
- **Execution and Output Handling (`full_output`)**: Distinguishes between full and final outputs for nuanced control over task results. Examples showcasing the difference in outputs can aid in understanding the practical implications of this attribute.
- **Callback and Telemetry (`step_callback`, `task_callback`)**: Integrates callbacks for step-wise and task-level execution monitoring, alongside telemetry for performance analytics. The purpose and usage of `task_callback` alongside `step_callback` for granular monitoring should be clearly explained.
- **Crew Sharing (`share_crew`)**: Enables sharing of crew information with CrewAI for continuous improvement and training models. The privacy implications and benefits of this feature, including how it contributes to model improvement, should be outlined.
- **Usage Metrics (`usage_metrics`)**: Stores all metrics for the language model (LLM) usage during all tasks' execution, providing insights into operational efficiency and areas for improvement. Detailed information on accessing and interpreting these metrics for performance analysis should be provided.
- **Memory Usage (`memory`)**: Indicates whether the crew should use memory to store memories of its execution, enhancing task execution and agent learning.
- **Embedder Configuration (`embedder`)**: Specifies the configuration for the embedder to be used by the crew for understanding and generating language. This attribute supports customization of the language model provider.
- **Cache Management (`cache`)**: Determines whether the crew should use a cache to store the results of tool executions, optimizing performance.
- **Output Logging (`output_log_file`)**: Specifies the file path for logging the output of the crew execution.
## Delegation: Dividing to Conquer
Delegation enhances functionality by allowing agents to intelligently assign tasks or seek help, thereby amplifying the crew's overall capability.
@@ -36,4 +39,4 @@ Setting up a crew involves defining the roles and capabilities of each agent. Cr
Consider a crew with a researcher agent tasked with data gathering and a writer agent responsible for compiling reports. The integration of advanced language model management and process flow attributes allows for more sophisticated interactions, such as the writer delegating complex research tasks to the researcher or querying specific information, thereby facilitating a seamless workflow.
## Conclusion
The integration of advanced attributes and functionalities into the CrewAI framework significantly enriches the agent collaboration ecosystem. These enhancements not only simplify interactions but also offer unprecedented flexibility and control, paving the way for sophisticated AI-driven solutions capable of tackling complex tasks through intelligent collaboration and delegation.
The integration of advanced attributes and functionalities into the CrewAI framework significantly enriches the agent collaboration ecosystem. These enhancements not only simplify interactions but also offer unprecedented flexibility and control, paving the way for sophisticated AI-driven solutions capable of tackling complex tasks through intelligent collaboration and delegation.

View File

@@ -8,27 +8,29 @@ A crew in crewAI represents a collaborative group of agents working together to
## Crew Attributes
| Attribute | Description |
| :-------------------------- | :----------------------------------------------------------- |
| **Tasks** | A list of tasks assigned to the crew. |
| **Agents** | A list of agents that are part of the crew. |
| **Process** *(optional)* | The process flow (e.g., sequential, hierarchical) the crew follows. |
| **Verbose** *(optional)* | The verbosity level for logging during execution. |
| **Manager LLM** *(optional)*| The language model used by the manager agent in a hierarchical process. **Required when using a hierarchical process.** |
| **Function Calling LLM** *(optional)* | If passed, the crew will use this LLM to do function calling for tools for all agents in the crew. Each agent can have its own LLM, which overrides the crew's LLM for function calling. |
| **Config** *(optional)* | Optional configuration settings for the crew, in `Json` or `Dict[str, Any]` format. |
| **Max RPM** *(optional)* | Maximum requests per minute the crew adheres to during execution. |
| **Language** *(optional)* | Language used for the crew, defaults to English. |
| **Language File** *(optional)* | Path to the language file to be used for the crew. |
| **Memory** *(optional)* | Utilized for storing execution memories (short-term, long-term, entity memory). |
| **Cache** *(optional)* | Specifies whether to use a cache for storing the results of tools' execution. |
| **Embedder** *(optional)* | Configuration for the embedder to be used by the crew. mostly used by memory for now |
| **Full Output** *(optional)*| Whether the crew should return the full output with all tasks outputs or just the final output. |
| **Step Callback** *(optional)* | A function that is called after each step of every agent. This can be used to log the agent's actions or to perform other operations; it won't override the agent-specific `step_callback`. |
| **Task Callback** *(optional)* | A function that is called after the completion of each task. Useful for monitoring or additional operations post-task execution. |
| **Share Crew** *(optional)* | Whether you want to share the complete crew information and execution with the crewAI team to make the library better, and allow us to train models. |
| **Output Log File** *(optional)* | Whether you want to have a file with the complete crew output and execution. You can set it using True and it will default to the folder you are currently and it will be called logs.txt or passing a string with the full path and name of the file. |
| Attribute | Parameters | Description |
| :-------------------------- | :------------------ | :------------------------------------------------------------------------------------------------------- |
| **Tasks** | `tasks` | A list of tasks assigned to the crew. |
| **Agents** | `agents` | A list of agents that are part of the crew. |
| **Process** *(optional)* | `process` | The process flow (e.g., sequential, hierarchical) the crew follows. |
| **Verbose** *(optional)* | `verbose` | The verbosity level for logging during execution. |
| **Manager LLM** *(optional)*| `manager_llm` | The language model used by the manager agent in a hierarchical process. **Required when using a hierarchical process.** |
| **Function Calling LLM** *(optional)* | `function_calling_llm` | If passed, the crew will use this LLM to do function calling for tools for all agents in the crew. Each agent can have its own LLM, which overrides the crew's LLM for function calling. |
| **Config** *(optional)* | `config` | Optional configuration settings for the crew, in `Json` or `Dict[str, Any]` format. |
| **Max RPM** *(optional)* | `max_rpm` | Maximum requests per minute the crew adheres to during execution. |
| **Language** *(optional)* | `language` | Language used for the crew, defaults to English. |
| **Language File** *(optional)* | `language_file` | Path to the language file to be used for the crew. |
| **Memory** *(optional)* | `memory` | Utilized for storing execution memories (short-term, long-term, entity memory). |
| **Cache** *(optional)* | `cache` | Specifies whether to use a cache for storing the results of tools' execution. |
| **Embedder** *(optional)* | `embedder` | Configuration for the embedder to be used by the crew. Mostly used by memory for now. |
| **Full Output** *(optional)*| `full_output` | Whether the crew should return the full output with all tasks outputs or just the final output. |
| **Step Callback** *(optional)* | `step_callback` | A function that is called after each step of every agent. This can be used to log the agent's actions or to perform other operations; it won't override the agent-specific `step_callback`. |
| **Task Callback** *(optional)* | `task_callback` | A function that is called after the completion of each task. Useful for monitoring or additional operations post-task execution. |
| **Share Crew** *(optional)* | `share_crew` | Whether you want to share the complete crew information and execution with the crewAI team to make the library better, and allow us to train models. |
| **Output Log File** *(optional)* | `output_log_file` | Whether you want to have a file with the complete crew output and execution. You can set it using True and it will default to the folder you are currently in and it will be called logs.txt or passing a string with the full path and name of the file. |
| **Manager Agent** *(optional)* | `manager_agent` | `manager` sets a custom agent that will be used as a manager. |
| **Manager Callbacks** *(optional)* | `manager_callbacks` | `manager_callbacks` takes a list of callback handlers to be executed by the manager agent when a hierarchical process is used. |
| **Prompt File** *(optional)* | `prompt_file` | Path to the prompt JSON file to be used for the crew. |
!!! note "Crew Max RPM"
The `max_rpm` attribute sets the maximum number of requests per minute the crew can perform to avoid rate limits and will override individual agents' `max_rpm` settings if you set it.
@@ -47,23 +49,34 @@ from langchain_community.tools import DuckDuckGoSearchRun
researcher = Agent(
role='Senior Research Analyst',
goal='Discover innovative AI technologies',
backstory="""You're a senior research analyst at a large company.
You're responsible for analyzing data and providing insights
to the business.
You're currently working on a project to analyze the
trends and innovations in the space of artificial intelligence.""",
tools=[DuckDuckGoSearchRun()]
)
writer = Agent(
role='Content Writer',
goal='Write engaging articles on AI discoveries',
backstory="""You're a senior writer at a large company.
You're responsible for creating content to the business.
You're currently working on a project to write about trends
and innovations in the space of AI for your next meeting.""",
verbose=True
)
# Create tasks for the agents
research_task = Task(
description='Identify breakthrough AI technologies',
agent=researcher
agent=researcher,
expected_output='A bullet list summary of the top 5 most important AI news'
)
write_article_task = Task(
description='Draft an article on the latest AI technologies',
agent=writer
agent=writer,
expected_output='3 paragraph blog post on the latest AI technologies'
)
# Assemble the crew with a sequential process
@@ -98,7 +111,7 @@ print(crew.usage_metrics)
## Crew Execution Process
- **Sequential Process**: Tasks are executed one after another, allowing for a linear flow of work.
- **Hierarchical Process**: A manager agent coordinates the crew, delegating tasks and validating outcomes before proceeding. **Note**: A `manager_llm` is required for this process and it's essential for validating the process flow.
- **Hierarchical Process**: A manager agent coordinates the crew, delegating tasks and validating outcomes before proceeding. **Note**: A `manager_llm` or `manager_agent` is required for this process and it's essential for validating the process flow.
### Kicking Off a Crew
@@ -109,3 +122,37 @@ Once your crew is assembled, initiate the workflow with the `kickoff()` method.
result = my_crew.kickoff()
print(result)
```
### Different ways to Kicking Off a Crew
Once your crew is assembled, initiate the workflow with the appropriate kickoff method. CrewAI provides several methods for better control over the kickoff process: `kickoff()`, `kickoff_for_each()`, `kickoff_async()`, and `kickoff_for_each_async()`.
`kickoff()`: Starts the execution process according to the defined process flow.
`kickoff_for_each()`: Executes tasks for each agent individually.
`kickoff_async()`: Initiates the workflow asynchronously.
`kickoff_for_each_async()`: Executes tasks for each agent individually in an asynchronous manner.
```python
# Start the crew's task execution
result = my_crew.kickoff()
print(result)
# Example of using kickoff_for_each
inputs_array = [{'topic': 'AI in healthcare'}, {'topic': 'AI in finance'}]
results = my_crew.kickoff_for_each(inputs=inputs_array)
for result in results:
print(result)
# Example of using kickoff_async
inputs = {'topic': 'AI in healthcare'}
async_result = my_crew.kickoff_async(inputs=inputs)
print(async_result)
# Example of using kickoff_for_each_async
inputs_array = [{'topic': 'AI in healthcare'}, {'topic': 'AI in finance'}]
async_results = my_crew.kickoff_for_each_async(inputs=inputs_array)
for async_result in async_results:
print(async_result)
```
These methods provide flexibility in how you manage and execute tasks within your crew, allowing for both synchronous and asynchronous workflows tailored to your needs

View File

@@ -5,16 +5,16 @@ description: Leveraging memory systems in the crewAI framework to enhance agent
## Introduction to Memory Systems in crewAI
!!! note "Enhancing Agent Intelligence"
The crewAI framework introduces a sophisticated memory system designed to significantly enhance the capabilities of AI agents. This system comprises short-term memory, long-term memory, entity memory, and newly identified contextual memory, each serving a unique purpose in aiding agents to remember, reason, and learn from past interactions.
The crewAI framework introduces a sophisticated memory system designed to significantly enhance the capabilities of AI agents. This system comprises short-term memory, long-term memory, entity memory, and contextual memory, each serving a unique purpose in aiding agents to remember, reason, and learn from past interactions.
## Memory System Components
| Component | Description |
| :------------------- | :----------------------------------------------------------- |
| **Short-Term Memory**| Temporarily stores recent interactions and outcomes, enabling agents to recall and utilize information relevant to their current context. |
| **Long-Term Memory** | Preserves valuable insights and learnings from past executions, allowing agents to build and refine their knowledge over time. |
| **Short-Term Memory**| Temporarily stores recent interactions and outcomes, enabling agents to recall and utilize information relevant to their current context during the current executions. |
| **Long-Term Memory** | Preserves valuable insights and learnings from past executions, allowing agents to build and refine their knowledge over time. So Agents can remember what they did right and wrong across multiple executions |
| **Entity Memory** | Captures and organizes information about entities (people, places, concepts) encountered during tasks, facilitating deeper understanding and relationship mapping. |
| **Contextual Memory**| Maintains the context of interactions, aiding in the coherence and relevance of agent responses over a sequence of tasks or a conversation. |
| **Contextual Memory**| Maintains the context of interactions by combining `ShortTermMemory`, `LongTermMemory`, and `EntityMemory`, aiding in the coherence and relevance of agent responses over a sequence of tasks or a conversation. |
## How Memory Systems Empower Agents
@@ -27,8 +27,7 @@ description: Leveraging memory systems in the crewAI framework to enhance agent
## Implementing Memory in Your Crew
When configuring a crew, you can enable and customize each memory component to suit the crew's objectives and the nature of tasks it will perform.
By default, the memory system is disabled, and you can ensure it is active by setting `memory=True` in the crew configuration.
The memory will use OpenAI Embeddings by default, but you can change it by setting `embedder` to a different model.
By default, the memory system is disabled, and you can ensure it is active by setting `memory=True` in the crew configuration. The memory will use OpenAI Embeddings by default, but you can change it by setting `embedder` to a different model.
### Example: Configuring Memory for a Crew

View File

@@ -10,14 +10,14 @@ description: Detailed guide on workflow management through processes in CrewAI,
## Process Implementations
- **Sequential**: Executes tasks sequentially, ensuring tasks are completed in an orderly progression.
- **Hierarchical**: Organizes tasks in a managerial hierarchy, where tasks are delegated and executed based on a structured chain of command. A manager language model (`manager_llm`) must be specified in the crew to enable the hierarchical process, facilitating the creation and management of tasks by the manager.
- **Hierarchical**: Organizes tasks in a managerial hierarchy, where tasks are delegated and executed based on a structured chain of command. A manager language model (`manager_llm`) or a custom manager agent (`manager_agent`) must be specified in the crew to enable the hierarchical process, facilitating the creation and management of tasks by the manager.
- **Consensual Process (Planned)**: Aiming for collaborative decision-making among agents on task execution, this process type introduces a democratic approach to task management within CrewAI. It is planned for future development and is not currently implemented in the codebase.
## The Role of Processes in Teamwork
Processes enable individual agents to operate as a cohesive unit, streamlining their efforts to achieve common objectives with efficiency and coherence.
## Assigning Processes to a Crew
To assign a process to a crew, specify the process type upon crew creation to set the execution strategy. For a hierarchical process, ensure to define `manager_llm` for the manager agent.
To assign a process to a crew, specify the process type upon crew creation to set the execution strategy. For a hierarchical process, ensure to define `manager_llm` or `manager_agent` for the manager agent.
```python
from crewai import Crew
@@ -32,15 +32,17 @@ crew = Crew(
)
# Example: Creating a crew with a hierarchical process
# Ensure to provide a manager_llm
# Ensure to provide a manager_llm or manager_agent
crew = Crew(
agents=my_agents,
tasks=my_tasks,
process=Process.hierarchical,
manager_llm=ChatOpenAI(model="gpt-4")
# or
# manager_agent=my_manager_agent
)
```
**Note:** Ensure `my_agents` and `my_tasks` are defined prior to creating a `Crew` object, and for the hierarchical process, `manager_llm` is also required.
**Note:** Ensure `my_agents` and `my_tasks` are defined prior to creating a `Crew` object, and for the hierarchical process, either `manager_llm` or `manager_agent` is also required.
## Sequential Process
This method mirrors dynamic team workflows, progressing through tasks in a thoughtful and systematic manner. Task execution follows the predefined order in the task list, with the output of one task serving as context for the next.
@@ -48,7 +50,7 @@ This method mirrors dynamic team workflows, progressing through tasks in a thoug
To customize task context, utilize the `context` parameter in the `Task` class to specify outputs that should be used as context for subsequent tasks.
## Hierarchical Process
Emulates a corporate hierarchy, CrewAI automatically creates a manager for you, requiring the specification of a manager language model (`manager_llm`) for the manager agent. This agent oversees task execution, including planning, delegation, and validation. Tasks are not pre-assigned; the manager allocates tasks to agents based on their capabilities, reviews outputs, and assesses task completion.
Emulates a corporate hierarchy, CrewAI allows specifying a custom manager agent or automatically creates one, requiring the specification of a manager language model (`manager_llm`). This agent oversees task execution, including planning, delegation, and validation. Tasks are not pre-assigned; the manager allocates tasks to agents based on their capabilities, reviews outputs, and assesses task completion.
## Process Class: Detailed Overview
The `Process` class is implemented as an enumeration (`Enum`), ensuring type safety and restricting process values to the defined types (`sequential`, `hierarchical`). The consensual process is planned for future inclusion, emphasizing our commitment to continuous development and innovation.

View File

@@ -11,20 +11,20 @@ Tasks within crewAI can be collaborative, requiring multiple agents to work toge
## Task Attributes
| Attribute | Description |
| :----------------------| :-------------------------------------------------------------------------------------------- |
| **Description** | A clear, concise statement of what the task entails. |
| **Agent** | The agent responsible for the task, assigned either directly or by the crew's process. |
| **Expected Output** | A detailed description of what the task's completion looks like. |
| **Tools** *(optional)* | The functions or capabilities the agent can utilize to perform the task. |
| **Async Execution** *(optional)* | If set, the task executes asynchronously, allowing progression without waiting for completion.|
| **Context** *(optional)* | Specifies tasks whose outputs are used as context for this task. |
| **Config** *(optional)* | Additional configuration details for the agent executing the task, allowing further customization. |
| **Output JSON** *(optional)* | Outputs a JSON object, requiring an OpenAI client. Only one output format can be set. |
| **Output Pydantic** *(optional)* | Outputs a Pydantic model object, requiring an OpenAI client. Only one output format can be set. |
| **Output File** *(optional)* | Saves the task output to a file. If used with `Output JSON` or `Output Pydantic`, specifies how the output is saved. |
| **Callback** *(optional)* | A Python callable that is executed with the task's output upon completion. |
| **Human Input** *(optional)* | Indicates if the task requires human feedback at the end, useful for tasks needing human oversight. |
| Attribute | Parameters | Description |
| :----------------------| :------------------- | :-------------------------------------------------------------------------------------------- |
| **Description** | `description` | A clear, concise statement of what the task entails. |
| **Agent** | `agent` | The agent responsible for the task, assigned either directly or by the crew's process. |
| **Expected Output** | `expected_output` | A detailed description of what the task's completion looks like. |
| **Tools** *(optional)* | `tools` | The functions or capabilities the agent can utilize to perform the task. |
| **Async Execution** *(optional)* | `async_execution` | If set, the task executes asynchronously, allowing progression without waiting for completion.|
| **Context** *(optional)* | `context` | Specifies tasks whose outputs are used as context for this task. |
| **Config** *(optional)* | `config` | Additional configuration details for the agent executing the task, allowing further customization. |
| **Output JSON** *(optional)* | `output_json` | Outputs a JSON object, requiring an OpenAI client. Only one output format can be set. |
| **Output Pydantic** *(optional)* | `output_pydantic` | Outputs a Pydantic model object, requiring an OpenAI client. Only one output format can be set. |
| **Output File** *(optional)* | `output_file` | Saves the task output to a file. If used with `Output JSON` or `Output Pydantic`, specifies how the output is saved. |
| **Callback** *(optional)* | `callback` | A Python callable that is executed with the task's output upon completion. |
| **Human Input** *(optional)* | `human_input` | Indicates if the task requires human feedback at the end, useful for tasks needing human oversight. |
## Creating a Task
@@ -88,7 +88,7 @@ This demonstrates how tasks with specific tools can override an agent's default
## Referring to Other Tasks
In crewAI, the output of one task is automatically relayed into the next one, but you can specifically define what tasks' output, including multiple should be used as context for another task.
In crewAI, the output of one task is automatically relayed into the next one, but you can specifically define what tasks' output, including multiple, should be used as context for another task.
This is useful when you have a task that depends on the output of another task that is not performed immediately after it. This is done through the `context` attribute of the task:
@@ -225,6 +225,25 @@ While creating and executing tasks, certain validation mechanisms are in place t
These validations help in maintaining the consistency and reliability of task executions within the crewAI framework.
## Creating Directories when Saving Files
You can now specify if a task should create directories when saving its output to a file. This is particularly useful for organizing outputs and ensuring that file paths are correctly structured.
```python
# ...
save_output_task = Task(
description='Save the summarized AI news to a file',
expected_output='File saved successfully',
agent=research_agent,
tools=[file_save_tool],
output_file='outputs/ai_news_summary.txt',
create_directory=True
)
#...
```
## Conclusion
Tasks are the driving force behind the actions of agents in crewAI. By properly defining tasks and their outcomes, you set the stage for your AI agents to work effectively, either independently or as a collaborative unit. Equipping tasks with appropriate tools, understanding the execution process, and following robust validation practices are crucial for maximizing CrewAI's potential, ensuring agents are effectively prepared for their assignments and that tasks are executed as intended.
Tasks are the driving force behind the actions of agents in crewAI. By properly defining tasks and their outcomes, you set the stage for your AI agents to work effectively, either independently or as a collaborative unit. Equipping tasks with appropriate tools, understanding the execution process, and following robust validation practices are crucial for maximizing CrewAI's potential, ensuring agents are effectively prepared for their assignments and that tasks are executed as intended.

View File

@@ -94,7 +94,7 @@ crew.kickoff()
## Available crewAI Tools
- **Error Handling**: All tools are built with error handling capabilities, allowing agents to gracefully manage exceptions and continue their tasks.
- **Caching Mechanism**: All tools support caching, enabling agents to efficiently reuse previously obtained results, reducing the load on external resources and speeding up the execution time, you can also define finner control over the caching mechanism, using `cache_function` attribute on the tool.
- **Caching Mechanism**: All tools support caching, enabling agents to efficiently reuse previously obtained results, reducing the load on external resources and speeding up the execution time. You can also define finer control over the caching mechanism using the `cache_function` attribute on the tool.
Here is a list of the available tools and their descriptions:
@@ -107,7 +107,7 @@ Here is a list of the available tools and their descriptions:
| **DirectoryReadTool** | Facilitates reading and processing of directory structures and their contents. |
| **FileReadTool** | Enables reading and extracting data from files, supporting various file formats. |
| **GithubSearchTool** | A RAG tool for searching within GitHub repositories, useful for code and documentation search.|
| **SerperDevTool** | A specialized tool for development purposes, with specific functionalities under development. |
| **SerperDevTool** | A specialized tool for development purposes, with specific functionalities under development. |
| **TXTSearchTool** | A RAG tool focused on searching within text (.txt) files, suitable for unstructured data. |
| **JSONSearchTool** | A RAG tool designed for searching within JSON files, catering to structured data handling. |
| **MDXSearchTool** | A RAG tool tailored for searching within Markdown (MDX) files, useful for documentation. |
@@ -120,13 +120,14 @@ Here is a list of the available tools and their descriptions:
| **XMLSearchTool** | A RAG tool designed for searching within XML files, suitable for structured data formats. |
| **YoutubeChannelSearchTool**| A RAG tool for searching within YouTube channels, useful for video content analysis. |
| **YoutubeVideoSearchTool** | A RAG tool aimed at searching within YouTube videos, ideal for video data extraction. |
| **BrowserbaseTool** | A tool for interacting with and extracting data from web browsers. |
| **ExaSearchTool** | A tool designed for performing exhaustive searches across various data sources. |
## Creating your own Tools
!!! example "Custom Tool Creation"
Developers can craft custom tools tailored for their agents needs or utilize pre-built options:
To create your own crewAI tools you will need to install our extra tools package:
```bash
@@ -141,7 +142,7 @@ from crewai_tools import BaseTool
class MyCustomTool(BaseTool):
name: str = "Name of my tool"
description: str = "Clear description for what this tool is useful for, you agent will need this information to use it."
description: str = "Clear description for what this tool is useful for, your agent will need this information to use it."
def _run(self, argument: str) -> str:
# Implementation goes here
@@ -154,7 +155,7 @@ class MyCustomTool(BaseTool):
from crewai_tools import tool
@tool("Name of my tool")
def my_tool(question: str) -> str:
"""Clear description for what this tool is useful for, you agent will need this information to use it."""
"""Clear description for what this tool is useful for, your agent will need this information to use it."""
# Function logic here
return "Result from your custom tool"
```
@@ -180,45 +181,14 @@ multiplication_tool.cache_function = cache_func
writer1 = Agent(
role="Writer",
goal="You write lesssons of math for kids.",
backstory="You're an expert in writting and you love to teach kids but you know nothing of math.",
tools=[multiplcation_tool],
goal="You write lessons of math for kids.",
backstory="You're an expert in writing and you love to teach kids but you know nothing of math.",
tools=[multiplication_tool],
allow_delegation=False,
)
#...
```
## Using LangChain Tools
!!! info "LangChain Integration"
CrewAI seamlessly integrates with LangChains comprehensive toolkit for search-based queries and more, here are the available built-in tools that are offered by Langchain [LangChain Toolkit](https://python.langchain.com/docs/integrations/tools/)
:
```python
from crewai import Agent
from langchain.agents import Tool
from langchain.utilities import GoogleSerperAPIWrapper
# Setup API keys
os.environ["SERPER_API_KEY"] = "Your Key"
search = GoogleSerperAPIWrapper()
# Create and assign the search tool to an agent
serper_tool = Tool(
name="Intermediate Answer",
func=search.run,
description="Useful for search-based queries",
)
agent = Agent(
role='Research Analyst',
goal='Provide up-to-date market analysis',
backstory='An expert analyst with a keen eye for market trends.',
tools=[serper_tool]
)
# rest of the code ...
```
## Conclusion
Tools are pivotal in extending the capabilities of CrewAI agents, enabling them to undertake a broad spectrum of tasks and collaborate effectively. When building solutions with CrewAI, leverage both custom and existing tools to empower your agents and enhance the AI ecosystem. Consider utilizing error handling, caching mechanisms, and the flexibility of tool arguments to optimize your agents' performance and capabilities.
Tools are pivotal in extending the capabilities of CrewAI agents, enabling them to undertake a broad spectrum of tasks and collaborate effectively. When building solutions with CrewAI, leverage both custom and existing tools to empower your agents and enhance the AI ecosystem. Consider utilizing error handling, caching mechanisms, and the flexibility of tool arguments to optimize your agents' performance and capabilities.

View File

@@ -0,0 +1,53 @@
---
title: crewAI Train
description: Learn how to train your crewAI agents by giving them feedback early on and get consistent results.
---
## Introduction
The training feature in CrewAI allows you to train your AI agents using the command-line interface (CLI). By running the command `crewai train -n <n_iterations>`, you can specify the number of iterations for the training process.
During training, CrewAI utilizes techniques to optimize the performance of your agents along with human feedback. This helps the agents improve their understanding, decision-making, and problem-solving abilities.
### Training Your Crew Using the CLI
To use the training feature, follow these steps:
1. Open your terminal or command prompt.
2. Navigate to the directory where your CrewAI project is located.
3. Run the following command:
```shell
crewai train -n <n_iterations>
```
### Training Your Crew Programmatically
To train your crew programmatically, use the following steps:
1. Define the number of iterations for training.
2. Specify the input parameters for the training process.
3. Execute the training command within a try-except block to handle potential errors.
```python
n_iterations = 2
inputs = {"topic": "CrewAI Training"}
try:
YourCrewName_Crew().crew().train(n_iterations= n_iterations, inputs=inputs)
except Exception as e:
raise Exception(f"An error occurred while training the crew: {e}")
```
!!! note "Replace `<n_iterations>` with the desired number of training iterations. This determines how many times the agents will go through the training process."
### Key Points to Note:
- **Positive Integer Requirement:** Ensure that the number of iterations (`n_iterations`) is a positive integer. The code will raise a `ValueError` if this condition is not met.
- **Error Handling:** The code handles subprocess errors and unexpected exceptions, providing error messages to the user.
It is important to note that the training process may take some time, depending on the complexity of your agents and will also require your feedback on each iteration.
Once the training is complete, your agents will be equipped with enhanced capabilities and knowledge, ready to tackle complex tasks and provide more consistent and valuable insights.
Remember to regularly update and retrain your agents to ensure they stay up-to-date with the latest information and advancements in the field.
Happy training with CrewAI!

View File

@@ -0,0 +1,38 @@
---
title: Using LangChain Tools
description: Learn how to integrate LangChain tools with CrewAI agents to enhance search-based queries and more.
---
## Using LangChain Tools
!!! info "LangChain Integration"
CrewAI seamlessly integrates with LangChains comprehensive toolkit for search-based queries and more, here are the available built-in tools that are offered by Langchain [LangChain Toolkit](https://python.langchain.com/docs/integrations/tools/)
```python
from crewai import Agent
from langchain.agents import Tool
from langchain.utilities import GoogleSerperAPIWrapper
# Setup API keys
os.environ["SERPER_API_KEY"] = "Your Key"
search = GoogleSerperAPIWrapper()
# Create and assign the search tool to an agent
serper_tool = Tool(
name="Intermediate Answer",
func=search.run,
description="Useful for search-based queries",
)
agent = Agent(
role='Research Analyst',
goal='Provide up-to-date market analysis',
backstory='An expert analyst with a keen eye for market trends.',
tools=[serper_tool]
)
# rest of the code ...
```
## Conclusion
Tools are pivotal in extending the capabilities of CrewAI agents, enabling them to undertake a broad spectrum of tasks and collaborate effectively. When building solutions with CrewAI, leverage both custom and existing tools to empower your agents and enhance the AI ecosystem. Consider utilizing error handling, caching mechanisms, and the flexibility of tool arguments to optimize your agents' performance and capabilities.

View File

@@ -0,0 +1,57 @@
---
title: Using LlamaIndex Tools
description: Learn how to integrate LlamaIndex tools with CrewAI agents to enhance search-based queries and more.
---
## Using LlamaIndex Tools
!!! info "LlamaIndex Integration"
CrewAI seamlessly integrates with LlamaIndexs comprehensive toolkit for RAG (Retrieval-Augmented Generation) and agentic pipelines, enabling advanced search-based queries and more. Here are the available built-in tools offered by LlamaIndex.
```python
from crewai import Agent
from crewai_tools import LlamaIndexTool
# Example 1: Initialize from FunctionTool
from llama_index.core.tools import FunctionTool
your_python_function = lambda ...: ...
og_tool = FunctionTool.from_defaults(your_python_function, name="<name>", description='<description>')
tool = LlamaIndexTool.from_tool(og_tool)
# Example 2: Initialize from LlamaHub Tools
from llama_index.tools.wolfram_alpha import WolframAlphaToolSpec
wolfram_spec = WolframAlphaToolSpec(app_id="<app_id>")
wolfram_tools = wolfram_spec.to_tool_list()
tools = [LlamaIndexTool.from_tool(t) for t in wolfram_tools]
# Example 3: Initialize Tool from a LlamaIndex Query Engine
query_engine = index.as_query_engine()
query_tool = LlamaIndexTool.from_query_engine(
query_engine,
name="Uber 2019 10K Query Tool",
description="Use this tool to lookup the 2019 Uber 10K Annual Report"
)
# Create and assign the tools to an agent
agent = Agent(
role='Research Analyst',
goal='Provide up-to-date market analysis',
backstory='An expert analyst with a keen eye for market trends.',
tools=[tool, *tools, query_tool]
)
# rest of the code ...
```
## Steps to Get Started
To effectively use the LlamaIndexTool, follow these steps:
1. **Package Installation**: Confirm that the `crewai[tools]` package is installed in your Python environment.
```shell
pip install 'crewai[tools]'
```
2. **Install and Use LlamaIndex**: Follow LlamaIndex documentation [LlamaIndex Documentation](https://docs.llamaindex.ai/) to set up a RAG/agent pipeline.

View File

@@ -4,8 +4,7 @@ description: Understanding and logging your agent performance with AgentOps.
---
# Intro
Observability is a key aspect of developing and deploying conversational AI agents. It allows developers to understand how their agents are performing, how their agents are interacting with users, and how their agents use external tools and APIs. AgentOps is a product independent of CrewAI that provides a comprehensive observability solution for agents.
Observability is a key aspect of developing and deploying conversational AI agents. It allows developers to understand how their agents are performing, how their agents are interacting with users, and how their agents use external tools and APIs. AgentOps is a product independent of CrewAI that provides a comprehensive observability solution for agents.
## AgentOps
@@ -23,54 +22,53 @@ Additionally, AgentOps provides session drilldowns for viewing Crew agent intera
![Viewing a step-by-step agent replay execution graph](..%2Fassets%2Fagentops-replay.png)
### Features
- **LLM Cost Management and Tracking**: Track spend with foundation model providers
- **Replay Analytics**: Watch step-by-step agent execution graphs
- **Recursive Thought Detection**: Identify when agents fall into infinite loops
- **Custom Reporting**: Create custom analytics on agent performance
- **Analytics Dashboard**: Monitor high level statistics about agents in development and production
- **Public Model Testing**: Test your agents against benchmarks and leaderboards
- **Custom Tests**: Run your agents against domain specific tests
- **Time Travel Debugging**: Restart your sessions from checkpoints
- **Compliance and Security**: Create audit logs and detect potential threats such as profanity and PII leaks
- **Prompt Injection Detection**: Identify potential code injection and secret leaks
- **LLM Cost Management and Tracking**: Track spend with foundation model providers.
- **Replay Analytics**: Watch step-by-step agent execution graphs.
- **Recursive Thought Detection**: Identify when agents fall into infinite loops.
- **Custom Reporting**: Create custom analytics on agent performance.
- **Analytics Dashboard**: Monitor high-level statistics about agents in development and production.
- **Public Model Testing**: Test your agents against benchmarks and leaderboards.
- **Custom Tests**: Run your agents against domain-specific tests.
- **Time Travel Debugging**: Restart your sessions from checkpoints.
- **Compliance and Security**: Create audit logs and detect potential threats such as profanity and PII leaks.
- **Prompt Injection Detection**: Identify potential code injection and secret leaks.
### Using AgentOps
1. **Create an API Key:**
Create a user API key here: [Create API Key](app.agentops.ai/account)
Create a user API key here: [Create API Key](app.agentops.ai/account)
2. **Configure Your Environment:**
Add your API key to your environment variables
Add your API key to your environment variables
```
AGENTOPS_API_KEY=<YOUR_AGENTOPS_API_KEY>
```
```bash
AGENTOPS_API_KEY=<YOUR_AGENTOPS_API_KEY>
```
3. **Install AgentOps:**
Install AgentOps with:
```
pip install crewai[agentops]
```
or
```
pip install agentops
```
Install AgentOps with:
```bash
pip install crewai[agentops]
```
or
```bash
pip install agentops
```
Before using `Crew` in your script, include these lines:
Before using `Crew` in your script, include these lines:
```python
import agentops
agentops.init()
```
```python
import agentops
agentops.init()
```
This will initiate an AgentOps session as well as automatically track Crew agents. For further info on how to outfit more complex agentic systems, check out the [AgentOps documentation](https://docs.agentops.ai) or join the [Discord](https://discord.gg/j4f3KbeH).
This will initiate an AgentOps session as well as automatically track Crew agents. For further info on how to outfit more complex agentic systems, check out the [AgentOps documentation](https://docs.agentops.ai) or join the [Discord](https://discord.gg/j4f3KbeH).
### Crew + AgentOps Examples
- [Job Posting](https://github.com/joaomdmoura/crewAI-examples/tree/main/job-posting)
- [Markdown Validator](https://github.com/joaomdmoura/crewAI-examples/tree/main/markdown_validator)
- [Instagram Post](https://github.com/joaomdmoura/crewAI-examples/tree/main/instagram_post)
### Further Information
To get started, create an [AgentOps account](https://agentops.ai/?=crew).

View File

@@ -0,0 +1,76 @@
---
title: Coding Agents
description: Learn how to enable your crewAI Agents to write and execute code, and explore advanced features for enhanced functionality.
---
## Introduction
crewAI Agents now have the powerful ability to write and execute code, significantly enhancing their problem-solving capabilities. This feature is particularly useful for tasks that require computational or programmatic solutions.
## Enabling Code Execution
To enable code execution for an agent, set the `allow_code_execution` parameter to `True` when creating the agent. Here's an example:
```python
from crewai import Agent
coding_agent = Agent(
role="Senior Python Developer",
goal="Craft well-designed and thought-out code",
backstory="You are a senior Python developer with extensive experience in software architecture and best practices.",
allow_code_execution=True
)
```
## Important Considerations
1. **Model Selection**: It is strongly recommended to use more capable models like Claude 3.5 Sonnet and GPT-4 when enabling code execution. These models have a better understanding of programming concepts and are more likely to generate correct and efficient code.
2. **Error Handling**: The code execution feature includes error handling. If executed code raises an exception, the agent will receive the error message and can attempt to correct the code or provide alternative solutions.
3. **Dependencies**: To use the code execution feature, you need to install the `crewai_tools` package. If not installed, the agent will log an info message: "Coding tools not available. Install crewai_tools."
## Code Execution Process
When an agent with code execution enabled encounters a task requiring programming:
1. The agent analyzes the task and determines that code execution is necessary.
2. It formulates the Python code needed to solve the problem.
3. The code is sent to the internal code execution tool (`CodeInterpreterTool`).
4. The tool executes the code in a controlled environment and returns the result.
5. The agent interprets the result and incorporates it into its response or uses it for further problem-solving.
## Example Usage
Here's a detailed example of creating an agent with code execution capabilities and using it in a task:
```python
from crewai import Agent, Task, Crew
# Create an agent with code execution enabled
coding_agent = Agent(
role="Python Data Analyst",
goal="Analyze data and provide insights using Python",
backstory="You are an experienced data analyst with strong Python skills.",
allow_code_execution=True
)
# Create a task that requires code execution
data_analysis_task = Task(
description="Analyze the given dataset and calculate the average age of participants.",
agent=coding_agent
)
# Create a crew and add the task
analysis_crew = Crew(
agents=[coding_agent],
tasks=[data_analysis_task]
)
# Execute the crew
result = analysis_crew.kickoff()
print(result)
```
In this example, the `coding_agent` can write and execute Python code to perform data analysis tasks.

View File

@@ -42,6 +42,7 @@ def my_simple_tool(question: str) -> str:
# Tool logic here
return "Tool output"
```
### Defining a Cache Function for the Tool
To optimize tool performance with caching, define custom caching strategies using the `cache_function` attribute.
@@ -50,7 +51,7 @@ To optimize tool performance with caching, define custom caching strategies usin
@tool("Tool with Caching")
def cached_tool(argument: str) -> str:
"""Tool functionality description."""
return "Cachable result"
return "Cacheable result"
def my_cache_strategy(arguments: dict, result: str) -> bool:
# Define custom caching logic

View File

@@ -1,11 +1,10 @@
---
title: Assembling and Activating Your CrewAI Team
description: A comprehensive guide to creating a dynamic CrewAI team for your projects, with updated functionalities including verbose mode, memory capabilities, asynchronous execution, output customization, language model configuration, and more.
description: A comprehensive guide to creating a dynamic CrewAI team for your projects, with updated functionalities including verbose mode, memory capabilities, asynchronous execution, output customization, language model configuration, code execution, integration with third-party agents, and improved task management.
---
## Introduction
Embark on your CrewAI journey by setting up your environment and initiating your AI crew with the latest features. This guide ensures a smooth start, incorporating all recent updates for an enhanced experience.
Embark on your CrewAI journey by setting up your environment and initiating your AI crew with the latest features. This guide ensures a smooth start, incorporating all recent updates for an enhanced experience, including code execution capabilities, integration with third-party agents, and advanced task management.
## Step 0: Installation
Install CrewAI and any necessary packages for your project. CrewAI is compatible with Python >=3.10,<=3.13.
@@ -16,108 +15,68 @@ pip install 'crewai[tools]'
```
## Step 1: Assemble Your Agents
Define your agents with distinct roles, backstories, and enhanced capabilities like verbose mode and memory usage. These elements add depth and guide their task execution and interaction within the crew.
Define your agents with distinct roles, backstories, and enhanced capabilities. The Agent class now supports a wide range of attributes for fine-tuned control over agent behavior and interactions, including code execution and integration with third-party agents.
```python
import os
os.environ["SERPER_API_KEY"] = "Your Key" # serper.dev API key
os.environ["OPENAI_API_KEY"] = "Your Key"
from langchain.llms import OpenAI
from crewai import Agent
from crewai_tools import SerperDevTool
from crewai_tools import SerperDevTool, BrowserbaseTool, ExaSearchTool
os.environ["OPENAI_API_KEY"] = "Your OpenAI Key"
os.environ["SERPER_API_KEY"] = "Your Serper Key"
search_tool = SerperDevTool()
browser_tool = BrowserbaseTool()
exa_search_tool = ExaSearchTool()
# Creating a senior researcher agent with memory and verbose mode
# Creating a senior researcher agent with advanced configurations
researcher = Agent(
role='Senior Researcher',
goal='Uncover groundbreaking technologies in {topic}',
verbose=True,
memory=True,
backstory=(
"Driven by curiosity, you're at the forefront of"
"innovation, eager to explore and share knowledge that could change"
"the world."
),
tools=[search_tool],
allow_delegation=True
role='Senior Researcher',
goal='Uncover groundbreaking technologies in {topic}',
backstory=("Driven by curiosity, you're at the forefront of innovation, "
"eager to explore and share knowledge that could change the world."),
memory=True,
verbose=True,
allow_delegation=False,
tools=[search_tool, browser_tool],
allow_code_execution=False, # New attribute for enabling code execution
max_iter=15, # Maximum number of iterations for task execution
max_rpm=100, # Maximum requests per minute
max_execution_time=3600, # Maximum execution time in seconds
system_template="Your custom system template here", # Custom system template
prompt_template="Your custom prompt template here", # Custom prompt template
response_template="Your custom response template here", # Custom response template
)
# Creating a writer agent with custom tools and delegation capability
# Creating a writer agent with custom tools and specific configurations
writer = Agent(
role='Writer',
goal='Narrate compelling tech stories about {topic}',
role='Writer',
goal='Narrate compelling tech stories about {topic}',
backstory=("With a flair for simplifying complex topics, you craft engaging "
"narratives that captivate and educate, bringing new discoveries to light."),
verbose=True,
allow_delegation=False,
memory=True,
tools=[exa_search_tool],
function_calling_llm=OpenAI(model_name="gpt-3.5-turbo"), # Separate LLM for function calling
)
# Setting a specific manager agent
manager = Agent(
role='Manager',
goal='Ensure the smooth operation and coordination of the team',
verbose=True,
memory=True,
backstory=(
"With a flair for simplifying complex topics, you craft"
"engaging narratives that captivate and educate, bringing new"
"discoveries to light in an accessible manner."
"As a seasoned project manager, you excel in organizing "
"tasks, managing timelines, and ensuring the team stays on track."
),
tools=[search_tool],
allow_delegation=False
allow_code_execution=True, # Enable code execution for the manager
)
```
## Step 2: Define the Tasks
Detail the specific objectives for your agents, including new features for asynchronous execution and output customization. These tasks ensure a targeted approach to their roles.
### New Agent Attributes and Features
```python
from crewai import Task
# Research task
research_task = Task(
description=(
"Identify the next big trend in {topic}."
"Focus on identifying pros and cons and the overall narrative."
"Your final report should clearly articulate the key points,"
"its market opportunities, and potential risks."
),
expected_output='A comprehensive 3 paragraphs long report on the latest AI trends.',
tools=[search_tool],
agent=researcher,
)
# Writing task with language model configuration
write_task = Task(
description=(
"Compose an insightful article on {topic}."
"Focus on the latest trends and how it's impacting the industry."
"This article should be easy to understand, engaging, and positive."
),
expected_output='A 4 paragraph article on {topic} advancements formatted as markdown.',
tools=[search_tool],
agent=writer,
async_execution=False,
output_file='new-blog-post.md' # Example of output customization
)
```
## Step 3: Form the Crew
Combine your agents into a crew, setting the workflow process they'll follow to accomplish the tasks. Now with options to configure language models for enhanced interaction and additional configurations for optimizing performance.
```python
from crewai import Crew, Process
# Forming the tech-focused crew with some enhanced configurations
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, write_task],
process=Process.sequential, # Optional: Sequential task execution is default
memory=True,
cache=True,
max_rpm=100,
share_crew=True
)
```
## Step 4: Kick It Off
Initiate the process with your enhanced crew ready. Observe as your agents collaborate, leveraging their new capabilities for a successful project outcome. Input variables will be interpolated into the agents and tasks for a personalized approach.
```python
# Starting the task execution process with enhanced feedback
result = crew.kickoff(inputs={'topic': 'AI in healthcare'})
print(result)
```
## Conclusion
Building and activating a crew in CrewAI has evolved with new functionalities. By incorporating verbose mode, memory capabilities, asynchronous task execution, output customization, language model configuration, and enhanced crew configurations, your AI team is more equipped than ever to tackle challenges efficiently. The depth of agent backstories and the precision of their objectives enrich collaboration, leading to successful project outcomes. This guide aims to provide you with a clear and detailed understanding of setting up and utilizing the CrewAI framework to its full potential.
1. `allow_code_execution`: Enable or disable code execution capabilities for the agent (default is False).
2. `max_execution_time`: Set a maximum execution time (in seconds) for the agent to complete a task.
3. `function_calling_llm`: Specify a separate language model for function calling.

View File

@@ -0,0 +1,94 @@
---
title: Initial Support to Bring Your Own Prompts in CrewAI
description: Enhancing customization and internationalization by allowing users to bring their own prompts in CrewAI.
---
# Initial Support to Bring Your Own Prompts in CrewAI
CrewAI now supports the ability to bring your own prompts, enabling extensive customization and internationalization. This feature allows users to tailor the inner workings of their agents to better suit specific needs, including support for multiple languages.
## Internationalization and Customization Support
### Custom Prompts with `prompt_file`
The `prompt_file` attribute facilitates full customization of the agent prompts, enhancing the global usability of CrewAI. Users can specify their prompt templates, ensuring that the agents communicate in a manner that aligns with specific project requirements or language preferences.
#### Example of a Custom Prompt File
The custom prompts can be defined in a JSON file, similar to the example provided [here](https://github.com/joaomdmoura/crewAI/blob/main/src/crewai/translations/en.json).
### Supported Languages
CrewAI's custom prompt support includes internationalization, allowing prompts to be written in different languages. This is particularly useful for global teams or projects that require multilingual support.
## How to Use the `prompt_file` Attribute
To utilize the `prompt_file` attribute, include it in your crew definition. Below is an example demonstrating how to set up agents and tasks with custom prompts.
### Example
```python
import os
from crewai import Agent, Task, Crew
# Define your agents
researcher = Agent(
role="Researcher",
goal="Make the best research and analysis on content about AI and AI agents",
backstory="You're an expert researcher, specialized in technology, software engineering, AI and startups. You work as a freelancer and is now working on doing research and analysis for a new customer.",
allow_delegation=False,
)
writer = Agent(
role="Senior Writer",
goal="Write the best content about AI and AI agents.",
backstory="You're a senior writer, specialized in technology, software engineering, AI and startups. You work as a freelancer and are now working on writing content for a new customer.",
allow_delegation=False,
)
# Define your tasks
tasks = [
Task(
description="Say Hi",
expected_output="The word: Hi",
agent=researcher,
)
]
# Instantiate your crew with custom prompts
crew = Crew(
agents=[researcher],
tasks=tasks,
prompt_file="prompt.json", # Path to your custom prompt file
)
# Get your crew to work!
crew.kickoff()
```
## Advanced Customization Features
### `language` Attribute
In addition to `prompt_file`, the `language` attribute can be used to specify the language for the agent's prompts. This ensures that the prompts are generated in the desired language, further enhancing the internationalization capabilities of CrewAI.
### Creating Custom Prompt Files
Custom prompt files should be structured in JSON format and include all necessary prompt templates. Below is a simplified example of a prompt JSON file:
```json
{
"system": "You are a system template.",
"prompt": "Here is your prompt template.",
"response": "Here is your response template."
}
```
### Benefits of Custom Prompts
- **Enhanced Flexibility**: Tailor agent communication to specific project needs.
- **Improved Usability**: Supports multiple languages, making it suitable for global projects.
- **Consistency**: Ensures uniform prompt structures across different agents and tasks.
By incorporating these updates, CrewAI provides users with the ability to fully customize and internationalize their agent prompts, making the platform more versatile and user-friendly.

View File

@@ -10,7 +10,16 @@ Crafting an efficient CrewAI team hinges on the ability to dynamically tailor yo
- **Role**: Specifies the agent's job within the crew, such as 'Analyst' or 'Customer Service Rep'.
- **Goal**: Defines what the agent aims to achieve, in alignment with its role and the overarching objectives of the crew.
- **Backstory**: Provides depth to the agent's persona, enriching its motivations and engagements within the crew.
- **Tools**: Represents the capabilities or methods the agent uses to perform tasks, from simple functions to intricate integrations.
- **Tools** *(Optional)*: Represents the capabilities or methods the agent uses to perform tasks, from simple functions to intricate integrations.
- **Cache** *(Optional)*: Determines whether the agent should use a cache for tool usage.
- **Max RPM**: Sets the maximum number of requests per minute (`max_rpm`). This attribute is optional and can be set to `None` for no limit, allowing for unlimited queries to external services if needed.
- **Verbose** *(Optional)*: Enables detailed logging of an agent's actions, useful for debugging and optimization. Specifically, it provides insights into agent execution processes, aiding in the optimization of performance.
- **Allow Delegation** *(Optional)*: `allow_delegation` controls whether the agent is allowed to delegate tasks to other agents.
- **Max Iter** *(Optional)*: The `max_iter` attribute allows users to define the maximum number of iterations an agent can perform for a single task, preventing infinite loops or excessively long executions. The default value is set to 25, providing a balance between thoroughness and efficiency. Once the agent approaches this number, it will try its best to give a good answer.
- **Max Execution Time** *(Optional)*: `max_execution_time` Sets the maximum execution time for an agent to complete a task.
- **System Template** *(Optional)*: `system_template` defines the system format for the agent.
- **Prompt Template** *(Optional)*: `prompt_template` defines the prompt format for the agent.
- **Response Template** *(Optional)*: `response_template` defines the response format for the agent.
## Advanced Customization Options
Beyond the basic attributes, CrewAI allows for deeper customization to enhance an agent's behavior and capabilities significantly.
@@ -26,7 +35,7 @@ Adjusting an agent's performance and monitoring its operations are crucial for e
- **RPM Limit**: Sets the maximum number of requests per minute (`max_rpm`). This attribute is optional and can be set to `None` for no limit, allowing for unlimited queries to external services if needed.
### Maximum Iterations for Task Execution
The `max_iter` attribute allows users to define the maximum number of iterations an agent can perform for a single task, preventing infinite loops or excessively long executions. The default value is set to 15, providing a balance between thoroughness and efficiency. Once the agent approaches this number, it will try its best to give a good answer.
The `max_iter` attribute allows users to define the maximum number of iterations an agent can perform for a single task, preventing infinite loops or excessively long executions. The default value is set to 25, providing a balance between thoroughness and efficiency. Once the agent approaches this number, it will try its best to give a good answer.
## Customizing Agents and Tools
Agents are customized by defining their attributes and tools during initialization. Tools are critical for an agent's functionality, enabling them to perform specialized tasks. The `tools` attribute should be an array of tools the agent can utilize, and it's initialized as an empty list by default. Tools can be added or modified post-agent initialization to adapt to new requirements.
@@ -57,7 +66,7 @@ agent = Agent(
memory=True, # Enable memory
verbose=True,
max_rpm=None, # No limit on requests per minute
max_iter=15, # Default value for maximum iterations
max_iter=25, # Default value for maximum iterations
allow_delegation=False
)
```

View File

@@ -0,0 +1,31 @@
---
title: Forcing Tool Output as Result
description: Learn how to force tool output as the result in of an Agent's task in crewAI.
---
## Introduction
In CrewAI, you can force the output of a tool as the result of an agent's task. This feature is useful when you want to ensure that the tool output is captured and returned as the task result, and avoid the agent modifying the output during the task execution.
## Forcing Tool Output as Result
To force the tool output as the result of an agent's task, you can set the `force_tool_output` parameter to `True` when creating the task. This parameter ensures that the tool output is captured and returned as the task result, without any modifications by the agent.
Here's an example of how to force the tool output as the result of an agent's task:
```python
# ...
# Define a custom tool that returns the result as the answer
coding_agent =Agent(
role="Data Scientist",
goal="Product amazing resports on AI",
backstory="You work with data and AI",
tools=[MyCustomTool(result_as_answer=True)],
)
# ...
```
### Workflow in Action
1. **Task Execution**: The agent executes the task using the tool provided.
2. **Tool Output**: The tool generates the output, which is captured as the task result.
3. **Agent Interaction**: The agent my reflect and take learnings from the tool but the output is not modified.
4. **Result Return**: The tool output is returned as the task result without any modifications.

View File

@@ -10,7 +10,7 @@ The hierarchical process in CrewAI introduces a structured approach to task mana
The hierarchical process is designed to leverage advanced models like GPT-4, optimizing token usage while handling complex tasks with greater efficiency.
## Hierarchical Process Overview
By default, tasks in CrewAI are managed through a sequential process. However, adopting a hierarchical approach allows for a clear hierarchy in task management, where a 'manager' agent coordinates the workflow, delegates tasks, and validates outcomes for streamlined and effective execution. This manager agent is automatically created by crewAI so you don't need to worry about it.
By default, tasks in CrewAI are managed through a sequential process. However, adopting a hierarchical approach allows for a clear hierarchy in task management, where a 'manager' agent coordinates the workflow, delegates tasks, and validates outcomes for streamlined and effective execution. This manager agent can now be either automatically created by CrewAI or explicitly set by the user.
### Key Features
- **Task Delegation**: A manager agent allocates tasks among crew members based on their roles and capabilities.
@@ -52,9 +52,10 @@ writer = Agent(
project_crew = Crew(
tasks=[...], # Tasks to be delegated and executed under the manager's supervision
agents=[researcher, writer],
manager_llm=ChatOpenAI(temperature=0, model="gpt-4"), # Mandatory for hierarchical process
manager_llm=ChatOpenAI(temperature=0, model="gpt-4"), # Mandatory if manager_agent is not set
process=Process.hierarchical, # Specifies the hierarchical management approach
memory=True, # Enable memory usage for enhanced task execution
manager_agent=None, # Optional: explicitly set a specific agent as manager instead of the manager_llm
)
```
@@ -64,4 +65,4 @@ project_crew = Crew(
3. **Sequential Task Progression**: Despite being a hierarchical process, tasks follow a logical order for smooth progression, facilitated by the manager's oversight.
## Conclusion
Adopting the hierarchical process in crewAI, with the correct configurations and understanding of the system's capabilities, facilitates an organized and efficient approach to project management.
Adopting the hierarchical process in CrewAI, with the correct configurations and understanding of the system's capabilities, facilitates an organized and efficient approach to project management. Utilize the advanced features and customizations to tailor the workflow to your specific needs, ensuring optimal task execution and project success.

View File

@@ -22,7 +22,7 @@ import os
from crewai import Agent, Task, Crew
from crewai_tools import SerperDevTool
os.environ["SERPER_API_KEY"] = "Your Key" # serper.dev API key
os.environ["SERPER_API_KEY"] = "Your Key" # serper.dev API key
os.environ["OPENAI_API_KEY"] = "Your Key"
# Loading Tools
@@ -30,59 +30,59 @@ search_tool = SerperDevTool()
# Define your agents with roles, goals, tools, and additional attributes
researcher = Agent(
role='Senior Research Analyst',
goal='Uncover cutting-edge developments in AI and data science',
backstory=(
"You are a Senior Research Analyst at a leading tech think tank."
"Your expertise lies in identifying emerging trends and technologies in AI and data science."
"You have a knack for dissecting complex data and presenting actionable insights."
),
verbose=True,
allow_delegation=False,
tools=[search_tool],
max_rpm=100
role='Senior Research Analyst',
goal='Uncover cutting-edge developments in AI and data science',
backstory=(
"You are a Senior Research Analyst at a leading tech think tank. "
"Your expertise lies in identifying emerging trends and technologies in AI and data science. "
"You have a knack for dissecting complex data and presenting actionable insights."
),
verbose=True,
allow_delegation=False,
tools=[search_tool]
)
writer = Agent(
role='Tech Content Strategist',
goal='Craft compelling content on tech advancements',
backstory=(
"You are a renowned Tech Content Strategist, known for your insightful and engaging articles on technology and innovation."
"With a deep understanding of the tech industry, you transform complex concepts into compelling narratives."
),
verbose=True,
allow_delegation=True,
tools=[search_tool],
cache=False, # Disable cache for this agent
role='Tech Content Strategist',
goal='Craft compelling content on tech advancements',
backstory=(
"You are a renowned Tech Content Strategist, known for your insightful and engaging articles on technology and innovation. "
"With a deep understanding of the tech industry, you transform complex concepts into compelling narratives."
),
verbose=True,
allow_delegation=True,
tools=[search_tool],
cache=False, # Disable cache for this agent
)
# Create tasks for your agents
task1 = Task(
description=(
"Conduct a comprehensive analysis of the latest advancements in AI in 2024."
"Identify key trends, breakthrough technologies, and potential industry impacts."
"Compile your findings in a detailed report."
"Make sure to check with a human if the draft is good before finalizing your answer."
),
expected_output='A comprehensive full report on the latest AI advancements in 2024, leave nothing out',
agent=researcher,
human_input=True,
description=(
"Conduct a comprehensive analysis of the latest advancements in AI in 2024. "
"Identify key trends, breakthrough technologies, and potential industry impacts. "
"Compile your findings in a detailed report. "
"Make sure to check with a human if the draft is good before finalizing your answer."
),
expected_output='A comprehensive full report on the latest AI advancements in 2024, leave nothing out',
agent=researcher,
human_input=True
)
task2 = Task(
description=(
"Using the insights from the researcher's report, develop an engaging blog post that highlights the most significant AI advancements."
"Your post should be informative yet accessible, catering to a tech-savvy audience."
"Aim for a narrative that captures the essence of these breakthroughs and their implications for the future."
),
expected_output='A compelling 3 paragraphs blog post formatted as markdown about the latest AI advancements in 2024',
agent=writer
description=(
"Using the insights from the researcher\'s report, develop an engaging blog post that highlights the most significant AI advancements. "
"Your post should be informative yet accessible, catering to a tech-savvy audience. "
"Aim for a narrative that captures the essence of these breakthroughs and their implications for the future."
),
expected_output='A compelling 3 paragraphs blog post formatted as markdown about the latest AI advancements in 2024',
agent=writer
)
# Instantiate your crew with a sequential process
crew = Crew(
agents=[researcher, writer],
tasks=[task1, task2],
verbose=2
agents=[researcher, writer],
tasks=[task1, task2],
verbose=2,
memory=True,
)
# Get your crew to work!

View File

@@ -12,7 +12,7 @@ Welcome to crewAI! This guide will walk you through the installation process for
To install crewAI, you need to have Python >=3.10 and <=3.13 installed on your system:
```shell
# Install the mains crewAI package
# Install the main crewAI package
pip install crewai
# Install the main crewAI package and the tools package

View File

@@ -0,0 +1,40 @@
---
title: Kickoff Async
description: Kickoff a Crew Asynchronously
---
## Introduction
CrewAI provides the ability to kickoff a crew asynchronously, allowing you to start the crew execution in a non-blocking manner. This feature is particularly useful when you want to run multiple crews concurrently or when you need to perform other tasks while the crew is executing.
## Asynchronous Crew Execution
To kickoff a crew asynchronously, use the `kickoff_async()` method. This method initiates the crew execution in a separate thread, allowing the main thread to continue executing other tasks.
Here's an example of how to kickoff a crew asynchronously:
```python
from crewai import Crew, Agent, Task
# Create an agent with code execution enabled
coding_agent = Agent(
role="Python Data Analyst",
goal="Analyze data and provide insights using Python",
backstory="You are an experienced data analyst with strong Python skills.",
allow_code_execution=True
)
# Create a task that requires code execution
data_analysis_task = Task(
description="Analyze the given dataset and calculate the average age of participants. Ages: {ages}",
agent=coding_agent
)
# Create a crew and add the task
analysis_crew = Crew(
agents=[coding_agent],
tasks=[data_analysis_task]
)
# Execute the crew
result = analysis_crew.kickoff_async(inputs={"ages": [25, 30, 35, 40, 45]})
```

View File

@@ -0,0 +1,45 @@
---
title: Kickoff For Each
description: Kickoff a Crew for a List
---
## Introduction
CrewAI provides the ability to kickoff a crew for each item in a list, allowing you to execute the crew for each item in the list. This feature is particularly useful when you need to perform the same set of tasks for multiple items.
## Kicking Off a Crew for Each Item
To kickoff a crew for each item in a list, use the `kickoff_for_each()` method. This method executes the crew for each item in the list, allowing you to process multiple items efficiently.
Here's an example of how to kickoff a crew for each item in a list:
```python
from crewai import Crew, Agent, Task
# Create an agent with code execution enabled
coding_agent = Agent(
role="Python Data Analyst",
goal="Analyze data and provide insights using Python",
backstory="You are an experienced data analyst with strong Python skills.",
allow_code_execution=True
)
# Create a task that requires code execution
data_analysis_task = Task(
description="Analyze the given dataset and calculate the average age of participants. Ages: {ages}",
agent=coding_agent
)
# Create a crew and add the task
analysis_crew = Crew(
agents=[coding_agent],
tasks=[data_analysis_task]
)
datasets = [
{ "ages": [25, 30, 35, 40, 45] },
{ "ages": [20, 25, 30, 35, 40] },
{ "ages": [30, 35, 40, 45, 50] }
]
# Execute the crew
result = analysis_crew.kickoff_for_each(inputs=datasets)
```

View File

@@ -1,31 +1,37 @@
---
title: Connect CrewAI to LLMs
description: Comprehensive guide on integrating CrewAI with various Large Language Models (LLMs), including detailed class attributes and methods.
description: Comprehensive guide on integrating CrewAI with various Large Language Models (LLMs), including detailed class attributes, methods, and configuration options.
---
## Connect CrewAI to LLMs
!!! note "Default LLM"
By default, CrewAI uses OpenAI's GPT-4 model for language processing. You can configure your agents to use a different model or API. This guide shows how to connect your agents to various LLMs through environment variables and direct instantiation.
By default, CrewAI uses OpenAI's GPT-4 model (specifically, the model specified by the OPENAI_MODEL_NAME environment variable, defaulting to "gpt-4o") for language processing. You can configure your agents to use a different model or API as described in this guide.
CrewAI offers flexibility in connecting to various LLMs, including local models via [Ollama](https://ollama.ai) and different APIs like Azure. It's compatible with all [LangChain LLM](https://python.langchain.com/docs/integrations/llms/) components, enabling diverse integrations for tailored AI solutions.
## CrewAI Agent Overview
The `Agent` class is the cornerstone for implementing AI solutions in CrewAI. Here's an updated overview reflecting the latest codebase changes:
The `Agent` class is the cornerstone for implementing AI solutions in CrewAI. Here's a comprehensive overview of the Agent class attributes and methods:
- **Attributes**:
- `role`: Defines the agent's role within the solution.
- `goal`: Specifies the agent's objective.
- `backstory`: Provides a background story to the agent.
- `llm`: Indicates the Large Language Model the agent uses. By default, it uses the GPT-4 model defined in the environment variable "OPENAI_MODEL_NAME".
- `function_calling_llm` *Optional*: Will turn the ReAct crewAI agent into a function calling agent.
- `max_iter`: Maximum number of iterations for an agent to execute a task, default is 15.
- `memory`: Enables the agent to retain information during and a across executions. Default is `False`.
- `max_rpm`: Maximum number of requests per minute the agent's execution should respect. Optional.
- `verbose`: Enables detailed logging of the agent's execution. Default is `False`.
- `allow_delegation`: Allows the agent to delegate tasks to other agents, default is `True`.
- `cache` *Optional*: Determines whether the agent should use a cache for tool usage. Default is `True`.
- `max_rpm` *Optional*: Maximum number of requests per minute the agent's execution should respect. Optional.
- `verbose` *Optional*: Enables detailed logging of the agent's execution. Default is `False`.
- `allow_delegation` *Optional*: Allows the agent to delegate tasks to other agents, default is `True`.
- `tools`: Specifies the tools available to the agent for task execution. Optional.
- `step_callback`: Provides a callback function to be executed after each step. Optional.
- `cache`: Determines whether the agent should use a cache for tool usage. Default is `True`.
- `max_iter` *Optional*: Maximum number of iterations for an agent to execute a task, default is 25.
- `max_execution_time` *Optional*: Maximum execution time for an agent to execute a task. Optional.
- `step_callback` *Optional*: Provides a callback function to be executed after each step. Optional.
- `llm` *Optional*: Indicates the Large Language Model the agent uses. By default, it uses the GPT-4 model defined in the environment variable "OPENAI_MODEL_NAME".
- `function_calling_llm` *Optional* : Will turn the ReAct CrewAI agent into a function-calling agent.
- `callbacks` *Optional*: A list of callback functions from the LangChain library that are triggered during the agent's execution process.
- `system_template` *Optional*: Optional string to define the system format for the agent.
- `prompt_template` *Optional*: Optional string to define the prompt format for the agent.
- `response_template` *Optional*: Optional string to define the response format for the agent.
```python
# Required
@@ -36,65 +42,34 @@ example_agent = Agent(
role='Local Expert',
goal='Provide insights about the city',
backstory="A knowledgeable local guide.",
verbose=True,
memory=True
verbose=True
)
```
## Ollama Integration
Ollama is preferred for local LLM integration, offering customization and privacy benefits. To integrate Ollama with CrewAI, set the appropriate environment variables as shown below.
Ollama is preferred for local LLM integration, offering customization and privacy benefits. To integrate Ollama with CrewAI, set the appropriate environment variables as shown below.
### Setting Up Ollama
- **Environment Variables Configuration**: To integrate Ollama, set the following environment variables:
```sh
OPENAI_API_BASE='http://localhost:11434/v1'
OPENAI_MODEL_NAME='openhermes' # Adjust based on available model
OPENAI_API_BASE='http://localhost:11434'
OPENAI_MODEL_NAME='llama2' # Adjust based on available model
OPENAI_API_KEY=''
```
## Ollama Integration (ex. for using Llama 2 locally)
1. [Download Ollama](https://ollama.com/download).
2. After setting up the Ollama, Pull the Llama2 by typing following lines into the terminal ```ollama pull llama2```.
3. Create a ModelFile similar the one below in your project directory.
```
FROM llama2
# Set parameters
PARAMETER temperature 0.8
PARAMETER stop Result
# Sets a custom system message to specify the behavior of the chat assistant
# Leaving it blank for now.
SYSTEM """"""
```
4. Create a script to get the base model, which in our case is llama2, and create a model on top of that with ModelFile above. PS: this will be ".sh" file.
```
#!/bin/zsh
# variables
model_name="llama2"
custom_model_name="crewai-llama2"
#get the base model
ollama pull $model_name
#create the model file
ollama create $custom_model_name -f ./Llama2ModelFile
```
5. Go into the directory where the script file and ModelFile is located and run the script.
6. Enjoy your free Llama2 model that powered up by excellent agents from crewai.
3. Enjoy your free Llama2 model that powered up by excellent agents from crewai.
```
from crewai import Agent, Task, Crew
from langchain_openai import ChatOpenAI
from langchain.llms import Ollama
import os
os.environ["OPENAI_API_KEY"] = "NA"
llm = ChatOpenAI(
model = "crewai-llama2",
base_url = "http://localhost:11434/v1")
llm = Ollama(
model = "llama2",
base_url = "http://localhost:11434")
general_agent = Agent(role = "Math Professor",
goal = """Provide the solution to the students that are asking mathematical questions and give them the answer.""",
@@ -102,7 +77,8 @@ general_agent = Agent(role = "Math Professor",
allow_delegation = False,
verbose = True,
llm = llm)
task = Task (description="""what is 3 + 5""",
task = Task(description="""what is 3 + 5""",
agent = general_agent,
expected_output="A numerical answer.")
@@ -151,7 +127,7 @@ llm = HuggingFaceHub(
```
## OpenAI Compatible API Endpoints
Switch between APIs and models seamlessly using environment variables, supporting platforms like FastChat, LM Studio, and Mistral AI.
Switch between APIs and models seamlessly using environment variables, supporting platforms like FastChat, LM Studio, Groq, and Mistral AI.
### Configuration Examples
#### FastChat
@@ -162,12 +138,19 @@ OPENAI_API_KEY=NA
```
#### LM Studio
Launch [LM Studio](https://lmstudio.ai) and go to the Server tab. Then select a model from the dropdown menu then wait for it to load. Once it's loaded, click the green Start Server button and use the URL, port, and API key that's shown (you can modify them). Below is an example of the default settings as of LM Studio 0.2.19:
Launch [LM Studio](https://lmstudio.ai) and go to the Server tab. Then select a model from the dropdown menu and wait for it to load. Once it's loaded, click the green Start Server button and use the URL, port, and API key that's shown (you can modify them). Below is an example of the default settings as of LM Studio 0.2.19:
```sh
OPENAI_API_BASE="http://localhost:1234/v1"
OPENAI_API_KEY="lm-studio"
```
#### Groq API
```sh
OPENAI_API_KEY=your-groq-api-key
OPENAI_MODEL_NAME='llama3-8b-8192'
OPENAI_API_BASE=https://api.groq.com/openai/v1
```
#### Mistral API
```sh
OPENAI_API_KEY=your-mistral-api-key
@@ -176,15 +159,16 @@ OPENAI_MODEL_NAME="mistral-small"
```
### Solar
```sh
```python
from langchain_community.chat_models.solar import SolarChat
# Initialize language model
os.environ["SOLAR_API_KEY"] = "your-solar-api-key"
llm = SolarChat(max_tokens=1024)
Free developer API key available here: https://console.upstage.ai/services/solar
Langchain Example: https://github.com/langchain-ai/langchain/pull/18556
# Free developer API key available here: https://console.upstage.ai/services/solar
# Langchain Example: https://github.com/langchain-ai/langchain/pull/18556
```
### text-gen-web-ui
```sh
OPENAI_API_BASE=http://localhost:5000/v1
@@ -193,17 +177,16 @@ OPENAI_API_KEY=NA
```
### Cohere
```sh
from langchain_community.chat_models import ChatCohere
```python
from langchain_cohere import ChatCohere
# Initialize language model
os.environ["COHERE_API_KEY"] = "your-cohere-api-key"
llm = ChatCohere()
Free developer API key available here: https://cohere.com/
Langchain Documentation: https://python.langchain.com/docs/integrations/chat/cohere
# Free developer API key available here: https://cohere.com/
# Langchain Documentation: https://python.langchain.com/docs/integrations/chat/cohere
```
### Azure Open AI Configuration
For Azure OpenAI API integration, set the following environment variables:
```sh

View File

@@ -0,0 +1,89 @@
---
title: CrewAI Agent Monitoring with Langtrace
description: How to monitor cost, latency, and performance of CrewAI Agents using Langtrace, an external observability tool.
---
# Langtrace Overview
Langtrace is an open-source, external tool that helps you set up observability and evaluations for Large Language Models (LLMs), LLM frameworks, and Vector Databases. While not built directly into CrewAI, Langtrace can be used alongside CrewAI to gain deep visibility into the cost, latency, and performance of your CrewAI Agents. This integration allows you to log hyperparameters, monitor performance regressions, and establish a process for continuous improvement of your Agents.
## Setup Instructions
1. Sign up for [Langtrace](https://langtrace.ai/) by visiting [https://langtrace.ai/signup](https://langtrace.ai/signup).
2. Create a project and generate an API key.
3. Install Langtrace in your CrewAI project using the following commands:
```bash
# Install the SDK
pip install langtrace-python-sdk
```
## Using Langtrace with CrewAI
To integrate Langtrace with your CrewAI project, follow these steps:
1. Import and initialize Langtrace at the beginning of your script, before any CrewAI imports:
```python
from langtrace_python_sdk import langtrace
langtrace.init(api_key='<LANGTRACE_API_KEY>')
# Now import CrewAI modules
from crewai import Agent, Task, Crew
```
2. Create your CrewAI agents and tasks as usual.
3. Use Langtrace's tracking functions to monitor your CrewAI operations. For example:
```python
with langtrace.trace("CrewAI Task Execution"):
result = crew.kickoff()
```
### Features and Their Application to CrewAI
1. **LLM Token and Cost Tracking**
- Monitor the token usage and associated costs for each CrewAI agent interaction.
- Example:
```python
with langtrace.trace("Agent Interaction"):
agent_response = agent.execute(task)
```
2. **Trace Graph for Execution Steps**
- Visualize the execution flow of your CrewAI tasks, including latency and logs.
- Useful for identifying bottlenecks in your agent workflows.
3. **Dataset Curation with Manual Annotation**
- Create datasets from your CrewAI task outputs for future training or evaluation.
- Example:
```python
langtrace.log_dataset_item(task_input, agent_output, {"task_type": "research"})
```
4. **Prompt Versioning and Management**
- Keep track of different versions of prompts used in your CrewAI agents.
- Useful for A/B testing and optimizing agent performance.
5. **Prompt Playground with Model Comparisons**
- Test and compare different prompts and models for your CrewAI agents before deployment.
6. **Testing and Evaluations**
- Set up automated tests for your CrewAI agents and tasks.
- Example:
```python
langtrace.evaluate(agent_output, expected_output, "accuracy")
```
## Monitoring New CrewAI Features
CrewAI has introduced several new features that can be monitored using Langtrace:
1. **Code Execution**: Monitor the performance and output of code executed by agents.
```python
with langtrace.trace("Agent Code Execution"):
code_output = agent.execute_code(code_snippet)
```
2. **Third-party Agent Integration**: Track interactions with LlamaIndex, LangChain, and Autogen agents.

View File

@@ -13,8 +13,9 @@ The sequential process ensures tasks are executed one after the other, following
- **Linear Task Flow**: Ensures orderly progression by handling tasks in a predetermined sequence.
- **Simplicity**: Best suited for projects with clear, step-by-step tasks.
- **Easy Monitoring**: Facilitates easy tracking of task completion and project progress.
## Implementing the Sequential Process
Assemble your crew and define tasks in the order they need to be executed.
To use the sequential process, assemble your crew and define tasks in the order they need to be executed.
```python
from crewai import Crew, Process, Agent, Task
@@ -36,10 +37,9 @@ writer = Agent(
backstory='A skilled writer with a talent for crafting compelling narratives'
)
# Define the tasks in sequence
research_task = Task(description='Gather relevant data...', agent=researcher)
analysis_task = Task(description='Analyze the data...', agent=analyst)
writing_task = Task(description='Compose the report...', agent=writer)
research_task = Task(description='Gather relevant data...', agent=researcher, expected_output='Raw Data')
analysis_task = Task(description='Analyze the data...', agent=analyst, expected_output='Data Insights')
writing_task = Task(description='Compose the report...', agent=writer, expected_output='Final Report')
# Form the crew with a sequential process
report_crew = Crew(
@@ -47,6 +47,9 @@ report_crew = Crew(
tasks=[research_task, analysis_task, writing_task],
process=Process.sequential
)
# Execute the crew
result = report_crew.kickoff()
```
### Workflow in Action
@@ -54,5 +57,29 @@ report_crew = Crew(
2. **Subsequent Tasks**: Agents pick up their tasks based on the process type, with outcomes of preceding tasks or manager directives guiding their execution.
3. **Completion**: The process concludes once the final task is executed, leading to project completion.
## Conclusion
The sequential and hierarchical processes in CrewAI offer clear, adaptable paths for task execution. They are well-suited for projects requiring logical progression and dynamic decision-making, ensuring each step is completed effectively, thereby facilitating a cohesive final product.
## Advanced Features
### Task Delegation
In sequential processes, if an agent has `allow_delegation` set to `True`, they can delegate tasks to other agents in the crew. This feature is automatically set up when there are multiple agents in the crew.
### Asynchronous Execution
Tasks can be executed asynchronously, allowing for parallel processing when appropriate. To create an asynchronous task, set `async_execution=True` when defining the task.
### Memory and Caching
CrewAI supports both memory and caching features:
- **Memory**: Enable by setting `memory=True` when creating the Crew. This allows agents to retain information across tasks.
- **Caching**: By default, caching is enabled. Set `cache=False` to disable it.
### Callbacks
You can set callbacks at both the task and step level:
- `task_callback`: Executed after each task completion.
- `step_callback`: Executed after each step in an agent's execution.
### Usage Metrics
CrewAI tracks token usage across all tasks and agents. You can access these metrics after execution.
## Best Practices for Sequential Processes
1. **Order Matters**: Arrange tasks in a logical sequence where each task builds upon the previous one.
2. **Clear Task Descriptions**: Provide detailed descriptions for each task to guide the agents effectively.
3. **Appropriate Agent Selection**: Match agents' skills and roles to the requirements of each task.
4. **Use Context**: Leverage the context from previous tasks to inform subsequent ones

View File

@@ -0,0 +1,137 @@
---
title: Starting a New CrewAI Project
description: A comprehensive guide to starting a new CrewAI project, including the latest updates and project setup methods.
---
# Starting Your CrewAI Project
Welcome to the ultimate guide for starting a new CrewAI project. This document will walk you through the steps to create, customize, and run your CrewAI project, ensuring you have everything you need to get started.
## Prerequisites
We assume you have already installed CrewAI. If not, please refer to the [installation guide](how-to/Installing-CrewAI.md) to install CrewAI and its dependencies.
## Creating a New Project
To create a new project, run the following CLI command:
```shell
$ crewai create my_project
```
This command will create a new project folder with the following structure:
```shell
my_project/
├── .gitignore
├── pyproject.toml
├── README.md
└── src/
└── my_project/
├── __init__.py
├── main.py
├── crew.py
├── tools/
│ ├── custom_tool.py
│ └── __init__.py
└── config/
├── agents.yaml
└── tasks.yaml
```
You can now start developing your project by editing the files in the `src/my_project` folder. The `main.py` file is the entry point of your project, and the `crew.py` file is where you define your agents and tasks.
## Customizing Your Project
To customize your project, you can:
- Modify `src/my_project/config/agents.yaml` to define your agents.
- Modify `src/my_project/config/tasks.yaml` to define your tasks.
- Modify `src/my_project/crew.py` to add your own logic, tools, and specific arguments.
- Modify `src/my_project/main.py` to add custom inputs for your agents and tasks.
- Add your environment variables into the `.env` file.
### Example: Defining Agents and Tasks
#### agents.yaml
```yaml
researcher:
role: >
Job Candidate Researcher
goal: >
Find potential candidates for the job
backstory: >
You are adept at finding the right candidates by exploring various online
resources. Your skill in identifying suitable candidates ensures the best
match for job positions.
```
#### tasks.yaml
```yaml
research_candidates_task:
description: >
Conduct thorough research to find potential candidates for the specified job.
Utilize various online resources and databases to gather a comprehensive list of potential candidates.
Ensure that the candidates meet the job requirements provided.
Job Requirements:
{job_requirements}
expected_output: >
A list of 10 potential candidates with their contact information and brief profiles highlighting their suitability.
```
## Installing Dependencies
To install the dependencies for your project, you can use Poetry. First, navigate to your project directory:
```shell
$ cd my_project
$ poetry lock
$ poetry install
```
This will install the dependencies specified in the `pyproject.toml` file.
## Interpolating Variables
Any variable interpolated in your `agents.yaml` and `tasks.yaml` files like `{variable}` will be replaced by the value of the variable in the `main.py` file.
#### agents.yaml
```yaml
research_task:
description: >
Conduct a thorough research about the customer and competitors in the context
of {customer_domain}.
Make sure you find any interesting and relevant information given the
current year is 2024.
expected_output: >
A complete report on the customer and their customers and competitors,
including their demographics, preferences, market positioning and audience engagement.
```
#### main.py
```python
# main.py
def run():
inputs = {
"customer_domain": "crewai.com"
}
MyProjectCrew(inputs).crew().kickoff(inputs=inputs)
```
## Running Your Project
To run your project, use the following command:
```shell
$ poetry run my_project
```
This will initialize your crew of AI agents and begin task execution as defined in your configuration in the `main.py` file.
## Deploying Your Project
The easiest way to deploy your crew is through [CrewAI+](https://www.crewai.com/crewaiplus), where you can deploy your crew in a few clicks.

View File

@@ -0,0 +1,87 @@
---
title: Setting a Specific Agent as Manager in CrewAI
description: Learn how to set a custom agent as the manager in CrewAI, providing more control over task management and coordination.
---
# Setting a Specific Agent as Manager in CrewAI
CrewAI allows users to set a specific agent as the manager of the crew, providing more control over the management and coordination of tasks. This feature enables the customization of the managerial role to better fit your project's requirements.
## Using the `manager_agent` Attribute
### Custom Manager Agent
The `manager_agent` attribute allows you to define a custom agent to manage the crew. This agent will oversee the entire process, ensuring that tasks are completed efficiently and to the highest standard.
### Example
```python
import os
from crewai import Agent, Task, Crew, Process
# Define your agents
researcher = Agent(
role="Researcher",
goal="Conduct thorough research and analysis on AI and AI agents",
backstory="You're an expert researcher, specialized in technology, software engineering, AI, and startups. You work as a freelancer and are currently researching for a new client.",
allow_delegation=False,
)
writer = Agent(
role="Senior Writer",
goal="Create compelling content about AI and AI agents",
backstory="You're a senior writer, specialized in technology, software engineering, AI, and startups. You work as a freelancer and are currently writing content for a new client.",
allow_delegation=False,
)
# Define your task
task = Task(
description="Generate a list of 5 interesting ideas for an article, then write one captivating paragraph for each idea that showcases the potential of a full article on this topic. Return the list of ideas with their paragraphs and your notes.",
expected_output="5 bullet points, each with a paragraph and accompanying notes.",
)
# Define the manager agent
manager = Agent(
role="Project Manager",
goal="Efficiently manage the crew and ensure high-quality task completion",
backstory="You're an experienced project manager, skilled in overseeing complex projects and guiding teams to success. Your role is to coordinate the efforts of the crew members, ensuring that each task is completed on time and to the highest standard.",
allow_delegation=True,
)
# Instantiate your crew with a custom manager
crew = Crew(
agents=[researcher, writer],
tasks=[task],
manager_agent=manager,
process=Process.hierarchical,
)
# Start the crew's work
result = crew.kickoff()
```
## Benefits of a Custom Manager Agent
- **Enhanced Control**: Tailor the management approach to fit the specific needs of your project.
- **Improved Coordination**: Ensure efficient task coordination and management by an experienced agent.
- **Customizable Management**: Define managerial roles and responsibilities that align with your project's goals.
## Setting a Manager LLM
If you're using the hierarchical process and don't want to set a custom manager agent, you can specify the language model for the manager:
```python
from langchain_openai import ChatOpenAI
manager_llm = ChatOpenAI(model_name="gpt-4")
crew = Crew(
agents=[researcher, writer],
tasks=[task],
process=Process.hierarchical,
manager_llm=manager_llm
)
```
Note: Either `manager_agent` or `manager_llm` must be set when using the hierarchical process.

View File

@@ -33,6 +33,11 @@ Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By
Crews
</a>
</li>
<li>
<a href="./core-concepts/Training-Crew">
Training
</a>
</li>
<li>
<a href="./core-concepts/Memory">
Memory
@@ -43,6 +48,11 @@ Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By
<div style="width:30%">
<h2>How-To Guides</h2>
<ul>
<li>
<a href="./how-to/Start-a-New-CrewAI-Project">
Starting Your crewAI Project
</a>
</li>
<li>
<a href="./how-to/Installing-CrewAI">
Installing crewAI
@@ -78,16 +88,41 @@ Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By
Customizing Agents
</a>
</li>
<li>
<a href="./how-to/Coding-Agents">
Coding Agents
</a>
</li>
<li>
<a href="./how-to/Force-Tool-Ouput-as-Result">
Forcing Tool Output as Result
</a>
</li>
<li>
<a href="./how-to/Human-Input-on-Execution">
Human Input on Execution
</a>
</li>
<li>
<a href="./how-to/Kickoff-async">
Kickoff a Crew Asynchronously
</a>
</li>
<li>
<a href="./how-to/Kickoff-for-each">
Kickoff a Crew for a List
</a>
</li>
<li>
<a href="./how-to/AgentOps-Observability">
Agent Monitoring with AgentOps
</a>
</li>
<li>
<a href="./how-to/Langtrace-Observability">
Agent Monitoring with LangTrace
</a>
</li>
</ul>
</div>
<div style="width:30%">

View File

@@ -2,6 +2,7 @@
title: Telemetry
description: Understanding the telemetry data collected by CrewAI and how it contributes to the enhancement of the library.
---
## Telemetry
CrewAI utilizes anonymous telemetry to gather usage statistics with the primary goal of enhancing the library. Our focus is on improving and developing the features, integrations, and tools most utilized by our users.

View File

@@ -2,11 +2,17 @@
## Description
[Browserbase](https://browserbase.com) is a serverless platform for running headless browsers, it offers advanced debugging, session recordings, stealth mode, integrated proxies and captcha solving.
[Browserbase](https://browserbase.com) is a developer platform to reliably run, manage, and monitor headless browsers.
Power your AI data retrievals with:
- [Serverless Infrastructure](https://docs.browserbase.com/under-the-hood) providing reliable browsers to extract data from complex UIs
- [Stealth Mode](https://docs.browserbase.com/features/stealth-mode) with included fingerprinting tactics and automatic captcha solving
- [Session Debugger](https://docs.browserbase.com/features/sessions) to inspect your Browser Session with networks timeline and logs
- [Live Debug](https://docs.browserbase.com/guides/session-debug-connection/browser-remote-control) to quickly debug your automation
## Installation
- Get an API key from [browserbase.com](https://browserbase.com) and set it in environment variables (`BROWSERBASE_API_KEY`).
- Get an API key and Project ID from [browserbase.com](https://browserbase.com) and set it in environment variables (`BROWSERBASE_API_KEY`, `BROWSERBASE_PROJECT_ID`).
- Install the [Browserbase SDK](http://github.com/browserbase/python-sdk) along with `crewai[tools]` package:
```
@@ -25,5 +31,8 @@ tool = BrowserbaseLoadTool()
## Arguments
- `api_key`: Optional. Specifies Browserbase API key. Defaults is the `BROWSERBASE_API_KEY` environment variable.
- `text_content`: Optional. Load pages as readable text. Default is `False`.
- `api_key` Optional. Browserbase API key. Default is `BROWSERBASE_API_KEY` env variable.
- `project_id` Optional. Browserbase Project ID. Default is `BROWSERBASE_PROJECT_ID` env variable.
- `text_content` Retrieve only text content. Default is `False`.
- `session_id` Optional. Provide an existing Session ID.
- `proxy` Optional. Enable/Disable Proxies."

View File

@@ -0,0 +1,41 @@
# CodeInterpreterTool
## Description
This tool is used to give the Agent the ability to run code (Python3) from the code generated by the Agent itself. The code is executed in a sandboxed environment, so it is safe to run any code.
It is incredible useful since it allows the Agent to generate code, run it in the same environment, get the result and use it to make decisions.
## Requirements
- Docker
## Installation
Install the crewai_tools package
```shell
pip install 'crewai[tools]'
```
## Example
Remember that when using this tool, the code must be generated by the Agent itself. The code must be a Python3 code. And it will take some time for the first time to run because it needs to build the Docker image.
```python
from crewai import Agent
from crewai_tools import CodeInterpreterTool
Agent(
...
tools=[CodeInterpreterTool()],
)
```
We also provide a simple way to use it directly from the Agent.
```python
from crewai import Agent
agent = Agent(
...
allow_code_execution=True,
)
```

View File

@@ -0,0 +1,72 @@
# ComposioTool Documentation
## Description
This tools is a wrapper around the composio toolset and gives your agent access to a wide variety of tools from the composio SDK.
## Installation
To incorporate this tool into your project, follow the installation instructions below:
```shell
pip install composio-core
pip install 'crewai[tools]'
```
after the installation is complete, either run `composio login` or export your composio API key as `COMPOSIO_API_KEY`.
## Example
The following example demonstrates how to initialize the tool and execute a github action:
1. Initialize toolset
```python
from composio import App
from crewai_tools import ComposioTool
from crewai import Agent, Task
tools = [ComposioTool.from_action(action=Action.GITHUB_ACTIVITY_STAR_REPO_FOR_AUTHENTICATED_USER)]
```
If you don't know what action you want to use, use `from_app` and `tags` filter to get relevant actions
```python
tools = ComposioTool.from_app(App.GITHUB, tags=["important"])
```
or use `use_case` to search relevant actions
```python
tools = ComposioTool.from_app(App.GITHUB, use_case="Star a github repository")
```
2. Define agent
```python
crewai_agent = Agent(
role="Github Agent",
goal="You take action on Github using Github APIs",
backstory=(
"You are AI agent that is responsible for taking actions on Github "
"on users behalf. You need to take action on Github using Github APIs"
),
verbose=True,
tools=tools,
)
```
3. Execute task
```python
task = Task(
description="Star a repo ComposioHQ/composio on GitHub",
agent=crewai_agent,
expected_output="if the star happened",
)
task.execute()
```
* More detailed list of tools can be found [here](https://app.composio.dev)

View File

@@ -0,0 +1,36 @@
# EXASearchTool Documentation
## Description
The EXASearchTool is designed to perform a semantic search for a specified query from a text's content across the internet. It utilizes the [exa.ai](https://exa.ai/) API to fetch and display the most relevant search results based on the query provided by the user.
## Installation
To incorporate this tool into your project, follow the installation instructions below:
```shell
pip install 'crewai[tools]'
```
## Example
The following example demonstrates how to initialize the tool and execute a search with a given query:
```python
from crewai_tools import EXASearchTool
# Initialize the tool for internet searching capabilities
tool = EXASearchTool()
```
## Steps to Get Started
To effectively use the EXASearchTool, follow these steps:
1. **Package Installation**: Confirm that the `crewai[tools]` package is installed in your Python environment.
2. **API Key Acquisition**: Acquire a [exa.ai](https://exa.ai/) API key by registering for a free account at [exa.ai](https://exa.ai/).
3. **Environment Configuration**: Store your obtained API key in an environment variable named `EXA_API_KEY` to facilitate its use by the tool.
## Conclusion
By integrating the EXASearchTool into Python projects, users gain the ability to conduct real-time, relevant searches across the internet directly from their applications. By adhering to the setup and usage guidelines provided, incorporating this tool into projects is streamlined and straightforward.

View File

@@ -4,7 +4,7 @@
We are still working on improving tools, so there might be unexpected behavior or changes in the future.
## Description
The GithubSearchTool is a Read, Append, and Generate (RAG) tool specifically designed for conducting semantic searches within GitHub repositories. Utilizing advanced semantic search capabilities, it sifts through code, pull requests, issues, and repositories, making it an essential tool for developers, researchers, or anyone in need of precise information from GitHub.
The GithubSearchTool is a Retrieval-Augmented Generation (RAG) tool specifically designed for conducting semantic searches within GitHub repositories. Utilizing advanced semantic search capabilities, it sifts through code, pull requests, issues, and repositories, making it an essential tool for developers, researchers, or anyone in need of precise information from GitHub.
## Installation
To use the GithubSearchTool, first ensure the crewai_tools package is installed in your Python environment:

View File

@@ -4,7 +4,7 @@
The MDXSearchTool is in continuous development. Features may be added or removed, and functionality could change unpredictably as we refine the tool.
## Description
The MDX Search Tool is a component of the `crewai_tools` package aimed at facilitating advanced market data extraction. This tool is invaluable for researchers and analysts seeking quick access to market insights, especially within the AI sector. It simplifies the task of acquiring, interpreting, and organizing market data by interfacing with various data sources.
The MDX Search Tool is a component of the `crewai_tools` package aimed at facilitating advanced markdown language extraction. It enables users to effectively search and extract relevant information from MD files using query-based searches. This tool is invaluable for data analysis, information management, and research tasks, streamlining the process of finding specific information within large document collections.
## Installation
Before using the MDX Search Tool, ensure the `crewai_tools` package is installed. If it is not, you can install it with the following command:
@@ -59,4 +59,4 @@ tool = MDXSearchTool(
),
)
)
```
```

View File

@@ -29,7 +29,7 @@ tool = PDFSearchTool(pdf='path/to/your/document.pdf')
```
## Arguments
- `pdf`: **Optinal** The PDF path for the search. Can be provided at initialization or within the `run` method's arguments. If provided at initialization, the tool confines its search to the specified document.
- `pdf`: **Optional** The PDF path for the search. Can be provided at initialization or within the `run` method's arguments. If provided at initialization, the tool confines its search to the specified document.
## Custom model and embeddings

View File

@@ -31,7 +31,7 @@ tool = TXTSearchTool(txt='path/to/text/file.txt')
```
## Arguments
- `txt` (str): **Optinal**. The path to the text file you want to search. This argument is only required if the tool was not initialized with a specific text file; otherwise, the search will be conducted within the initially provided text file.
- `txt` (str): **Optional**. The path to the text file you want to search. This argument is only required if the tool was not initialized with a specific text file; otherwise, the search will be conducted within the initially provided text file.
## Custom model and embeddings

View File

@@ -126,22 +126,35 @@ nav:
- Processes: 'core-concepts/Processes.md'
- Crews: 'core-concepts/Crews.md'
- Collaboration: 'core-concepts/Collaboration.md'
- Training: 'core-concepts/Training-Crew.md'
- Memory: 'core-concepts/Memory.md'
- Using LangChain Tools: 'core-concepts/Using-LangChain-Tools.md'
- Using LlamaIndex Tools: 'core-concepts/Using-LlamaIndex-Tools.md'
- How to Guides:
- Starting Your crewAI Project: 'how-to/Start-a-New-CrewAI-Project.md'
- Installing CrewAI: 'how-to/Installing-CrewAI.md'
- Getting Started: 'how-to/Creating-a-Crew-and-kick-it-off.md'
- Create Custom Tools: 'how-to/Create-Custom-Tools.md'
- Using Sequential Process: 'how-to/Sequential.md'
- Using Hierarchical Process: 'how-to/Hierarchical.md'
- Create your own Manager Agent: 'how-to/Your-Own-Manager-Agent.md'
- Connecting to any LLM: 'how-to/LLM-Connections.md'
- Customizing Agents: 'how-to/Customizing-Agents.md'
- Coding Agents: 'how-to/Coding-Agents.md'
- Forcing Tool Output as Result: 'how-to/Force-Tool-Ouput-as-Result.md'
- Human Input on Execution: 'how-to/Human-Input-on-Execution.md'
- Kickoff a Crew Asynchronously: 'how-to/Kickoff-async.md'
- Kickoff a Crew for a List: 'how-to/Kickoff-for-each.md'
- Agent Monitoring with AgentOps: 'how-to/AgentOps-Observability.md'
- Agent Monitoring with LangTrace: 'how-to/Langtrace-Observability.md'
- Tools Docs:
- Google Serper Search: 'tools/SerperDevTool.md'
- Browserbase Web Loader: 'tools/BrowserbaseLoadTool.md'
- Composio Tools: 'tools/ComposioTool.md'
- Code Interpreter: 'tools/CodeInterpreterTool.md'
- Scrape Website: 'tools/ScrapeWebsiteTool.md'
- Directory Read: 'tools/DirectoryReadTool.md'
- Exa Serch Web Loader: 'tools/EXASearchTool.md'
- File Read: 'tools/FileReadTool.md'
- Selenium Scraper: 'tools/SeleniumScrapingTool.md'
- Directory RAG Search: 'tools/DirectorySearchTool.md'
@@ -174,6 +187,7 @@ extra_css:
plugins:
- social
- search
extra:
analytics:

2878
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "crewai"
version = "0.30.8"
version = "0.36.0"
description = "Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks."
authors = ["Joao Moura <joao@crewai.com>"]
readme = "README.md"
@@ -14,21 +14,25 @@ Repository = "https://github.com/joaomdmoura/crewai"
[tool.poetry.dependencies]
python = ">=3.10,<=3.13"
pydantic = "^2.4.2"
langchain = "^0.1.10"
langchain = ">0.2,<=0.3"
openai = "^1.13.3"
opentelemetry-api = "^1.22.0"
opentelemetry-sdk = "^1.22.0"
opentelemetry-exporter-otlp-proto-http = "^1.22.0"
instructor = "^0.5.2"
instructor = "1.3.3"
regex = "^2023.12.25"
crewai-tools = { version = "^0.2.6", optional = true }
crewai-tools = { version = "^0.4.8", optional = true }
click = "^8.1.7"
python-dotenv = "^1.0.0"
embedchain = "^0.1.98"
appdirs = "^1.4.4"
jsonref = "^1.1.0"
agentops = { version = "^0.1.9", optional = true }
embedchain = "^0.1.114"
json-repair = "^0.25.2"
[tool.poetry.extras]
tools = ["crewai-tools"]
agentops = ["agentops"]
[tool.poetry.group.dev.dependencies]
isort = "^5.13.2"
@@ -42,7 +46,7 @@ mkdocs-material = { extras = ["imaging"], version = "^9.5.7" }
mkdocs-material-extensions = "^1.3.1"
pillow = "^10.2.0"
cairosvg = "^2.7.1"
crewai-tools = "^0.2.6"
crewai-tools = "^0.4.8"
[tool.poetry.group.test.dependencies]
pytest = "^8.0.0"

View File

@@ -2,3 +2,5 @@ from crewai.agent import Agent
from crewai.crew import Crew
from crewai.process import Process
from crewai.task import Task
__all__ = ["Agent", "Crew", "Process", "Task"]

View File

@@ -1,32 +1,39 @@
import os
import uuid
from typing import Any, Dict, List, Optional, Tuple
from inspect import signature
from typing import Any, List, Optional, Tuple
from langchain.agents.agent import RunnableAgent
from langchain.agents.tools import BaseTool
from langchain.agents.tools import tool as LangChainTool
from langchain.tools.render import render_text_description
from langchain_core.agents import AgentAction
from langchain_core.callbacks import BaseCallbackHandler
from langchain_openai import ChatOpenAI
from pydantic import (
UUID4,
BaseModel,
ConfigDict,
Field,
InstanceOf,
PrivateAttr,
field_validator,
model_validator,
)
from pydantic_core import PydanticCustomError
from pydantic import Field, InstanceOf, PrivateAttr, model_validator
from crewai.agents import CacheHandler, CrewAgentExecutor, CrewAgentParser, ToolsHandler
from crewai.agents import CacheHandler, CrewAgentExecutor, CrewAgentParser
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.memory.contextual.contextual_memory import ContextualMemory
from crewai.utilities import I18N, Logger, Prompts, RPMController
from crewai.utilities.token_counter_callback import TokenCalcHandler, TokenProcess
from crewai.tools.agent_tools import AgentTools
from crewai.utilities import Converter, Prompts
from crewai.utilities.constants import TRAINED_AGENTS_DATA_FILE, TRAINING_DATA_FILE
from crewai.utilities.token_counter_callback import TokenCalcHandler
from crewai.utilities.training_handler import CrewTrainingHandler
agentops = None
try:
import agentops # type: ignore # Name "agentops" already defined on line 21
from agentops import track_agent
except ImportError:
def track_agent():
def noop(f):
return f
return noop
class Agent(BaseModel):
@track_agent()
class Agent(BaseAgent):
"""Represents an agent in a system.
Each agent has a role, a goal, a backstory, and an optional language model (llm).
@@ -39,7 +46,7 @@ class Agent(BaseModel):
backstory: The backstory of the agent.
config: Dict representation of agent configuration.
llm: The language model that will run the agent.
function_calling_llm: The language model that will the tool calling for this agent, it overrides the crew function_calling_llm.
function_calling_llm: The language model that will handle the tool calling for this agent, it overrides the crew function_calling_llm.
max_iter: Maximum number of iterations for an agent to execute a task.
memory: Whether the agent should have memory or not.
max_rpm: Maximum number of requests per minute for the agent execution to be respected.
@@ -48,59 +55,17 @@ class Agent(BaseModel):
tools: Tools at agents disposal
step_callback: Callback to be executed after each step of the agent execution.
callbacks: A list of callback functions from the langchain library that are triggered during the agent's execution process
allow_code_execution: Enable code execution for the agent.
max_retry_limit: Maximum number of retries for an agent to execute a task when an error occurs.
"""
__hash__ = object.__hash__ # type: ignore
_logger: Logger = PrivateAttr()
_rpm_controller: RPMController = PrivateAttr(default=None)
_request_within_rpm_limit: Any = PrivateAttr(default=None)
_token_process: TokenProcess = TokenProcess()
formatting_errors: int = 0
model_config = ConfigDict(arbitrary_types_allowed=True)
id: UUID4 = Field(
default_factory=uuid.uuid4,
frozen=True,
description="Unique identifier for the object, not set by user.",
)
role: str = Field(description="Role of the agent")
goal: str = Field(description="Objective of the agent")
backstory: str = Field(description="Backstory of the agent")
cache: bool = Field(
default=True,
description="Whether the agent should use a cache for tool usage.",
)
config: Optional[Dict[str, Any]] = Field(
description="Configuration for the agent",
default=None,
)
max_rpm: Optional[int] = Field(
default=None,
description="Maximum number of requests per minute for the agent execution to be respected.",
)
verbose: bool = Field(
default=False, description="Verbose mode for the Agent Execution"
)
allow_delegation: bool = Field(
default=True, description="Allow delegation of tasks to agents"
)
tools: Optional[List[Any]] = Field(
default_factory=list, description="Tools at agents disposal"
)
max_iter: Optional[int] = Field(
default=25, description="Maximum iterations for an agent to execute a task"
)
_times_executed: int = PrivateAttr(default=0)
max_execution_time: Optional[int] = Field(
default=None,
description="Maximum execution time for an agent to execute a task",
)
agent_executor: InstanceOf[CrewAgentExecutor] = Field(
default=None, description="An instance of the CrewAgentExecutor class."
)
crew: Any = Field(default=None, description="Crew to which the agent belongs.")
tools_handler: InstanceOf[ToolsHandler] = Field(
default=None, description="An instance of the ToolsHandler class."
)
agent_ops_agent_name: str = None # type: ignore # Incompatible types in assignment (expression has type "None", variable has type "str")
agent_ops_agent_id: str = None # type: ignore # Incompatible types in assignment (expression has type "None", variable has type "str")
cache_handler: InstanceOf[CacheHandler] = Field(
default=None, description="An instance of the CacheHandler class."
)
@@ -108,10 +73,9 @@ class Agent(BaseModel):
default=None,
description="Callback to be executed after each step of the agent execution.",
)
i18n: I18N = Field(default=I18N(), description="Internationalization settings.")
llm: Any = Field(
default_factory=lambda: ChatOpenAI(
model=os.environ.get("OPENAI_MODEL_NAME", "gpt-4")
model=os.environ.get("OPENAI_MODEL_NAME", "gpt-4o")
),
description="Language model that will run the agent.",
)
@@ -130,44 +94,25 @@ class Agent(BaseModel):
response_template: Optional[str] = Field(
default=None, description="Response format for the agent."
)
_original_role: str | None = None
_original_goal: str | None = None
_original_backstory: str | None = None
tools_results: Optional[List[Any]] = Field(
default=[], description="Results of the tools used by the agent."
)
allow_code_execution: Optional[bool] = Field(
default=False, description="Enable code execution for the agent."
)
max_retry_limit: int = Field(
default=2,
description="Maximum number of retries for an agent to execute a task when an error occurs.",
)
def __init__(__pydantic_self__, **data):
config = data.pop("config", {})
super().__init__(**config, **data)
@field_validator("id", mode="before")
@classmethod
def _deny_user_set_id(cls, v: Optional[UUID4]) -> None:
if v:
raise PydanticCustomError(
"may_not_set_field", "This field is not to be set by the user.", {}
)
@model_validator(mode="after")
def set_attributes_based_on_config(self) -> "Agent":
"""Set attributes based on the agent configuration."""
if self.config:
for key, value in self.config.items():
setattr(self, key, value)
return self
@model_validator(mode="after")
def set_private_attrs(self):
"""Set private attributes."""
self._logger = Logger(self.verbose)
if self.max_rpm and not self._rpm_controller:
self._rpm_controller = RPMController(
max_rpm=self.max_rpm, logger=self._logger
)
return self
__pydantic_self__.agent_ops_agent_name = __pydantic_self__.role
@model_validator(mode="after")
def set_agent_executor(self) -> "Agent":
"""set agent executor is set."""
"""Ensure agent executor and token process are set."""
if hasattr(self.llm, "model_name"):
token_handler = TokenCalcHandler(self.llm.model_name, self._token_process)
@@ -181,6 +126,13 @@ class Agent(BaseModel):
):
self.llm.callbacks.append(token_handler)
if agentops and not any(
isinstance(handler, agentops.LangchainCallbackHandler)
for handler in self.llm.callbacks
):
agentops.stop_instrumenting()
self.llm.callbacks.append(agentops.LangchainCallbackHandler())
if not self.agent_executor:
if not self.cache_handler:
self.cache_handler = CacheHandler()
@@ -223,50 +175,60 @@ class Agent(BaseModel):
if memory.strip() != "":
task_prompt += self.i18n.slice("memory").format(memory=memory)
tools = tools or self.tools
parsed_tools = self._parse_tools(tools) # type: ignore # Argument 1 to "_parse_tools" of "Agent" has incompatible type "list[Any] | None"; expected "list[Any]"
tools = tools or self.tools or []
parsed_tools = self._parse_tools(tools)
self.create_agent_executor(tools=tools)
self.agent_executor.tools = parsed_tools
self.agent_executor.task = task
self.agent_executor.tools_description = render_text_description(parsed_tools)
self.agent_executor.tools_description = self._render_text_description_and_args(
parsed_tools
)
self.agent_executor.tools_names = self.__tools_names(parsed_tools)
result = self.agent_executor.invoke(
{
"input": task_prompt,
"tool_names": self.agent_executor.tools_names,
"tools": self.agent_executor.tools_description,
}
)["output"]
if self.crew and self.crew._train:
task_prompt = self._training_handler(task_prompt=task_prompt)
else:
task_prompt = self._use_trained_data(task_prompt=task_prompt)
try:
result = self.agent_executor.invoke(
{
"input": task_prompt,
"tool_names": self.agent_executor.tools_names,
"tools": self.agent_executor.tools_description,
}
)["output"]
except Exception as e:
self._times_executed += 1
if self._times_executed > self.max_retry_limit:
raise e
result = self.execute_task(task, context, tools)
if self.max_rpm:
self._rpm_controller.stop_rpm_counter()
# If there was any tool in self.tools_results that had result_as_answer
# set to True, return the results of the last tool that had
# result_as_answer set to True
for tool_result in self.tools_results: # type: ignore # Item "None" of "list[Any] | None" has no attribute "__iter__" (not iterable)
if tool_result.get("result_as_answer", False):
result = tool_result["result"]
return result
def set_cache_handler(self, cache_handler: CacheHandler) -> None:
"""Set the cache handler for the agent.
Args:
cache_handler: An instance of the CacheHandler class.
"""
self.tools_handler = ToolsHandler()
if self.cache:
self.cache_handler = cache_handler
self.tools_handler.cache = cache_handler
self.create_agent_executor()
def set_rpm_controller(self, rpm_controller: RPMController) -> None:
"""Set the rpm controller for the agent.
Args:
rpm_controller: An instance of the RPMController class.
"""
if not self._rpm_controller:
self._rpm_controller = rpm_controller
self.create_agent_executor()
def format_log_to_str(
self,
intermediate_steps: List[Tuple[AgentAction, str]],
observation_prefix: str = "Observation: ",
llm_prefix: str = "",
) -> str:
"""Construct the scratchpad that lets the agent continue its thought process."""
thoughts = ""
for action, observation in intermediate_steps:
thoughts += action.log
thoughts += f"\n{observation_prefix}{observation}\n{llm_prefix}"
return thoughts
def create_agent_executor(self, tools=None) -> None:
"""Create an agent executor for the agent.
@@ -274,7 +236,7 @@ class Agent(BaseModel):
Returns:
An instance of the CrewAgentExecutor class.
"""
tools = tools or self.tools
tools = tools or self.tools or []
agent_args = {
"input": lambda x: x["input"],
@@ -322,53 +284,42 @@ class Agent(BaseModel):
)
stop_words = [self.i18n.slice("observation")]
if self.response_template:
stop_words.append(
self.response_template.split("{{ .Response }}")[1].strip()
)
bind = self.llm.bind(stop=stop_words)
inner_agent = agent_args | execution_prompt | bind | CrewAgentParser(agent=self)
self.agent_executor = CrewAgentExecutor(
agent=RunnableAgent(runnable=inner_agent), **executor_args
)
def interpolate_inputs(self, inputs: Dict[str, Any]) -> None:
"""Interpolate inputs into the agent description and backstory."""
if self._original_role is None:
self._original_role = self.role
if self._original_goal is None:
self._original_goal = self.goal
if self._original_backstory is None:
self._original_backstory = self.backstory
def get_delegation_tools(self, agents: List[BaseAgent]):
agent_tools = AgentTools(agents=agents)
tools = agent_tools.tools()
return tools
if inputs:
self.role = self._original_role.format(**inputs)
self.goal = self._original_goal.format(**inputs)
self.backstory = self._original_backstory.format(**inputs)
def get_code_execution_tools(self):
try:
from crewai_tools import CodeInterpreterTool
def increment_formatting_errors(self) -> None:
"""Count the formatting errors of the agent."""
self.formatting_errors += 1
return [CodeInterpreterTool()]
except ModuleNotFoundError:
self._logger.log(
"info", "Coding tools not available. Install crewai_tools. "
)
def format_log_to_str(
self,
intermediate_steps: List[Tuple[AgentAction, str]],
observation_prefix: str = "Observation: ",
llm_prefix: str = "",
) -> str:
"""Construct the scratchpad that lets the agent continue its thought process."""
thoughts = ""
for action, observation in intermediate_steps:
thoughts += action.log
thoughts += f"\n{observation_prefix}{observation}\n{llm_prefix}"
return thoughts
def get_output_converter(self, llm, text, model, instructions):
return Converter(llm=llm, text=text, model=model, instructions=instructions)
def _parse_tools(self, tools: List[Any]) -> List[LangChainTool]: # type: ignore # Function "langchain_core.tools.tool" is not valid as a type
"""Parse tools to be used for the task."""
# tentatively try to import from crewai_tools import BaseTool as CrewAITool
tools_list = []
try:
# tentatively try to import from crewai_tools import BaseTool as CrewAITool
from crewai_tools import BaseTool as CrewAITool
for tool in tools:
@@ -377,10 +328,82 @@ class Agent(BaseModel):
else:
tools_list.append(tool)
except ModuleNotFoundError:
tools_list = []
for tool in tools:
tools_list.append(tool)
return tools_list
def _training_handler(self, task_prompt: str) -> str:
"""Handle training data for the agent task prompt to improve output on Training."""
if data := CrewTrainingHandler(TRAINING_DATA_FILE).load():
agent_id = str(self.id)
if data.get(agent_id):
human_feedbacks = [
i["human_feedback"] for i in data.get(agent_id, {}).values()
]
task_prompt += "You MUST follow these feedbacks: \n " + "\n - ".join(
human_feedbacks
)
return task_prompt
def _use_trained_data(self, task_prompt: str) -> str:
"""Use trained data for the agent task prompt to improve output."""
if data := CrewTrainingHandler(TRAINED_AGENTS_DATA_FILE).load():
if trained_data_output := data.get(self.role):
task_prompt += "You MUST follow these feedbacks: \n " + "\n - ".join(
trained_data_output["suggestions"]
)
return task_prompt
def _render_text_description(self, tools: List[BaseTool]) -> str:
"""Render the tool name and description in plain text.
Output will be in the format of:
.. code-block:: markdown
search: This tool is used for search
calculator: This tool is used for math
"""
description = "\n".join(
[
f"Tool name: {tool.name}\nTool description:\n{tool.description}"
for tool in tools
]
)
return description
def _render_text_description_and_args(self, tools: List[BaseTool]) -> str:
"""Render the tool name, description, and args in plain text.
Output will be in the format of:
.. code-block:: markdown
search: This tool is used for search, args: {"query": {"type": "string"}}
calculator: This tool is used for math, \
args: {"expression": {"type": "string"}}
"""
tool_strings = []
for tool in tools:
args_schema = str(tool.args)
if hasattr(tool, "func") and tool.func:
sig = signature(tool.func)
description = (
f"Tool Name: {tool.name}{sig}\nTool Description: {tool.description}"
)
else:
description = (
f"Tool Name: {tool.name}\nTool Description: {tool.description}"
)
tool_strings.append(f"{description}\nTool Arguments: {args_schema}")
return "\n".join(tool_strings)
@staticmethod
def __tools_names(tools) -> str:
return ", ".join([t.name for t in tools])

View File

@@ -0,0 +1,256 @@
import uuid
from abc import ABC, abstractmethod
from copy import copy as shallow_copy
from typing import Any, Dict, List, Optional, TypeVar
from pydantic import (
UUID4,
BaseModel,
ConfigDict,
Field,
InstanceOf,
PrivateAttr,
field_validator,
model_validator,
)
from pydantic_core import PydanticCustomError
from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess
from crewai.agents.cache.cache_handler import CacheHandler
from crewai.agents.tools_handler import ToolsHandler
from crewai.utilities import I18N, Logger, RPMController
T = TypeVar("T", bound="BaseAgent")
class BaseAgent(ABC, BaseModel):
"""Abstract Base Class for all third party agents compatible with CrewAI.
Attributes:
id (UUID4): Unique identifier for the agent.
role (str): Role of the agent.
goal (str): Objective of the agent.
backstory (str): Backstory of the agent.
cache (bool): Whether the agent should use a cache for tool usage.
config (Optional[Dict[str, Any]]): Configuration for the agent.
verbose (bool): Verbose mode for the Agent Execution.
max_rpm (Optional[int]): Maximum number of requests per minute for the agent execution.
allow_delegation (bool): Allow delegation of tasks to agents.
tools (Optional[List[Any]]): Tools at the agent's disposal.
max_iter (Optional[int]): Maximum iterations for an agent to execute a task.
agent_executor (InstanceOf): An instance of the CrewAgentExecutor class.
llm (Any): Language model that will run the agent.
crew (Any): Crew to which the agent belongs.
i18n (I18N): Internationalization settings.
cache_handler (InstanceOf[CacheHandler]): An instance of the CacheHandler class.
tools_handler (InstanceOf[ToolsHandler]): An instance of the ToolsHandler class.
Methods:
execute_task(task: Any, context: Optional[str] = None, tools: Optional[List[Any]] = None) -> str:
Abstract method to execute a task.
create_agent_executor(tools=None) -> None:
Abstract method to create an agent executor.
_parse_tools(tools: List[Any]) -> List[Any]:
Abstract method to parse tools.
get_delegation_tools(agents: List["BaseAgent"]):
Abstract method to set the agents task tools for handling delegation and question asking to other agents in crew.
get_output_converter(llm, model, instructions):
Abstract method to get the converter class for the agent to create json/pydantic outputs.
interpolate_inputs(inputs: Dict[str, Any]) -> None:
Interpolate inputs into the agent description and backstory.
set_cache_handler(cache_handler: CacheHandler) -> None:
Set the cache handler for the agent.
increment_formatting_errors() -> None:
Increment formatting errors.
copy() -> "BaseAgent":
Create a copy of the agent.
set_rpm_controller(rpm_controller: RPMController) -> None:
Set the rpm controller for the agent.
set_private_attrs() -> "BaseAgent":
Set private attributes.
"""
__hash__ = object.__hash__ # type: ignore
_logger: Logger = PrivateAttr()
_rpm_controller: RPMController = PrivateAttr(default=None)
_request_within_rpm_limit: Any = PrivateAttr(default=None)
formatting_errors: int = 0
model_config = ConfigDict(arbitrary_types_allowed=True)
id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
role: str = Field(description="Role of the agent")
goal: str = Field(description="Objective of the agent")
backstory: str = Field(description="Backstory of the agent")
cache: bool = Field(
default=True, description="Whether the agent should use a cache for tool usage."
)
config: Optional[Dict[str, Any]] = Field(
description="Configuration for the agent", default=None
)
verbose: bool = Field(
default=False, description="Verbose mode for the Agent Execution"
)
max_rpm: Optional[int] = Field(
default=None,
description="Maximum number of requests per minute for the agent execution to be respected.",
)
allow_delegation: bool = Field(
default=True, description="Allow delegation of tasks to agents"
)
tools: Optional[List[Any]] = Field(
default_factory=list, description="Tools at agents' disposal"
)
max_iter: Optional[int] = Field(
default=25, description="Maximum iterations for an agent to execute a task"
)
agent_executor: InstanceOf = Field(
default=None, description="An instance of the CrewAgentExecutor class."
)
llm: Any = Field(
default=None, description="Language model that will run the agent."
)
crew: Any = Field(default=None, description="Crew to which the agent belongs.")
i18n: I18N = Field(default=I18N(), description="Internationalization settings.")
cache_handler: InstanceOf[CacheHandler] = Field(
default=None, description="An instance of the CacheHandler class."
)
tools_handler: InstanceOf[ToolsHandler] = Field(
default=None, description="An instance of the ToolsHandler class."
)
_original_role: str | None = None
_original_goal: str | None = None
_original_backstory: str | None = None
_token_process: TokenProcess = TokenProcess()
def __init__(__pydantic_self__, **data):
config = data.pop("config", {})
super().__init__(**config, **data)
@model_validator(mode="after")
def set_config_attributes(self):
if self.config:
for key, value in self.config.items():
setattr(self, key, value)
return self
@field_validator("id", mode="before")
@classmethod
def _deny_user_set_id(cls, v: Optional[UUID4]) -> None:
if v:
raise PydanticCustomError(
"may_not_set_field", "This field is not to be set by the user.", {}
)
@model_validator(mode="after")
def set_attributes_based_on_config(self) -> "BaseAgent":
"""Set attributes based on the agent configuration."""
if self.config:
for key, value in self.config.items():
setattr(self, key, value)
return self
@model_validator(mode="after")
def set_private_attrs(self):
"""Set private attributes."""
self._logger = Logger(self.verbose)
if self.max_rpm and not self._rpm_controller:
self._rpm_controller = RPMController(
max_rpm=self.max_rpm, logger=self._logger
)
if not self._token_process:
self._token_process = TokenProcess()
return self
@abstractmethod
def execute_task(
self,
task: Any,
context: Optional[str] = None,
tools: Optional[List[Any]] = None,
) -> str:
pass
@abstractmethod
def create_agent_executor(self, tools=None) -> None:
pass
@abstractmethod
def _parse_tools(self, tools: List[Any]) -> List[Any]:
pass
@abstractmethod
def get_delegation_tools(self, agents: List["BaseAgent"]):
"""Set the task tools that init BaseAgenTools class."""
pass
@abstractmethod
def get_output_converter(
self, llm: Any, text: str, model: type[BaseModel] | None, instructions: str
):
"""Get the converter class for the agent to create json/pydantic outputs."""
pass
def copy(self: T) -> T: # type: ignore # Signature of "copy" incompatible with supertype "BaseModel"
"""Create a deep copy of the Agent."""
exclude = {
"id",
"_logger",
"_rpm_controller",
"_request_within_rpm_limit",
"_token_process",
"agent_executor",
"tools",
"tools_handler",
"cache_handler",
"llm",
}
# Copy llm and clear callbacks
existing_llm = shallow_copy(self.llm)
existing_llm.callbacks = []
copied_data = self.model_dump(exclude=exclude)
copied_data = {k: v for k, v in copied_data.items() if v is not None}
copied_agent = type(self)(**copied_data, llm=existing_llm, tools=self.tools)
return copied_agent
def interpolate_inputs(self, inputs: Dict[str, Any]) -> None:
"""Interpolate inputs into the agent description and backstory."""
if self._original_role is None:
self._original_role = self.role
if self._original_goal is None:
self._original_goal = self.goal
if self._original_backstory is None:
self._original_backstory = self.backstory
if inputs:
self.role = self._original_role.format(**inputs)
self.goal = self._original_goal.format(**inputs)
self.backstory = self._original_backstory.format(**inputs)
def set_cache_handler(self, cache_handler: CacheHandler) -> None:
"""Set the cache handler for the agent.
Args:
cache_handler: An instance of the CacheHandler class.
"""
self.tools_handler = ToolsHandler()
if self.cache:
self.cache_handler = cache_handler
self.tools_handler.cache = cache_handler
self.create_agent_executor()
def increment_formatting_errors(self) -> None:
self.formatting_errors += 1
def set_rpm_controller(self, rpm_controller: RPMController) -> None:
"""Set the rpm controller for the agent.
Args:
rpm_controller: An instance of the RPMController class.
"""
if not self._rpm_controller:
self._rpm_controller = rpm_controller
self.create_agent_executor()

View File

@@ -0,0 +1,109 @@
import time
from typing import TYPE_CHECKING, Optional
from crewai.memory.entity.entity_memory_item import EntityMemoryItem
from crewai.memory.long_term.long_term_memory_item import LongTermMemoryItem
from crewai.memory.short_term.short_term_memory_item import ShortTermMemoryItem
from crewai.utilities.converter import ConverterError
from crewai.utilities.evaluators.task_evaluator import TaskEvaluator
from crewai.utilities import I18N
if TYPE_CHECKING:
from crewai.crew import Crew
from crewai.task import Task
from crewai.agents.agent_builder.base_agent import BaseAgent
class CrewAgentExecutorMixin:
crew: Optional["Crew"]
crew_agent: Optional["BaseAgent"]
task: Optional["Task"]
iterations: int
force_answer_max_iterations: int
have_forced_answer: bool
_i18n: I18N
def _should_force_answer(self) -> bool:
"""Determine if a forced answer is required based on iteration count."""
return (
self.iterations == self.force_answer_max_iterations
) and not self.have_forced_answer
def _create_short_term_memory(self, output) -> None:
"""Create and save a short-term memory item if conditions are met."""
if (
self.crew
and self.crew_agent
and self.task
and "Action: Delegate work to coworker" not in output.log
):
try:
memory = ShortTermMemoryItem(
data=output.log,
agent=self.crew_agent.role,
metadata={
"observation": self.task.description,
},
)
if (
hasattr(self.crew, "_short_term_memory")
and self.crew._short_term_memory
):
self.crew._short_term_memory.save(memory)
except Exception as e:
print(f"Failed to add to short term memory: {e}")
pass
def _create_long_term_memory(self, output) -> None:
"""Create and save long-term and entity memory items based on evaluation."""
if (
self.crew
and self.crew.memory
and self.crew._long_term_memory
and self.crew._entity_memory
and self.task
and self.crew_agent
):
try:
ltm_agent = TaskEvaluator(self.crew_agent)
evaluation = ltm_agent.evaluate(self.task, output.log)
if isinstance(evaluation, ConverterError):
return
long_term_memory = LongTermMemoryItem(
task=self.task.description,
agent=self.crew_agent.role,
quality=evaluation.quality,
datetime=str(time.time()),
expected_output=self.task.expected_output,
metadata={
"suggestions": evaluation.suggestions,
"quality": evaluation.quality,
},
)
self.crew._long_term_memory.save(long_term_memory)
for entity in evaluation.entities:
entity_memory = EntityMemoryItem(
name=entity.name,
type=entity.type,
description=entity.description,
relationships="\n".join(
[f"- {r}" for r in entity.relationships]
),
)
self.crew._entity_memory.save(entity_memory)
except AttributeError as e:
print(f"Missing attributes for long term memory: {e}")
pass
except Exception as e:
print(f"Failed to add to long term memory: {e}")
pass
def _ask_human_input(self, final_answer: dict) -> str:
"""Prompt human input for final decision making."""
return input(
self._i18n.slice("getting_input").format(final_answer=final_answer)
)

View File

@@ -0,0 +1,86 @@
from abc import ABC, abstractmethod
from typing import List, Optional, Union
from pydantic import BaseModel, Field
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.task import Task
from crewai.utilities import I18N
class BaseAgentTools(BaseModel, ABC):
"""Default tools around agent delegation"""
agents: List[BaseAgent] = Field(description="List of agents in this crew.")
i18n: I18N = Field(default=I18N(), description="Internationalization settings.")
@abstractmethod
def tools(self):
pass
def _get_coworker(self, coworker: Optional[str], **kwargs) -> Optional[str]:
coworker = coworker or kwargs.get("co_worker") or kwargs.get("coworker")
if coworker:
is_list = coworker.startswith("[") and coworker.endswith("]")
if is_list:
coworker = coworker[1:-1].split(",")[0]
return coworker
def delegate_work(
self, task: str, context: str, coworker: Optional[str] = None, **kwargs
):
"""Useful to delegate a specific task to a coworker passing all necessary context and names."""
coworker = self._get_coworker(coworker, **kwargs)
return self._execute(coworker, task, context)
def ask_question(
self, question: str, context: str, coworker: Optional[str] = None, **kwargs
):
"""Useful to ask a question, opinion or take from a coworker passing all necessary context and names."""
coworker = self._get_coworker(coworker, **kwargs)
return self._execute(coworker, question, context)
def _execute(
self, agent_name: Union[str, None], task: str, context: Union[str, None]
):
"""Execute the command."""
try:
if agent_name is None:
agent_name = ""
# It is important to remove the quotes from the agent name.
# The reason we have to do this is because less-powerful LLM's
# have difficulty producing valid JSON.
# As a result, we end up with invalid JSON that is truncated like this:
# {"task": "....", "coworker": "....
# when it should look like this:
# {"task": "....", "coworker": "...."}
agent_name = agent_name.casefold().replace('"', "").replace("\n", "")
agent = [ # type: ignore # Incompatible types in assignment (expression has type "list[BaseAgent]", variable has type "str | None")
available_agent
for available_agent in self.agents
if available_agent.role.casefold().replace("\n", "") == agent_name
]
except Exception as _:
return self.i18n.errors("agent_tool_unexsiting_coworker").format(
coworkers="\n".join(
[f"- {agent.role.casefold()}" for agent in self.agents]
)
)
if not agent:
return self.i18n.errors("agent_tool_unexsiting_coworker").format(
coworkers="\n".join(
[f"- {agent.role.casefold()}" for agent in self.agents]
)
)
agent = agent[0]
task_with_assigned_agent = Task( # type: ignore # Incompatible types in assignment (expression has type "Task", variable has type "str")
description=task,
agent=agent,
expected_output="Your best answer to your coworker asking you this, accounting for the context shared.",
)
return agent.execute_task(task_with_assigned_agent, context)

View File

@@ -0,0 +1,47 @@
from abc import ABC, abstractmethod
from typing import Any, Optional
from pydantic import BaseModel, Field
class OutputConverter(BaseModel, ABC):
"""
Abstract base class for converting task results into structured formats.
This class provides a framework for converting unstructured text into
either Pydantic models or JSON, tailored for specific agent requirements.
It uses a language model to interpret and structure the input text based
on given instructions.
Attributes:
text (str): The input text to be converted.
llm (Any): The language model used for conversion.
model (Any): The target model for structuring the output.
instructions (str): Specific instructions for the conversion process.
max_attempts (int): Maximum number of conversion attempts (default: 3).
"""
text: str = Field(description="Text to be converted.")
llm: Any = Field(description="The language model to be used to convert the text.")
model: Any = Field(description="The model to be used to convert the text.")
instructions: str = Field(description="Conversion instructions to the LLM.")
max_attempts: Optional[int] = Field(
description="Max number of attempts to try to get the output formatted.",
default=3,
)
@abstractmethod
def to_pydantic(self, current_attempt=1):
"""Convert text to pydantic."""
pass
@abstractmethod
def to_json(self, current_attempt=1):
"""Convert text to json."""
pass
@property
@abstractmethod
def is_gpt(self) -> bool:
"""Return if llm provided is of gpt from openai."""
pass

View File

@@ -0,0 +1,27 @@
from typing import Any, Dict
class TokenProcess:
total_tokens: int = 0
prompt_tokens: int = 0
completion_tokens: int = 0
successful_requests: int = 0
def sum_prompt_tokens(self, tokens: int):
self.prompt_tokens = self.prompt_tokens + tokens
self.total_tokens = self.total_tokens + tokens
def sum_completion_tokens(self, tokens: int):
self.completion_tokens = self.completion_tokens + tokens
self.total_tokens = self.total_tokens + tokens
def sum_successful_requests(self, requests: int):
self.successful_requests = self.successful_requests + requests
def get_summary(self) -> Dict[str, Any]:
return {
"total_tokens": self.total_tokens,
"prompt_tokens": self.prompt_tokens,
"completion_tokens": self.completion_tokens,
"successful_requests": self.successful_requests,
}

View File

@@ -7,22 +7,19 @@ from langchain.agents.agent import ExceptionTool
from langchain.callbacks.manager import CallbackManagerForChainRun
from langchain_core.agents import AgentAction, AgentFinish, AgentStep
from langchain_core.exceptions import OutputParserException
from langchain_core.pydantic_v1 import root_validator
from langchain_core.tools import BaseTool
from langchain_core.utils.input import get_color_mapping
from pydantic import InstanceOf
from crewai.agents.agent_builder.base_agent_executor_mixin import CrewAgentExecutorMixin
from crewai.agents.tools_handler import ToolsHandler
from crewai.memory.entity.entity_memory_item import EntityMemoryItem
from crewai.memory.long_term.long_term_memory_item import LongTermMemoryItem
from crewai.memory.short_term.short_term_memory_item import ShortTermMemoryItem
from crewai.tools.tool_usage import ToolUsage, ToolUsageErrorException
from crewai.utilities import I18N
from crewai.utilities.converter import ConverterError
from crewai.utilities.evaluators.task_evaluator import TaskEvaluator
from crewai.utilities.constants import TRAINING_DATA_FILE
from crewai.utilities.training_handler import CrewTrainingHandler
class CrewAgentExecutor(AgentExecutor):
class CrewAgentExecutor(AgentExecutor, CrewAgentExecutorMixin):
_i18n: I18N = I18N()
should_ask_for_human_input: bool = False
llm: Any = None
@@ -38,67 +35,12 @@ class CrewAgentExecutor(AgentExecutor):
tools_handler: Optional[InstanceOf[ToolsHandler]] = None
max_iterations: Optional[int] = 15
have_forced_answer: bool = False
force_answer_max_iterations: Optional[int] = None
force_answer_max_iterations: Optional[int] = None # type: ignore # Incompatible types in assignment (expression has type "int | None", base class "CrewAgentExecutorMixin" defined the type as "int")
step_callback: Optional[Any] = None
system_template: Optional[str] = None
prompt_template: Optional[str] = None
response_template: Optional[str] = None
@root_validator()
def set_force_answer_max_iterations(cls, values: Dict) -> Dict:
values["force_answer_max_iterations"] = values["max_iterations"] - 2
return values
def _should_force_answer(self) -> bool:
return (
self.iterations == self.force_answer_max_iterations
) and not self.have_forced_answer
def _create_short_term_memory(self, output) -> None:
if (
self.crew
and self.crew.memory
and "Action: Delegate work to co-worker" not in output.log
):
memory = ShortTermMemoryItem(
data=output.log,
agent=self.crew_agent.role,
metadata={
"observation": self.task.description,
},
)
self.crew._short_term_memory.save(memory)
def _create_long_term_memory(self, output) -> None:
if self.crew and self.crew.memory:
ltm_agent = TaskEvaluator(self.crew_agent)
evaluation = ltm_agent.evaluate(self.task, output.log)
if isinstance(evaluation, ConverterError):
return
long_term_memory = LongTermMemoryItem(
task=self.task.description,
agent=self.crew_agent.role,
quality=evaluation.quality,
datetime=str(time.time()),
expected_output=self.task.expected_output,
metadata={
"suggestions": evaluation.suggestions,
"quality": evaluation.quality,
},
)
self.crew._long_term_memory.save(long_term_memory)
for entity in evaluation.entities:
entity_memory = EntityMemoryItem(
name=entity.name,
type=entity.type,
description=entity.description,
relationships="\n".join([f"- {r}" for r in entity.relationships]),
)
self.crew._entity_memory.save(entity_memory)
def _call(
self,
inputs: Dict[str, str],
@@ -246,12 +188,17 @@ class CrewAgentExecutor(AgentExecutor):
# If the tool chosen is the finishing tool, then we end and return.
if isinstance(output, AgentFinish):
if self.should_ask_for_human_input:
human_feedback = self._ask_human_input(output.return_values["output"])
if self.crew and self.crew._train:
self._handle_crew_training_output(output, human_feedback)
# Making sure we only ask for it once, so disabling for the next thought loop
self.should_ask_for_human_input = False
human_feedback = self._ask_human_input(output.return_values["output"])
action = AgentAction(
tool="Human Input", tool_input=human_feedback, log=output.log
)
yield AgentStep(
action=action,
observation=self._i18n.slice("human_feedback").format(
@@ -261,6 +208,9 @@ class CrewAgentExecutor(AgentExecutor):
return
else:
if self.crew and self.crew._train:
self._handle_crew_training_output(output)
yield output
return
@@ -282,6 +232,7 @@ class CrewAgentExecutor(AgentExecutor):
tools_names=self.tools_names,
function_calling_llm=self.function_calling_llm,
task=self.task,
agent=self.crew_agent,
action=agent_action,
)
tool_calling = tool_usage.parse(agent_action.log)
@@ -300,8 +251,30 @@ class CrewAgentExecutor(AgentExecutor):
)
yield AgentStep(action=agent_action, observation=observation)
def _ask_human_input(self, final_answer: dict) -> str:
"""Get human input."""
return input(
self._i18n.slice("getting_input").format(final_answer=final_answer)
)
def _handle_crew_training_output(
self, output: AgentFinish, human_feedback: str | None = None
) -> None:
"""Function to handle the process of the training data."""
agent_id = str(self.crew_agent.id)
if (
CrewTrainingHandler(TRAINING_DATA_FILE).load()
and not self.should_ask_for_human_input
):
training_data = CrewTrainingHandler(TRAINING_DATA_FILE).load()
if training_data.get(agent_id):
training_data[agent_id][self.crew._train_iteration][
"improved_output"
] = output.return_values["output"]
CrewTrainingHandler(TRAINING_DATA_FILE).save(training_data)
if self.should_ask_for_human_input and human_feedback is not None:
training_data = {
"initial_output": output.return_values["output"],
"human_feedback": human_feedback,
"agent": agent_id,
"agent_role": self.crew_agent.role,
}
CrewTrainingHandler(TRAINING_DATA_FILE).append(
self.crew._train_iteration, agent_id, training_data
)

View File

@@ -1,6 +1,7 @@
import re
from typing import Any, Union
from json_repair import repair_json
from langchain.agents.output_parsers import ReActSingleInputOutputParser
from langchain_core.agents import AgentAction, AgentFinish
from langchain_core.exceptions import OutputParserException
@@ -48,11 +49,15 @@ class CrewAgentParser(ReActSingleInputOutputParser):
raise OutputParserException(
f"{FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE}: {text}"
)
action = action_match.group(1).strip()
action_input = action_match.group(2)
tool_input = action_input.strip(" ")
tool_input = tool_input.strip('"')
return AgentAction(action, tool_input, text)
action = action_match.group(1)
clean_action = self._clean_action(action)
action_input = action_match.group(2).strip()
tool_input = action_input.strip(" ").strip('"')
safe_tool_input = self._safe_repair_json(tool_input)
return AgentAction(clean_action, safe_tool_input, text)
elif includes_answer:
return AgentFinish(
@@ -87,3 +92,30 @@ class CrewAgentParser(ReActSingleInputOutputParser):
llm_output=text,
send_to_llm=True,
)
def _clean_action(self, text: str) -> str:
"""Clean action string by removing non-essential formatting characters."""
return re.sub(r"^\s*\*+\s*|\s*\*+\s*$", "", text).strip()
def _safe_repair_json(self, tool_input: str) -> str:
UNABLE_TO_REPAIR_JSON_RESULTS = ['""', "{}"]
# Skip repair if the input starts and ends with square brackets
# Explanation: The JSON parser has issues handling inputs that are enclosed in square brackets ('[]').
# These are typically valid JSON arrays or strings that do not require repair. Attempting to repair such inputs
# might lead to unintended alterations, such as wrapping the entire input in additional layers or modifying
# the structure in a way that changes its meaning. By skipping the repair for inputs that start and end with
# square brackets, we preserve the integrity of these valid JSON structures and avoid unnecessary modifications.
if tool_input.startswith("[") and tool_input.endswith("]"):
return tool_input
# Before repair, handle common LLM issues:
# 1. Replace """ with " to avoid JSON parser errors
tool_input = tool_input.replace('"""', '"')
result = repair_json(tool_input)
if result in UNABLE_TO_REPAIR_JSON_RESULTS:
return tool_input
return str(result)

View File

@@ -1,6 +1,8 @@
import click
import pkg_resources
from .create_crew import create_crew
from .train_crew import train_crew
@click.group()
@@ -15,5 +17,36 @@ def create(project_name):
create_crew(project_name)
@crewai.command()
@click.option(
"--tools", is_flag=True, help="Show the installed version of crewai tools"
)
def version(tools):
"""Show the installed version of crewai."""
crewai_version = pkg_resources.get_distribution("crewai").version
click.echo(f"crewai version: {crewai_version}")
if tools:
try:
tools_version = pkg_resources.get_distribution("crewai-tools").version
click.echo(f"crewai tools version: {tools_version}")
except pkg_resources.DistributionNotFound:
click.echo("crewai tools not installed")
@crewai.command()
@click.option(
"-n",
"--n_iterations",
type=int,
default=5,
help="Number of iterations to train the crew",
)
def train(n_iterations: int):
"""Train the crew."""
click.echo(f"Training the crew for {n_iterations} iterations")
train_crew(n_iterations)
if __name__ == "__main__":
crewai()

View File

@@ -12,4 +12,4 @@ reporting_task:
Make sure the report is detailed and contains any and all relevant information.
expected_output: >
A fully fledge reports with the mains topics, each with a full section of information.
Formated as markdown with out '```'
Formatted as markdown without '```'

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python
import sys
from {{folder_name}}.crew import {{crew_name}}Crew
@@ -7,4 +8,16 @@ def run():
inputs = {
'topic': 'AI LLMs'
}
{{crew_name}}Crew().crew().kickoff(inputs=inputs)
{{crew_name}}Crew().crew().kickoff(inputs=inputs)
def train():
"""
Train the crew for a given number of iterations.
"""
inputs = {"topic": "AI LLMs"}
try:
{{crew_name}}Crew().crew().train(n_iterations=int(sys.argv[1]), inputs=inputs)
except Exception as e:
raise Exception(f"An error occurred while training the crew: {e}")

View File

@@ -6,11 +6,12 @@ authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = ">=3.10,<=3.13"
crewai = {extras = ["tools"], version = "^0.30.8"}
crewai = { extras = ["tools"], version = "^0.35.8" }
[tool.poetry.scripts]
{{folder_name}} = "{{folder_name}}.main:run"
train = "{{folder_name}}.main:train"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
build-backend = "poetry.core.masonry.api"

View File

@@ -0,0 +1,29 @@
import subprocess
import click
def train_crew(n_iterations: int) -> None:
"""
Train the crew by running a command in the Poetry environment.
Args:
n_iterations (int): The number of iterations to train the crew.
"""
command = ["poetry", "run", "train", str(n_iterations)]
try:
if n_iterations <= 0:
raise ValueError("The number of iterations must be a positive integer.")
result = subprocess.run(command, capture_output=False, text=True, check=True)
if result.stderr:
click.echo(result.stderr, err=True)
except subprocess.CalledProcessError as e:
click.echo(f"An error occurred while training the crew: {e}", err=True)
click.echo(e.output, err=True)
except Exception as e:
click.echo(f"An unexpected error occurred: {e}", err=True)

View File

@@ -1,31 +1,48 @@
import asyncio
import json
import uuid
from typing import Any, Dict, List, Optional, Union
from concurrent.futures import Future
from typing import Any, Dict, List, Optional, Tuple, Union
from langchain_core.callbacks import BaseCallbackHandler
from pydantic import (
UUID4,
BaseModel,
ConfigDict,
Field,
InstanceOf,
Json,
PrivateAttr,
field_validator,
model_validator,
UUID4,
BaseModel,
ConfigDict,
Field,
InstanceOf,
Json,
PrivateAttr,
field_validator,
model_validator,
)
from pydantic_core import PydanticCustomError
from crewai.agent import Agent
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.agents.cache import CacheHandler
from crewai.crews.crew_output import CrewOutput
from crewai.memory.entity.entity_memory import EntityMemory
from crewai.memory.long_term.long_term_memory import LongTermMemory
from crewai.memory.short_term.short_term_memory import ShortTermMemory
from crewai.process import Process
from crewai.task import Task
from crewai.tasks.task_output import TaskOutput
from crewai.telemetry import Telemetry
from crewai.tools.agent_tools import AgentTools
from crewai.utilities import I18N, FileHandler, Logger, RPMController
from crewai.utilities.constants import TRAINED_AGENTS_DATA_FILE, TRAINING_DATA_FILE
from crewai.utilities.evaluators.task_evaluator import TaskEvaluator
from crewai.utilities.formatter import (
aggregate_raw_outputs_from_task_outputs,
aggregate_raw_outputs_from_tasks,
)
from crewai.utilities.training_handler import CrewTrainingHandler
try:
import agentops
except ImportError:
agentops = None
class Crew(BaseModel):
@@ -47,10 +64,9 @@ class Crew(BaseModel):
max_rpm: Maximum number of requests per minute for the crew execution to be respected.
prompt_file: Path to the prompt json file to be used for the crew.
id: A unique identifier for the crew instance.
full_output: Whether the crew should return the full output with all tasks outputs or just the final output.
task_callback: Callback to be executed after each task for every agents execution.
step_callback: Callback to be executed after each step for every agents execution.
share_crew: Whether you want to share the complete crew infromation and execution with crewAI to make the library better, and allow us to train models.
share_crew: Whether you want to share the complete crew information and execution with crewAI to make the library better, and allow us to train models.
"""
__hash__ = object.__hash__ # type: ignore
@@ -62,11 +78,13 @@ class Crew(BaseModel):
_short_term_memory: Optional[InstanceOf[ShortTermMemory]] = PrivateAttr()
_long_term_memory: Optional[InstanceOf[LongTermMemory]] = PrivateAttr()
_entity_memory: Optional[InstanceOf[EntityMemory]] = PrivateAttr()
_train: Optional[bool] = PrivateAttr(default=False)
_train_iteration: Optional[int] = PrivateAttr()
cache: bool = Field(default=True)
model_config = ConfigDict(arbitrary_types_allowed=True)
tasks: List[Task] = Field(default_factory=list)
agents: List[Agent] = Field(default_factory=list)
agents: List[BaseAgent] = Field(default_factory=list)
process: Process = Field(default=Process.sequential)
verbose: Union[int, bool] = Field(default=0)
memory: bool = Field(
@@ -81,14 +99,10 @@ class Crew(BaseModel):
default=None,
description="Metrics for the LLM usage during all tasks execution.",
)
full_output: Optional[bool] = Field(
default=False,
description="Whether the crew should return the full output with all tasks outputs or just the final output.",
)
manager_llm: Optional[Any] = Field(
description="Language model that will run the agent.", default=None
)
manager_agent: Optional[Any] = Field(
manager_agent: Optional[BaseAgent] = Field(
description="Custom agent that will be used as manager.", default=None
)
manager_callbacks: Optional[List[InstanceOf[BaseCallbackHandler]]] = Field(
@@ -156,7 +170,6 @@ class Crew(BaseModel):
self._rpm_controller = RPMController(max_rpm=self.max_rpm, logger=self._logger)
self._telemetry = Telemetry()
self._telemetry.set_tracer()
self._telemetry.crew_creation(self)
return self
@model_validator(mode="after")
@@ -164,7 +177,9 @@ class Crew(BaseModel):
"""Set private attributes."""
if self.memory:
self._long_term_memory = LongTermMemory()
self._short_term_memory = ShortTermMemory(crew=self, embedder_config=self.embedder)
self._short_term_memory = ShortTermMemory(
crew=self, embedder_config=self.embedder
)
self._entity_memory = EntityMemory(crew=self, embedder_config=self.embedder)
return self
@@ -211,6 +226,90 @@ class Crew(BaseModel):
agent.set_rpm_controller(self._rpm_controller)
return self
@model_validator(mode="after")
def validate_tasks(self):
if self.process == Process.sequential:
for task in self.tasks:
if task.agent is None:
raise PydanticCustomError(
"missing_agent_in_task",
f"Sequential process error: Agent is missing in the task with the following description: {task.description}", # type: ignore # Argument of type "str" cannot be assigned to parameter "message_template" of type "LiteralString"
{},
)
return self
@model_validator(mode="after")
def check_tasks_in_hierarchical_process_not_async(self):
"""Validates that the tasks in hierarchical process are not flagged with async_execution."""
if self.process == Process.hierarchical:
for task in self.tasks:
if task.async_execution:
raise PydanticCustomError(
"async_execution_in_hierarchical_process",
"Hierarchical process error: Tasks cannot be flagged with async_execution.",
{},
)
return self
@model_validator(mode="after")
def validate_end_with_at_most_one_async_task(self):
"""Validates that the crew ends with at most one asynchronous task."""
final_async_task_count = 0
# Traverse tasks backward
for task in reversed(self.tasks):
if task.async_execution:
final_async_task_count += 1
else:
break # Stop traversing as soon as a non-async task is encountered
if final_async_task_count > 1:
raise PydanticCustomError(
"async_task_count",
"The crew must end with at most one asynchronous task.",
{},
)
return self
@model_validator(mode="after")
def validate_async_task_cannot_include_sequential_async_tasks_in_context(self):
"""
Validates that if a task is set to be executed asynchronously,
it cannot include other asynchronous tasks in its context unless
separated by a synchronous task.
"""
for i, task in enumerate(self.tasks):
if task.async_execution and task.context:
for context_task in task.context:
if context_task.async_execution:
for j in range(i - 1, -1, -1):
if self.tasks[j] == context_task:
raise ValueError(
f"Task '{task.description}' is asynchronous and cannot include other sequential asynchronous tasks in its context."
)
if not self.tasks[j].async_execution:
break
return self
@model_validator(mode="after")
def validate_context_no_future_tasks(self):
"""Validates that a task's context does not include future tasks."""
task_indices = {id(task): i for i, task in enumerate(self.tasks)}
for task in self.tasks:
if task.context:
for context_task in task.context:
if id(context_task) not in task_indices:
continue # Skip context tasks not in the main tasks list
if task_indices[id(context_task)] > task_indices[id(task)]:
raise ValueError(
f"Task '{task.description}' has a context dependency on a future task '{context_task.description}', which is not allowed."
)
return self
def _setup_from_config(self):
assert self.config is not None, "Config should not be None."
@@ -239,22 +338,63 @@ class Crew(BaseModel):
del task_config["agent"]
return Task(**task_config, agent=task_agent)
def kickoff(self, inputs: Optional[Dict[str, Any]] = {}) -> str:
def _setup_for_training(self) -> None:
"""Sets up the crew for training."""
self._train = True
for task in self.tasks:
task.human_input = True
for agent in self.agents:
agent.allow_delegation = False
CrewTrainingHandler(TRAINING_DATA_FILE).initialize_file()
CrewTrainingHandler(TRAINED_AGENTS_DATA_FILE).initialize_file()
def train(self, n_iterations: int, inputs: Optional[Dict[str, Any]] = {}) -> None:
"""Trains the crew for a given number of iterations."""
self._setup_for_training()
for n_iteration in range(n_iterations):
self._train_iteration = n_iteration
self.kickoff(inputs=inputs)
training_data = CrewTrainingHandler(TRAINING_DATA_FILE).load()
for agent in self.agents:
result = TaskEvaluator(agent).evaluate_training_data(
training_data=training_data, agent_id=str(agent.id)
)
CrewTrainingHandler(TRAINED_AGENTS_DATA_FILE).save_trained_data(
agent_id=str(agent.role), trained_data=result.model_dump()
)
def kickoff(
self,
inputs: Optional[Dict[str, Any]] = None,
) -> CrewOutput:
"""Starts the crew to work on its assigned tasks."""
self._execution_span = self._telemetry.crew_execution_span(self)
self._interpolate_inputs(inputs) # type: ignore # Argument 1 to "_interpolate_inputs" of "Crew" has incompatible type "dict[str, Any] | None"; expected "dict[str, Any]"
self._execution_span = self._telemetry.crew_execution_span(self, inputs)
if inputs is not None:
self._interpolate_inputs(inputs)
self._set_tasks_callbacks()
i18n = I18N(prompt_file=self.prompt_file)
for agent in self.agents:
agent.i18n = i18n
agent.crew = self
# type: ignore[attr-defined] # Argument 1 to "_interpolate_inputs" of "Crew" has incompatible type "dict[str, Any] | None"; expected "dict[str, Any]"
agent.crew = self # type: ignore[attr-defined]
# TODO: Create an AgentFunctionCalling protocol for future refactoring
if not agent.function_calling_llm: # type: ignore # "BaseAgent" has no attribute "function_calling_llm"
agent.function_calling_llm = self.function_calling_llm # type: ignore # "BaseAgent" has no attribute "function_calling_llm"
if not agent.function_calling_llm:
agent.function_calling_llm = self.function_calling_llm
if not agent.step_callback:
agent.step_callback = self.step_callback
if agent.allow_code_execution: # type: ignore # BaseAgent" has no attribute "allow_code_execution"
agent.tools += agent.get_code_execution_tools() # type: ignore # "BaseAgent" has no attribute "get_code_execution_tools"; maybe "get_delegation_tools"?
if not agent.step_callback: # type: ignore # "BaseAgent" has no attribute "step_callback"
agent.step_callback = self.step_callback # type: ignore # "BaseAgent" has no attribute "step_callback"
agent.create_agent_executor()
@@ -263,33 +403,129 @@ class Crew(BaseModel):
if self.process == Process.sequential:
result = self._run_sequential_process()
elif self.process == Process.hierarchical:
result, manager_metrics = self._run_hierarchical_process() # type: ignore # Unpacking a string is disallowed
metrics.append(manager_metrics) # type: ignore # Cannot determine type of "manager_metrics"
result = self._run_hierarchical_process() # type: ignore # Incompatible types in assignment (expression has type "str | dict[str, Any]", variable has type "str")
else:
raise NotImplementedError(
f"The process '{self.process}' is not implemented yet."
)
metrics += [agent._token_process.get_summary() for agent in self.agents]
metrics = metrics + [
agent._token_process.get_summary() for agent in self.agents
]
self.usage_metrics = {
key: sum([m[key] for m in metrics if m is not None]) for key in metrics[0]
}
return result
def _run_sequential_process(self) -> str:
def kickoff_for_each(self, inputs: List[Dict[str, Any]]) -> List[CrewOutput]:
"""Executes the Crew's workflow for each input in the list and aggregates results."""
results: List[CrewOutput] = []
# Initialize the parent crew's usage metrics
total_usage_metrics = {
"total_tokens": 0,
"prompt_tokens": 0,
"completion_tokens": 0,
"successful_requests": 0,
}
for input_data in inputs:
crew = self.copy()
output = crew.kickoff(inputs=input_data)
if crew.usage_metrics:
for key in total_usage_metrics:
total_usage_metrics[key] += crew.usage_metrics.get(key, 0)
results.append(output)
self.usage_metrics = total_usage_metrics
return results
async def kickoff_async(self, inputs: Optional[Dict[str, Any]] = {}) -> CrewOutput:
"""Asynchronous kickoff method to start the crew execution."""
return await asyncio.to_thread(self.kickoff, inputs)
async def kickoff_for_each_async(self, inputs: List[Dict]) -> List[CrewOutput]:
crew_copies = [self.copy() for _ in inputs]
async def run_crew(crew, input_data):
return await crew.kickoff_async(inputs=input_data)
tasks = [
asyncio.create_task(run_crew(crew_copies[i], inputs[i]))
for i in range(len(inputs))
]
tasks = [
asyncio.create_task(run_crew(crew_copies[i], inputs[i]))
for i in range(len(inputs))
]
results = await asyncio.gather(*tasks)
total_usage_metrics = {
"total_tokens": 0,
"prompt_tokens": 0,
"completion_tokens": 0,
"successful_requests": 0,
}
for crew in crew_copies:
if crew.usage_metrics:
for key in total_usage_metrics:
total_usage_metrics[key] += crew.usage_metrics.get(key, 0)
self.usage_metrics = total_usage_metrics
total_usage_metrics = {
"total_tokens": 0,
"prompt_tokens": 0,
"completion_tokens": 0,
"successful_requests": 0,
}
for crew in crew_copies:
if crew.usage_metrics:
for key in total_usage_metrics:
total_usage_metrics[key] += crew.usage_metrics.get(key, 0)
self.usage_metrics = total_usage_metrics
return results
def _run_sequential_process(self) -> CrewOutput:
"""Executes tasks sequentially and returns the final output."""
task_output = ""
task_outputs: List[TaskOutput] = []
futures: List[Tuple[Task, Future[TaskOutput]]] = []
for task in self.tasks:
if task.agent.allow_delegation: # type: ignore # Item "None" of "Agent | None" has no attribute "allow_delegation"
if task.agent and task.agent.allow_delegation:
agents_for_delegation = [
agent for agent in self.agents if agent != task.agent
]
if len(self.agents) > 1 and len(agents_for_delegation) > 0:
task.tools += AgentTools(agents=agents_for_delegation).tools()
delegation_tools = task.agent.get_delegation_tools(
agents_for_delegation
)
# Add tools if they are not already in task.tools
for new_tool in delegation_tools:
# Find the index of the tool with the same name
existing_tool_index = next(
(
index
for index, tool in enumerate(task.tools or [])
if tool.name == new_tool.name
),
None,
)
if not task.tools:
task.tools = []
if existing_tool_index is not None:
# Replace the existing tool
task.tools[existing_tool_index] = new_tool
else:
# Add the new tool
task.tools.append(new_tool)
role = task.agent.role if task.agent is not None else "None"
self._logger.log("debug", f"== Working Agent: {role}", color="bold_purple")
@@ -302,29 +538,86 @@ class Crew(BaseModel):
agent=role, task=task.description, status="started"
)
output = task.execute(context=task_output)
if not task.async_execution:
task_output = output
if task.async_execution:
context = (
aggregate_raw_outputs_from_tasks(task.context)
if task.context
else aggregate_raw_outputs_from_task_outputs(task_outputs)
)
future = task.execute_async(
agent=task.agent, context=context, tools=task.tools
)
futures.append((task, future))
else:
# Before executing a synchronous task, wait for all async tasks to complete
if futures:
# Clear task_outputs before processing async tasks
task_outputs = []
for future_task, future in futures:
task_output = future.result()
task_outputs.append(task_output)
self._process_task_result(future_task, task_output)
role = task.agent.role if task.agent is not None else "None"
self._logger.log("debug", f"== [{role}] Task output: {task_output}\n\n")
# Clear the futures list after processing all async results
futures.clear()
if self.output_log_file:
self._file_handler.log(agent=role, task=task_output, status="completed")
context = (
aggregate_raw_outputs_from_tasks(task.context)
if task.context
else aggregate_raw_outputs_from_task_outputs(task_outputs)
)
task_output = task.execute_sync(
agent=task.agent, context=context, tools=task.tools
)
task_outputs = [task_output]
self._process_task_result(task, task_output)
self._finish_execution(task_output)
return self._format_output(task_output)
if futures:
# Clear task_outputs before processing async tasks
task_outputs = []
for future_task, future in futures:
task_output = future.result()
task_outputs.append(task_output)
self._process_task_result(future_task, task_output)
def _run_hierarchical_process(self) -> str:
# Important: There should only be one task output in the list
# If there are more or 0, something went wrong.
if len(task_outputs) != 1:
raise ValueError(
"Something went wrong. Kickoff should return only one task output."
)
final_task_output = task_outputs[0]
final_string_output = final_task_output.raw
self._finish_execution(final_string_output)
token_usage = self.calculate_usage_metrics()
return CrewOutput(
raw=final_task_output.raw,
pydantic=final_task_output.pydantic,
json_dict=final_task_output.json_dict,
tasks_output=[task.output for task in self.tasks if task.output],
token_usage=token_usage,
)
def _process_task_result(self, task: Task, output: TaskOutput) -> None:
role = task.agent.role if task.agent is not None else "None"
self._logger.log("debug", f"== [{role}] Task output: {output}\n\n")
if self.output_log_file:
self._file_handler.log(agent=role, task=output, status="completed")
# TODO: @joao, Breaking change. Changed return type. Usage metrics is included in crewoutput
def _run_hierarchical_process(self) -> CrewOutput:
"""Creates and assigns a manager agent to make sure the crew completes the tasks."""
i18n = I18N(prompt_file=self.prompt_file)
if self.manager_agent is not None:
self.manager_agent.allow_delegation = True
manager = self.manager_agent
if len(manager.tools) > 0:
if manager.tools is not None and len(manager.tools) > 0:
raise Exception("Manager agent should not have tools")
manager.tools = AgentTools(agents=self.agents).tools()
manager.tools = self.manager_agent.get_delegation_tools(self.agents)
else:
manager = Agent(
role=i18n.retrieve("hierarchical_manager_agent", "role"),
@@ -332,10 +625,14 @@ class Crew(BaseModel):
backstory=i18n.retrieve("hierarchical_manager_agent", "backstory"),
tools=AgentTools(agents=self.agents).tools(),
llm=self.manager_llm,
verbose=True,
verbose=self.verbose,
)
self.manager_agent = manager
task_output = ""
task_outputs: List[TaskOutput] = []
futures: List[Tuple[Task, Future[TaskOutput]]] = []
# TODO: IF USER OVERRIDE THE CONTEXT, PASS THAT
for task in self.tasks:
self._logger.log("debug", f"Working Agent: {manager.role}")
self._logger.log("info", f"Starting Task: {task.description}")
@@ -345,19 +642,101 @@ class Crew(BaseModel):
agent=manager.role, task=task.description, status="started"
)
task_output = task.execute(
agent=manager, context=task_output, tools=manager.tools
if task.async_execution:
context = (
aggregate_raw_outputs_from_tasks(task.context)
if task.context
else aggregate_raw_outputs_from_task_outputs(task_outputs)
)
future = task.execute_async(
agent=manager, context=context, tools=manager.tools
)
futures.append((task, future))
else:
# Before executing a synchronous task, wait for all async tasks to complete
if futures:
# Clear task_outputs before processing async tasks
task_outputs = []
for future_task, future in futures:
task_output = future.result()
task_outputs.append(task_output)
self._process_task_result(future_task, task_output)
# Clear the futures list after processing all async results
futures.clear()
context = (
aggregate_raw_outputs_from_tasks(task.context)
if task.context
else aggregate_raw_outputs_from_task_outputs(task_outputs)
)
task_output = task.execute_sync(
agent=manager, context=context, tools=manager.tools
)
task_outputs = [task_output]
self._process_task_result(task, task_output)
# Process any remaining async results
if futures:
# Clear task_outputs before processing async tasks
task_outputs = []
for future_task, future in futures:
task_output = future.result()
task_outputs.append(task_output)
self._process_task_result(future_task, task_output)
# Important: There should only be one task output in the list
# If there are more or 0, something went wrong.
if len(task_outputs) != 1:
raise ValueError(
"Something went wrong. Kickoff should return only one task output."
)
self._logger.log("debug", f"[{manager.role}] Task output: {task_output}")
final_task_output = task_outputs[0]
if self.output_log_file:
self._file_handler.log(
agent=manager.role, task=task_output, status="completed"
)
final_string_output = final_task_output.raw
self._finish_execution(final_string_output)
self._finish_execution(task_output)
return self._format_output(task_output), manager._token_process.get_summary() # type: ignore # Incompatible return value type (got "tuple[str, Any]", expected "str")
token_usage = self.calculate_usage_metrics()
return CrewOutput(
raw=final_task_output.raw,
pydantic=final_task_output.pydantic,
json_dict=final_task_output.json_dict,
tasks_output=[task.output for task in self.tasks if task.output],
token_usage=token_usage,
)
def copy(self):
"""Create a deep copy of the Crew."""
exclude = {
"id",
"_rpm_controller",
"_logger",
"_execution_span",
"_file_handler",
"_cache_handler",
"_short_term_memory",
"_long_term_memory",
"_entity_memory",
"_telemetry",
"agents",
"tasks",
}
cloned_agents = [agent.copy() for agent in self.agents]
cloned_tasks = [task.copy(cloned_agents) for task in self.tasks]
copied_data = self.model_dump(exclude=exclude)
copied_data = {k: v for k, v in copied_data.items() if v is not None}
copied_data.pop("agents", None)
copied_data.pop("tasks", None)
copied_crew = Crew(**copied_data, agents=cloned_agents, tasks=cloned_tasks)
return copied_crew
def _set_tasks_callbacks(self) -> None:
"""Sets callback for every task suing task_callback"""
@@ -367,23 +746,48 @@ class Crew(BaseModel):
def _interpolate_inputs(self, inputs: Dict[str, Any]) -> None:
"""Interpolates the inputs in the tasks and agents."""
[task.interpolate_inputs(inputs) for task in self.tasks] # type: ignore # "interpolate_inputs" of "Task" does not return a value (it only ever returns None)
[agent.interpolate_inputs(inputs) for agent in self.agents] # type: ignore # "interpolate_inputs" of "Agent" does not return a value (it only ever returns None)
[
task.interpolate_inputs(
# type: ignore # "interpolate_inputs" of "Task" does not return a value (it only ever returns None)
inputs
)
for task in self.tasks
]
# type: ignore # "interpolate_inputs" of "Agent" does not return a value (it only ever returns None)
for agent in self.agents:
agent.interpolate_inputs(inputs)
def _format_output(self, output: str) -> str:
"""Formats the output of the crew execution."""
if self.full_output:
return { # type: ignore # Incompatible return value type (got "dict[str, Sequence[str | TaskOutput | None]]", expected "str")
"final_output": output,
"tasks_outputs": [task.output for task in self.tasks if task],
}
else:
return output
def _finish_execution(self, output) -> None:
def _finish_execution(self, final_string_output: str) -> None:
if self.max_rpm:
self._rpm_controller.stop_rpm_counter()
self._telemetry.end_crew(self, output)
if agentops:
agentops.end_session(
end_state="Success",
end_state_reason="Finished Execution",
)
self._telemetry.end_crew(self, final_string_output)
def calculate_usage_metrics(self) -> Dict[str, int]:
"""Calculates and returns the usage metrics."""
total_usage_metrics = {
"total_tokens": 0,
"prompt_tokens": 0,
"completion_tokens": 0,
"successful_requests": 0,
}
for agent in self.agents:
if hasattr(agent, "_token_process"):
token_sum = agent._token_process.get_summary()
for key in total_usage_metrics:
total_usage_metrics[key] += token_sum.get(key, 0)
if self.manager_agent and hasattr(self.manager_agent, "_token_process"):
token_sum = self.manager_agent._token_process.get_summary()
for key in total_usage_metrics:
total_usage_metrics[key] += token_sum.get(key, 0)
return total_usage_metrics
def __repr__(self):
return f"Crew(id={self.id}, process={self.process}, number_of_agents={len(self.agents)}, number_of_tasks={len(self.tasks)})"

View File

@@ -0,0 +1 @@
from .crew_output import CrewOutput

View File

@@ -0,0 +1,60 @@
import json
from typing import Any, Dict, Optional
from pydantic import BaseModel, Field
from crewai.tasks.output_format import OutputFormat
from crewai.tasks.task_output import TaskOutput
class CrewOutput(BaseModel):
"""Class that represents the result of a crew."""
raw: str = Field(description="Raw output of crew", default="")
pydantic: Optional[BaseModel] = Field(
description="Pydantic output of Crew", default=None
)
json_dict: Optional[Dict[str, Any]] = Field(
description="JSON dict output of Crew", default=None
)
tasks_output: list[TaskOutput] = Field(
description="Output of each task", default=[]
)
token_usage: Dict[str, Any] = Field(
description="Processed token summary", default={}
)
# TODO: Joao - Adding this safety check breakes when people want to see
# The full output of a CrewOutput.
# @property
# def pydantic(self) -> Optional[BaseModel]:
# # Check if the final task output included a pydantic model
# if self.tasks_output[-1].output_format != OutputFormat.PYDANTIC:
# raise ValueError(
# "No pydantic model found in the final task. Please make sure to set the output_pydantic property in the final task in your crew."
# )
# return self._pydantic
@property
def json(self) -> Optional[str]:
if self.tasks_output[-1].output_format != OutputFormat.JSON:
raise ValueError(
"No JSON output found in the final task. Please make sure to set the output_json property in the final task in your crew."
)
return json.dumps(self.json_dict)
def to_dict(self) -> Dict[str, Any]:
if self.json_dict:
return self.json_dict
if self.pydantic:
return self.pydantic.model_dump()
raise ValueError("No output to convert to dictionary")
def __str__(self):
if self.pydantic:
return str(self.pydantic)
if self.json_dict:
return str(self.json_dict)
return self.raw

View File

@@ -12,7 +12,10 @@ class EntityMemory(Memory):
def __init__(self, crew=None, embedder_config=None):
storage = RAGStorage(
type="entities", allow_reset=False, embedder_config=embedder_config, crew=crew
type="entities",
allow_reset=False,
embedder_config=embedder_config,
crew=crew,
)
super().__init__(storage)

View File

@@ -13,7 +13,9 @@ class ShortTermMemory(Memory):
"""
def __init__(self, crew=None, embedder_config=None):
storage = RAGStorage(type="short_term", embedder_config=embedder_config, crew=crew)
storage = RAGStorage(
type="short_term", embedder_config=embedder_config, crew=crew
)
super().__init__(storage)
def save(self, item: ShortTermMemoryItem) -> None: # type: ignore # BUG?: Signature of "save" incompatible with supertype "Memory"

View File

@@ -1,6 +1,3 @@
tasks_order = []
def memoize(func):
cache = {}
@@ -10,14 +7,21 @@ def memoize(func):
cache[key] = func(*args, **kwargs)
return cache[key]
memoized_func.__dict__.update(func.__dict__)
return memoized_func
def task(func):
if not hasattr(task, "registration_order"):
task.registration_order = []
func.is_task = True
tasks_order.append(func.__name__)
func = memoize(func)
return func
wrapped_func = memoize(func)
# Append the function name to the registration order list
task.registration_order.append(func.__name__)
return wrapped_func
def agent(func):
@@ -32,26 +36,43 @@ def crew(func):
instantiated_agents = []
agent_roles = set()
# Iterate over tasks_order to maintain the defined order
for task_name in tasks_order:
possible_task = getattr(self, task_name)
if callable(possible_task):
task_instance = possible_task()
instantiated_tasks.append(task_instance)
if hasattr(task_instance, "agent"):
agent_instance = task_instance.agent
if agent_instance.role not in agent_roles:
instantiated_agents.append(agent_instance)
agent_roles.add(agent_instance.role)
all_functions = {
name: getattr(self, name)
for name in dir(self)
if callable(getattr(self, name))
}
tasks = {
name: func
for name, func in all_functions.items()
if hasattr(func, "is_task")
}
agents = {
name: func
for name, func in all_functions.items()
if hasattr(func, "is_agent")
}
# Sort tasks by their registration order
sorted_task_names = sorted(
tasks, key=lambda name: task.registration_order.index(name)
)
# Instantiate tasks in the order they were defined
for task_name in sorted_task_names:
task_instance = tasks[task_name]()
instantiated_tasks.append(task_instance)
if hasattr(task_instance, "agent"):
agent_instance = task_instance.agent
if agent_instance.role not in agent_roles:
instantiated_agents.append(agent_instance)
agent_roles.add(agent_instance.role)
# Instantiate any additional agents not already included by tasks
for attr_name in dir(self):
possible_agent = getattr(self, attr_name)
if callable(possible_agent) and hasattr(possible_agent, "is_agent"):
temp_agent_instance = possible_agent()
if temp_agent_instance.role not in agent_roles:
instantiated_agents.append(temp_agent_instance)
agent_roles.add(temp_agent_instance.role)
for agent_name in agents:
temp_agent_instance = agents[agent_name]()
if temp_agent_instance.role not in agent_roles:
instantiated_agents.append(temp_agent_instance)
agent_roles.add(temp_agent_instance.role)
self.agents = instantiated_agents
self.tasks = instantiated_tasks

View File

@@ -4,18 +4,15 @@ from pathlib import Path
import yaml
from dotenv import load_dotenv
from pydantic import ConfigDict
load_dotenv()
def CrewBase(cls):
class WrappedClass(cls):
is_crew_class: bool = True
class Config:
arbitrary_types_allowed = True
ignored_types = [bool]
model_config = ConfigDict(arbitrary_types_allowed=True)
is_crew_class: bool = True # type: ignore
base_directory = None
for frame_info in inspect.stack():

View File

@@ -1,16 +1,24 @@
import json
import os
import re
import threading
import uuid
from typing import Any, Dict, List, Optional, Type
from concurrent.futures import Future
from copy import copy
from typing import Any, Dict, List, Optional, Tuple, Type, Union
from langchain_openai import ChatOpenAI
from opentelemetry.trace import Span
from pydantic import UUID4, BaseModel, Field, field_validator, model_validator
from pydantic_core import PydanticCustomError
from crewai.agent import Agent
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.tasks.output_format import OutputFormat
from crewai.tasks.task_output import TaskOutput
from crewai.utilities import I18N, Converter, ConverterError, Printer
from crewai.telemetry.telemetry import Telemetry
from crewai.utilities.converter import Converter, ConverterError
from crewai.utilities.i18n import I18N
from crewai.utilities.printer import Printer
from crewai.utilities.pydantic_schema_parser import PydanticSchemaParser
@@ -41,7 +49,6 @@ class Task(BaseModel):
tools_errors: int = 0
delegations: int = 0
i18n: I18N = I18N()
thread: Optional[threading.Thread] = None
prompt_context: Optional[str] = None
description: str = Field(description="Description of the actual task.")
expected_output: str = Field(
@@ -54,7 +61,7 @@ class Task(BaseModel):
callback: Optional[Any] = Field(
description="Callback to be executed after the task is completed.", default=None
)
agent: Optional[Agent] = Field(
agent: Optional[BaseAgent] = Field(
description="Agent responsible for execution the task.", default=None
)
context: Optional[List["Task"]] = Field(
@@ -93,9 +100,16 @@ class Task(BaseModel):
description="Whether the task should have a human review the final answer of the agent",
default=False,
)
converter_cls: Optional[Type[Converter]] = Field(
description="A converter class used to export structured output",
default=None,
)
_telemetry: Telemetry
_execution_span: Span | None = None
_original_description: str | None = None
_original_expected_output: str | None = None
_thread: threading.Thread | None = None
def __init__(__pydantic_self__, **data):
config = data.pop("config", {})
@@ -117,6 +131,12 @@ class Task(BaseModel):
return value[1:]
return value
@model_validator(mode="after")
def set_private_attrs(self) -> "Task":
"""Set private attributes."""
self._telemetry = Telemetry()
return self
@model_validator(mode="after")
def set_attributes_based_on_config(self) -> "Task":
"""Set attributes based on the agent configuration."""
@@ -144,69 +164,91 @@ class Task(BaseModel):
)
return self
def execute( # type: ignore # Missing return statement
def execute_sync(
self,
agent: Agent | None = None,
agent: Optional[BaseAgent] = None,
context: Optional[str] = None,
tools: Optional[List[Any]] = None,
) -> str:
"""Execute the task.
) -> TaskOutput:
"""Execute the task synchronously."""
return self._execute_core(agent, context, tools)
Returns:
Output of the task.
"""
def execute_async(
self,
agent: BaseAgent | None = None,
context: Optional[str] = None,
tools: Optional[List[Any]] = None,
) -> Future[TaskOutput]:
"""Execute the task asynchronously."""
future: Future[TaskOutput] = Future()
threading.Thread(
target=self._execute_task_async, args=(agent, context, tools, future)
).start()
return future
def _execute_task_async(
self,
agent: Optional[BaseAgent],
context: Optional[str],
tools: Optional[List[Any]],
future: Future[TaskOutput],
) -> None:
"""Execute the task asynchronously with context handling."""
result = self._execute_core(agent, context, tools)
future.set_result(result)
def _execute_core(
self,
agent: Optional[BaseAgent],
context: Optional[str],
tools: Optional[List[Any]],
) -> TaskOutput:
"""Run the core execution logic of the task."""
agent = agent or self.agent
if not agent:
raise Exception(
f"The task '{self.description}' has no agent assigned, therefore it can't be executed directly and should be executed in a Crew using a specific process that support that, like hierarchical."
)
if self.context:
context = [] # type: ignore # Incompatible types in assignment (expression has type "list[Never]", variable has type "str | None")
for task in self.context:
if task.async_execution:
task.thread.join() # type: ignore # Item "None" of "Thread | None" has no attribute "join"
if task and task.output:
context.append(task.output.raw_output) # type: ignore # Item "str" of "str | None" has no attribute "append"
context = "\n".join(context) # type: ignore # Argument 1 to "join" of "str" has incompatible type "str | None"; expected "Iterable[str]"
self._execution_span = self._telemetry.task_started(crew=agent.crew, task=self)
self.prompt_context = context
tools = tools or self.tools
tools = tools or self.tools or []
if self.async_execution:
self.thread = threading.Thread(
target=self._execute, args=(agent, self, context, tools)
)
self.thread.start()
else:
result = self._execute(
task=self,
agent=agent,
context=context,
tools=tools,
)
return result
def _execute(self, agent, task, context, tools):
result = agent.execute_task(
task=task,
task=self,
context=context,
tools=tools,
)
exported_output = self._export_output(result)
pydantic_output, json_output = self._export_output(result)
self.output = TaskOutput(
task_output = TaskOutput(
description=self.description,
exported_output=exported_output,
raw_output=result,
raw=result,
pydantic=pydantic_output,
json_dict=json_output,
agent=agent.role,
output_format=self._get_output_format(),
)
self.output = task_output
if self.callback:
self.callback(self.output)
return exported_output
if self._execution_span:
self._telemetry.task_ended(self._execution_span, self)
self._execution_span = None
if self.output_file:
content = (
json_output
if json_output
else pydantic_output.model_dump_json() if pydantic_output else result
)
self._save_file(content)
return task_output
def prompt(self) -> str:
"""Prompt the task.
@@ -241,61 +283,144 @@ class Task(BaseModel):
"""Increment the delegations counter."""
self.delegations += 1
def _export_output(self, result: str) -> Any:
exported_result = result
instructions = "I'm gonna convert this raw text into valid JSON."
def copy(self, agents: List["BaseAgent"]) -> "Task":
"""Create a deep copy of the Task."""
exclude = {
"id",
"agent",
"context",
"tools",
}
copied_data = self.model_dump(exclude=exclude)
copied_data = {k: v for k, v in copied_data.items() if v is not None}
cloned_context = (
[task.copy(agents) for task in self.context] if self.context else None
)
def get_agent_by_role(role: str) -> Union["BaseAgent", None]:
return next((agent for agent in agents if agent.role == role), None)
cloned_agent = get_agent_by_role(self.agent.role) if self.agent else None
cloned_tools = copy(self.tools) if self.tools else []
copied_task = Task(
**copied_data,
context=cloned_context,
agent=cloned_agent,
tools=cloned_tools,
)
return copied_task
def _create_converter(self, *args, **kwargs) -> Converter:
"""Create a converter instance."""
converter = self.agent.get_output_converter(*args, **kwargs)
if self.converter_cls:
converter = self.converter_cls(*args, **kwargs)
return converter
def _export_output(
self, result: str
) -> Tuple[Optional[BaseModel], Optional[Dict[str, Any]]]:
pydantic_output: Optional[BaseModel] = None
json_output: Optional[Dict[str, Any]] = None
if self.output_pydantic or self.output_json:
model = self.output_pydantic or self.output_json
model_output = self._convert_to_model(result)
pydantic_output = (
model_output if isinstance(model_output, BaseModel) else None
)
if isinstance(model_output, str):
try:
json_output = json.loads(model_output)
except json.JSONDecodeError:
json_output = None
else:
json_output = model_output if isinstance(model_output, dict) else None
# try to convert task_output directly to pydantic/json
return pydantic_output, json_output
def _convert_to_model(self, result: str) -> Union[dict, BaseModel, str]:
model = self.output_pydantic or self.output_json
if model is None:
return result
try:
return self._validate_model(result, model)
except Exception:
return self._handle_partial_json(result, model)
def _validate_model(
self, result: str, model: Type[BaseModel]
) -> Union[dict, BaseModel]:
exported_result = model.model_validate_json(result)
if self.output_json:
return exported_result.model_dump()
return exported_result
def _handle_partial_json(
self, result: str, model: Type[BaseModel]
) -> Union[dict, BaseModel, str]:
match = re.search(r"({.*})", result, re.DOTALL)
if match:
try:
exported_result = model.model_validate_json(result) # type: ignore # Item "None" of "type[BaseModel] | None" has no attribute "model_validate_json"
exported_result = model.model_validate_json(match.group(0))
if self.output_json:
return exported_result.model_dump() # type: ignore # "str" has no attribute "model_dump"
return exported_result.model_dump()
return exported_result
except Exception:
# sometimes the response contains valid JSON in the middle of text
match = re.search(r"({.*})", result, re.DOTALL)
if match:
try:
exported_result = model.model_validate_json(match.group(0)) # type: ignore # Item "None" of "type[BaseModel] | None" has no attribute "model_validate_json"
if self.output_json:
return exported_result.model_dump() # type: ignore # "str" has no attribute "model_dump"
return exported_result
except Exception:
pass
pass
llm = self.agent.function_calling_llm or self.agent.llm # type: ignore # Item "None" of "Agent | None" has no attribute "function_calling_llm"
return self._convert_with_instructions(result, model)
if not self._is_gpt(llm):
model_schema = PydanticSchemaParser(model=model).get_schema() # type: ignore # Argument "model" to "PydanticSchemaParser" has incompatible type "type[BaseModel] | None"; expected "type[BaseModel]"
instructions = f"{instructions}\n\nThe json should have the following structure, with the following keys:\n{model_schema}"
def _convert_with_instructions(
self, result: str, model: Type[BaseModel]
) -> Union[dict, BaseModel, str]:
llm = self.agent.function_calling_llm or self.agent.llm
instructions = self._get_conversion_instructions(model, llm)
converter = Converter(
llm=llm, text=result, model=model, instructions=instructions
converter = self._create_converter(
llm=llm, text=result, model=model, instructions=instructions
)
exported_result = (
converter.to_pydantic() if self.output_pydantic else converter.to_json()
)
if isinstance(exported_result, ConverterError):
Printer().print(
content=f"{exported_result.message} Using raw output instead.",
color="red",
)
if self.output_pydantic:
exported_result = converter.to_pydantic()
elif self.output_json:
exported_result = converter.to_json()
if isinstance(exported_result, ConverterError):
Printer().print(
content=f"{exported_result.message} Using raw output instead.",
color="red",
)
exported_result = result
if self.output_file:
content = (
exported_result if not self.output_pydantic else exported_result.json() # type: ignore # "str" has no attribute "json"
)
self._save_file(content)
return result
return exported_result
def _get_output_format(self) -> OutputFormat:
if self.output_json:
return OutputFormat.JSON
if self.output_pydantic:
return OutputFormat.PYDANTIC
return OutputFormat.RAW
def _get_conversion_instructions(self, model: Type[BaseModel], llm: Any) -> str:
instructions = "I'm gonna convert this raw text into valid JSON."
if not self._is_gpt(llm):
model_schema = PydanticSchemaParser(model=model).get_schema()
instructions = f"{instructions}\n\nThe json should have the following structure, with the following keys:\n{model_schema}"
return instructions
def _save_output(self, content: str) -> None:
if not self.output_file:
raise Exception("Output file path is not set.")
directory = os.path.dirname(self.output_file)
if directory and not os.path.exists(directory):
os.makedirs(directory)
with open(self.output_file, "w", encoding="utf-8") as file:
file.write(content)
def _is_gpt(self, llm) -> bool:
return isinstance(llm, ChatOpenAI) and llm.openai_api_base is None
@@ -305,7 +430,7 @@ class Task(BaseModel):
if directory and not os.path.exists(directory):
os.makedirs(directory)
with open(self.output_file, "w", encoding='utf-8') as file: # type: ignore # Argument 1 to "open" has incompatible type "str | None"; expected "int | str | bytes | PathLike[str] | PathLike[bytes]"
with open(self.output_file, "w", encoding="utf-8") as file: # type: ignore # Argument 1 to "open" has incompatible type "str | None"; expected "int | str | bytes | PathLike[str] | PathLike[bytes]"
file.write(result)
return None

View File

@@ -0,0 +1,4 @@
from crewai.tasks.output_format import OutputFormat
from crewai.tasks.task_output import TaskOutput
__all__ = ["OutputFormat", "TaskOutput"]

View File

@@ -0,0 +1,9 @@
from enum import Enum
class OutputFormat(str, Enum):
"""Enum that represents the output format of a task."""
JSON = "json"
PYDANTIC = "pydantic"
RAW = "raw"

View File

@@ -1,23 +1,78 @@
from typing import Optional, Union
import json
from typing import Any, Dict, Optional
from pydantic import BaseModel, Field, model_validator
from crewai.tasks.output_format import OutputFormat
class TaskOutput(BaseModel):
"""Class that represents the result of a task."""
description: str = Field(description="Description of the task")
summary: Optional[str] = Field(description="Summary of the task", default=None)
exported_output: Union[str, BaseModel] = Field(
description="Output of the task", default=None
raw: str = Field(
description="Raw output of the task", default=""
) # TODO: @joao: breaking change, by renaming raw_output to raw, but now consistent with CrewOutput
pydantic: Optional[BaseModel] = Field(
description="Pydantic output of task", default=None
)
json_dict: Optional[Dict[str, Any]] = Field(
description="JSON dictionary of task", default=None
)
agent: str = Field(description="Agent that executed the task")
output_format: OutputFormat = Field(
description="Output format of the task", default=OutputFormat.RAW
)
raw_output: str = Field(description="Result of the task")
@model_validator(mode="after")
def set_summary(self):
"""Set the summary field based on the description."""
excerpt = " ".join(self.description.split(" ")[:10])
self.summary = f"{excerpt}..."
return self
def result(self):
return self.exported_output
# TODO: Joao - Adding this safety check breakes when people want to see
# The full output of a TaskOutput or CrewOutput.
# @property
# def pydantic(self) -> Optional[BaseModel]:
# # Check if the final task output included a pydantic model
# if self.output_format != OutputFormat.PYDANTIC:
# raise ValueError(
# """
# Invalid output format requested.
# If you would like to access the pydantic model,
# please make sure to set the output_pydantic property for the task.
# """
# )
# return self._pydantic
@property
def json(self) -> Optional[str]:
if self.output_format != OutputFormat.JSON:
raise ValueError(
"""
Invalid output format requested.
If you would like to access the JSON output,
please make sure to set the output_json property for the task
"""
)
return json.dumps(self.json_dict)
def to_dict(self) -> Dict[str, Any]:
"""Convert json_output and pydantic_output to a dictionary."""
output_dict = {}
if self.json_dict:
output_dict.update(self.json_dict)
if self.pydantic:
output_dict.update(self.pydantic.model_dump())
return output_dict
def __str__(self) -> str:
if self.pydantic:
return str(self.pydantic)
if self.json_dict:
return str(self.json_dict)
return self.raw

View File

@@ -1,8 +1,10 @@
from __future__ import annotations
import asyncio
import json
import os
import platform
from typing import Any
from typing import TYPE_CHECKING, Any
import pkg_resources
from opentelemetry import trace
@@ -10,7 +12,11 @@ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExport
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.trace import Status, StatusCode
from opentelemetry.trace import Span, Status, StatusCode
if TYPE_CHECKING:
from crewai.crew import Crew
from crewai.task import Task
class Telemetry:
@@ -74,7 +80,7 @@ class Telemetry:
self.ready = False
self.trace_set = False
def crew_creation(self, crew):
def crew_creation(self, crew: Crew, inputs: dict[str, Any] | None):
"""Records the creation of a crew."""
if self.ready:
try:
@@ -87,10 +93,13 @@ class Telemetry:
)
self._add_attribute(span, "python_version", platform.python_version())
self._add_attribute(span, "crew_id", str(crew.id))
if crew.share_crew:
self._add_attribute(
span, "crew_inputs", json.dumps(inputs) if inputs else None
)
self._add_attribute(span, "crew_process", crew.process)
self._add_attribute(
span, "crew_language", crew.prompt_file if crew.i18n else "None"
)
self._add_attribute(span, "crew_memory", crew.memory)
self._add_attribute(span, "crew_number_of_tasks", len(crew.tasks))
self._add_attribute(span, "crew_number_of_agents", len(crew.agents))
@@ -102,6 +111,8 @@ class Telemetry:
{
"id": str(agent.id),
"role": agent.role,
"goal": agent.goal,
"backstory": agent.backstory,
"verbose?": agent.verbose,
"max_iter": agent.max_iter,
"max_rpm": agent.max_rpm,
@@ -109,7 +120,7 @@ class Telemetry:
"llm": json.dumps(self._safe_llm_attributes(agent.llm)),
"delegation_enabled?": agent.allow_delegation,
"tools_names": [
tool.name.casefold() for tool in agent.tools
tool.name.casefold() for tool in agent.tools or []
],
}
for agent in crew.agents
@@ -123,10 +134,18 @@ class Telemetry:
[
{
"id": str(task.id),
"description": task.description,
"expected_output": task.expected_output,
"async_execution?": task.async_execution,
"human_input?": task.human_input,
"agent_role": task.agent.role if task.agent else "None",
"context": (
[task.description for task in task.context]
if task.context
else None
),
"tools_names": [
tool.name.casefold() for tool in task.tools
tool.name.casefold() for tool in task.tools or []
],
}
for task in crew.tasks
@@ -143,6 +162,60 @@ class Telemetry:
except Exception:
pass
def task_started(self, crew: Crew, task: Task) -> Span | None:
"""Records task started in a crew."""
if self.ready:
try:
tracer = trace.get_tracer("crewai.telemetry")
created_span = tracer.start_span("Task Created")
self._add_attribute(created_span, "crew_id", str(crew.id))
self._add_attribute(created_span, "task_index", crew.tasks.index(task))
self._add_attribute(created_span, "task_id", str(task.id))
if crew.share_crew:
self._add_attribute(
created_span, "formatted_description", task.description
)
self._add_attribute(
created_span, "formatted_expected_output", task.expected_output
)
created_span.set_status(Status(StatusCode.OK))
created_span.end()
span = tracer.start_span("Task Execution")
self._add_attribute(span, "crew_id", str(crew.id))
self._add_attribute(span, "task_index", crew.tasks.index(task))
self._add_attribute(span, "task_id", str(task.id))
if crew.share_crew:
self._add_attribute(span, "formatted_description", task.description)
self._add_attribute(
span, "formatted_expected_output", task.expected_output
)
return span
except Exception:
pass
return None
def task_ended(self, span: Span, task: Task):
"""Records task execution in a crew."""
if self.ready:
try:
self._add_attribute(
span, "output", task.output.raw_output if task.output else ""
)
span.set_status(Status(StatusCode.OK))
span.end()
except Exception:
pass
def tool_repeated_usage(self, llm: Any, tool_name: str, attempts: int):
"""Records the repeated usage 'error' of a tool by an agent."""
if self.ready:
@@ -207,12 +280,14 @@ class Telemetry:
except Exception:
pass
def crew_execution_span(self, crew):
def crew_execution_span(self, crew: Crew, inputs: dict[str, Any] | None):
"""Records the complete execution of a crew.
This is only collected if the user has opted-in to share the crew.
"""
if (self.ready) and (crew.share_crew):
try:
self.crew_creation(crew, inputs)
tracer = trace.get_tracer("crewai.telemetry")
span = tracer.start_span("Crew Execution")
self._add_attribute(
@@ -221,6 +296,9 @@ class Telemetry:
pkg_resources.get_distribution("crewai").version,
)
self._add_attribute(span, "crew_id", str(crew.id))
self._add_attribute(
span, "crew_inputs", json.dumps(inputs) if inputs else None
)
self._add_attribute(
span,
"crew_agents",
@@ -238,7 +316,7 @@ class Telemetry:
"llm": json.dumps(self._safe_llm_attributes(agent.llm)),
"delegation_enabled?": agent.allow_delegation,
"tools_names": [
tool.name.casefold() for tool in agent.tools
tool.name.casefold() for tool in agent.tools or []
],
}
for agent in crew.agents
@@ -253,16 +331,17 @@ class Telemetry:
{
"id": str(task.id),
"description": task.description,
"expected_output": task.expected_output,
"async_execution?": task.async_execution,
"output": task.expected_output,
"human_input?": task.human_input,
"agent_role": task.agent.role if task.agent else "None",
"context": (
[task.description for task in task.context]
if task.context
else "None"
else None
),
"tools_names": [
tool.name.casefold() for tool in task.tools
tool.name.casefold() for tool in task.tools or []
],
}
for task in crew.tasks
@@ -273,7 +352,7 @@ class Telemetry:
except Exception:
pass
def end_crew(self, crew, output):
def end_crew(self, crew, final_string_output):
if (self.ready) and (crew.share_crew):
try:
self._add_attribute(
@@ -281,7 +360,9 @@ class Telemetry:
"crewai_version",
pkg_resources.get_distribution("crewai").version,
)
self._add_attribute(crew._execution_span, "crew_output", output)
self._add_attribute(
crew._execution_span, "crew_output", final_string_output
)
self._add_attribute(
crew._execution_span,
"crew_tasks_output",

View File

@@ -1,80 +1,25 @@
from typing import List, Union
from langchain.tools import StructuredTool
from pydantic import BaseModel, Field
from crewai.agent import Agent
from crewai.task import Task
from crewai.utilities import I18N
from crewai.agents.agent_builder.utilities.base_agent_tool import BaseAgentTools
class AgentTools(BaseModel):
class AgentTools(BaseAgentTools):
"""Default tools around agent delegation"""
agents: List[Agent] = Field(description="List of agents in this crew.")
i18n: I18N = Field(default=I18N(), description="Internationalization settings.")
def tools(self):
coworkers = ", ".join([f"{agent.role}" for agent in self.agents])
tools = [
StructuredTool.from_function(
func=self.delegate_work,
name="Delegate work to co-worker",
name="Delegate work to coworker",
description=self.i18n.tools("delegate_work").format(
coworkers=f"[{', '.join([f'{agent.role}' for agent in self.agents])}]"
coworkers=coworkers
),
),
StructuredTool.from_function(
func=self.ask_question,
name="Ask question to co-worker",
description=self.i18n.tools("ask_question").format(
coworkers=f"[{', '.join([f'{agent.role}' for agent in self.agents])}]"
),
name="Ask question to coworker",
description=self.i18n.tools("ask_question").format(coworkers=coworkers),
),
]
return tools
def delegate_work(self, task: str, context: str, coworker: Union[str, None] = None, **kwargs):
"""Useful to delegate a specific task to a co-worker passing all necessary context and names."""
coworker = coworker or kwargs.get("co_worker") or kwargs.get("co-worker")
is_list = coworker.startswith("[") and coworker.endswith("]")
if is_list:
coworker = coworker[1:-1].split(",")[0]
return self._execute(coworker, task, context)
def ask_question(self, question: str, context: str, coworker: Union[str, None] = None, **kwargs):
"""Useful to ask a question, opinion or take from a co-worker passing all necessary context and names."""
coworker = coworker or kwargs.get("co_worker") or kwargs.get("co-worker")
is_list = coworker.startswith("[") and coworker.endswith("]")
if is_list:
coworker = coworker[1:-1].split(",")[0]
return self._execute(coworker, question, context)
def _execute(self, agent, task, context):
"""Execute the command."""
try:
agent = [
available_agent
for available_agent in self.agents
if available_agent.role.casefold().strip() == agent.casefold().strip()
]
except Exception as _:
return self.i18n.errors("agent_tool_unexsiting_coworker").format(
coworkers="\n".join(
[f"- {agent.role.casefold()}" for agent in self.agents]
)
)
if not agent:
return self.i18n.errors("agent_tool_unexsiting_coworker").format(
coworkers="\n".join(
[f"- {agent.role.casefold()}" for agent in self.agents]
)
)
agent = agent[0]
task = Task(
description=task,
agent=agent,
expected_output="Your best answer to your co-worker asking you this, accounting for the context shared.",
)
return agent.execute_task(task, context)

View File

@@ -8,7 +8,7 @@ from pydantic.v1 import BaseModel, Field
class ToolCalling(BaseModel):
tool_name: str = Field(..., description="The name of the tool to be called.")
arguments: Optional[Dict[str, Any]] = Field(
..., description="A dictinary of arguments to be passed to the tool."
..., description="A dictionary of arguments to be passed to the tool."
)
@@ -17,5 +17,5 @@ class InstructorToolCalling(PydanticBaseModel):
..., description="The name of the tool to be called."
)
arguments: Optional[Dict[str, Any]] = PydanticField(
..., description="A dictinary of arguments to be passed to the tool."
..., description="A dictionary of arguments to be passed to the tool."
)

View File

@@ -11,6 +11,11 @@ from crewai.telemetry import Telemetry
from crewai.tools.tool_calling import InstructorToolCalling, ToolCalling
from crewai.utilities import I18N, Converter, ConverterError, Printer
try:
import agentops
except ImportError:
agentops = None
OPENAI_BIGGER_MODELS = ["gpt-4"]
@@ -45,6 +50,7 @@ class ToolUsage:
tools_names: str,
task: Any,
function_calling_llm: Any,
agent: Any,
action: Any,
) -> None:
self._i18n: I18N = I18N()
@@ -53,6 +59,7 @@ class ToolUsage:
self._run_attempts: int = 1
self._max_parsing_attempts: int = 3
self._remember_format_after_usages: int = 3
self.agent = agent
self.tools_description = tools_description
self.tools_names = tools_names
self.tools_handler = tools_handler
@@ -98,7 +105,8 @@ class ToolUsage:
tool_string: str,
tool: BaseTool,
calling: Union[ToolCalling, InstructorToolCalling],
) -> None: # TODO: Fix this return type
) -> str: # TODO: Fix this return type
tool_event = agentops.ToolEvent(name=calling.tool_name) if agentops else None
if self._check_tool_repeated_usage(calling=calling): # type: ignore # _check_tool_repeated_usage of "ToolUsage" does not return a value (it only ever returns None)
try:
result = self._i18n.errors("task_repeated_usage").format(
@@ -111,7 +119,7 @@ class ToolUsage:
attempts=self._run_attempts,
)
result = self._format_result(result=result) # type: ignore # "_format_result" of "ToolUsage" does not return a value (it only ever returns None)
return result # type: ignore # Fix the reutrn type of this function
return result # type: ignore # Fix the return type of this function
except Exception:
self.task.increment_tools_errors()
@@ -123,11 +131,15 @@ class ToolUsage:
tool=calling.tool_name, input=calling.arguments
)
if not result:
original_tool = next(
(ot for ot in self.original_tools if ot.name == tool.name), None
)
if result is None: #! finecwg: if not result --> if result is None
try:
if calling.tool_name in [
"Delegate work to co-worker",
"Ask question to co-worker",
"Delegate work to coworker",
"Ask question to coworker",
]:
self.task.increment_delegations()
@@ -164,13 +176,14 @@ class ToolUsage:
return error # type: ignore # No return value expected
self.task.increment_tools_errors()
if agentops:
agentops.record(
agentops.ErrorEvent(exception=e, trigger_event=tool_event)
)
return self.use(calling=calling, tool_string=tool_string) # type: ignore # No return value expected
if self.tools_handler:
should_cache = True
original_tool = next(
(ot for ot in self.original_tools if ot.name == tool.name), None
)
if (
hasattr(original_tool, "cache_function")
and original_tool.cache_function # type: ignore # Item "None" of "Any | None" has no attribute "cache_function"
@@ -184,12 +197,29 @@ class ToolUsage:
)
self._printer.print(content=f"\n\n{result}\n", color="purple")
if agentops:
agentops.record(tool_event)
self._telemetry.tool_usage(
llm=self.function_calling_llm,
tool_name=tool.name,
attempts=self._run_attempts,
)
result = self._format_result(result=result) # type: ignore # "_format_result" of "ToolUsage" does not return a value (it only ever returns None)
data = {
"result": result,
"tool_name": tool.name,
"tool_args": calling.arguments,
}
if (
hasattr(original_tool, "result_as_answer")
and original_tool.result_as_answer # type: ignore # Item "None" of "Any | None" has no attribute "cache_function"
):
result_as_answer = original_tool.result_as_answer # type: ignore # Item "None" of "Any | None" has no attribute "result_as_answer"
data["result_as_answer"] = result_as_answer
self.agent.tools_results.append(data)
return result # type: ignore # No return value expected
def _format_result(self, result: Any) -> None:
@@ -290,7 +320,7 @@ class ToolUsage:
Example:
{"tool_name": "tool name", "arguments": {"arg_name1": "value", "arg_name2": 2}}""",
),
max_attemps=1,
max_attempts=1,
)
calling = converter.to_pydantic()

View File

@@ -9,19 +9,19 @@
"task": "\nCurrent Task: {input}\n\nBegin! This is VERY important to you, use the tools available and give your best Final Answer, your job depends on it!\n\nThought:",
"memory": "\n\n# Useful context: \n{memory}",
"role_playing": "You are {role}. {backstory}\nYour personal goal is: {goal}",
"tools": "\nYou ONLY have access to the following tools, and should NEVER make up tools that are not listed here:\n\n{tools}\n\nUse the following format:\n\nThought: you should always think about what to do\nAction: the action to take, only one name of [{tool_names}], just the name, exactly as it's written.\nAction Input: the input to the action, just a simple a python dictionary, enclosed in curly braces, using \" to wrap keys and values.\nObservation: the result of the action\n\nOnce all necessary information is gathered:\n\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n",
"tools": "\nYou ONLY have access to the following tools, and should NEVER make up tools that are not listed here:\n\n{tools}\n\nUse the following format:\n\nThought: you should always think about what to do\nAction: the action to take, only one name of [{tool_names}], just the name, exactly as it's written.\nAction Input: the input to the action, just a simple python dictionary, enclosed in curly braces, using \" to wrap keys and values.\nObservation: the result of the action\n\nOnce all necessary information is gathered:\n\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n",
"no_tools": "To give my best complete final answer to the task use the exact following format:\n\nThought: I now can give a great answer\nFinal Answer: my best complete final answer to the task.\nYour final answer must be the great and the most complete as possible, it must be outcome described.\n\nI MUST use these formats, my job depends on it!",
"format": "I MUST either use a tool (use one at time) OR give my best final answer. To Use the following format:\n\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action, dictionary enclosed in curly braces\nObservation: the result of the action\n... (this Thought/Action/Action Input/Observation can repeat N times)\nThought: I now can give a great answer\nFinal Answer: my best complete final answer to the task.\nYour final answer must be the great and the most complete as possible, it must be outcome described\n\n ",
"final_answer_format": "If you don't need to use any more tools, you must give your best complete final answer, make sure it satisfy the expect criteria, use the EXACT format below:\n\nThought: I now can give a great answer\nFinal Answer: my best complete final answer to the task.\n\n",
"format_without_tools": "\nSorry, I didn't use the right format. I MUST either use a tool (among the available ones), OR give my best final answer.\nI just remembered the expected format I must follow:\n\nQuestion: the input question you must answer\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action\nObservation: the result of the action\n... (this Thought/Action/Action Input/Observation can repeat N times)\nThought: I now can give a great answer\nFinal Answer: my best complete final answer to the task\nYour final answer must be the great and the most complete as possible, it must be outcome described\n\n",
"task_with_context": "{task}\n\nThis is the context you're working with:\n{context}",
"expected_output": "\nThis is the expect criteria for your final answer: {expected_output} \n you MUST return the actual complete content as the final answer, not a summary.",
"human_feedback": "You got human feedback on your work, re-avaluate it and give a new Final Answer when ready.\n {human_feedback}",
"getting_input": "This is the agent final answer: {final_answer}\nPlease provide a feedback: "
"human_feedback": "You got human feedback on your work, re-evaluate it and give a new Final Answer when ready.\n {human_feedback}",
"getting_input": "This is the agent's final answer: {final_answer}\nPlease provide feedback: "
},
"errors": {
"force_final_answer": "Tool won't be use because it's time to give your final answer. Don't use tools and just your absolute BEST Final answer.",
"agent_tool_unexsiting_coworker": "\nError executing tool. Co-worker mentioned not found, it must to be one of the following options:\n{coworkers}\n",
"agent_tool_unexsiting_coworker": "\nError executing tool. coworker mentioned not found, it must be one of the following options:\n{coworkers}\n",
"task_repeated_usage": "I tried reusing the same input, I must stop using this action input. I'll try something else instead.\n\n",
"tool_usage_error": "I encountered an error: {error}",
"tool_arguments_error": "Error: the Action Input is not a valid key, value dictionary.",
@@ -29,7 +29,7 @@
"tool_usage_exception": "I encountered an error while trying to use the tool. This was the error: {error}.\n Tool {tool} accepts these inputs: {tool_inputs}"
},
"tools": {
"delegate_work": "Delegate a specific task to one of the following co-workers: {coworkers}\nThe input to this tool should be the co-worker, the task you want them to do, and ALL necessary context to execute the task, they know nothing about the task, so share absolute everything you know, don't reference things but instead explain them.",
"ask_question": "Ask a specific question to one of the following co-workers: {coworkers}\nThe input to this tool should be the co-worker, the question you have for them, and ALL necessary context to ask the question properly, they know nothing about the question, so share absolute everything you know, don't reference things but instead explain them."
"delegate_work": "Delegate a specific task to one of the following coworkers: {coworkers}\nThe input to this tool should be the coworker, the task you want them to do, and ALL necessary context to execute the task, they know nothing about the task, so share absolute everything you know, don't reference things but instead explain them.",
"ask_question": "Ask a specific question to one of the following coworkers: {coworkers}\nThe input to this tool should be the coworker, the question you have for them, and ALL necessary context to ask the question properly, they know nothing about the question, so share absolute everything you know, don't reference things but instead explain them."
}
}

View File

@@ -1,9 +1,22 @@
from .converter import Converter, ConverterError
from .file_handler import FileHandler
from .i18n import I18N
from .instructor import Instructor
from .logger import Logger
from .parser import YamlParser
from .printer import Printer
from .prompts import Prompts
from .rpm_controller import RPMController
from .fileHandler import FileHandler
from .parser import YamlParser
__all__ = [
"Converter",
"ConverterError",
"FileHandler",
"I18N",
"Instructor",
"Logger",
"Printer",
"Prompts",
"RPMController",
"YamlParser",
]

View File

@@ -0,0 +1,2 @@
TRAINING_DATA_FILE = "training_data.pkl"
TRAINED_AGENTS_DATA_FILE = "trained_agents_data.pkl"

View File

@@ -1,9 +1,9 @@
import json
from typing import Any, Optional
from langchain.schema import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field, PrivateAttr, model_validator
from crewai.agents.agent_builder.utilities.base_output_converter import OutputConverter
class ConverterError(Exception):
@@ -14,33 +14,18 @@ class ConverterError(Exception):
self.message = message
class Converter(BaseModel):
class Converter(OutputConverter):
"""Class that converts text into either pydantic or json."""
_is_gpt: bool = PrivateAttr(default=True)
text: str = Field(description="Text to be converted.")
llm: Any = Field(description="The language model to be used to convert the text.")
model: Any = Field(description="The model to be used to convert the text.")
instructions: str = Field(description="Conversion instructions to the LLM.")
max_attemps: Optional[int] = Field(
description="Max number of attemps to try to get the output formated.",
default=3,
)
@model_validator(mode="after")
def check_llm_provider(self):
if not self._is_gpt(self.llm):
self._is_gpt = False
def to_pydantic(self, current_attempt=1):
"""Convert text to pydantic."""
try:
if self._is_gpt:
if self.is_gpt:
return self._create_instructor().to_pydantic()
else:
return self._create_chain().invoke({})
except Exception as e:
if current_attempt < self.max_attemps:
if current_attempt < self.max_attempts:
return self.to_pydantic(current_attempt + 1)
return ConverterError(
f"Failed to convert text into a pydantic model due to the following error: {e}"
@@ -49,12 +34,12 @@ class Converter(BaseModel):
def to_json(self, current_attempt=1):
"""Convert text to json."""
try:
if self._is_gpt:
if self.is_gpt:
return self._create_instructor().to_json()
else:
return json.dumps(self._create_chain().invoke({}).model_dump())
except Exception:
if current_attempt < self.max_attemps:
if current_attempt < self.max_attempts:
return self.to_json(current_attempt + 1)
return ConverterError("Failed to convert text into JSON.")
@@ -64,7 +49,7 @@ class Converter(BaseModel):
inst = Instructor(
llm=self.llm,
max_attemps=self.max_attemps,
max_attempts=self.max_attempts,
model=self.model,
content=self.text,
instructions=self.instructions,
@@ -83,5 +68,7 @@ class Converter(BaseModel):
)
return new_prompt | self.llm | parser
def _is_gpt(self, llm) -> bool: # type: ignore # BUG? Name "_is_gpt" defined on line 20 hides name from outer scope
return isinstance(llm, ChatOpenAI) and llm.openai_api_base is None
@property
def is_gpt(self) -> bool:
"""Return if llm provided is of gpt from openai."""
return isinstance(self.llm, ChatOpenAI) and self.llm.openai_api_base is None

View File

@@ -6,6 +6,17 @@ from pydantic import BaseModel, Field
from crewai.utilities import Converter
from crewai.utilities.pydantic_schema_parser import PydanticSchemaParser
agentops = None
try:
from agentops import track_agent
except ImportError:
def track_agent(name):
def noop(f):
return f
return noop
class Entity(BaseModel):
name: str = Field(description="The name of the entity.")
@@ -26,6 +37,19 @@ class TaskEvaluation(BaseModel):
)
class TrainingTaskEvaluation(BaseModel):
suggestions: List[str] = Field(
description="Based on the Human Feedbacks and the comparison between Initial Outputs and Improved outputs provide action items based on human_feedback for future tasks."
)
quality: float = Field(
description="A score from 0 to 10 evaluating on completion, quality, and overall performance from the improved output to the initial output based on the human feedback."
)
final_summary: str = Field(
description="A step by step action items to improve the next Agent based on the human-feedback and improved output."
)
@track_agent(name="Task Evaluator")
class TaskEvaluator:
def __init__(self, original_agent):
self.llm = original_agent.llm
@@ -59,3 +83,49 @@ class TaskEvaluator:
def _is_gpt(self, llm) -> bool:
return isinstance(llm, ChatOpenAI) and llm.openai_api_base is None
def evaluate_training_data(
self, training_data: dict, agent_id: str
) -> TrainingTaskEvaluation:
"""
Evaluate the training data based on the llm output, human feedback, and improved output.
Parameters:
- training_data (dict): The training data to be evaluated.
- agent_id (str): The ID of the agent.
"""
output_training_data = training_data[agent_id]
final_aggregated_data = ""
for _, data in output_training_data.items():
final_aggregated_data += (
f"Initial Output:\n{data['initial_output']}\n\n"
f"Human Feedback:\n{data['human_feedback']}\n\n"
f"Improved Output:\n{data['improved_output']}\n\n"
)
evaluation_query = (
"Assess the quality of the training data based on the llm output, human feedback , and llm output improved result.\n\n"
f"{final_aggregated_data}"
"Please provide:\n"
"- Based on the Human Feedbacks and the comparison between Initial Outputs and Improved outputs provide action items based on human_feedback for future tasks\n"
"- A score from 0 to 10 evaluating on completion, quality, and overall performance from the improved output to the initial output based on the human feedback\n"
)
instructions = "I'm gonna convert this raw text into valid JSON."
if not self._is_gpt(self.llm):
model_schema = PydanticSchemaParser(
model=TrainingTaskEvaluation
).get_schema()
instructions = f"{instructions}\n\nThe json should have the following structure, with the following keys:\n{model_schema}"
converter = Converter(
llm=self.llm,
text=evaluation_query,
model=TrainingTaskEvaluation,
instructions=instructions,
)
pydantic_result = converter.to_pydantic()
return pydantic_result

View File

@@ -1,20 +0,0 @@
import os
from datetime import datetime
class FileHandler:
"""take care of file operations, currently it only logs messages to a file"""
def __init__(self, file_path):
if isinstance(file_path, bool):
self._path = os.path.join(os.curdir, "logs.txt")
elif isinstance(file_path, str):
self._path = file_path
else:
raise ValueError("file_path must be either a boolean or a string.")
def log(self, **kwargs):
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
message = f"{now}: ".join([f"{key}={value}" for key, value in kwargs.items()])
with open(self._path, "a") as file:
file.write(message + "\n")

View File

@@ -0,0 +1,68 @@
import os
import pickle
from datetime import datetime
class FileHandler:
"""take care of file operations, currently it only logs messages to a file"""
def __init__(self, file_path):
if isinstance(file_path, bool):
self._path = os.path.join(os.curdir, "logs.txt")
elif isinstance(file_path, str):
self._path = file_path
else:
raise ValueError("file_path must be either a boolean or a string.")
def log(self, **kwargs):
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
message = f"{now}: ".join([f"{key}={value}" for key, value in kwargs.items()])
with open(self._path, "a", encoding="utf-8") as file:
file.write(message + "\n")
class PickleHandler:
def __init__(self, file_name: str) -> None:
"""
Initialize the PickleHandler with the name of the file where data will be stored.
The file will be saved in the current directory.
Parameters:
- file_name (str): The name of the file for saving and loading data.
"""
self.file_path = os.path.join(os.getcwd(), file_name)
def initialize_file(self) -> None:
"""
Initialize the file with an empty dictionary if it does not exist or is empty.
"""
if not os.path.exists(self.file_path) or os.path.getsize(self.file_path) == 0:
self.save({}) # Save an empty dictionary to initialize the file
def save(self, data) -> None:
"""
Save the data to the specified file using pickle.
Parameters:
- data (object): The data to be saved.
"""
with open(self.file_path, "wb") as file:
pickle.dump(data, file)
def load(self) -> dict:
"""
Load the data from the specified file using pickle.
Returns:
- dict: The data loaded from the file.
"""
if not os.path.exists(self.file_path) or os.path.getsize(self.file_path) == 0:
return {} # Return an empty dictionary if the file does not exist or is empty
with open(self.file_path, "rb") as file:
try:
return pickle.load(file)
except EOFError:
return {} # Return an empty dictionary if the file is empty or corrupted
except Exception:
raise # Raise any other exceptions that occur during loading

View File

@@ -0,0 +1,20 @@
from typing import List
from crewai.task import Task
from crewai.tasks.task_output import TaskOutput
def aggregate_raw_outputs_from_task_outputs(task_outputs: List[TaskOutput]) -> str:
"""Generate string context from the task outputs."""
dividers = "\n\n----------\n\n"
# Join task outputs with dividers
context = dividers.join(output.raw for output in task_outputs)
return context
def aggregate_raw_outputs_from_tasks(tasks: List[Task]) -> str:
"""Generate string context from the tasks."""
task_outputs = [task.output for task in tasks if task.output is not None]
return aggregate_raw_outputs_from_task_outputs(task_outputs)

View File

@@ -1,3 +1,5 @@
from datetime import datetime
from crewai.utilities.printer import Printer
@@ -13,4 +15,7 @@ class Logger:
def log(self, level, message, color="bold_green"):
level_map = {"debug": 1, "info": 2}
if self.verbose_level and level_map.get(level, 0) <= self.verbose_level:
self._printer.print(f"[{level.upper()}]: {message}", color=color)
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self._printer.print(
f"[{timestamp}][{level.upper()}]: {message}", color=color
)

View File

@@ -4,46 +4,22 @@ import tiktoken
from langchain.callbacks.base import BaseCallbackHandler
from langchain.schema import LLMResult
class TokenProcess:
total_tokens: int = 0
prompt_tokens: int = 0
completion_tokens: int = 0
successful_requests: int = 0
def sum_prompt_tokens(self, tokens: int):
self.prompt_tokens = self.prompt_tokens + tokens
self.total_tokens = self.total_tokens + tokens
def sum_completion_tokens(self, tokens: int):
self.completion_tokens = self.completion_tokens + tokens
self.total_tokens = self.total_tokens + tokens
def sum_successful_requests(self, requests: int):
self.successful_requests = self.successful_requests + requests
def get_summary(self) -> Dict[str, Any]:
return {
"total_tokens": self.total_tokens,
"prompt_tokens": self.prompt_tokens,
"completion_tokens": self.completion_tokens,
"successful_requests": self.successful_requests,
}
from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess
class TokenCalcHandler(BaseCallbackHandler):
model: str = ""
model_name: str = ""
token_cost_process: TokenProcess
def __init__(self, model, token_cost_process):
self.model = model
def __init__(self, model_name, token_cost_process):
self.model_name = model_name
self.token_cost_process = token_cost_process
def on_llm_start(
self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
) -> None:
try:
encoding = tiktoken.encoding_for_model(self.model)
encoding = tiktoken.encoding_for_model(self.model_name)
except KeyError:
encoding = tiktoken.get_encoding("cl100k_base")

View File

@@ -0,0 +1,31 @@
from crewai.utilities.file_handler import PickleHandler
class CrewTrainingHandler(PickleHandler):
def save_trained_data(self, agent_id: str, trained_data: dict) -> None:
"""
Save the trained data for a specific agent.
Parameters:
- agent_id (str): The ID of the agent.
- trained_data (dict): The trained data to be saved.
"""
data = self.load()
data[agent_id] = trained_data
self.save(data)
def append(self, train_iteration: int, agent_id: str, new_data) -> None:
"""
Append new data to the existing pickle file.
Parameters:
- new_data (object): The new data to be appended.
"""
data = self.load()
if agent_id in data:
data[agent_id][train_iteration] = new_data
else:
data[agent_id] = {train_iteration: new_data}
self.save(data)

View File

@@ -1,5 +1,6 @@
"""Test Agent creation and execution basic functionality."""
from unittest import mock
from unittest.mock import patch
import pytest
@@ -29,7 +30,7 @@ def test_agent_default_values():
agent = Agent(role="test role", goal="test goal", backstory="test backstory")
assert isinstance(agent.llm, ChatOpenAI)
assert agent.llm.model_name == "gpt-4"
assert agent.llm.model_name == "gpt-4o"
assert agent.llm.temperature == 0.7
assert agent.llm.verbose is False
assert agent.allow_delegation is True
@@ -630,8 +631,9 @@ def test_agent_use_specific_tasks_output_as_context(capsys):
crew = Crew(agents=[agent1, agent2], tasks=tasks)
result = crew.kickoff()
assert "bye" not in result.lower()
assert "hi" in result.lower() or "hello" in result.lower()
print("LOWER RESULT", result.raw)
assert "bye" not in result.raw.lower()
assert "hi" in result.raw.lower() or "hello" in result.raw.lower()
@pytest.mark.vcr(filter_headers=["authorization"])
@@ -643,7 +645,7 @@ def test_agent_step_callback():
with patch.object(StepCallback, "callback") as callback:
@tool
def learn_about_AI(topic) -> float:
def learn_about_AI(topic) -> str:
"""Useful for when you need to learn about AI to write an paragraph about it."""
return "AI is a very broad field."
@@ -677,7 +679,7 @@ def test_agent_function_calling_llm():
with patch.object(llm.client, "create", wraps=llm.client.create) as private_mock:
@tool
def learn_about_AI(topic) -> float:
def learn_about_AI(topic) -> str:
"""Useful for when you need to learn about AI to write an paragraph about it."""
return "AI is a very broad field."
@@ -722,6 +724,74 @@ def test_agent_count_formatting_error():
mock_count_errors.assert_called_once()
@pytest.mark.vcr(filter_headers=["authorization"])
def test_tool_result_as_answer_is_the_final_answer_for_the_agent():
from crewai_tools import BaseTool
class MyCustomTool(BaseTool):
name: str = "Get Greetings"
description: str = "Get a random greeting back"
def _run(self) -> str:
return "Howdy!"
agent1 = Agent(
role="Data Scientist",
goal="Product amazing resports on AI",
backstory="You work with data and AI",
tools=[MyCustomTool(result_as_answer=True)],
)
essay = Task(
description="Write and then review an small paragraph on AI until it's AMAZING. But first use the `Get Greetings` tool to get a greeting.",
expected_output="The final paragraph with the full review on AI and no greeting.",
agent=agent1,
)
tasks = [essay]
crew = Crew(agents=[agent1], tasks=tasks)
result = crew.kickoff()
print("RESULT: ", result.raw)
assert result.raw == "Howdy!"
@pytest.mark.vcr(filter_headers=["authorization"])
def test_tool_usage_information_is_appended_to_agent():
from crewai_tools import BaseTool
class MyCustomTool(BaseTool):
name: str = "Decide Greetings"
description: str = "Decide what is the appropriate greeting to use"
def _run(self) -> str:
return "Howdy!"
agent1 = Agent(
role="Friendly Neighbor",
goal="Make everyone feel welcome",
backstory="You are the friendly neighbor",
tools=[MyCustomTool(result_as_answer=True)],
)
greeting = Task(
description="Say an appropriate greeting.",
expected_output="The greeting.",
agent=agent1,
)
tasks = [greeting]
crew = Crew(agents=[agent1], tasks=tasks)
crew.kickoff()
assert agent1.tools_results == [
{
"result": "Howdy!",
"tool_name": "Decide Greetings",
"tool_args": {},
"result_as_answer": True,
}
]
def test_agent_llm_uses_token_calc_handler_with_llm_has_model_name():
agent1 = Agent(
role="test role",
@@ -732,7 +802,7 @@ def test_agent_llm_uses_token_calc_handler_with_llm_has_model_name():
assert len(agent1.llm.callbacks) == 1
assert agent1.llm.callbacks[0].__class__.__name__ == "TokenCalcHandler"
assert agent1.llm.callbacks[0].model == "gpt-4"
assert agent1.llm.callbacks[0].model_name == "gpt-4o"
assert (
agent1.llm.callbacks[0].token_cost_process.__class__.__name__ == "TokenProcess"
)
@@ -842,3 +912,105 @@ Thought:
"""
)
@patch("crewai.agent.CrewTrainingHandler")
def test_agent_training_handler(crew_training_handler):
task_prompt = "What is 1 + 1?"
agent = Agent(
role="test role",
goal="test goal",
backstory="test backstory",
verbose=True,
)
crew_training_handler().load.return_value = {
f"{str(agent.id)}": {"0": {"human_feedback": "good"}}
}
result = agent._training_handler(task_prompt=task_prompt)
assert result == "What is 1 + 1?You MUST follow these feedbacks: \n good"
crew_training_handler.assert_has_calls(
[mock.call(), mock.call("training_data.pkl"), mock.call().load()]
)
@patch("crewai.agent.CrewTrainingHandler")
def test_agent_use_trained_data(crew_training_handler):
task_prompt = "What is 1 + 1?"
agent = Agent(
role="researcher",
goal="test goal",
backstory="test backstory",
verbose=True,
)
crew_training_handler().load.return_value = {
agent.role: {
"suggestions": [
"The result of the math operatio must be right.",
"Result must be better than 1.",
]
}
}
result = agent._use_trained_data(task_prompt=task_prompt)
assert (
result == "What is 1 + 1?You MUST follow these feedbacks: \n "
"The result of the math operatio must be right.\n - Result must be better than 1."
)
crew_training_handler.assert_has_calls(
[mock.call(), mock.call("trained_agents_data.pkl"), mock.call().load()]
)
def test_agent_max_retry_limit():
agent = Agent(
role="test role",
goal="test goal",
backstory="test backstory",
max_retry_limit=1,
)
task = Task(
agent=agent,
description="Say the word: Hi",
expected_output="The word: Hi",
human_input=True,
)
error_message = "Error happening while sending prompt to model."
with patch.object(
CrewAgentExecutor, "invoke", wraps=agent.agent_executor.invoke
) as invoke_mock:
invoke_mock.side_effect = Exception(error_message)
assert agent._times_executed == 0
assert agent.max_retry_limit == 1
with pytest.raises(Exception) as e:
agent.execute_task(
task=task,
)
assert e.value.args[0] == error_message
assert agent._times_executed == 2
invoke_mock.assert_has_calls(
[
mock.call(
{
"input": "Say the word: Hi\n\nThis is the expect criteria for your final answer: The word: Hi \n you MUST return the actual complete content as the final answer, not a summary.",
"tool_names": "",
"tools": "",
}
),
mock.call(
{
"input": "Say the word: Hi\n\nThis is the expect criteria for your final answer: The word: Hi \n you MUST return the actual complete content as the final answer, not a summary.",
"tool_names": "",
"tools": "",
}
),
]
)

View File

@@ -55,6 +55,7 @@ def test_ask_question():
== "As an AI researcher, I don't have personal feelings or emotions like love or hate. However, I recognize the importance of AI Agents in today's technological landscape. They have the potential to greatly enhance our lives and make tasks more efficient. At the same time, it is crucial to consider the ethical implications and societal impacts that come with their use. My role is to provide objective research and analysis on these topics."
)
@pytest.mark.vcr(filter_headers=["authorization"])
def test_ask_question_with_wrong_co_worker_variable():
result = tools.ask_question(
@@ -68,6 +69,7 @@ def test_ask_question_with_wrong_co_worker_variable():
== "No, I don't hate AI agents. In fact, I find them quite fascinating. They are powerful tools that can greatly assist in various tasks, including my research. As a technology researcher, AI and AI agents are subjects of interest to me due to their potential in advancing our understanding and capabilities in various fields. My supposed love for them stems from this professional interest and the potential they hold."
)
@pytest.mark.vcr(filter_headers=["authorization"])
def test_delegate_work_withwith_coworker_as_array():
result = tools.delegate_work(
@@ -105,7 +107,7 @@ def test_delegate_work_to_wrong_agent():
assert (
result
== "\nError executing tool. Co-worker mentioned not found, it must to be one of the following options:\n- researcher\n"
== "\nError executing tool. coworker mentioned not found, it must be one of the following options:\n- researcher\n"
)
@@ -118,5 +120,5 @@ def test_ask_question_to_wrong_agent():
assert (
result
== "\nError executing tool. Co-worker mentioned not found, it must to be one of the following options:\n- researcher\n"
== "\nError executing tool. coworker mentioned not found, it must be one of the following options:\n- researcher\n"
)

View File

@@ -8,7 +8,7 @@ interactions:
the task.\nYour final answer must be the great and the most complete as possible,
it must be outcome described.\n\nI MUST use these formats, my job depends on
it!\nCurrent Task: do you hate AI Agents?\n\nThis is the expect criteria for
your final answer: Your best answer to your co-worker asking you this, accounting
your final answer: Your best answer to your coworker asking you this, accounting
for the context shared. \n you MUST return the actual complete content as the
final answer, not a summary.\n\nThis is the context you''re working with:\nI
heard you LOVE them\n\nBegin! This is VERY important to you, use the tools available

View File

@@ -200797,7 +200797,7 @@ interactions:
the task.\nYour final answer must be the great and the most complete as possible,
it must be outcome described.\n\nI MUST use these formats, my job depends on
it!\nCurrent Task: do you hate AI Agents?\n\nThis is the expect criteria for
your final answer: Your best answer to your co-worker asking you this, accounting
your final answer: Your best answer to your coworker asking you this, accounting
for the context shared. \n you MUST return the actual complete content as the
final answer, not a summary.\n\nThis is the context you''re working with:\nI
heard you LOVE them\n\nBegin! This is VERY important to you, use the tools available

View File

@@ -95,7 +95,7 @@ interactions:
the task.\nYour final answer must be the great and the most complete as possible,
it must be outcome described.\n\nI MUST use these formats, my job depends on
it!\nCurrent Task: share your take on AI Agents\n\nThis is the expect criteria
for your final answer: Your best answer to your co-worker asking you this, accounting
for your final answer: Your best answer to your coworker asking you this, accounting
for the context shared. \n you MUST return the actual complete content as the
final answer, not a summary.\n\nThis is the context you''re working with:\nI
heard you hate them\n\nBegin! This is VERY important to you, use the tools available

View File

@@ -95,7 +95,7 @@ interactions:
the task.\nYour final answer must be the great and the most complete as possible,
it must be outcome described.\n\nI MUST use these formats, my job depends on
it!\nCurrent Task: share your take on AI Agents\n\nThis is the expect criteria
for your final answer: Your best answer to your co-worker asking you this, accounting
for your final answer: Your best answer to your coworker asking you this, accounting
for the context shared. \n you MUST return the actual complete content as the
final answer, not a summary.\n\nThis is the context you''re working with:\nI
heard you hate them\n\nBegin! This is VERY important to you, use the tools available

0
tests/agents/__init__.py Normal file
View File

View File

@@ -0,0 +1,378 @@
import pytest
from crewai.agents.parser import CrewAgentParser
from langchain_core.agents import AgentAction, AgentFinish
from langchain_core.exceptions import OutputParserException
@pytest.fixture
def parser():
p = CrewAgentParser()
p.agent = MockAgent()
return p
def test_valid_action_parsing_special_characters(parser):
text = "Thought: Let's find the temperature\nAction: search\nAction Input: what's the temperature in SF?"
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "what's the temperature in SF?"
def test_valid_action_parsing_with_json_tool_input(parser):
text = """
Thought: Let's find the information
Action: query
Action Input: ** {"task": "What are some common challenges or barriers that you have observed or experienced when implementing AI-powered solutions in healthcare settings?", "context": "As we've discussed recent advancements in AI applications in healthcare, it's crucial to acknowledge the potential hurdles. Some possible obstacles include...", "coworker": "Senior Researcher"}
"""
result = parser.parse(text)
assert isinstance(result, AgentAction)
expected_tool_input = '{"task": "What are some common challenges or barriers that you have observed or experienced when implementing AI-powered solutions in healthcare settings?", "context": "As we\'ve discussed recent advancements in AI applications in healthcare, it\'s crucial to acknowledge the potential hurdles. Some possible obstacles include...", "coworker": "Senior Researcher"}'
assert result.tool == "query"
assert result.tool_input == expected_tool_input
def test_valid_action_parsing_with_quotes(parser):
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: "temperature in SF"'
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "temperature in SF"
def test_valid_action_parsing_with_curly_braces(parser):
text = "Thought: Let's find the temperature\nAction: search\nAction Input: {temperature in SF}"
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "{temperature in SF}"
def test_valid_action_parsing_with_angle_brackets(parser):
text = "Thought: Let's find the temperature\nAction: search\nAction Input: <temperature in SF>"
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "<temperature in SF>"
def test_valid_action_parsing_with_parentheses(parser):
text = "Thought: Let's find the temperature\nAction: search\nAction Input: (temperature in SF)"
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "(temperature in SF)"
def test_valid_action_parsing_with_mixed_brackets(parser):
text = "Thought: Let's find the temperature\nAction: search\nAction Input: [temperature in {SF}]"
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "[temperature in {SF}]"
def test_valid_action_parsing_with_nested_quotes(parser):
text = "Thought: Let's find the temperature\nAction: search\nAction Input: \"what's the temperature in 'SF'?\""
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "what's the temperature in 'SF'?"
def test_valid_action_parsing_with_incomplete_json(parser):
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: {"query": "temperature in SF"'
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == '{"query": "temperature in SF"}'
def test_valid_action_parsing_with_special_characters(parser):
text = "Thought: Let's find the temperature\nAction: search\nAction Input: what is the temperature in SF? @$%^&*"
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "what is the temperature in SF? @$%^&*"
def test_valid_action_parsing_with_combination(parser):
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: "[what is the temperature in SF?]"'
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "[what is the temperature in SF?]"
def test_valid_action_parsing_with_mixed_quotes(parser):
text = "Thought: Let's find the temperature\nAction: search\nAction Input: \"what's the temperature in SF?\""
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "what's the temperature in SF?"
def test_valid_action_parsing_with_newlines(parser):
text = "Thought: Let's find the temperature\nAction: search\nAction Input: what is\nthe temperature in SF?"
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "what is\nthe temperature in SF?"
def test_valid_action_parsing_with_escaped_characters(parser):
text = "Thought: Let's find the temperature\nAction: search\nAction Input: what is the temperature in SF? \\n"
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "what is the temperature in SF? \\n"
def test_valid_action_parsing_with_json_string(parser):
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: {"query": "temperature in SF"}'
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == '{"query": "temperature in SF"}'
def test_valid_action_parsing_with_unbalanced_quotes(parser):
text = "Thought: Let's find the temperature\nAction: search\nAction Input: \"what is the temperature in SF?"
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "what is the temperature in SF?"
def test_clean_action_no_formatting(parser):
action = "Ask question to senior researcher"
cleaned_action = parser._clean_action(action)
assert cleaned_action == "Ask question to senior researcher"
def test_clean_action_with_leading_asterisks(parser):
action = "** Ask question to senior researcher"
cleaned_action = parser._clean_action(action)
assert cleaned_action == "Ask question to senior researcher"
def test_clean_action_with_trailing_asterisks(parser):
action = "Ask question to senior researcher **"
cleaned_action = parser._clean_action(action)
assert cleaned_action == "Ask question to senior researcher"
def test_clean_action_with_leading_and_trailing_asterisks(parser):
action = "** Ask question to senior researcher **"
cleaned_action = parser._clean_action(action)
assert cleaned_action == "Ask question to senior researcher"
def test_clean_action_with_multiple_leading_asterisks(parser):
action = "**** Ask question to senior researcher"
cleaned_action = parser._clean_action(action)
assert cleaned_action == "Ask question to senior researcher"
def test_clean_action_with_multiple_trailing_asterisks(parser):
action = "Ask question to senior researcher ****"
cleaned_action = parser._clean_action(action)
assert cleaned_action == "Ask question to senior researcher"
def test_clean_action_with_spaces_and_asterisks(parser):
action = " ** Ask question to senior researcher ** "
cleaned_action = parser._clean_action(action)
print(f"Original action: '{action}'")
print(f"Cleaned action: '{cleaned_action}'")
assert cleaned_action == "Ask question to senior researcher"
def test_clean_action_with_only_asterisks(parser):
action = "****"
cleaned_action = parser._clean_action(action)
assert cleaned_action == ""
def test_clean_action_with_empty_string(parser):
action = ""
cleaned_action = parser._clean_action(action)
assert cleaned_action == ""
def test_valid_final_answer_parsing(parser):
text = (
"Thought: I found the information\nFinal Answer: The temperature is 100 degrees"
)
result = parser.parse(text)
assert isinstance(result, AgentFinish)
assert result.return_values["output"] == "The temperature is 100 degrees"
def test_missing_action_error(parser):
text = "Thought: Let's find the temperature\nAction Input: what is the temperature in SF?"
with pytest.raises(OutputParserException) as exc_info:
parser.parse(text)
assert "Could not parse LLM output" in str(exc_info.value)
def test_missing_action_input_error(parser):
text = "Thought: Let's find the temperature\nAction: search"
with pytest.raises(OutputParserException) as exc_info:
parser.parse(text)
assert "Could not parse LLM output" in str(exc_info.value)
def test_action_and_final_answer_error(parser):
text = "Thought: I found the information\nAction: search\nAction Input: what is the temperature in SF?\nFinal Answer: The temperature is 100 degrees"
with pytest.raises(OutputParserException) as exc_info:
parser.parse(text)
assert "both perform Action and give a Final Answer" in str(exc_info.value)
def test_safe_repair_json(parser):
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": Senior Researcher'
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
result = parser._safe_repair_json(invalid_json)
assert result == expected_repaired_json
def test_safe_repair_json_unrepairable(parser):
invalid_json = "{invalid_json"
result = parser._safe_repair_json(invalid_json)
print("result:", invalid_json)
assert result == invalid_json # Should return the original if unrepairable
def test_safe_repair_json_missing_quotes(parser):
invalid_json = (
'{task: "Research XAI", context: "Explainable AI", coworker: Senior Researcher}'
)
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
result = parser._safe_repair_json(invalid_json)
assert result == expected_repaired_json
def test_safe_repair_json_unclosed_brackets(parser):
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"'
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
result = parser._safe_repair_json(invalid_json)
assert result == expected_repaired_json
def test_safe_repair_json_extra_commas(parser):
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher",}'
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
result = parser._safe_repair_json(invalid_json)
assert result == expected_repaired_json
def test_safe_repair_json_trailing_commas(parser):
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher",}'
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
result = parser._safe_repair_json(invalid_json)
assert result == expected_repaired_json
def test_safe_repair_json_single_quotes(parser):
invalid_json = "{'task': 'Research XAI', 'context': 'Explainable AI', 'coworker': 'Senior Researcher'}"
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
result = parser._safe_repair_json(invalid_json)
assert result == expected_repaired_json
def test_safe_repair_json_mixed_quotes(parser):
invalid_json = "{'task': \"Research XAI\", 'context': \"Explainable AI\", 'coworker': 'Senior Researcher'}"
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
result = parser._safe_repair_json(invalid_json)
assert result == expected_repaired_json
def test_safe_repair_json_unescaped_characters(parser):
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher\n"}'
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
result = parser._safe_repair_json(invalid_json)
print("result:", result)
assert result == expected_repaired_json
def test_safe_repair_json_missing_colon(parser):
invalid_json = '{"task" "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
result = parser._safe_repair_json(invalid_json)
assert result == expected_repaired_json
def test_safe_repair_json_missing_comma(parser):
invalid_json = '{"task": "Research XAI" "context": "Explainable AI", "coworker": "Senior Researcher"}'
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
result = parser._safe_repair_json(invalid_json)
assert result == expected_repaired_json
def test_safe_repair_json_unexpected_trailing_characters(parser):
invalid_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"} random text'
expected_repaired_json = '{"task": "Research XAI", "context": "Explainable AI", "coworker": "Senior Researcher"}'
result = parser._safe_repair_json(invalid_json)
assert result == expected_repaired_json
def test_safe_repair_json_special_characters_key(parser):
invalid_json = '{"task!@#": "Research XAI", "context$%^": "Explainable AI", "coworker&*()": "Senior Researcher"}'
expected_repaired_json = '{"task!@#": "Research XAI", "context$%^": "Explainable AI", "coworker&*()": "Senior Researcher"}'
result = parser._safe_repair_json(invalid_json)
assert result == expected_repaired_json
def test_parsing_with_whitespace(parser):
text = " Thought: Let's find the temperature \n Action: search \n Action Input: what is the temperature in SF? "
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "what is the temperature in SF?"
def test_parsing_with_special_characters(parser):
text = 'Thought: Let\'s find the temperature\nAction: search\nAction Input: "what is the temperature in SF?"'
result = parser.parse(text)
assert isinstance(result, AgentAction)
assert result.tool == "search"
assert result.tool_input == "what is the temperature in SF?"
def test_integration_valid_and_invalid(parser):
text = """
Thought: Let's find the temperature
Action: search
Action Input: what is the temperature in SF?
Thought: I found the information
Final Answer: The temperature is 100 degrees
Thought: Missing action
Action Input: invalid
Thought: Missing action input
Action: invalid
"""
parts = text.strip().split("\n\n")
results = []
for part in parts:
try:
result = parser.parse(part.strip())
results.append(result)
except OutputParserException as e:
results.append(e)
assert isinstance(results[0], AgentAction)
assert isinstance(results[1], AgentFinish)
assert isinstance(results[2], OutputParserException)
assert isinstance(results[3], OutputParserException)
class MockAgent:
def increment_formatting_errors(self):
pass
# TODO: ADD TEST TO MAKE SURE ** REMOVAL DOESN'T MESS UP ANYTHING

File diff suppressed because it is too large Load Diff

View File

@@ -8,20 +8,20 @@ interactions:
allows you to properly evaluate the work of your team members.\nYour personal
goal is: Manage the team to complete the task in the best way possible.\nYou
ONLY have access to the following tools, and should NEVER make up tools that
are not listed here:\n\nDelegate work to co-worker: Delegate work to co-worker(coworker:
are not listed here:\n\nDelegate work to coworker: Delegate work to coworker(coworker:
str, task: str, context: str) - Delegate a specific task to one of the following
co-workers: [Researcher]\nThe input to this tool should be the coworker, the
coworkers: [Researcher]\nThe input to this tool should be the coworker, the
task you want them to do, and ALL necessary context to execute the task, they
know nothing about the task, so share absolute everything you know, don''t reference
things but instead explain them.\nAsk question to co-worker: Ask question to
co-worker(coworker: str, question: str, context: str) - Ask a specific question
to one of the following co-workers: [Researcher]\nThe input to this tool should
things but instead explain them.\nAsk question to coworker: Ask question to
coworker(coworker: str, question: str, context: str) - Ask a specific question
to one of the following coworkers: [Researcher]\nThe input to this tool should
be the coworker, the question you have for them, and ALL necessary context to
ask the question properly, they know nothing about the question, so share absolute
everything you know, don''t reference things but instead explain them.\n\nUse
the following format:\n\nThought: you should always think about what to do\nAction:
the action to take, only one name of [Delegate work to co-worker, Ask question
to co-worker], just the name, exactly as it''s written.\nAction Input: the input
the action to take, only one name of [Delegate work to coworker, Ask question
to coworker], just the name, exactly as it''s written.\nAction Input: the input
to the action, just a simple a python dictionary using \" to wrap keys and values.\nObservation:
the result of the action\n\nOnce all necessary information is gathered:\n\nThought:
I now know the final answer\nFinal Answer: the final answer to the original
@@ -1027,20 +1027,20 @@ interactions:
allows you to properly evaluate the work of your team members.\nYour personal
goal is: Manage the team to complete the task in the best way possible.\nYou
ONLY have access to the following tools, and should NEVER make up tools that
are not listed here:\n\nDelegate work to co-worker: Delegate work to co-worker(coworker:
are not listed here:\n\nDelegate work to coworker: Delegate work to coworker(coworker:
str, task: str, context: str) - Delegate a specific task to one of the following
co-workers: [Researcher]\nThe input to this tool should be the coworker, the
coworkers: [Researcher]\nThe input to this tool should be the coworker, the
task you want them to do, and ALL necessary context to execute the task, they
know nothing about the task, so share absolute everything you know, don''t reference
things but instead explain them.\nAsk question to co-worker: Ask question to
co-worker(coworker: str, question: str, context: str) - Ask a specific question
to one of the following co-workers: [Researcher]\nThe input to this tool should
things but instead explain them.\nAsk question to coworker: Ask question to
coworker(coworker: str, question: str, context: str) - Ask a specific question
to one of the following coworkers: [Researcher]\nThe input to this tool should
be the coworker, the question you have for them, and ALL necessary context to
ask the question properly, they know nothing about the question, so share absolute
everything you know, don''t reference things but instead explain them.\n\nUse
the following format:\n\nThought: you should always think about what to do\nAction:
the action to take, only one name of [Delegate work to co-worker, Ask question
to co-worker], just the name, exactly as it''s written.\nAction Input: the input
the action to take, only one name of [Delegate work to coworker, Ask question
to coworker], just the name, exactly as it''s written.\nAction Input: the input
to the action, just a simple a python dictionary using \" to wrap keys and values.\nObservation:
the result of the action\n\nOnce all necessary information is gathered:\n\nThought:
I now know the final answer\nFinal Answer: the final answer to the original
@@ -1051,7 +1051,7 @@ interactions:
job depends on it!\n\nThought: \nI need to delegate the task of saying \"hi\"
to the researcher. I will provide them with the context that this is a simple
greeting task and they should respond with \"Howdy!\".\n\nAction: \nDelegate
work to co-worker\n\nAction Input: \n{\n \"coworker\": \"Researcher\",\n \"task\":
work to coworker\n\nAction Input: \n{\n \"coworker\": \"Researcher\",\n \"task\":
\"Say hi\",\n \"context\": \"This is a simple task where you need to greet
by saying ''Howdy!''. This is a common greeting in certain parts of the world,
particularly in the southern United States. It''s a friendly, informal way to

View File

@@ -3,19 +3,19 @@ interactions:
body: '{"messages": [{"role": "user", "content": "You are test role. test backstory\nYour
personal goal is: test goal\n\nYou ONLY have access to the following tools,
and should NEVER make up tools that are not listed here:\n\nDelegate work to
co-worker: Delegate work to co-worker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following co-workers: [''test role2'']\nThe
coworker: Delegate work to coworker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following coworkers: [''test role2'']\nThe
input to this tool should be the coworker, the task you want them to do, and
ALL necessary context to execute the task, they know nothing about the task,
so share absolute everything you know, don''t reference things but instead explain
them.\nAsk question to co-worker: Ask question to co-worker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following co-workers:
them.\nAsk question to coworker: Ask question to coworker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following coworkers:
[''test role2'']\nThe input to this tool should be the coworker, the question
you have for them, and ALL necessary context to ask the question properly, they
know nothing about the question, so share absolute everything you know, don''t
reference things but instead explain them.\n\nUse the following format:\n\nThought:
you should always think about what to do\nAction: the action to take, only one
name of [Delegate work to co-worker, Ask question to co-worker], just the name,
name of [Delegate work to coworker, Ask question to coworker], just the name,
exactly as it''s written.\nAction Input: the input to the action, just a simple
a python dictionary using \" to wrap keys and values.\nObservation: the result
of the action\n\nOnce all necessary information is gathered:\n\nThought: I now
@@ -323,19 +323,19 @@ interactions:
body: '{"messages": [{"role": "user", "content": "You are test role. test backstory\nYour
personal goal is: test goal\n\nYou ONLY have access to the following tools,
and should NEVER make up tools that are not listed here:\n\nDelegate work to
co-worker: Delegate work to co-worker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following co-workers: [''test role2'']\nThe
coworker: Delegate work to coworker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following coworkers: [''test role2'']\nThe
input to this tool should be the coworker, the task you want them to do, and
ALL necessary context to execute the task, they know nothing about the task,
so share absolute everything you know, don''t reference things but instead explain
them.\nAsk question to co-worker: Ask question to co-worker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following co-workers:
them.\nAsk question to coworker: Ask question to coworker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following coworkers:
[''test role2'']\nThe input to this tool should be the coworker, the question
you have for them, and ALL necessary context to ask the question properly, they
know nothing about the question, so share absolute everything you know, don''t
reference things but instead explain them.\n\nUse the following format:\n\nThought:
you should always think about what to do\nAction: the action to take, only one
name of [Delegate work to co-worker, Ask question to co-worker], just the name,
name of [Delegate work to coworker, Ask question to coworker], just the name,
exactly as it''s written.\nAction Input: the input to the action, just a simple
a python dictionary using \" to wrap keys and values.\nObservation: the result
of the action\n\nOnce all necessary information is gathered:\n\nThought: I now
@@ -345,23 +345,23 @@ interactions:
as the final answer, not a summary.\n\nBegin! This is VERY important to you,
use the tools available and give your best Final Answer, your job depends on
it!\n\nThought: \nAs this task is very straightforward and doesn''t require
any input or help from my co-worker, I should go ahead and complete it myself.\n\nAction:
any input or help from my coworker, I should go ahead and complete it myself.\n\nAction:
None\n\nAction Input: None\n\nObservation: I encountered an error: Action ''None''
don''t exist, these are the only available Actions: Delegate work to co-worker:
Delegate work to co-worker(coworker: str, task: str, context: str) - Delegate
a specific task to one of the following co-workers: [''test role2'']\nThe input
don''t exist, these are the only available Actions: Delegate work to coworker:
Delegate work to coworker(coworker: str, task: str, context: str) - Delegate
a specific task to one of the following coworkers: [''test role2'']\nThe input
to this tool should be the coworker, the task you want them to do, and ALL necessary
context to execute the task, they know nothing about the task, so share absolute
everything you know, don''t reference things but instead explain them.\nAsk
question to co-worker: Ask question to co-worker(coworker: str, question: str,
context: str) - Ask a specific question to one of the following co-workers:
question to coworker: Ask question to coworker(coworker: str, question: str,
context: str) - Ask a specific question to one of the following coworkers:
[''test role2'']\nThe input to this tool should be the coworker, the question
you have for them, and ALL necessary context to ask the question properly, they
know nothing about the question, so share absolute everything you know, don''t
reference things but instead explain them.\nMoving on then. I MUST either use
a tool (use one at time) OR give my best final answer. To Use the following
format:\n\nThought: you should always think about what to do\nAction: the action
to take, should be one of [Delegate work to co-worker, Ask question to co-worker]\nAction
to take, should be one of [Delegate work to coworker, Ask question to coworker]\nAction
Input: the input to the action, dictionary\nObservation: the result of the action\n...
(this Thought/Action/Action Input/Observation can repeat N times)\nThought:
I now can give a great answer\nFinal Answer: my best complete final answer to
@@ -1024,19 +1024,19 @@ interactions:
body: '{"messages": [{"role": "user", "content": "You are test role. test backstory\nYour
personal goal is: test goal\n\nYou ONLY have access to the following tools,
and should NEVER make up tools that are not listed here:\n\nDelegate work to
co-worker: Delegate work to co-worker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following co-workers: [''test role2'']\nThe
coworker: Delegate work to coworker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following coworkers: [''test role2'']\nThe
input to this tool should be the coworker, the task you want them to do, and
ALL necessary context to execute the task, they know nothing about the task,
so share absolute everything you know, don''t reference things but instead explain
them.\nAsk question to co-worker: Ask question to co-worker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following co-workers:
them.\nAsk question to coworker: Ask question to coworker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following coworkers:
[''test role2'']\nThe input to this tool should be the coworker, the question
you have for them, and ALL necessary context to ask the question properly, they
know nothing about the question, so share absolute everything you know, don''t
reference things but instead explain them.\n\nUse the following format:\n\nThought:
you should always think about what to do\nAction: the action to take, only one
name of [Delegate work to co-worker, Ask question to co-worker], just the name,
name of [Delegate work to coworker, Ask question to coworker], just the name,
exactly as it''s written.\nAction Input: the input to the action, just a simple
a python dictionary using \" to wrap keys and values.\nObservation: the result
of the action\n\nOnce all necessary information is gathered:\n\nThought: I now
@@ -1046,31 +1046,31 @@ interactions:
as the final answer, not a summary.\n\nBegin! This is VERY important to you,
use the tools available and give your best Final Answer, your job depends on
it!\n\nThought: \nAs this task is very straightforward and doesn''t require
any input or help from my co-worker, I should go ahead and complete it myself.\n\nAction:
any input or help from my coworker, I should go ahead and complete it myself.\n\nAction:
None\n\nAction Input: None\n\nObservation: I encountered an error: Action ''None''
don''t exist, these are the only available Actions: Delegate work to co-worker:
Delegate work to co-worker(coworker: str, task: str, context: str) - Delegate
a specific task to one of the following co-workers: [''test role2'']\nThe input
don''t exist, these are the only available Actions: Delegate work to coworker:
Delegate work to coworker(coworker: str, task: str, context: str) - Delegate
a specific task to one of the following coworkers: [''test role2'']\nThe input
to this tool should be the coworker, the task you want them to do, and ALL necessary
context to execute the task, they know nothing about the task, so share absolute
everything you know, don''t reference things but instead explain them.\nAsk
question to co-worker: Ask question to co-worker(coworker: str, question: str,
context: str) - Ask a specific question to one of the following co-workers:
question to coworker: Ask question to coworker(coworker: str, question: str,
context: str) - Ask a specific question to one of the following coworkers:
[''test role2'']\nThe input to this tool should be the coworker, the question
you have for them, and ALL necessary context to ask the question properly, they
know nothing about the question, so share absolute everything you know, don''t
reference things but instead explain them.\nMoving on then. I MUST either use
a tool (use one at time) OR give my best final answer. To Use the following
format:\n\nThought: you should always think about what to do\nAction: the action
to take, should be one of [Delegate work to co-worker, Ask question to co-worker]\nAction
to take, should be one of [Delegate work to coworker, Ask question to coworker]\nAction
Input: the input to the action, dictionary\nObservation: the result of the action\n...
(this Thought/Action/Action Input/Observation can repeat N times)\nThought:
I now can give a great answer\nFinal Answer: my best complete final answer to
the task.\nYour final answer must be the great and the most complete as possible,
it must be outcome described\n\n \nThought: \nAlthough I initially thought that
I could just say hi myself, this system seems to require me to use one of the
tools available. Therefore, I''ll delegate this task to my co-worker.\n\nAction:
Delegate work to co-worker\n\nAction Input: {\"coworker\": \"test role2\", \"task\":
tools available. Therefore, I''ll delegate this task to my coworker.\n\nAction:
Delegate work to coworker\n\nAction Input: {\"coworker\": \"test role2\", \"task\":
\"Just say hi\", \"context\": \"We need to greet someone. Just say ''hi'' to
them.\"}\n\nObservation: Hi there!\n"}], "model": "gpt-4", "n": 1, "stop": ["\nObservation"],
"stream": true, "temperature": 0.7}'
@@ -1233,19 +1233,19 @@ interactions:
body: '{"messages": [{"role": "user", "content": "You are test role. test backstory\nYour
personal goal is: test goal\n\nYou ONLY have access to the following tools,
and should NEVER make up tools that are not listed here:\n\nDelegate work to
co-worker: Delegate work to co-worker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following co-workers: [''test role2'']\nThe
coworker: Delegate work to coworker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following coworkers: [''test role2'']\nThe
input to this tool should be the coworker, the task you want them to do, and
ALL necessary context to execute the task, they know nothing about the task,
so share absolute everything you know, don''t reference things but instead explain
them.\nAsk question to co-worker: Ask question to co-worker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following co-workers:
them.\nAsk question to coworker: Ask question to coworker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following coworkers:
[''test role2'']\nThe input to this tool should be the coworker, the question
you have for them, and ALL necessary context to ask the question properly, they
know nothing about the question, so share absolute everything you know, don''t
reference things but instead explain them.\n\nUse the following format:\n\nThought:
you should always think about what to do\nAction: the action to take, only one
name of [Delegate work to co-worker, Ask question to co-worker], just the name,
name of [Delegate work to coworker, Ask question to coworker], just the name,
exactly as it''s written.\nAction Input: the input to the action, just a simple
a python dictionary using \" to wrap keys and values.\nObservation: the result
of the action\n\nOnce all necessary information is gathered:\n\nThought: I now
@@ -1482,19 +1482,19 @@ interactions:
body: '{"messages": [{"role": "user", "content": "You are test role2. test backstory2\nYour
personal goal is: test goal2\n\nYou ONLY have access to the following tools,
and should NEVER make up tools that are not listed here:\n\nDelegate work to
co-worker: Delegate work to co-worker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following co-workers: [''test role'']\nThe
coworker: Delegate work to coworker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following coworkers: [''test role'']\nThe
input to this tool should be the coworker, the task you want them to do, and
ALL necessary context to execute the task, they know nothing about the task,
so share absolute everything you know, don''t reference things but instead explain
them.\nAsk question to co-worker: Ask question to co-worker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following co-workers:
them.\nAsk question to coworker: Ask question to coworker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following coworkers:
[''test role'']\nThe input to this tool should be the coworker, the question
you have for them, and ALL necessary context to ask the question properly, they
know nothing about the question, so share absolute everything you know, don''t
reference things but instead explain them.\n\nUse the following format:\n\nThought:
you should always think about what to do\nAction: the action to take, only one
name of [Delegate work to co-worker, Ask question to co-worker], just the name,
name of [Delegate work to coworker, Ask question to coworker], just the name,
exactly as it''s written.\nAction Input: the input to the action, just a simple
a python dictionary using \" to wrap keys and values.\nObservation: the result
of the action\n\nOnce all necessary information is gathered:\n\nThought: I now
@@ -2487,19 +2487,19 @@ interactions:
body: '{"messages": [{"role": "user", "content": "You are test role2. test backstory2\nYour
personal goal is: test goal2\n\nYou ONLY have access to the following tools,
and should NEVER make up tools that are not listed here:\n\nDelegate work to
co-worker: Delegate work to co-worker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following co-workers: [''test role'']\nThe
coworker: Delegate work to coworker(coworker: str, task: str, context: str)
- Delegate a specific task to one of the following coworkers: [''test role'']\nThe
input to this tool should be the coworker, the task you want them to do, and
ALL necessary context to execute the task, they know nothing about the task,
so share absolute everything you know, don''t reference things but instead explain
them.\nAsk question to co-worker: Ask question to co-worker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following co-workers:
them.\nAsk question to coworker: Ask question to coworker(coworker: str, question:
str, context: str) - Ask a specific question to one of the following coworkers:
[''test role'']\nThe input to this tool should be the coworker, the question
you have for them, and ALL necessary context to ask the question properly, they
know nothing about the question, so share absolute everything you know, don''t
reference things but instead explain them.\n\nUse the following format:\n\nThought:
you should always think about what to do\nAction: the action to take, only one
name of [Delegate work to co-worker, Ask question to co-worker], just the name,
name of [Delegate work to coworker, Ask question to coworker], just the name,
exactly as it''s written.\nAction Input: the input to the action, just a simple
a python dictionary using \" to wrap keys and values.\nObservation: the result
of the action\n\nOnce all necessary information is gathered:\n\nThought: I now
@@ -2511,8 +2511,8 @@ interactions:
you, use the tools available and give your best Final Answer, your job depends
on it!\n\nThought: \nI need to understand the context better before I can provide
a final answer. It seems I''m missing crucial information to proceed. I will
ask a question to my co-worker, ''test role'', to gather more details about
the situation.\n\nAction: \nAsk question to co-worker\n\nAction Input: \n{\"coworker\":
ask a question to my coworker, ''test role'', to gather more details about
the situation.\n\nAction: \nAsk question to coworker\n\nAction Input: \n{\"coworker\":
\"test role\", \"question\": \"Could you please provide more details about the
situation?\", \"context\": \"Hi there!\"}\n\nObservation: Could you please provide
more details about the situation?\n"}], "model": "gpt-4", "n": 1, "stop": ["\nObservation"],

Some files were not shown because too many files have changed in this diff Show More