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
}