Skip to content

Add MemoryPal Search Agent Notebook #695

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
300 changes: 300 additions & 0 deletions examples/cookbooks/Chile_Government_Services_Assistant.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "ZH_nR-SvvkDG"
},
"source": [
"# Chile Government Services Assistant - AI Chatbot"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "w8B741JgvpFj"
},
"source": [
"This notebook demonstrates how to use an AI-powered assistant to answer questions about Chilean government services and procedures, using the Firecrawl API and a friendly, step-by-step conversational approach."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "y8jiJYf4FA0m"
},
"source": [
"[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/DhivyaBharathy-web/PraisonAI/blob/main/examples/cookbooks/Chile_Government_Services_Assistant.ipynb)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "RRw8sPG89KNb"
},
"source": [
"# Install dependencies"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "rW8ltqCICV8o"
},
"outputs": [],
"source": [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The flask library is installed here, but it does not appear to be used anywhere in this notebook. Consider removing unnecessary dependencies to keep the environment lean.

!pip install firecrawl praisonaiagents google-genai python-dotenv deep-translator

"!pip install flask firecrawl praisonaiagents google-genai python-dotenv deep-translator"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "XGjyt-B_EfbM"
},
"source": [
"# Set API Keys"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "qf8B_YltDiIe"
},
"outputs": [],
"source": [
"import os\n",
"\n",
"os.environ['FIRECRAWL_API_KEY'] = \"your api key here\"\n",
"os.environ['OPENAI_API_KEY'] = \"your api key here\""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Hardcoding API keys directly in the notebook is generally discouraged, as it can lead to accidental exposure if the notebook is shared or committed to a public repository. While this is a common pattern for Colab notebooks, for better security and user experience, consider using input() or getpass to prompt the user for the key, or loading it from a .env file using python-dotenv (which is already installed).

os.environ['FIRECRAWL_API_KEY'] = input("Enter your Firecrawl API key: ")
os.environ['OPENAI_API_KEY'] = input("Enter your OpenAI API key: ")

]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ccO0vwvCEqUJ"
},
"source": [
"# Import Libraries & Translator"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "0prDQ5TpDnFu"
},
"outputs": [],
"source": [
"from firecrawl import FirecrawlApp, ScrapeOptions\n",
"from deep_translator import GoogleTranslator\n",
"import re\n",
"\n",
"def translate_to_spanish(text):\n",
" try:\n",
" return GoogleTranslator(source='auto', target='es').translate(text)\n",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Creating a new GoogleTranslator instance inside the translate_to_spanish function on every call can be inefficient, especially if this function is called frequently. It's more efficient to create the translator instance once (e.g., globally or as a class member) and reuse it.

translator_es = GoogleTranslator(source='auto', target='es')
def translate_to_spanish(text):
    try:
        return translator_es.translate(text)

" except Exception as e:\n",
" print(\"Translation to Spanish failed:\", e)\n",
" return text\n",
"\n",
"def translate_to_english(text):\n",
" try:\n",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Creating a new GoogleTranslator instance inside the translate_to_english function on every call can be inefficient, especially if this function is called frequently. It's more efficient to create the translator instance once (e.g., globally or as a class member) and reuse it.

translator_en = GoogleTranslator(source='auto', target='en')
def translate_to_english(text):
    try:
        # Remove Markdown images and None values before translation
        text = str(text).replace("None", "")
        text = re.sub(r'!\[.*?\]\\(.*?\\)', '', text)
        return translator_en.translate(text)

" # Remove Markdown images and None values before translation\n",
" text = str(text).replace(\"None\", \"\")\n",
" text = re.sub(r'!\\[.*?\\]\\(.*?\\)', '', text)\n",
" return GoogleTranslator(source='auto', target='en').translate(text)\n",
" except Exception as e:\n",
" print(\"Translation to English failed:\", e)\n",
" return text"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "WxOlCHMmEuK2"
},
"source": [
"# Firecrawl Tool Class"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "G4RyzJ5mDp0t"
},
"outputs": [],
"source": [
"class FirecrawlTool:\n",
" def __init__(self, api_key, instruction: str, template: str):\n",
" if not api_key:\n",
" raise ValueError(\"Firecrawl API key not provided.\")\n",
" self.app = FirecrawlApp(api_key=api_key)\n",
" self.instruction = instruction\n",
" self.template = template\n",
"\n",
" def search(self, search: str) -> str:\n",
" if not search or len(search) < 5:\n",
" return \"Error: Please provide a valid search query (at least 5 characters).\"\n",
" response_md = \"\"\n",
" try:\n",
" search_result = self.app.search(\n",
" query=self.instruction + search,\n",
" limit=2,\n",
" country=\"cl\",\n",
" lang=\"es\", # Always search in Spanish for best results\n",
" scrape_options=ScrapeOptions(formats=[\"markdown\", \"links\"])\n",
" )\n",
" if search_result and hasattr(search_result, 'data') and search_result.data:\n",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The check hasattr(search_result, 'data') is often redundant if search_result is expected to be an object with a data attribute or None. In Python, if search_result and search_result.data: would typically suffice, as accessing search_result.data on a None object would raise an AttributeError which is caught by the outer try-except block. While not strictly incorrect, it adds verbosity.

            if search_result and search_result.data:

" filtered_results = [\n",
" result for result in search_result.data\n",
" if str(result.get(\"url\", \"\")).startswith(\"https://www.chileatiende.gob.cl/fichas\") and not str(result.get(\"url\", \"\")).endswith(\"pdf\")\n",
" ]\n",
" if filtered_results:\n",
" for num, result in enumerate(filtered_results, start=1):\n",
" response_md += self.template.format(\n",
" result_number=num,\n",
" page_title=str(result.get(\"title\", \"\")),\n",
" page_url=str(result.get(\"url\", \"\")),\n",
" page_content=str(result.get(\"markdown\", \"\"))\n",
" )\n",
" return response_md\n",
" else:\n",
" return None\n",
" else:\n",
" return None\n",
" except Exception as e:\n",
" return f\"Error during search: {e}\""
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "MjkjTWn_ExS0"
},
"source": [
"# Firecrawl Prompt Template"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "AfivymU8Dufz"
},
"outputs": [],
"source": [
"FIRECRAWL_INSTRUCTION = \"ChileAtiende: \"\n",
"FIRECRAWL_TEMPLATE = \"\"\"\n",
"# Result {result_number}\n",
"\n",
"## Page Name:\n",
"\"{page_title}\"\n",
"\n",
"## URL:\n",
"{page_url}\n",
"\n",
"## Content:\n",
"{page_content}\n",
"\n",
"\"\"\""
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "zK8AA_DlEz9K"
},
"source": [
"# Initialize Firecrawl Tool"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "c3NKK0ZjDwKT"
},
"outputs": [],
"source": [
"firecrawl_tool = FirecrawlTool(\n",
" api_key=os.environ['FIRECRAWL_API_KEY'],\n",
" instruction=FIRECRAWL_INSTRUCTION,\n",
" template=FIRECRAWL_TEMPLATE\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "uzXYIF_gE3XV"
},
"source": [
"# Main Chat Loop"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "TXMgZQNkDx7n",
"outputId": "76303cd1-a576-483f-a22d-9857e5e6d797"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello! I am your ChileAtiende assistant, Tomás. How can I help you today?\n",
"You can ask me, for example: How to renew your ID card, How to apply for the Winter Bonus, etc.\n",
"\n",
"You: exit\n",
"Tomás: It was a pleasure to help you. Goodbye!\n"
]
}
],
"source": [
"print(\"Hello! I am your ChileAtiende assistant, Tomás. How can I help you today?\")\n",
"print(\"You can ask me, for example: How to renew your ID card, How to apply for the Winter Bonus, etc.\")\n",
"\n",
"while True:\n",
" user_input = input(\"\\nYou: \")\n",
" if user_input.lower() in [\"exit\", \"quit\"]:\n",
" print(\"Tomás: It was a pleasure to help you. Goodbye!\")\n",
" break\n",
"\n",
" # Translate English input to Spanish for Firecrawl\n",
" spanish_query = translate_to_spanish(user_input)\n",
" spanish_answer = firecrawl_tool.search(spanish_query)\n",
"\n",
" # Only translate if we got a real answer\n",
" if spanish_answer and isinstance(spanish_answer, str) and spanish_answer.strip() and \"Error\" not in spanish_answer:\n",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Checking for errors by looking for the string "Error" not in spanish_answer is brittle. If the firecrawl_tool.search method's error message changes or if valid content happens to contain the word "Error", this logic could break. A more robust approach would be for firecrawl_tool.search to raise a specific exception on failure, or return a distinct error object/enum, which can then be handled explicitly.

    if spanish_answer and isinstance(spanish_answer, str) and spanish_answer.strip() and not spanish_answer.startswith("Error:"):

" try:\n",
" english_answer = translate_to_english(spanish_answer)\n",
" print(\"\\nTomás (in English):\\n\", english_answer)\n",
" except Exception as e:\n",
" print(f\"\\nTomás: I found information, but couldn't translate it. Here it is in Spanish:\\n{spanish_answer}\\n(Translation error: {e})\")\n",
" else:\n",
" print(\"\\nTomás: Sorry, I couldn't find relevant information. Try rephrasing your question or ask about another service.\")"
]
}
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"name": "python3"
},
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Loading
Loading