263 lines
9.7 KiB
Plaintext
263 lines
9.7 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "a1b2c3d4",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Lesson 05 - Agentic RAG"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "b2c3d4e5",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Setup\n",
|
|
"\n",
|
|
"This notebook demonstrates the Agentic RAG (Retrieval-Augmented Generation) pattern using the Microsoft Agent Framework.\n",
|
|
"\n",
|
|
"**Prerequisites:**\n",
|
|
"- `AZURE_SEARCH_SERVICE_ENDPOINT` — your Azure AI Search service endpoint\n",
|
|
"- `AZURE_SEARCH_API_KEY` — your Azure AI Search API key\n",
|
|
"- Azure OpenAI deployment configured via environment variables\n",
|
|
"- Azure CLI authenticated (`az login`)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "c3d4e5f6",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"%pip install agent-framework azure-ai-projects azure-identity python-dotenv -q"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "d4e5f6a7",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import logging\n",
|
|
"logging.getLogger(\"agent_framework.foundry\").setLevel(logging.ERROR)\n",
|
|
"\n",
|
|
"import os\n",
|
|
"import asyncio\n",
|
|
"import dotenv\n",
|
|
"from typing import Annotated\n",
|
|
"\n",
|
|
"from agent_framework import tool\n",
|
|
"from agent_framework.foundry import FoundryChatClient\n",
|
|
"from azure.identity import DefaultAzureCredential\n",
|
|
"\n",
|
|
"dotenv.load_dotenv()\n",
|
|
"\n",
|
|
"endpoint = os.getenv(\"AZURE_AI_PROJECT_ENDPOINT\")\n",
|
|
"deployment_name = os.getenv(\"AZURE_AI_MODEL_DEPLOYMENT_NAME\")\n",
|
|
"\n",
|
|
"missing = [k for k, v in {\n",
|
|
" \"AZURE_AI_PROJECT_ENDPOINT\": endpoint,\n",
|
|
" \"AZURE_AI_MODEL_DEPLOYMENT_NAME\": deployment_name\n",
|
|
"}.items() if not v]\n",
|
|
"\n",
|
|
"if missing:\n",
|
|
" raise ValueError(\n",
|
|
" f\"Missing required environment variables: {', '.join(missing)}. \"\n",
|
|
" \"Please set them as environment variables (e.g., in your .env file or shell environment).\"\n",
|
|
" )"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "e5f6a7b8",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Create the Microsoft Foundry client\n",
|
|
"client = FoundryChatClient(\n",
|
|
" project_endpoint=endpoint,\n",
|
|
" model=deployment_name,\n",
|
|
" credential=DefaultAzureCredential()\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "f6a7b8c9",
|
|
"metadata": {},
|
|
"source": [
|
|
"## What is Agentic RAG?\n",
|
|
"\n",
|
|
"Traditional RAG follows a fixed pipeline: retrieve documents, then generate a response. **Agentic RAG** goes further by giving the agent autonomy to decide **when** and **how** to retrieve information.\n",
|
|
"\n",
|
|
"With Agentic RAG, the agent can:\n",
|
|
"- **Decide** whether retrieval is needed before answering a question\n",
|
|
"- **Choose** which data source or tool to query\n",
|
|
"- **Evaluate** retrieved results and perform follow-up retrievals if the first attempt is insufficient\n",
|
|
"- **Combine** information from multiple retrieval steps into a coherent answer\n",
|
|
"\n",
|
|
"This makes the agent more flexible and accurate compared to a static retrieve-then-generate pipeline."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "a7b8c9d0",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Creating a Search Tool\n",
|
|
"\n",
|
|
"In Agentic RAG, external data sources are wrapped as **tools** that the agent can invoke on demand. This lets the agent treat retrieval as just another action it can take, rather than a mandatory step.\n",
|
|
"\n",
|
|
"Below we define a travel knowledge base and expose it as a tool the agent can call to look up destination information."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "b8c9d0e1",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"TRAVEL_KNOWLEDGE_BASE = {\n",
|
|
" \"Barcelona\": \"Barcelona is Spain's cosmopolitan capital of Catalonia. Best visited Mar-May or Sep-Nov. Known for Gaudí architecture, La Rambla, beaches. Average daily cost: $150-200.\",\n",
|
|
" \"Tokyo\": \"Tokyo is Japan's capital, mixing ultramodern with traditional. Best visited Mar-Apr (cherry blossoms) or Oct-Nov. Known for Shibuya, temples, sushi. Average daily cost: $200-250.\",\n",
|
|
" \"Paris\": \"Paris is France's capital and a global center for art, fashion, and culture. Best visited Apr-Jun or Sep-Oct. Known for Eiffel Tower, Louvre, cuisine. Average daily cost: $180-250.\",\n",
|
|
" \"Cape Town\": \"Cape Town sits on South Africa's southwest tip. Best visited Nov-Mar. Known for Table Mountain, wine regions, wildlife. Average daily cost: $100-150.\",\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"@tool(approval_mode=\"never_require\")\n",
|
|
"def search_travel_knowledge(\n",
|
|
" query: Annotated[str, \"The search query about a travel destination\"]\n",
|
|
") -> str:\n",
|
|
" \"\"\"Search the travel knowledge base for destination information.\"\"\"\n",
|
|
" results = []\n",
|
|
" for destination, info in TRAVEL_KNOWLEDGE_BASE.items():\n",
|
|
" if query.lower() in destination.lower() or any(\n",
|
|
" word in info.lower() for word in query.lower().split()\n",
|
|
" ):\n",
|
|
" results.append(f\"**{destination}**: {info}\")\n",
|
|
" return (\n",
|
|
" \"\\n\\n\".join(results)\n",
|
|
" if results\n",
|
|
" else \"No matching destinations found in the knowledge base.\"\n",
|
|
" )"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "c9d0e1f2",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Building the RAG Agent\n",
|
|
"\n",
|
|
"Now we create an agent that is instructed to **always retrieve information before answering**. The agent uses the `search_travel_knowledge` tool to ground its responses in the knowledge base rather than relying on its own training data."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "d0e1f2a3",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"agent = client.as_agent(\n",
|
|
" tools=[search_travel_knowledge],\n",
|
|
" name=\"TravelRAGAgent\",\n",
|
|
" instructions=\"\"\"You are a knowledgeable travel advisor. Before answering questions about destinations:\n",
|
|
"1. ALWAYS search the travel knowledge base first\n",
|
|
"2. Base your answers on retrieved information\n",
|
|
"3. If information is not in the knowledge base, say so clearly\n",
|
|
"4. Provide specific details like costs, best seasons, and highlights.\"\"\",\n",
|
|
")\n",
|
|
"\n",
|
|
"response = await agent.run(\n",
|
|
" \"I'm interested in visiting somewhere with great architecture. What destinations would you recommend?\",\n",
|
|
" )\n",
|
|
"print(response)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "e1f2a3b4",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Iterative Retrieval — The Maker-Checker Pattern\n",
|
|
"\n",
|
|
"A key advantage of Agentic RAG is **iterative retrieval**. The agent can perform multiple rounds of search to verify, refine, or expand on its initial findings — similar to a \"maker-checker\" workflow:\n",
|
|
"\n",
|
|
"1. **Maker step**: The agent retrieves initial information and drafts a response.\n",
|
|
"2. **Checker step**: The agent performs additional retrievals to verify details or fill gaps.\n",
|
|
"\n",
|
|
"Below, the agent is asked a question that requires comparing multiple destinations, prompting it to search several times."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "f2a3b4c5",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"checker_agent = client.as_agent(\n",
|
|
" tools=[search_travel_knowledge],\n",
|
|
" name=\"TravelRAGCheckerAgent\",\n",
|
|
" instructions=\"\"\"You are a meticulous travel advisor who double-checks recommendations.\n",
|
|
"When answering travel questions:\n",
|
|
"1. Search for relevant destinations first\n",
|
|
"2. For each destination found, search again with the destination name to get full details\n",
|
|
"3. Compare the options using verified information\n",
|
|
"4. Present a final recommendation with specific costs, best travel times, and highlights\n",
|
|
"5. If any detail seems incomplete, search once more to confirm before responding.\"\"\",\n",
|
|
")\n",
|
|
"\n",
|
|
"response = await checker_agent.run(\n",
|
|
" \"I have a $175/day budget and want to travel in April. Which destinations fit my budget and timing?\",\n",
|
|
" )\n",
|
|
"print(response)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "a3b4c5d6",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Summary\n",
|
|
"\n",
|
|
"In this lesson you learned how to build an **Agentic RAG** system using the Microsoft Agent Framework:\n",
|
|
"\n",
|
|
"- **Agentic RAG** lets agents autonomously decide when to retrieve information, making retrieval dynamic rather than fixed.\n",
|
|
"- **Tools as data sources**: External knowledge bases (like Azure AI Search) are wrapped as tools the agent can invoke.\n",
|
|
"- **Iterative retrieval**: The maker-checker pattern enables the agent to perform multiple retrieval rounds — searching, verifying, and refining — before producing a final answer.\n",
|
|
"\n",
|
|
"In production, you would replace the in-memory `TRAVEL_KNOWLEDGE_BASE` with a real Azure AI Search index to handle large-scale travel document retrieval."
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3",
|
|
"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.13"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|