2-notebooks/2-agent_service/4-bing_grounding.ipynb (333 lines of code) (raw):

{ "cells": [ { "cell_type": "markdown", "id": "intro-bing-grounding", "metadata": {}, "source": [ "# šŸ Health & Fitness Agent with Bing Grounding šŸŽ\n", "\n", "Welcome to our **Health & Fitness Agent with Bing Grounding** tutorial! In this notebook, we'll demonstrate how to:\n", "\n", "1. **Initialize** a project using Azure AI Foundry.\n", "2. **Create an Agent** with the **BingGroundingTool** for web search.\n", "3. **Ask real-world questions** about health and fitness.\n", "4. **Retrieve and display** answers, including Bing query URLs and disclaimers.\n", "\n", "### āš ļø Important Model Support Note āš ļø\n", "> Bing grounding is currently only supported in certain Azure OpenAI models (e.g. `gpt-4o-0513`).\n", "> \n", "> Make sure you specify a supported model and set the `\"x-ms-enable-preview\": \"true\"` header.\n", "\n", "## Prerequisites\n", "- Complete Agent basics notebook - [1-basics.ipynb](1-basics.ipynb)\n", "- Grounding with Bing connection in Azure AI Foundry, which has to be provisioned from Azure portal.\n", "See [\"Setup Bing Grounding\"](https://learn.microsoft.com/en-us/azure/ai-services/agents/how-to/tools/bing-grounding?tabs=python&pivots=overview#setup) in the documentation for full details.\n", "\n", "<img src=\"./seq-diagrams/bing-api-key.png\" width=\"30%\"/>\n", "\n", "- A `.env` file in the parent directory containing:\n", " ```bash\n", " PROJECT_CONNECTION_STRING=<your-connection-string>\n", " MODEL_DEPLOYMENT_NAME=<supported-model>\n", " BING_CONNECTION_NAME=<the-name-of-your-bing-connection>\n", " ```\n", "\n", "## Let's Explore Grounding with Bing!\n", "We'll integrate **Grounding with Bing** search results into our agent so it can gather extra context from the web. We'll store and display the Bing search query link for transparency. šŸŽ‰\n", "\n", "<br/>\n", "\n", "<img src=\"./seq-diagrams/4-bing-grounding.png\" width=\"30%\"/>\n" ] }, { "cell_type": "markdown", "id": "setup-initialize", "metadata": {}, "source": [ "## 1. Initial Setup\n", "We'll load environment variables from `.env` and initialize our **AIProjectClient** to manage agents." ] }, { "cell_type": "code", "execution_count": null, "id": "init-code", "metadata": {}, "outputs": [], "source": [ "import os\n", "import time\n", "from pathlib import Path\n", "\n", "from dotenv import load_dotenv\n", "from azure.identity import DefaultAzureCredential\n", "from azure.ai.projects import AIProjectClient\n", "from azure.ai.projects.models import BingGroundingTool, MessageTextContent\n", "\n", "# Load environment variables\n", "notebook_path = Path().absolute()\n", "parent_dir = notebook_path.parent\n", "load_dotenv(parent_dir.parent / '.env')\n", "\n", "# Initialize AIProjectClient\n", "try:\n", " project_client = AIProjectClient.from_connection_string(\n", " credential=DefaultAzureCredential(),\n", " conn_str=os.environ.get(\"PROJECT_CONNECTION_STRING\")\n", " )\n", " print(\"āœ… Successfully initialized AIProjectClient\")\n", "except Exception as e:\n", " print(f\"āŒ Error initializing project client: {e}\")" ] }, { "cell_type": "markdown", "id": "create-agent-with-bing-grounding", "metadata": {}, "source": [ "## 2. Create Bing-Grounded Agent 🌐\n", "We'll fetch our Bing connection from AI Foundry and use `BingGroundingTool` to let our agent search the web. Then we'll create a new agent with disclaimers about not being a doctor, etc.\n", "\n", "Make sure your `MODEL_DEPLOYMENT_NAME` is set to a Bing-supported model (for example, `gpt-4o-0513`) and that you add the header `{\"x-ms-enable-preview\": \"true\"}`." ] }, { "cell_type": "code", "execution_count": null, "id": "agent-bing-code", "metadata": {}, "outputs": [], "source": [ "def create_bing_grounded_agent():\n", " \"\"\"Create an agent that can use Bing to ground queries with up-to-date info.\"\"\"\n", " try:\n", " # 1. Retrieve Bing connection from your AI Foundry project\n", " bing_conn_name = os.environ.get(\"BING_CONNECTION_NAME\")\n", " if not bing_conn_name:\n", " raise ValueError(\"BING_CONNECTION_NAME not set in .env\")\n", "\n", " bing_connection = project_client.connections.get(connection_name=bing_conn_name)\n", " conn_id = bing_connection.id\n", " print(f\"Bing Connection ID: {conn_id}\")\n", "\n", " # 2. Initialize Bing grounding tool\n", " bing_tool = BingGroundingTool(connection_id=conn_id)\n", "\n", " # 3. Create an agent that can search with Bing\n", " agent = project_client.agents.create_agent(\n", " model=os.environ.get(\"MODEL_DEPLOYMENT_NAME\", \"gpt-4o-0513\"),\n", " name=\"health-bing-agent\",\n", " instructions=\"\"\"\n", " You are a health and fitness assistant with Bing search capabilities.\n", " Always:\n", " 1. Provide disclaimers that you are not a medical professional.\n", " 2. Encourage professional consultation.\n", " 3. Use Bing for real-time references.\n", " 4. Provide brief, helpful answers.\n", " \"\"\",\n", " tools=bing_tool.definitions,\n", " # Must pass special preview header to use Bing grounding (subject to change)\n", " headers={\"x-ms-enable-preview\": \"true\"},\n", " )\n", "\n", " print(f\"šŸŽ‰ Created Bing-grounded agent, ID: {agent.id}\")\n", " return agent\n", " except Exception as e:\n", " print(f\"āŒ Error creating Bing-grounded agent: {e}\")\n", " return None\n", "\n", "# Create our Bing-based agent\n", "bing_agent = create_bing_grounded_agent()" ] }, { "cell_type": "markdown", "id": "create-thread", "metadata": {}, "source": [ "## 3. Starting Threads & Asking Questions šŸ’¬\n", "We'll create conversation threads for each user query, letting the agent search with Bing to find relevant info. We will store all `(thread, run)` pairs in a list so we can review them in the next step." ] }, { "cell_type": "code", "execution_count": null, "id": "create-thread-code", "metadata": {}, "outputs": [], "source": [ "bing_threads = []\n", "\n", "def ask_bing_question(agent, user_query):\n", " try:\n", " thread = project_client.agents.create_thread()\n", " print(f\"šŸ“ Created a conversation thread, ID: {thread.id}\")\n", "\n", " # Post user query as a message\n", " user_message = project_client.agents.create_message(\n", " thread_id=thread.id,\n", " role=\"user\",\n", " content=user_query\n", " )\n", " print(f\"šŸ“Ø Created user message with query: '{user_query}'\")\n", "\n", " # Process the query with the agent\n", " run = project_client.agents.create_and_process_run(\n", " thread_id=thread.id,\n", " agent_id=agent.id\n", " )\n", " print(f\"šŸ¤– Run finished with status: {run.status}\\n\")\n", " if run.last_error:\n", " print(f\"Error detail: {run.last_error}\\n\")\n", "\n", " return thread, run\n", " except Exception as e:\n", " print(f\"āŒ Error asking Bing question: {e}\")\n", " return None, None\n", "\n", "if bing_agent:\n", " # We'll ask a few fun questions!\n", " questions = [\n", " \"What are some new HIIT workout trends I should know about?\",\n", " \"What's the current WHO recommendation for sugar intake?\",\n", " \"Any news on intermittent fasting for weight management?\"\n", " ]\n", "\n", " for q in questions:\n", " thr, rn = ask_bing_question(bing_agent, q)\n", " if thr and rn:\n", " bing_threads.append((thr, rn))" ] }, { "cell_type": "markdown", "id": "view-thread", "metadata": {}, "source": [ "## 4. Viewing Bing-Grounded Answers & Query URLs\n", "We’ll retrieve each thread's messages, printing both the user queries and the agent's responses. We'll also fetch the run steps to display the **Bing Search Query URL** (so you can comply with the requirement to show where the data came from). You can replace `api.bing.microsoft.com` with `www.bing.com` to form a user-friendly link.\n", "\n", "Because `RunStep` objects do **not** have `.details`, we look instead for `'request_url'` in `step[\"parameters\"]`. If found, it's presumably the Bing step." ] }, { "cell_type": "code", "execution_count": null, "id": "view-thread-code", "metadata": {}, "outputs": [], "source": [ "def view_bing_conversation(thread_id, run_id):\n", " try:\n", " # Print conversation\n", " messages = project_client.agents.list_messages(thread_id=thread_id)\n", " print(\"\\nšŸ—£ļø Conversation for thread:\", thread_id)\n", " reversed_msgs = list(reversed(messages.data)) # oldest first\n", " for msg in reversed_msgs:\n", " role = msg.role.upper()\n", " if msg.content:\n", " for c in msg.content:\n", " if hasattr(c, 'text') and c.text:\n", " print(f\"{role}: {c.text.value}\\n\")\n", "\n", " # Retrieve run steps to get Bing search query link\n", " run_steps = project_client.agents.list_run_steps(thread_id=thread_id, run_id=run_id)\n", " steps_data = run_steps.get('data', [])\n", " if steps_data:\n", " print(\"\\nšŸ”Ž Bing run steps:\")\n", " for step in steps_data:\n", " # 'parameters' is typically where BingGroundingTool stores request_url\n", " params = step.get('parameters', {})\n", " if 'request_url' in params:\n", " original_url = params['request_url']\n", " friendly_url = original_url.replace(\"api.bing.microsoft.com\", \"www.bing.com\")\n", " print(f\" Bing search URL: {friendly_url}\")\n", " else:\n", " print(\"No run step data found for this conversation.\")\n", " except Exception as e:\n", " print(f\"āŒ Error viewing Bing conversation: {e}\")\n", "\n", "# Display all queries and agent responses\n", "if bing_threads:\n", " for (thr, rn) in bing_threads:\n", " view_bing_conversation(thr.id, rn.id)" ] }, { "cell_type": "markdown", "id": "cleanup", "metadata": {}, "source": [ "## 5. Cleanup & Best Practices\n", "You can optionally delete the agent once you're done. In production, you might keep it around for repeated usage.\n", "\n", "### Best Practices\n", "1. **Accuracy** – Bing search results may include disclaimers or partial info. Encourage verification with credible sources.\n", "2. **Bing Query Display** – For compliance with Bing's use and display requirements, show both **website URLs** (in the agent's response) and **Bing search query URLs** (shown above). If the model includes citations, display them as well.\n", "3. **Limits** – Keep an eye on usage, rate limits, or policy constraints for Bing.\n", "4. **Privacy** – Filter search queries to avoid sending sensitive data.\n", "5. **Evaluations** – Use `azure-ai-evaluation` for iterative improvement.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "cleanup-code", "metadata": {}, "outputs": [], "source": [ "def cleanup_bing_agent(agent):\n", " if agent:\n", " try:\n", " project_client.agents.delete_agent(agent.id)\n", " print(f\"šŸ—‘ļø Deleted Bing-grounded agent: {agent.name}\")\n", " except Exception as e:\n", " print(f\"āŒ Error cleaning up agent: {e}\")\n", " else:\n", " print(\"No agent to clean up.\")\n", "\n", "# Uncomment if you want to remove the agent now\n", "cleanup_bing_agent(bing_agent)" ] }, { "cell_type": "markdown", "id": "final-note", "metadata": {}, "source": [ "# Congratulations! šŸŽ‰\n", "You've built a **Bing-Grounded Health & Fitness Agent** that can:\n", "1. **Search** the web with Bing.\n", "2. **Answer** health/fitness questions with disclaimers.\n", "3. **Include** references and Bing search query links.\n", "\n", "Feel free to expand this approach by combining the BingGroundingTool with other tools (e.g., **FileSearchTool**, **CodeInterpreterTool**) to build a robust advisor.\n", "\n", "#### Let's proceed to [5-agents-aisearch.ipynb](./5-agents-aisearch.ipynb)" ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.9" } }, "nbformat": 4, "nbformat_minor": 5 }