From b8c8640f22a20057796de508ccf03c3cc7346899 Mon Sep 17 00:00:00 2001 From: Brandon Hancock Date: Fri, 28 Mar 2025 10:28:15 -0400 Subject: [PATCH] cleanup --- docs/concepts/flows.mdx | 93 +++++++++++++++++++++++++++++++- docs/concepts/lite-agent.mdx | 62 ++++++++++++++++++++- examples/flow_lite_agent.py | 1 - examples/lite_agent_example_2.py | 46 ++++++++++++++++ src/crewai/agents/parser.py | 24 ++++----- src/crewai/lite_agent.py | 1 + 6 files changed, 211 insertions(+), 16 deletions(-) create mode 100644 examples/lite_agent_example_2.py diff --git a/docs/concepts/flows.mdx b/docs/concepts/flows.mdx index 62d00550c..b0bb07648 100644 --- a/docs/concepts/flows.mdx +++ b/docs/concepts/flows.mdx @@ -48,8 +48,6 @@ class ExampleFlow(Flow): ], ) - #TODO: NEED TO ADD AN EXAMPLE AGENT IN HERE AS WELL. - random_city = response["choices"][0]["message"]["content"] # Store the city in our state self.state["city"] = random_city @@ -547,6 +545,97 @@ The `third_method` and `fourth_method` listen to the output of the `second_metho When you run this Flow, the output will change based on the random boolean value generated by the `start_method`. +## Adding LiteAgent to Flows + +LiteAgents can be seamlessly integrated into your flows, providing a lightweight alternative to full Crews when you need simpler, focused task execution. Here's an example of how to use a LiteAgent within a flow to perform market research: + +```python +from typing import List, cast +from crewai_tools.tools.website_search.website_search_tool import WebsiteSearchTool +from pydantic import BaseModel, Field +from crewai.flow.flow import Flow, listen, start +from crewai.lite_agent import LiteAgent + +# Define a structured output format +class MarketAnalysis(BaseModel): + key_trends: List[str] = Field(description="List of identified market trends") + market_size: str = Field(description="Estimated market size") + competitors: List[str] = Field(description="Major competitors in the space") + +# Define flow state +class MarketResearchState(BaseModel): + product: str = "" + analysis: MarketAnalysis | None = None + +class MarketResearchFlow(Flow[MarketResearchState]): + @start() + def initialize_research(self): + print(f"Starting market research for {self.state.product}") + + @listen(initialize_research) + def analyze_market(self): + # Create a LiteAgent for market research + analyst = LiteAgent( + role="Market Research Analyst", + goal=f"Analyze the market for {self.state.product}", + backstory="You are an experienced market analyst with expertise in " + "identifying market trends and opportunities.", + llm="gpt-4o", + tools=[WebsiteSearchTool()], + verbose=True, + response_format=MarketAnalysis, + ) + + # Define the research query + query = f""" + Research the market for {self.state.product}. Include: + 1. Key market trends + 2. Market size + 3. Major competitors + + Format your response according to the specified structure. + """ + + # Execute the analysis + result = analyst.kickoff(query) + self.state.analysis = cast(MarketAnalysis, result.pydantic) + return result.pydantic + + @listen(analyze_market) + def present_results(self): + analysis = self.state.analysis + if analysis is None: + print("No analysis results available") + return + + print("\nMarket Analysis Results") + print("=====================") + + print("\nKey Market Trends:") + for trend in analysis.key_trends: + print(f"- {trend}") + + print(f"\nMarket Size: {analysis.market_size}") + + print("\nMajor Competitors:") + for competitor in analysis.competitors: + print(f"- {competitor}") + +# Usage example +flow = MarketResearchFlow() +result = flow.kickoff(inputs={"product": "AI-powered chatbots"}) +``` + +This example demonstrates several key features of using LiteAgents in flows: + +1. **Structured Output**: Using Pydantic models to define the expected output format (`MarketAnalysis`) ensures type safety and structured data throughout the flow. + +2. **State Management**: The flow state (`MarketResearchState`) maintains context between steps and stores both inputs and outputs. + +3. **Tool Integration**: LiteAgents can use tools (like `WebsiteSearchTool`) to enhance their capabilities. + +If you want to learn more about LiteAgents, check out the [LiteAgent](/concepts/lite-agent) page. + ## Adding Crews to Flows Creating a flow with multiple crews in CrewAI is straightforward. diff --git a/docs/concepts/lite-agent.mdx b/docs/concepts/lite-agent.mdx index c88462638..e0da5cbc0 100644 --- a/docs/concepts/lite-agent.mdx +++ b/docs/concepts/lite-agent.mdx @@ -26,7 +26,67 @@ Think of a LiteAgent as a specialized worker that excels at individual tasks. Wh ## Creating a LiteAgent -Here's a simple example of creating and using a LiteAgent in a flow: +Here's a simple example of creating and using a standalone LiteAgent: + +```python +from typing import List, cast + +from crewai_tools.tools.website_search.website_search_tool import WebsiteSearchTool +from pydantic import BaseModel, Field + +from crewai.lite_agent import LiteAgent + + +# Define a structured output format +class MovieReview(BaseModel): + title: str = Field(description="The title of the movie") + rating: float = Field(description="Rating out of 10") + pros: List[str] = Field(description="List of positive aspects") + cons: List[str] = Field(description="List of negative aspects") + + +# Create a LiteAgent +critic = LiteAgent( + role="Movie Critic", + goal="Provide insightful movie reviews", + backstory="You are an experienced film critic known for balanced, thoughtful reviews.", + tools=[WebsiteSearchTool()], + verbose=True, + response_format=MovieReview, +) + +# Use the agent +query = """ +Review the movie 'Inception'. Include: +1. Your rating out of 10 +2. Key positive aspects +3. Areas that could be improved +""" + +result = critic.kickoff(query) + +# Access the structured output +review = cast(MovieReview, result.pydantic) +print(f"\nMovie Review: {review.title}") +print(f"Rating: {review.rating}/10") +print("\nPros:") +for pro in review.pros: + print(f"- {pro}") +print("\nCons:") +for con in review.cons: + print(f"- {con}") + +``` + +This example demonstrates the core features of a LiteAgent: +- Structured output using Pydantic models +- Tool integration with WebSearchTool +- Simple execution with `kickoff()` +- Easy access to both raw and structured results + +## Using LiteAgent in a Flow + +For more complex scenarios, you can integrate LiteAgents into a Flow. Here's an example of a market research flow: ```python from typing import List diff --git a/examples/flow_lite_agent.py b/examples/flow_lite_agent.py index 85f0a03a0..2df35d119 100644 --- a/examples/flow_lite_agent.py +++ b/examples/flow_lite_agent.py @@ -20,7 +20,6 @@ class MarketResearchState(BaseModel): analysis: MarketAnalysis | None = None -# Create a flow class class MarketResearchFlow(Flow[MarketResearchState]): @start() def initialize_research(self): diff --git a/examples/lite_agent_example_2.py b/examples/lite_agent_example_2.py new file mode 100644 index 000000000..f01589007 --- /dev/null +++ b/examples/lite_agent_example_2.py @@ -0,0 +1,46 @@ +from typing import List, cast + +from crewai_tools.tools.website_search.website_search_tool import WebsiteSearchTool +from pydantic import BaseModel, Field + +from crewai.lite_agent import LiteAgent + + +# Define a structured output format +class MovieReview(BaseModel): + title: str = Field(description="The title of the movie") + rating: float = Field(description="Rating out of 10") + pros: List[str] = Field(description="List of positive aspects") + cons: List[str] = Field(description="List of negative aspects") + + +# Create a LiteAgent +critic = LiteAgent( + role="Movie Critic", + goal="Provide insightful movie reviews", + backstory="You are an experienced film critic known for balanced, thoughtful reviews.", + tools=[WebsiteSearchTool()], + verbose=True, + response_format=MovieReview, +) + +# Use the agent +query = """ +Review the movie 'Inception'. Include: +1. Your rating out of 10 +2. Key positive aspects +3. Areas that could be improved +""" + +result = critic.kickoff(query) + +# Access the structured output +review = cast(MovieReview, result.pydantic) +print(f"\nMovie Review: {review.title}") +print(f"Rating: {review.rating}/10") +print("\nPros:") +for pro in review.pros: + print(f"- {pro}") +print("\nCons:") +for con in review.cons: + print(f"- {con}") diff --git a/src/crewai/agents/parser.py b/src/crewai/agents/parser.py index e35e01131..f558b272d 100644 --- a/src/crewai/agents/parser.py +++ b/src/crewai/agents/parser.py @@ -87,7 +87,18 @@ class CrewAgentParser: r"Action\s*\d*\s*:[\s]*(.*?)[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)" ) action_match = re.search(regex, text, re.DOTALL) - if action_match: + if includes_answer: + final_answer = text.split(FINAL_ANSWER_ACTION)[-1].strip() + # Check whether the final answer ends with triple backticks. + if final_answer.endswith("```"): + # Count occurrences of triple backticks in the final answer. + count = final_answer.count("```") + # If count is odd then it's an unmatched trailing set; remove it. + if count % 2 != 0: + final_answer = final_answer[:-3].rstrip() + return AgentFinish(thought, final_answer, text) + + elif action_match: if includes_answer: raise OutputParserException( f"{FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE}" @@ -102,17 +113,6 @@ class CrewAgentParser: return AgentAction(thought, clean_action, safe_tool_input, text) - elif includes_answer: - final_answer = text.split(FINAL_ANSWER_ACTION)[-1].strip() - # Check whether the final answer ends with triple backticks. - if final_answer.endswith("```"): - # Count occurrences of triple backticks in the final answer. - count = final_answer.count("```") - # If count is odd then it's an unmatched trailing set; remove it. - if count % 2 != 0: - final_answer = final_answer[:-3].rstrip() - return AgentFinish(thought, final_answer, text) - if not re.search(r"Action\s*\d*\s*:[\s]*(.*?)", text, re.DOTALL): raise OutputParserException( f"{MISSING_ACTION_AFTER_THOUGHT_ERROR_MESSAGE}\n{self._i18n.slice('final_answer_format')}", diff --git a/src/crewai/lite_agent.py b/src/crewai/lite_agent.py index 812219680..5535204f4 100644 --- a/src/crewai/lite_agent.py +++ b/src/crewai/lite_agent.py @@ -395,6 +395,7 @@ class LiteAgent(BaseModel): callbacks=self._callbacks, printer=self._printer, ) + # Emit LLM call completed event crewai_event_bus.emit( self,