From a6bd9f66f1ef727ca2a7453498a9b51ec829fd36 Mon Sep 17 00:00:00 2001 From: Madhu Date: Thu, 13 Feb 2025 03:39:49 +0530 Subject: [PATCH 1/8] NEW PROJ: AI Real Estate Agent --- .../ai_real_estate_agent/README.md | 0 .../ai_real_estate_agent.py | 214 ++++++++++++++++++ .../ai_real_estate_agent/requirements.txt | 0 3 files changed, 214 insertions(+) create mode 100644 ai_agent_tutorials/ai_real_estate_agent/README.md create mode 100644 ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py create mode 100644 ai_agent_tutorials/ai_real_estate_agent/requirements.txt diff --git a/ai_agent_tutorials/ai_real_estate_agent/README.md b/ai_agent_tutorials/ai_real_estate_agent/README.md new file mode 100644 index 0000000..e69de29 diff --git a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py new file mode 100644 index 0000000..75dca1a --- /dev/null +++ b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py @@ -0,0 +1,214 @@ +from typing import Dict, List +from pydantic import BaseModel, Field +from agno.agent import Agent +from agno.models.openai import OpenAIChat +from firecrawl import FirecrawlApp + +class PropertyData(BaseModel): + """Schema for property data extraction""" + building_name: str = Field(description="Name of the building/property", alias="Building_name") + property_type: str = Field(description="Type of property (commercial, residential, etc)", alias="Property_type") + location_address: str = Field(description="Complete address of the property") + price: str = Field(description="Price of the property", alias="Price") + description: str = Field(description="Detailed description of the property", alias="Description") + +class MarketNewsData(BaseModel): + """Schema for market news extraction""" + title: str = Field(description="Title of the article/news") + content: str = Field(description="Main content of the article") + date: str = Field(description="Publication date") + source: str = Field(description="Source of the article") + +class FirecrawlResponse(BaseModel): + """Schema for Firecrawl API response""" + success: bool + data: Dict + status: str + expiresAt: str + +class PriceFindingAgent: + """Agent responsible for finding properties and providing recommendations""" + + def __init__(self, firecrawl_api_key: str): + self.agent = Agent( + model=OpenAIChat(id="o3-mini"), + markdown=True, + description="I am a real estate expert who helps find and analyze properties based on user preferences." + ) + self.firecrawl = FirecrawlApp(api_key=firecrawl_api_key) + + def find_properties( + self, + city: str, + max_price: float, + property_category: str = "Residential", + property_type: str = "Flat" + ) -> str: + """Find and analyze properties based on user preferences""" + formatted_location = city.lower() + + # First, extract properties using Firecrawl + urls = [ + f"https://www.99acres.com/property-in-{formatted_location}-ffid/*", + f"https://housing.com/in/buy/{formatted_location}/{formatted_location}", + f"https://www.squareyards.com/sale/property-for-sale-in-{formatted_location}/*", + f"https://www.nobroker.in/*", + f"https://www.nobroker.in/property/sale/{city}/{formatted_location}", + f"https://www.magicbricks.com/*" + ] + + property_type_prompt = "Flats" if property_type == "Flat" else "Individual Houses" + + raw_response = self.firecrawl.extract( + urls=urls, + params={ + 'prompt': f"""Extract {property_category} {property_type_prompt} from {city} that cost less than {max_price} crores. + + Requirements: + - Property Category: {property_category} properties only + - Property Type: {property_type_prompt} only + - Location: {city} + - Maximum Price: {max_price} crores + - Include complete property details with exact location + """, + 'schema': PropertyData.model_json_schema() + } + ) + + # Process the properties data + properties = [] + if isinstance(raw_response, dict): + response = FirecrawlResponse(**raw_response) + properties = [response.data] + elif isinstance(raw_response, list): + responses = [FirecrawlResponse(**item) for item in raw_response] + properties = [resp.data for resp in responses] + + # Now use the agent to analyze and provide recommendations + properties_context = "\n".join([ + f"Property: {p['Building_name']}\nPrice: {p['Price']}\nLocation: {p['location_address']}\nType: {p['Property_type']}\nDescription: {p['Description']}" + for p in properties + ]) + + analysis = self.agent.run( + f"""As a real estate expert, analyze these properties and provide detailed recommendations: + + Properties Found: + {properties_context} + + Please provide: + 1. A summary of available properties + 2. Best value properties and why + 3. Location-specific advantages + 4. Price comparison with market rates + 5. Specific recommendations based on the {property_category} {property_type} requirement + 6. Any red flags or concerns to consider + 7. Negotiation tips for the best properties + + Format your response in a clear, structured way that helps the user make an informed decision. + """ + ) + + return analysis + +class MarketAnalysisAgent: + """Agent responsible for analyzing market trends and conditions""" + + def __init__(self, firecrawl_api_key: str): + self.agent = Agent( + model=OpenAIChat(id="o3-mini"), + markdown=True, + description="I am a real estate market analyst who provides insights on market trends and conditions." + ) + self.firecrawl = FirecrawlApp(api_key=firecrawl_api_key) + + def analyze_market(self, city: str) -> str: + """Analyze market conditions using news and market reports""" + # Extract market information from news and analysis sites + urls = [ + "https://www.moneycontrol.com/real-estate-property/*", + "https://economictimes.indiatimes.com/wealth/real-estate/*", + "https://housing.com/news/*", + f"https://www.99acres.com/articles/real-estate-market-{city.lower()}*" + ] + + raw_response = self.firecrawl.extract( + urls=urls, + params={ + 'prompt': f"""Extract recent real estate market information and trends for {city}. + Focus on: + - Market trends + - Price movements + - Development projects + - Infrastructure updates + - Investment potential + Only extract articles from the last 6 months. + """, + 'schema': MarketNewsData.model_json_schema() + } + ) + + # Process the market data + market_data = [] + if isinstance(raw_response, dict): + response = FirecrawlResponse(**raw_response) + market_data = [response.data] + elif isinstance(raw_response, list): + responses = [FirecrawlResponse(**item) for item in raw_response] + market_data = [resp.data for resp in responses] + + # Analyze the market data + market_context = "\n".join([ + f"Title: {article['title']}\nDate: {article['date']}\nContent: {article['content']}\nSource: {article['source']}" + for article in market_data + ]) + + analysis = self.agent.run( + f"""As a real estate market analyst, provide a comprehensive market analysis for {city}: + + Market Data: + {market_context} + + Please provide: + 1. Current market overview + 2. Price trends and predictions + 3. Development and infrastructure updates + 4. Investment opportunities and risks + 5. Regulatory changes affecting the market + 6. Future outlook + + Format your response as a detailed market report with clear sections and actionable insights. + """ + ) + + return analysis + +def main(): + """Main function to demonstrate the agents""" + firecrawl_api_key = "YOUR_FIRECRAWL_API_KEY" + + try: + # Initialize agents + property_agent = PriceFindingAgent(firecrawl_api_key) + market_agent = MarketAnalysisAgent(firecrawl_api_key) + + # Get property recommendations + print("=== Property Analysis ===") + property_analysis = property_agent.find_properties( + city="Hyderabad", + max_price=5.0, + property_category="Residential", + property_type="Flat" + ) + print(property_analysis) + + # Get market analysis + print("\n=== Market Analysis ===") + market_analysis = market_agent.analyze_market("Hyderabad") + print(market_analysis) + + except Exception as e: + print(f"An error occurred: {str(e)}") + +if __name__ == "__main__": + main() diff --git a/ai_agent_tutorials/ai_real_estate_agent/requirements.txt b/ai_agent_tutorials/ai_real_estate_agent/requirements.txt new file mode 100644 index 0000000..e69de29 From b42b879761b61ff464fdae20cd1ce496f32359ce Mon Sep 17 00:00:00 2001 From: Madhu Date: Thu, 13 Feb 2025 04:29:26 +0530 Subject: [PATCH 2/8] few additions --- .../ai_real_estate_agent.cpython-312.pyc | Bin 0 -> 10664 bytes .../ai_real_estate_agent.py | 24 ++++++++-------- .../ai_real_estate_agent/requirements.txt | 3 ++ .../test_property_agent.py | 26 ++++++++++++++++++ 4 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 ai_agent_tutorials/ai_real_estate_agent/__pycache__/ai_real_estate_agent.cpython-312.pyc create mode 100644 ai_agent_tutorials/ai_real_estate_agent/test_property_agent.py diff --git a/ai_agent_tutorials/ai_real_estate_agent/__pycache__/ai_real_estate_agent.cpython-312.pyc b/ai_agent_tutorials/ai_real_estate_agent/__pycache__/ai_real_estate_agent.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2292b9bb2897f16e18fd23e2c06e441ff0ee4fa GIT binary patch literal 10664 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 G&i?`{ECSO2 literal 0 HcmV?d00001 diff --git a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py index 75dca1a..28412b8 100644 --- a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py +++ b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py @@ -26,12 +26,12 @@ class FirecrawlResponse(BaseModel): status: str expiresAt: str -class PriceFindingAgent: +class PropertyFindingAgent: """Agent responsible for finding properties and providing recommendations""" - def __init__(self, firecrawl_api_key: str): + def __init__(self, firecrawl_api_key: str, openai_api_key: str): self.agent = Agent( - model=OpenAIChat(id="o3-mini"), + model=OpenAIChat(id="o3-mini", api_key=openai_api_key), markdown=True, description="I am a real estate expert who helps find and analyze properties based on user preferences." ) @@ -52,7 +52,6 @@ class PriceFindingAgent: f"https://www.99acres.com/property-in-{formatted_location}-ffid/*", f"https://housing.com/in/buy/{formatted_location}/{formatted_location}", f"https://www.squareyards.com/sale/property-for-sale-in-{formatted_location}/*", - f"https://www.nobroker.in/*", f"https://www.nobroker.in/property/sale/{city}/{formatted_location}", f"https://www.magicbricks.com/*" ] @@ -62,7 +61,7 @@ class PriceFindingAgent: raw_response = self.firecrawl.extract( urls=urls, params={ - 'prompt': f"""Extract {property_category} {property_type_prompt} from {city} that cost less than {max_price} crores. + 'prompt': f"""Extract at least 2-3 different {property_category} {property_type_prompt} from {city} that cost less than {max_price} crores. Requirements: - Property Category: {property_category} properties only @@ -70,11 +69,11 @@ class PriceFindingAgent: - Location: {city} - Maximum Price: {max_price} crores - Include complete property details with exact location + - IMPORTANT: Return at least 2 different property listings """, 'schema': PropertyData.model_json_schema() - } - ) - + } ) + print(raw_response) # Process the properties data properties = [] if isinstance(raw_response, dict): @@ -83,7 +82,7 @@ class PriceFindingAgent: elif isinstance(raw_response, list): responses = [FirecrawlResponse(**item) for item in raw_response] properties = [resp.data for resp in responses] - + print(properties) # Now use the agent to analyze and provide recommendations properties_context = "\n".join([ f"Property: {p['Building_name']}\nPrice: {p['Price']}\nLocation: {p['location_address']}\nType: {p['Property_type']}\nDescription: {p['Description']}" @@ -114,9 +113,9 @@ class PriceFindingAgent: class MarketAnalysisAgent: """Agent responsible for analyzing market trends and conditions""" - def __init__(self, firecrawl_api_key: str): + def __init__(self, firecrawl_api_key: str, openai_api_key: str): self.agent = Agent( - model=OpenAIChat(id="o3-mini"), + model=OpenAIChat(id="o3-mini", api_key=openai_api_key), markdown=True, description="I am a real estate market analyst who provides insights on market trends and conditions." ) @@ -186,10 +185,11 @@ class MarketAnalysisAgent: def main(): """Main function to demonstrate the agents""" firecrawl_api_key = "YOUR_FIRECRAWL_API_KEY" + openai_api_key = "OPENAI_API_KEY" try: # Initialize agents - property_agent = PriceFindingAgent(firecrawl_api_key) + property_agent = PropertyFindingAgent(firecrawl_api_key, openai_api_key) market_agent = MarketAnalysisAgent(firecrawl_api_key) # Get property recommendations diff --git a/ai_agent_tutorials/ai_real_estate_agent/requirements.txt b/ai_agent_tutorials/ai_real_estate_agent/requirements.txt index e69de29..9233a09 100644 --- a/ai_agent_tutorials/ai_real_estate_agent/requirements.txt +++ b/ai_agent_tutorials/ai_real_estate_agent/requirements.txt @@ -0,0 +1,3 @@ +agno +firecrawl +pydantic \ No newline at end of file diff --git a/ai_agent_tutorials/ai_real_estate_agent/test_property_agent.py b/ai_agent_tutorials/ai_real_estate_agent/test_property_agent.py new file mode 100644 index 0000000..fb65f78 --- /dev/null +++ b/ai_agent_tutorials/ai_real_estate_agent/test_property_agent.py @@ -0,0 +1,26 @@ +from ai_real_estate_agent import PropertyFindingAgent + +def test_property_agent(): + # Initialize the agent with your Firecrawl and OpenAI API keys + agent = PropertyFindingAgent( + firecrawl_api_key="", # Replace with your Firecrawl key + openai_api_key="" + ) + + try: + # Test property search + results = agent.find_properties( + city="Visakhapatnam", + max_price=4.0, + property_category="Residential", + property_type="Individual House" + ) + + print("\n=== Property Search Results ===") + print(results.content) + + except Exception as e: + print(f"Error during testing: {str(e)}") + +if __name__ == "__main__": + test_property_agent() \ No newline at end of file From a211286993afd6047849bcc9a511f8f00a84b414 Mon Sep 17 00:00:00 2001 From: Madhu Date: Thu, 13 Feb 2025 06:24:27 +0530 Subject: [PATCH 3/8] addition of market analyst --- .../ai_real_estate_agent.py | 121 +++++++++++++++--- .../test_property_agent.py | 26 ---- 2 files changed, 101 insertions(+), 46 deletions(-) diff --git a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py index 28412b8..6308c5e 100644 --- a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py +++ b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py @@ -12,6 +12,21 @@ class PropertyData(BaseModel): price: str = Field(description="Price of the property", alias="Price") description: str = Field(description="Detailed description of the property", alias="Description") +class PropertiesResponse(BaseModel): + """Schema for multiple properties response""" + properties: List[PropertyData] = Field(description="List of property details") + +class LocationData(BaseModel): + """Schema for location price trends""" + location: str + price_per_sqft: float + percent_increase: float + rental_yield: float + +class LocationsResponse(BaseModel): + """Schema for multiple locations response""" + locations: List[LocationData] = Field(description="List of location data points") + class MarketNewsData(BaseModel): """Schema for market news extraction""" title: str = Field(description="Title of the article/news") @@ -49,9 +64,9 @@ class PropertyFindingAgent: # First, extract properties using Firecrawl urls = [ + f"https://www.squareyards.com/sale/property-for-sale-in-{formatted_location}/*", f"https://www.99acres.com/property-in-{formatted_location}-ffid/*", f"https://housing.com/in/buy/{formatted_location}/{formatted_location}", - f"https://www.squareyards.com/sale/property-for-sale-in-{formatted_location}/*", f"https://www.nobroker.in/property/sale/{city}/{formatted_location}", f"https://www.magicbricks.com/*" ] @@ -61,7 +76,7 @@ class PropertyFindingAgent: raw_response = self.firecrawl.extract( urls=urls, params={ - 'prompt': f"""Extract at least 2-3 different {property_category} {property_type_prompt} from {city} that cost less than {max_price} crores. + 'prompt': f"""Extract at least 3-6 different {property_category} {property_type_prompt} from {city} that cost less than {max_price} crores. Requirements: - Property Category: {property_category} properties only @@ -69,38 +84,47 @@ class PropertyFindingAgent: - Location: {city} - Maximum Price: {max_price} crores - Include complete property details with exact location - - IMPORTANT: Return at least 2 different property listings + - IMPORTANT: Return data for at least 3 different properties + - Format as a list of properties with their respective details """, - 'schema': PropertyData.model_json_schema() - } ) - print(raw_response) + 'schema': PropertiesResponse.model_json_schema() + } + ) + + print("Raw Property Response:", raw_response) + # Process the properties data - properties = [] - if isinstance(raw_response, dict): - response = FirecrawlResponse(**raw_response) - properties = [response.data] - elif isinstance(raw_response, list): - responses = [FirecrawlResponse(**item) for item in raw_response] - properties = [resp.data for resp in responses] - print(properties) + if isinstance(raw_response, dict) and raw_response.get('success'): + properties = raw_response['data'].get('properties', []) + else: + properties = [] + + print("Processed Properties:", properties) + # Now use the agent to analyze and provide recommendations properties_context = "\n".join([ f"Property: {p['Building_name']}\nPrice: {p['Price']}\nLocation: {p['location_address']}\nType: {p['Property_type']}\nDescription: {p['Description']}" for p in properties ]) + # Get location price trends + price_trends = self.get_location_trends(city) + analysis = self.agent.run( - f"""As a real estate expert, analyze these properties and provide detailed recommendations: + f"""As a real estate expert, analyze these properties and market trends: Properties Found: {properties_context} + Location Price Trends: + {price_trends} + Please provide: 1. A summary of available properties - 2. Best value properties and why - 3. Location-specific advantages - 4. Price comparison with market rates - 5. Specific recommendations based on the {property_category} {property_type} requirement + 2. Best value properties based on current market rates + 3. Location-specific advantages and price trends + 4. Specific recommendations based on the {property_category} {property_type} requirement + 5. Investment potential based on price trends 6. Any red flags or concerns to consider 7. Negotiation tips for the best properties @@ -110,6 +134,63 @@ class PropertyFindingAgent: return analysis + def get_location_trends(self, city: str) -> str: + """Get price trends for different localities in the city""" + raw_response = self.firecrawl.extract([ + f"https://www.99acres.com/property-rates-and-price-trends-in-{city.lower()}-prffid/*" + ], { + 'prompt': """Extract price trends data for ALL major localities in the city. + IMPORTANT: + - Return data for at least 5-10 different localities + - Include both premium and affordable areas + - Do not skip any locality mentioned in the source + - Format as a list of locations with their respective data + """, + 'schema': LocationsResponse.model_json_schema(), + }) + + if isinstance(raw_response, dict) and raw_response.get('success'): + locations = raw_response['data'].get('locations', []) + + + # Use agent to analyze the trends + analysis = self.agent.run( + f"""As a real estate expert, analyze these location price trends for {city}: + + {locations} + + Please provide: + 1. A bullet-point summary of the price trends for each location + 2. Identify the top 3 locations with: + - Highest price appreciation + - Best rental yields + - Best value for money + 3. Investment recommendations: + - Best locations for long-term investment + - Best locations for rental income + - Areas showing emerging potential + 4. Specific advice for investors based on these trends + + Format the response as follows: + + 📊 LOCATION TRENDS SUMMARY + • [Bullet points for each location] + + 🏆 TOP PERFORMING AREAS + • [Bullet points for best areas] + + 💡 INVESTMENT INSIGHTS + • [Bullet points with investment advice] + + 🎯 RECOMMENDATIONS + • [Bullet points with specific recommendations] + """ + ) + + return analysis.content + + return "No price trends data available" + class MarketAnalysisAgent: """Agent responsible for analyzing market trends and conditions""" @@ -126,7 +207,7 @@ class MarketAnalysisAgent: # Extract market information from news and analysis sites urls = [ "https://www.moneycontrol.com/real-estate-property/*", - "https://economictimes.indiatimes.com/wealth/real-estate/*", + f"https://www.99acres.com/property-rates-and-price-trends-in-{city.lower()}/*", "https://housing.com/news/*", f"https://www.99acres.com/articles/real-estate-market-{city.lower()}*" ] diff --git a/ai_agent_tutorials/ai_real_estate_agent/test_property_agent.py b/ai_agent_tutorials/ai_real_estate_agent/test_property_agent.py index fb65f78..e69de29 100644 --- a/ai_agent_tutorials/ai_real_estate_agent/test_property_agent.py +++ b/ai_agent_tutorials/ai_real_estate_agent/test_property_agent.py @@ -1,26 +0,0 @@ -from ai_real_estate_agent import PropertyFindingAgent - -def test_property_agent(): - # Initialize the agent with your Firecrawl and OpenAI API keys - agent = PropertyFindingAgent( - firecrawl_api_key="", # Replace with your Firecrawl key - openai_api_key="" - ) - - try: - # Test property search - results = agent.find_properties( - city="Visakhapatnam", - max_price=4.0, - property_category="Residential", - property_type="Individual House" - ) - - print("\n=== Property Search Results ===") - print(results.content) - - except Exception as e: - print(f"Error during testing: {str(e)}") - -if __name__ == "__main__": - test_property_agent() \ No newline at end of file From 5b7b75fa6e5e1ad4be71e4c97eb324bbd31daefd Mon Sep 17 00:00:00 2001 From: Madhu Date: Thu, 13 Feb 2025 06:53:05 +0530 Subject: [PATCH 4/8] added readme requirements.txt complete file --- .../ai_real_estate_agent/README.md | 61 +++++ .../ai_real_estate_agent.cpython-312.pyc | Bin 10664 -> 0 bytes .../ai_real_estate_agent.py | 215 +++++++++--------- .../ai_real_estate_agent/requirements.txt | 5 +- .../test_property_agent.py | 0 5 files changed, 170 insertions(+), 111 deletions(-) delete mode 100644 ai_agent_tutorials/ai_real_estate_agent/__pycache__/ai_real_estate_agent.cpython-312.pyc delete mode 100644 ai_agent_tutorials/ai_real_estate_agent/test_property_agent.py diff --git a/ai_agent_tutorials/ai_real_estate_agent/README.md b/ai_agent_tutorials/ai_real_estate_agent/README.md index e69de29..c9e9f44 100644 --- a/ai_agent_tutorials/ai_real_estate_agent/README.md +++ b/ai_agent_tutorials/ai_real_estate_agent/README.md @@ -0,0 +1,61 @@ +## 🏠 AI Real Estate Agent - Powered by Firecrawl's Extract Endpoint + +The AI Real Estate Agent automates property search and market analysis using Firecrawl's Extract endpoint and Agno AI Agent's insights. It helps users find properties matching their criteria while providing detailed location trends and investment recommendations. This agent streamlines the property search process by combining data from multiple real estate websites and offering intelligent analysis. + +### Features +- **Smart Property Search**: Uses Firecrawl's Extract endpoint to find properties across multiple real estate websites +- **Multi-Source Integration**: Aggregates data from 99acres, Housing.com, Square Yards, Nobroker, and MagicBricks +- **Location Analysis**: Provides detailed price trends and investment insights for different localities +- **AI-Powered Recommendations**: Uses GPT models to analyze properties and provide structured recommendations +- **User-Friendly Interface**: Clean Streamlit UI for easy property search and results viewing +- **Customizable Search**: Filter by city, property type, category, and budget + +### How to Get Started +1. **Clone the repository**: + ```bash + git clone https://github.com/Shubhamsaboo/awesome-llm-apps.git + cd ai_agent_tutorials/ai_real_estate_agent + ``` + +2. **Install the required packages**: + ```bash + pip install -r requirements.txt + ``` + +3. **Set up your API keys**: + - Get your Firecrawl API key from [Firecrawl's website](https://www.firecrawl.dev/app/api-keys) + - Get your OpenAI API key from [OpenAI's website](https://platform.openai.com/api-keys) + +4. **Run the application**: + ```bash + streamlit run ai_real_estate_agent.py + ``` + +### Using the Agent +1. **Enter API Keys**: + - Input your Firecrawl and OpenAI API keys in the sidebar + - Keys are securely stored in the session state + +2. **Set Search Criteria**: + - Enter the city name + - Select property category (Residential/Commercial) + - Choose property type (Flat/Individual House) + - Set maximum budget in Crores + +3. **View Results**: + - Property recommendations with detailed analysis + - Location trends with investment insights + - Expandable sections for easy reading + +### Features in Detail +- **Property Finding**: + - Searches across multiple real estate websites + - Returns 3-6 properties matching criteria + - Provides detailed property information and analysis + +- **Location Analysis**: + - Price trends for different localities + - Rental yield analysis + - Investment potential assessment + - Top performing areas identification + diff --git a/ai_agent_tutorials/ai_real_estate_agent/__pycache__/ai_real_estate_agent.cpython-312.pyc b/ai_agent_tutorials/ai_real_estate_agent/__pycache__/ai_real_estate_agent.cpython-312.pyc deleted file mode 100644 index a2292b9bb2897f16e18fd23e2c06e441ff0ee4fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10664 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 G&i?`{ECSO2 diff --git a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py index 6308c5e..a8c9b30 100644 --- a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py +++ b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py @@ -3,6 +3,7 @@ from pydantic import BaseModel, Field from agno.agent import Agent from agno.models.openai import OpenAIChat from firecrawl import FirecrawlApp +import streamlit as st class PropertyData(BaseModel): """Schema for property data extraction""" @@ -27,13 +28,6 @@ class LocationsResponse(BaseModel): """Schema for multiple locations response""" locations: List[LocationData] = Field(description="List of location data points") -class MarketNewsData(BaseModel): - """Schema for market news extraction""" - title: str = Field(description="Title of the article/news") - content: str = Field(description="Main content of the article") - date: str = Field(description="Publication date") - source: str = Field(description="Source of the article") - class FirecrawlResponse(BaseModel): """Schema for Firecrawl API response""" success: bool @@ -62,13 +56,11 @@ class PropertyFindingAgent: """Find and analyze properties based on user preferences""" formatted_location = city.lower() - # First, extract properties using Firecrawl urls = [ f"https://www.squareyards.com/sale/property-for-sale-in-{formatted_location}/*", f"https://www.99acres.com/property-in-{formatted_location}-ffid/*", f"https://housing.com/in/buy/{formatted_location}/{formatted_location}", f"https://www.nobroker.in/property/sale/{city}/{formatted_location}", - f"https://www.magicbricks.com/*" ] property_type_prompt = "Flats" if property_type == "Flat" else "Individual Houses" @@ -76,7 +68,7 @@ class PropertyFindingAgent: raw_response = self.firecrawl.extract( urls=urls, params={ - 'prompt': f"""Extract at least 3-6 different {property_category} {property_type_prompt} from {city} that cost less than {max_price} crores. + 'prompt': f"""Extract at least 3-10 different {property_category} {property_type_prompt} from {city} that cost less than {max_price} crores. Requirements: - Property Category: {property_category} properties only @@ -84,7 +76,7 @@ class PropertyFindingAgent: - Location: {city} - Maximum Price: {max_price} crores - Include complete property details with exact location - - IMPORTANT: Return data for at least 3 different properties + - IMPORTANT: Return data for at least 3 different properties. MAXIMUM 10. - Format as a list of properties with their respective details """, 'schema': PropertiesResponse.model_json_schema() @@ -93,7 +85,6 @@ class PropertyFindingAgent: print("Raw Property Response:", raw_response) - # Process the properties data if isinstance(raw_response, dict) and raw_response.get('success'): properties = raw_response['data'].get('properties', []) else: @@ -101,13 +92,11 @@ class PropertyFindingAgent: print("Processed Properties:", properties) - # Now use the agent to analyze and provide recommendations properties_context = "\n".join([ f"Property: {p['Building_name']}\nPrice: {p['Price']}\nLocation: {p['location_address']}\nType: {p['Property_type']}\nDescription: {p['Description']}" for p in properties ]) - # Get location price trends price_trends = self.get_location_trends(city) analysis = self.agent.run( @@ -132,7 +121,7 @@ class PropertyFindingAgent: """ ) - return analysis + return analysis.content def get_location_trends(self, city: str) -> str: """Get price trends for different localities in the city""" @@ -151,9 +140,7 @@ class PropertyFindingAgent: if isinstance(raw_response, dict) and raw_response.get('success'): locations = raw_response['data'].get('locations', []) - - # Use agent to analyze the trends analysis = self.agent.run( f"""As a real estate expert, analyze these location price trends for {city}: @@ -191,105 +178,115 @@ class PropertyFindingAgent: return "No price trends data available" -class MarketAnalysisAgent: - """Agent responsible for analyzing market trends and conditions""" - - def __init__(self, firecrawl_api_key: str, openai_api_key: str): - self.agent = Agent( - model=OpenAIChat(id="o3-mini", api_key=openai_api_key), - markdown=True, - description="I am a real estate market analyst who provides insights on market trends and conditions." +def create_property_agent(): + """Create PropertyFindingAgent with API keys from session state""" + if 'property_agent' not in st.session_state: + st.session_state.property_agent = PropertyFindingAgent( + firecrawl_api_key=st.session_state.firecrawl_key, + openai_api_key=st.session_state.openai_key ) - self.firecrawl = FirecrawlApp(api_key=firecrawl_api_key) - - def analyze_market(self, city: str) -> str: - """Analyze market conditions using news and market reports""" - # Extract market information from news and analysis sites - urls = [ - "https://www.moneycontrol.com/real-estate-property/*", - f"https://www.99acres.com/property-rates-and-price-trends-in-{city.lower()}/*", - "https://housing.com/news/*", - f"https://www.99acres.com/articles/real-estate-market-{city.lower()}*" - ] - - raw_response = self.firecrawl.extract( - urls=urls, - params={ - 'prompt': f"""Extract recent real estate market information and trends for {city}. - Focus on: - - Market trends - - Price movements - - Development projects - - Infrastructure updates - - Investment potential - Only extract articles from the last 6 months. - """, - 'schema': MarketNewsData.model_json_schema() - } - ) - - # Process the market data - market_data = [] - if isinstance(raw_response, dict): - response = FirecrawlResponse(**raw_response) - market_data = [response.data] - elif isinstance(raw_response, list): - responses = [FirecrawlResponse(**item) for item in raw_response] - market_data = [resp.data for resp in responses] - - # Analyze the market data - market_context = "\n".join([ - f"Title: {article['title']}\nDate: {article['date']}\nContent: {article['content']}\nSource: {article['source']}" - for article in market_data - ]) - - analysis = self.agent.run( - f"""As a real estate market analyst, provide a comprehensive market analysis for {city}: - - Market Data: - {market_context} - - Please provide: - 1. Current market overview - 2. Price trends and predictions - 3. Development and infrastructure updates - 4. Investment opportunities and risks - 5. Regulatory changes affecting the market - 6. Future outlook - - Format your response as a detailed market report with clear sections and actionable insights. - """ - ) - - return analysis def main(): - """Main function to demonstrate the agents""" - firecrawl_api_key = "YOUR_FIRECRAWL_API_KEY" - openai_api_key = "OPENAI_API_KEY" - - try: - # Initialize agents - property_agent = PropertyFindingAgent(firecrawl_api_key, openai_api_key) - market_agent = MarketAnalysisAgent(firecrawl_api_key) + st.set_page_config( + page_title="AI Real Estate Agent", + page_icon="🏠", + layout="wide" + ) + + with st.sidebar: + st.title("🔑 API Configuration") - # Get property recommendations - print("=== Property Analysis ===") - property_analysis = property_agent.find_properties( - city="Hyderabad", - max_price=5.0, - property_category="Residential", - property_type="Flat" + firecrawl_key = st.text_input( + "Firecrawl API Key", + type="password", + help="Enter your Firecrawl API key" + ) + openai_key = st.text_input( + "OpenAI API Key", + type="password", + help="Enter your OpenAI API key" ) - print(property_analysis) - # Get market analysis - print("\n=== Market Analysis ===") - market_analysis = market_agent.analyze_market("Hyderabad") - print(market_analysis) + if firecrawl_key and openai_key: + st.session_state.firecrawl_key = firecrawl_key + st.session_state.openai_key = openai_key + create_property_agent() + + st.title("🏠 AI Real Estate Agent") + st.info( + """ + Welcome to the AI Real Estate Agent! + Enter your search criteria below to get property recommendations + and location insights. + """ + ) + + col1, col2 = st.columns(2) + + with col1: + city = st.text_input( + "City", + placeholder="Enter city name (e.g., Bangalore)", + help="Enter the city where you want to search for properties" + ) + + property_category = st.selectbox( + "Property Category", + options=["Residential", "Commercial"], + help="Select the type of property you're interested in" + ) + + with col2: + max_price = st.number_input( + "Maximum Price (in Crores)", + min_value=0.1, + max_value=100.0, + value=5.0, + step=0.1, + help="Enter your maximum budget in Crores" + ) + + property_type = st.selectbox( + "Property Type", + options=["Flat", "Individual House"], + help="Select the specific type of property" + ) + + if st.button("🔍 Start Search", use_container_width=True): + if 'property_agent' not in st.session_state: + st.error("⚠️ Please enter your API keys in the sidebar first!") + return - except Exception as e: - print(f"An error occurred: {str(e)}") + if not city: + st.error("⚠️ Please enter a city name!") + return + + try: + with st.spinner("🔍 Searching for properties..."): + property_results = st.session_state.property_agent.find_properties( + city=city, + max_price=max_price, + property_category=property_category, + property_type=property_type + ) + + st.success("✅ Property search completed!") + + st.subheader("🏘️ Property Recommendations") + st.markdown(property_results) + + st.divider() + + with st.spinner("📊 Analyzing location trends..."): + location_trends = st.session_state.property_agent.get_location_trends(city) + + st.success("✅ Location analysis completed!") + + with st.expander("📈 Location Trends Analysis"): + st.markdown(location_trends) + + except Exception as e: + st.error(f"❌ An error occurred: {str(e)}") if __name__ == "__main__": main() diff --git a/ai_agent_tutorials/ai_real_estate_agent/requirements.txt b/ai_agent_tutorials/ai_real_estate_agent/requirements.txt index 9233a09..8414d6e 100644 --- a/ai_agent_tutorials/ai_real_estate_agent/requirements.txt +++ b/ai_agent_tutorials/ai_real_estate_agent/requirements.txt @@ -1,3 +1,4 @@ agno -firecrawl -pydantic \ No newline at end of file +firecrawl-py==1.9.0 +pydantic +streamlit diff --git a/ai_agent_tutorials/ai_real_estate_agent/test_property_agent.py b/ai_agent_tutorials/ai_real_estate_agent/test_property_agent.py deleted file mode 100644 index e69de29..0000000 From 5fb0f11a124eef256a65e7f43f8799fc6b71efdf Mon Sep 17 00:00:00 2001 From: Madhu Date: Thu, 13 Feb 2025 06:56:41 +0530 Subject: [PATCH 5/8] gpt 4o support --- .../ai_real_estate_agent.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py index a8c9b30..d3f2db1 100644 --- a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py +++ b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py @@ -38,9 +38,9 @@ class FirecrawlResponse(BaseModel): class PropertyFindingAgent: """Agent responsible for finding properties and providing recommendations""" - def __init__(self, firecrawl_api_key: str, openai_api_key: str): + def __init__(self, firecrawl_api_key: str, openai_api_key: str, model_id: str = "o3-mini"): self.agent = Agent( - model=OpenAIChat(id="o3-mini", api_key=openai_api_key), + model=OpenAIChat(id=model_id, api_key=openai_api_key), markdown=True, description="I am a real estate expert who helps find and analyze properties based on user preferences." ) @@ -183,7 +183,8 @@ def create_property_agent(): if 'property_agent' not in st.session_state: st.session_state.property_agent = PropertyFindingAgent( firecrawl_api_key=st.session_state.firecrawl_key, - openai_api_key=st.session_state.openai_key + openai_api_key=st.session_state.openai_key, + model_id=st.session_state.model_id ) def main(): @@ -196,6 +197,17 @@ def main(): with st.sidebar: st.title("🔑 API Configuration") + st.subheader("🤖 Model Selection") + model_id = st.selectbox( + "Choose OpenAI Model", + options=["o3-mini", "gpt-4o"], + help="Select the AI model to use. Choose gpt-4o if your api doesn't have access to o3-mini" + ) + st.session_state.model_id = model_id + + st.divider() + + st.subheader("🔐 API Keys") firecrawl_key = st.text_input( "Firecrawl API Key", type="password", From c6452341657e567cd315c1b4bf14958c0892a8c4 Mon Sep 17 00:00:00 2001 From: Madhu Date: Thu, 13 Feb 2025 22:11:05 +0530 Subject: [PATCH 6/8] few changes --- .../ai_real_estate_agent.py | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py index d3f2db1..818b2e6 100644 --- a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py +++ b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py @@ -60,7 +60,7 @@ class PropertyFindingAgent: f"https://www.squareyards.com/sale/property-for-sale-in-{formatted_location}/*", f"https://www.99acres.com/property-in-{formatted_location}-ffid/*", f"https://housing.com/in/buy/{formatted_location}/{formatted_location}", - f"https://www.nobroker.in/property/sale/{city}/{formatted_location}", + # f"https://www.nobroker.in/property/sale/{city}/{formatted_location}", ] property_type_prompt = "Flats" if property_type == "Flat" else "Individual Houses" @@ -92,24 +92,15 @@ class PropertyFindingAgent: print("Processed Properties:", properties) - properties_context = "\n".join([ - f"Property: {p['Building_name']}\nPrice: {p['Price']}\nLocation: {p['location_address']}\nType: {p['Property_type']}\nDescription: {p['Description']}" - for p in properties - ]) - - price_trends = self.get_location_trends(city) analysis = self.agent.run( f"""As a real estate expert, analyze these properties and market trends: - Properties Found: - {properties_context} - - Location Price Trends: - {price_trends} + Properties Found in json format: + {properties} Please provide: - 1. A summary of available properties + 1. Bullet Point Information for each property that are available based on the user's requirements. 2. Best value properties based on current market rates 3. Location-specific advantages and price trends 4. Specific recommendations based on the {property_category} {property_type} requirement @@ -285,6 +276,8 @@ def main(): st.success("✅ Property search completed!") st.subheader("🏘️ Property Recommendations") + with st.expander("🏘️ Property Recommendations"): + st.write(property) st.markdown(property_results) st.divider() @@ -294,7 +287,7 @@ def main(): st.success("✅ Location analysis completed!") - with st.expander("📈 Location Trends Analysis"): + with st.expander("📈 Location Trends Analysis of the city"): st.markdown(location_trends) except Exception as e: From a4dfffc9a4d2889ab398659e8a4c7f9b2c309ede Mon Sep 17 00:00:00 2001 From: Madhu Date: Thu, 13 Feb 2025 22:37:08 +0530 Subject: [PATCH 7/8] NEW PROJ: AI Real Estate Agent --- .../ai_real_estate_agent.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py index 818b2e6..5d708ea 100644 --- a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py +++ b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py @@ -68,7 +68,7 @@ class PropertyFindingAgent: raw_response = self.firecrawl.extract( urls=urls, params={ - 'prompt': f"""Extract at least 3-10 different {property_category} {property_type_prompt} from {city} that cost less than {max_price} crores. + 'prompt': f"""Extract ONLY 10 OR LESS different {property_category} {property_type_prompt} from {city} that cost less than {max_price} crores. Requirements: - Property Category: {property_category} properties only @@ -99,13 +99,16 @@ class PropertyFindingAgent: Properties Found in json format: {properties} + **IMPORTANT: IN THESE VARIOUS PROPERTIES, RETRIEVE THE BEST 5-10 PROPERTIES ONLY BASED ON PRICE OF PROPERTY. IT SHOULD BE AS CLOSE AS POSSIBLE TO THE {max_price} CRORES.** + Please provide: - 1. Bullet Point Information for each property that are available based on the user's requirements. - 2. Best value properties based on current market rates - 3. Location-specific advantages and price trends - 4. Specific recommendations based on the {property_category} {property_type} requirement - 5. Investment potential based on price trends - 6. Any red flags or concerns to consider + 1 FIRST RETRIEVE THE BEST 5-10 PROPERTIES ONLY BASED ON PRICE OF PROPERTY. IT SHOULD BE AS CLOSE AS POSSIBLE TO THE {max_price} CRORES. + 2. Bullet Point Information for each of retrieved propertiesthat are available based on the user's requirements. + 3. Best value properties based on current market rates + 4. Location-specific advantages and price trends + 5. Specific recommendations based on the {property_category} {property_type} requirement + 6. Investment potential based on price trends + 7. Any red flags or concerns to consider 7. Negotiation tips for the best properties Format your response in a clear, structured way that helps the user make an informed decision. From c9a885e14614e29962e0bf528f25978adf0b4487 Mon Sep 17 00:00:00 2001 From: Madhu Date: Thu, 13 Feb 2025 22:56:57 +0530 Subject: [PATCH 8/8] changed the system prompt --- .../ai_real_estate_agent.py | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py index 5d708ea..f9b65d2 100644 --- a/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py +++ b/ai_agent_tutorials/ai_real_estate_agent/ai_real_estate_agent.py @@ -99,19 +99,42 @@ class PropertyFindingAgent: Properties Found in json format: {properties} - **IMPORTANT: IN THESE VARIOUS PROPERTIES, RETRIEVE THE BEST 5-10 PROPERTIES ONLY BASED ON PRICE OF PROPERTY. IT SHOULD BE AS CLOSE AS POSSIBLE TO THE {max_price} CRORES.** + **IMPORTANT INSTRUCTIONS:** + 1. ONLY analyze properties from the above JSON data that match the user's requirements: + - Property Category: {property_category} + - Property Type: {property_type} + - Maximum Price: {max_price} crores + 2. DO NOT create new categories or property types + 3. From the matching properties, select 5-6 properties with prices closest to {max_price} crores - Please provide: - 1 FIRST RETRIEVE THE BEST 5-10 PROPERTIES ONLY BASED ON PRICE OF PROPERTY. IT SHOULD BE AS CLOSE AS POSSIBLE TO THE {max_price} CRORES. - 2. Bullet Point Information for each of retrieved propertiesthat are available based on the user's requirements. - 3. Best value properties based on current market rates - 4. Location-specific advantages and price trends - 5. Specific recommendations based on the {property_category} {property_type} requirement - 6. Investment potential based on price trends - 7. Any red flags or concerns to consider - 7. Negotiation tips for the best properties + Please provide your analysis in this format: + + 🏠 SELECTED PROPERTIES + • List only 5-6 best matching properties with prices closest to {max_price} crores + • For each property include: + - Name and Location + - Price (with value analysis) + - Key Features + - Pros and Cons - Format your response in a clear, structured way that helps the user make an informed decision. + 💰 BEST VALUE ANALYSIS + • Compare the selected properties based on: + - Price per sq ft + - Location advantage + - Amenities offered + + 📍 LOCATION INSIGHTS + • Specific advantages of the areas where selected properties are located + + 💡 RECOMMENDATIONS + • Top 3 properties from the selection with reasoning + • Investment potential + • Points to consider before purchase + + 🤝 NEGOTIATION TIPS + • Property-specific negotiation strategies + + Format your response in a clear, structured way using the above sections. """ ) @@ -279,8 +302,6 @@ def main(): st.success("✅ Property search completed!") st.subheader("🏘️ Property Recommendations") - with st.expander("🏘️ Property Recommendations"): - st.write(property) st.markdown(property_results) st.divider()