From d89dbb7c16f27c69d36c939a2ea058416c7a0613 Mon Sep 17 00:00:00 2001 From: Douglas Hellinger Date: Sat, 11 Jul 2026 22:26:05 +0800 Subject: [PATCH] feat: add Docker setup for labs and fix notebook env detection Closes #38 - Add Dockerfile.labs, docker-compose.yml, and lab-entrypoint.sh for running lab notebooks in Jupyter Lab via Docker - Add requirements-labs.txt with pinned deps for labs 1-4 and 6 - Add .env.example.labs with API key template - Rewrite README to be lab-first; move backend docs to optional section - Fix all 5 lab notebooks: change globals().get() to os.environ.get() so env vars from .env are picked up without re-prompting --- .../agent-mastery-course/.env.example.labs | 10 + .../agent-mastery-course/Dockerfile.labs | 16 ++ .../llm/agents/agent-mastery-course/README.md | 182 ++++++++++++------ .../agent-mastery-course/docker-compose.yml | 11 ++ .../agent-mastery-course/lab-entrypoint.sh | 33 ++++ .../labs/lab1and2_base_agent.ipynb | 8 +- .../labs/lab3_agent_architectures.ipynb | 2 +- .../labs/lab4_tools.ipynb | 8 +- .../agent-mastery-course/labs/lab5_RAG.ipynb | 2 +- .../labs/lab6_evals.ipynb | 8 +- .../requirements-labs.txt | 10 + 11 files changed, 222 insertions(+), 68 deletions(-) create mode 100644 python/llm/agents/agent-mastery-course/.env.example.labs create mode 100644 python/llm/agents/agent-mastery-course/Dockerfile.labs create mode 100644 python/llm/agents/agent-mastery-course/docker-compose.yml create mode 100644 python/llm/agents/agent-mastery-course/lab-entrypoint.sh create mode 100644 python/llm/agents/agent-mastery-course/requirements-labs.txt diff --git a/python/llm/agents/agent-mastery-course/.env.example.labs b/python/llm/agents/agent-mastery-course/.env.example.labs new file mode 100644 index 0000000..00ba613 --- /dev/null +++ b/python/llm/agents/agent-mastery-course/.env.example.labs @@ -0,0 +1,10 @@ +# API keys for the Agent Mastery Course labs +# Get these free accounts before starting: +# Arize: https://app.arize.com (observe traces) +# OpenAI: https://platform.openai.com/api-keys +# Tavily: https://auth.tavily.com (web search) + +ARIZE_SPACE_ID= +ARIZE_API_KEY= +OPENAI_API_KEY= +TAVILY_API_KEY= diff --git a/python/llm/agents/agent-mastery-course/Dockerfile.labs b/python/llm/agents/agent-mastery-course/Dockerfile.labs new file mode 100644 index 0000000..2c4d624 --- /dev/null +++ b/python/llm/agents/agent-mastery-course/Dockerfile.labs @@ -0,0 +1,16 @@ +FROM python:3.11-slim + +RUN pip install --no-cache-dir jupyterlab + +COPY requirements-labs.txt /tmp/requirements-labs.txt +RUN pip install --no-cache-dir -r /tmp/requirements-labs.txt + +WORKDIR /workspace + +COPY labs/ /workspace/labs/ +COPY lab-entrypoint.sh /lab-entrypoint.sh +RUN chmod +x /lab-entrypoint.sh + +EXPOSE 8888 + +ENTRYPOINT ["/lab-entrypoint.sh"] diff --git a/python/llm/agents/agent-mastery-course/README.md b/python/llm/agents/agent-mastery-course/README.md index 6bc6658..250a01e 100644 --- a/python/llm/agents/agent-mastery-course/README.md +++ b/python/llm/agents/agent-mastery-course/README.md @@ -1,72 +1,146 @@ -# AI Trip Planner +# AI Trip Planner — Agent Mastery Course -Fast, sequential trip planning with FastAPI (backend), React (frontend), and LangGraph for orchestration. Optional Arize tracing is supported. +This repo has two things: + +1. **Agent Mastery Course labs** (start here) — Jupyter notebooks that build an AI travel agent step by step using the [Agno](https://github.com/agno-agi/agno) framework, with tracing on [Arize](https://app.arize.com). +2. **Trip Planner app** (optional) — A FastAPI + LangGraph backend with a minimal UI, RAG, and MCP demos. + +--- ## Quickstart -1) Requirements -- Python 3.10+ (Docker optional) +### 1. Prerequisites + +- [Docker](https://docs.docker.com/get-docker/) + +### 2. Get API keys (free) + +You need 3 free accounts — the labs will prompt you for these keys: -2) Configure environment -- Copy `backend/env_example.txt` to `backend/.env`. -- Set one LLM key: `OPENAI_API_KEY=...` or `OPENROUTER_API_KEY=...`. -- Optional: `ARIZE_SPACE_ID` and `ARIZE_API_KEY` for tracing. +| Service | Sign up | What for | +|---------|---------|----------| +| **Arize** | https://app.arize.com | Observe agent traces | +| **OpenAI** | https://platform.openai.com/api-keys | LLM calls | +| **Tavily** | https://auth.tavily.com | Web search tool | + +### 3. Set up -3) Install dependencies ```bash -cd backend -uv pip install -r requirements.txt # faster, deterministic installs -# If uv is not installed: curl -LsSf https://astral.sh/uv/install.sh | sh -# Fallback: pip install -r requirements.txt +git clone +cd agent-mastery-course + +# Copy and fill in your API keys +cp .env.example.labs .env +``` + +Open `.env` with a text editor and add your keys. + +### 4. Start + +```bash +docker compose up --build +``` + +Docker builds the environment with all dependencies and starts Jupyter Lab. +The terminal will show logs — keep it running and open a new browser tab. + +### 5. Open the lab + +Open http://localhost:8888 in your browser. You'll see the lab notebooks: + +``` +labs/ +├── lab1and2_base_agent.ipynb ← start here +├── lab3_agent_architectures.ipynb +├── lab4_tools.ipynb +├── lab5_RAG.ipynb +└── lab6_evals.ipynb +``` + +Double-click **lab1and2_base_agent.ipynb** and run the cells one by one. + +### 6. What you'll see + +- The agent plans a trip to Tokyo +- Each tool call and LLM request is traced to Arize +- Open https://app.arize.com to explore the traces + +--- + +## Labs overview + +| Lab | Topic | What you build | +|-----|-------|----------------| +| 1 & 2 | Base agent + tracing | A trip planner agent with tools | +| 3 | Agent architectures | Orchestrator-worker & parallel patterns | +| 4 | Tools | Enhance tools with real APIs | +| 5 | RAG | Add retrieval-augmented generation | +| 6 | Evals | Evaluate and log agent performance | + +--- + +## Project structure + +``` +agent-mastery-course/ +├── labs/ ← Jupyter notebooks (start here) +│ ├── lab1and2_base_agent.ipynb +│ ├── lab3_agent_architectures.ipynb +│ ├── lab4_tools.ipynb +│ ├── lab5_RAG.ipynb +│ └── lab6_evals.ipynb +├── backend/ ← FastAPI + LangGraph app (optional) +├── frontend/ ← Minimal UI (optional) +├── Dockerfile.labs ← Container for the labs +├── docker-compose.yml ← Runs Jupyter Lab +├── requirements-labs.txt ← Python deps for all labs +├── .env.example.labs ← API key template +└── README.md ``` -4) Run +--- + +## Optional: Run the Trip Planner app + +The `backend/` directory contains a FastAPI server with a LangGraph agent, optional RAG, MCP weather demo, and a minimal frontend. + +### Backend setup + ```bash -./start.sh # starts backend on 8000; serves minimal UI at '/' -# or -cd backend && uvicorn main:app --host 0.0.0.0 --port 8000 --reload +cd backend +cp .env.example .env +# Set OPENAI_API_KEY or OPENROUTER_API_KEY in backend/.env +uv pip install -r requirements.txt # or: pip install -r requirements.txt +uvicorn main:app --host 0.0.0.0 --port 8000 --reload ``` -5) Open -- Frontend: http://localhost:3000 -- API: http://localhost:8000 -- Docs: http://localhost:8000/docs - - Minimal UI: http://localhost:8000/ +Open http://localhost:8000/docs to try the API. + +### Backend with Docker -Docker (optional) ```bash -docker-compose up --build +docker compose -f docker-compose.backend.yml up --build ``` -## Project Structure -- `backend/`: FastAPI app (`main.py`), LangGraph agents, tracing hooks. -- `frontend/index.html`: Minimal static UI served by backend at `/`. -- `optional/airtable/`: Airtable integration (optional, not on critical path). -- `test scripts/`: `test_api.py`, `synthetic_data_gen.py` for quick checks/evals. -- Root: `start.sh`, `docker-compose.yml`, `README.md`. - -## Development Commands -- Backend (dev): `uvicorn main:app --host 0.0.0.0 --port 8000 --reload` -- API smoke test: `python "test scripts"/test_api.py` -- Synthetic evals: `python "test scripts"/synthetic_data_gen.py --base-url http://localhost:8000 --count 12` - -## API -- POST `/plan-trip` → returns a generated itinerary. - Example body: - ```json - {"destination":"Tokyo, Japan","duration":"7 days","budget":"$2000","interests":"food, culture"} - ``` -- GET `/health` → simple status. - -## Notes on Tracing (Optional) -- If `ARIZE_SPACE_ID` and `ARIZE_API_KEY` are set, OpenInference exports spans for agents/tools/LLM calls. View at https://app.arize.com. +(Not yet created — the backend docker-compose is coming soon.) + +### Backend features + +- POST `/plan-trip` — generates a travel itinerary +- GET `/health` — health check +- GET `/` — minimal frontend +- Optional Arize tracing via `ARIZE_SPACE_ID` / `ARIZE_API_KEY` +- Optional RAG via `ENABLE_RAG=1` (curated local guide content) +- Optional MCP weather demo via `ENABLE_MCP=1` + +--- ## Troubleshooting -- 401/empty results: verify `OPENAI_API_KEY` or `OPENROUTER_API_KEY` in `backend/.env`. -- No traces: ensure Arize credentials are set and reachable. -- Port conflicts: stop existing services on 3000/8000 or change ports. - -## Deploy on Render -- This repo includes `render.yaml`. Connect your GitHub repo in Render and deploy as a Web Service. -- Render will run: `pip install -r backend/requirements.txt` and `uvicorn main:app --host 0.0.0.0 --port $PORT`. -- Set `OPENAI_API_KEY` (or `OPENROUTER_API_KEY`) and optional Arize vars in the Render dashboard. + +| Symptom | Fix | +|---------|-----| +| "No module named ..." in notebook | `Kernel → Restart Kernel` after the `!pip install` cell | +| API returns 401 | Verify the key is correct in your `.env` | +| No traces in Arize | Check `ARIZE_SPACE_ID` and `ARIZE_API_KEY` are set | +| Port 8888 in use | Change the port in `docker-compose.yml`: `"8889:8888"` | +| Docker build slow | Only the first build downloads packages. Rebuilds are instant. | diff --git a/python/llm/agents/agent-mastery-course/docker-compose.yml b/python/llm/agents/agent-mastery-course/docker-compose.yml new file mode 100644 index 0000000..0f66eed --- /dev/null +++ b/python/llm/agents/agent-mastery-course/docker-compose.yml @@ -0,0 +1,11 @@ +services: + labs: + build: + context: . + dockerfile: Dockerfile.labs + ports: + - "8888:8888" + volumes: + - ./labs:/workspace/labs + env_file: + - .env diff --git a/python/llm/agents/agent-mastery-course/lab-entrypoint.sh b/python/llm/agents/agent-mastery-course/lab-entrypoint.sh new file mode 100644 index 0000000..f9910de --- /dev/null +++ b/python/llm/agents/agent-mastery-course/lab-entrypoint.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -euo pipefail + +cat <<'BANNER' + +╔══════════════════════════════════════════════════════════════╗ +║ AI Trip Planner — Agent Mastery Course ║ +╚══════════════════════════════════════════════════════════════╝ + + ✓ Jupyter Lab is ready! + + Open http://localhost:8888 in your browser + Then open: labs/lab1and2_base_agent.ipynb + + Labs in this course: + lab1and2_base_agent.ipynb ← Start here + lab3_agent_architectures.ipynb + lab4_tools.ipynb + lab5_RAG.ipynb + lab6_evals.ipynb + + To stop: press Ctrl+C in this terminal + +BANNER + +exec jupyter lab \ + --ip=0.0.0.0 \ + --port=8888 \ + --no-browser \ + --allow-root \ + --NotebookApp.token='' \ + --NotebookApp.password='' \ + --LabApp.default_url=/lab/tree/labs diff --git a/python/llm/agents/agent-mastery-course/labs/lab1and2_base_agent.ipynb b/python/llm/agents/agent-mastery-course/labs/lab1and2_base_agent.ipynb index 69554be..fe1d56d 100644 --- a/python/llm/agents/agent-mastery-course/labs/lab1and2_base_agent.ipynb +++ b/python/llm/agents/agent-mastery-course/labs/lab1and2_base_agent.ipynb @@ -110,13 +110,13 @@ "import os\n", "from getpass import getpass\n", "\n", - "os.environ[\"ARIZE_SPACE_ID\"] = globals().get(\"ARIZE_SPACE_ID\") or getpass(\"🔑 Enter your Arize Space ID: \")\n", + "os.environ[\"ARIZE_SPACE_ID\"] = os.environ.get(\"ARIZE_SPACE_ID\") or getpass(\"🔑 Enter your Arize Space ID: \")\n", "\n", - "os.environ[\"ARIZE_API_KEY\"] = globals().get(\"ARIZE_API_KEY\") or getpass(\"🔑 Enter your Arize API Key: \")\n", + "os.environ[\"ARIZE_API_KEY\"] = os.environ.get(\"ARIZE_API_KEY\") or getpass(\"🔑 Enter your Arize API Key: \")\n", "\n", - "os.environ[\"OPENAI_API_KEY\"] = globals().get(\"OPENAI_API_KEY\") or getpass(\"🔑 Enter your OpenAI API Key: \")\n", + "os.environ[\"OPENAI_API_KEY\"] = os.environ.get(\"OPENAI_API_KEY\") or getpass(\"🔑 Enter your OpenAI API Key: \")\n", "\n", - "os.environ[\"TAVILY_API_KEY\"] = globals().get(\"TAVILY_API_KEY\") or getpass(\"🔑 Enter your Tavily API Key: \")" + "os.environ[\"TAVILY_API_KEY\"] = os.environ.get(\"TAVILY_API_KEY\") or getpass(\"🔑 Enter your Tavily API Key: \")" ] }, { diff --git a/python/llm/agents/agent-mastery-course/labs/lab3_agent_architectures.ipynb b/python/llm/agents/agent-mastery-course/labs/lab3_agent_architectures.ipynb index c96f8a6..72d308a 100644 --- a/python/llm/agents/agent-mastery-course/labs/lab3_agent_architectures.ipynb +++ b/python/llm/agents/agent-mastery-course/labs/lab3_agent_architectures.ipynb @@ -1 +1 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[]},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["
\n","
\n","

\n"," \"arize\n","
\n"," Docs\n"," |\n"," GitHub\n"," |\n"," Slack Community\n","

\n","
"],"metadata":{"id":"hrMHoZXRvqnX"}},{"cell_type":"markdown","source":["# **Arize Agent Mastry Course: Agent Architectures**"],"metadata":{"id":"0c726hqfv0s6"}},{"cell_type":"markdown","source":["In this lab, we’ll explore agent architectures by implementing two common frameworks. Understanding different architectures is key to identifying which approach best fits your workflow and use case.\n","\n","We’ll use the same setup as before, then leverage features of the Agno framework to demonstrate both the Orchestrator–Worker architecture and a Parallelization architecture. Finally, we’ll examine the traces of each framework within Arize to better understand their behavior and performance."],"metadata":{"id":"j22ZkUGoke1W"}},{"cell_type":"markdown","source":["# Set Up"],"metadata":{"id":"wVtdGM5Dkbq1"}},{"cell_type":"code","execution_count":null,"metadata":{"id":"1XtfV7qF6HKg"},"outputs":[],"source":["!pip install -qqqqqq arize-otel agno openai openinference-instrumentation-agno openinference-instrumentation-openai httpx"]},{"cell_type":"code","source":["import os\n","from getpass import getpass\n","\n","os.environ[\"ARIZE_SPACE_ID\"] = globals().get(\"ARIZE_SPACE_ID\") or getpass(\"🔑 Enter your Arize Space ID: \")\n","\n","os.environ[\"ARIZE_API_KEY\"] = globals().get(\"ARIZE_API_KEY\") or getpass(\"🔑 Enter your Arize API Key: \")\n","\n","os.environ[\"OPENAI_API_KEY\"] = globals().get(\"OPENAI_API_KEY\") or getpass(\"🔑 Enter your OpenAI API Key: \")\n","\n","os.environ[\"TAVILY_API_KEY\"] = globals().get(\"TAVILY_API_KEY\") or getpass(\"🔑 Enter your Tavily API Key: \")"],"metadata":{"id":"hAapTFAi-35H"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["from arize.otel import register\n","from openinference.instrumentation.openai import OpenAIInstrumentor\n","from openinference.instrumentation.agno import AgnoInstrumentor\n","\n","model_id = \"travel-agent-demo\"\n","tracer_provider = register(\n"," space_id=os.getenv(\"ARIZE_SPACE_ID\"),\n"," api_key=os.getenv(\"ARIZE_API_KEY\"),\n"," project_name=model_id,\n"," set_global_tracer_provider=True\n",")\n","OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)\n","AgnoInstrumentor().instrument(tracer_provider=tracer_provider)"],"metadata":{"id":"Y8dSorscJDPT"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["# Define Tools"],"metadata":{"id":"VaNh2t7NkhH5"}},{"cell_type":"code","source":["# --- Helper functions for tools ---\n","import httpx\n","\n","def _search_api(query: str) -> str | None:\n"," \"\"\"Try Tavily search first, fall back to None.\"\"\"\n"," tavily_key = os.getenv(\"TAVILY_API_KEY\")\n"," if not tavily_key:\n"," return None\n"," try:\n"," resp = httpx.post(\n"," \"https://api.tavily.com/search\",\n"," json={\n"," \"api_key\": tavily_key,\n"," \"query\": query,\n"," \"max_results\": 3,\n"," \"search_depth\": \"basic\",\n"," \"include_answer\": True,\n"," },\n"," timeout=8,\n"," )\n"," data = resp.json()\n"," answer = data.get(\"answer\") or \"\"\n"," snippets = [r.get(\"content\", \"\") for r in data.get(\"results\", [])]\n"," combined = \" \".join([answer] + snippets).strip()\n"," return combined[:400] if combined else None\n"," except Exception:\n"," return None\n","\n","\n","def _compact(text: str, limit: int = 200) -> str:\n"," \"\"\"Compact text for cleaner outputs.\"\"\"\n"," cleaned = \" \".join(text.split())\n"," return cleaned if len(cleaned) <= limit else cleaned[:limit].rsplit(\" \", 1)[0]\n"],"metadata":{"id":"H3gLnYV2_KhS"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["from agno.tools import tool\n","\n","@tool\n","def essential_info(destination: str) -> str:\n"," q = f\"{destination} travel essentials weather best time top attractions etiquette\"\n"," s = _search_api(q)\n"," if s:\n"," return f\"{destination} essentials: {_compact(s)}\"\n"," return f\"{destination} is a popular travel destination. Expect local culture, cuisine, and landmarks worth exploring.\"\n","\n","@tool\n","def budget_basics(destination: str, duration: str) -> str:\n"," q = f\"{destination} travel budget average daily costs {duration}\"\n"," s = _search_api(q)\n"," if s:\n"," return f\"{destination} budget ({duration}): {_compact(s)}\"\n"," return f\"Budget for {duration} in {destination} depends on lodging, meals, transport, and attractions.\"\n","\n","@tool\n","def local_flavor(destination: str, interests: str = \"local culture\") -> str:\n"," q = f\"{destination} authentic local experiences {interests}\"\n"," s = _search_api(q)\n"," if s:\n"," return f\"{destination} {interests}: {_compact(s)}\"\n"," return f\"Explore {destination}'s unique {interests} through markets, neighborhoods, and local eateries.\"\n"],"metadata":{"id":"tNgVS4Fk_QRR"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["# Agent Architecture 1: Orchestrator-Worker Framework"],"metadata":{"id":"BkXH80GwxhaV"}},{"cell_type":"markdown","source":["In the Orchestrator–Worker framework, we will structure our system using multiple specialized sub-agents (one for each of our 3 tools).\n","\n","Each sub-agent focuses on a specific capability, such as getting essential information, estimating budgets, or suggesting local experiences.\n","\n","A centralized orchestrator agent coordinates these sub-agents by delegating tasks to the appropriate one and then synthesizing their outputs into a cohesive final response. This approach mirrors how complex workflows can be broken down into smaller, focused tasks that work together seamlessly."],"metadata":{"id":"oaTA4OYFlr9E"}},{"cell_type":"markdown","source":["![Diagram](https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-course-orchestrator-worker-diagram.png)"],"metadata":{"id":"vgO9MBCelctf"}},{"cell_type":"code","source":["from agno.agent import Agent\n","from agno.models.openai import OpenAIChat\n","\n","# --- Define Subagents ---\n","destination_agent = Agent(\n"," name=\"DestinationInfo\",\n"," model=OpenAIChat(id=\"gpt-4o\", temperature=0.2),\n"," description=\"Get basic travel info (weather, best time, attractions, etiquette).\",\n"," instructions=[\"Provide concise, reliable travel info for a destination using the essential_info tool.\"],\n"," tools=[essential_info],\n",")\n","\n","budget_agent = Agent(\n"," name=\"Budget\",\n"," model=OpenAIChat(id=\"gpt-4o\"),\n"," description=\"Summarize travel cost.\",\n"," instructions=[\"Give clear travel budget summaries with hotel, meal, and transport cost ranges; give multiple options with prices and locations.\"],\n"," tools = [budget_basics],\n"," markdown=True,\n",")\n","\n","local_activity_agent = Agent(\n"," name=\"ActivitySuggester\",\n"," model=OpenAIChat(id=\"gpt-4o\", temperature=0.8),\n"," description=\"Suggest authentic local experiences.\",\n"," instructions=[\n"," \"Group local activities by category: cultural, food, outdoors.\",\n"," \"Include both popular and hidden-gem recommendations.\"\n"," ],\n"," tools=[local_flavor],\n"," markdown=True,\n",")\n"],"metadata":{"id":"c5nCZgJANlOu"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["from agno.team import Team\n","\n","travel_team = Team(\n"," name=\"Orchestrator-TripPlanner\",\n"," members=[destination_agent, budget_agent, local_activity_agent],\n"," model=OpenAIChat(id=\"gpt-4o\"),\n"," instructions=[\n"," \"You are a friendly and knowledgeable travel planner. \"\n"," \"Combine coordinate agents to create a trip plan including essentials, budget, and local flavor. \"\n"," \"Keep the tone natural, clear, and under 1000 words.\"\n"," ],\n"," show_members_responses=True,\n"," markdown=True,\n",")\n","\n"],"metadata":{"id":"Sb1jqHcU_R3r"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["# --- Example usage ---\n","destination = \"Tokyo\"\n","duration = \"5 days\"\n","interests = \"food, culture\"\n","\n","query = f\"\"\"\n","Plan a {duration} trip to {destination}.\n","Focus on {interests}.\n","Include essential info, budget breakdown, and local experiences.\n","\"\"\"\n","travel_team.print_response(\n"," query,\n"," stream=True\n",")"],"metadata":{"id":"FVDKZT9x_U-5"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Tracing this agent reveals the Orchestrator–Worker workflow in action. We can observe how tasks are explicitly delegated to individual sub-agents and how their outputs are combined to produce the final response."],"metadata":{"id":"bC13rYIHmTBF"}},{"cell_type":"markdown","source":["![Trace](https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-course-orchestrator-worker-trace.png)"],"metadata":{"id":"c86CFdhfmD_S"}},{"cell_type":"markdown","source":["# Agent Architecture 2: Parallelization Framework"],"metadata":{"id":"qfeZp0lp0P3b"}},{"cell_type":"markdown","source":["In the Parallelization framework, we run all sub-agents concurrently instead of sequentially. Each sub-agent works independently on its assigned task — for example, retrieving essential information, estimating budgets, or finding local experiences — while the main agent waits to gather their results. Once all sub-agents complete their work, the agent synthesizes their outputs into a unified response.\n","\n","This approach offers a significant latency advantage, as parallel execution reduces overall response time without compromising the quality or completeness of the final answer."],"metadata":{"id":"WYsyTGVNn_Xj"}},{"cell_type":"markdown","source":["![Diagram](https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-course-parallelization.png)"],"metadata":{"id":"NGSf4AApoXrN"}},{"cell_type":"code","source":["import asyncio\n","from agno.agent import Agent\n","from agno.models.openai import OpenAIChat\n"],"metadata":{"id":"H5PyEo4NJm0G"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["# --- Define Subagents ---\n","destination_agent = Agent(\n"," name=\"DestinationInfo\",\n"," model=OpenAIChat(id=\"gpt-4o\", temperature=0.2),\n"," tools=[essential_info],\n"," instructions=[\"Provide concise, reliable travel info for a destination using the essential_info tool.\"],\n",")\n","\n","budget_agent = Agent(\n"," name=\"Budget\",\n"," model=OpenAIChat(id=\"gpt-4o\"),\n"," tools=[budget_basics],\n"," instructions=[\"Give clear travel budget summaries with hotel, meal, and transport cost ranges; give multiple options with prices and locations.\"],\n",")\n","\n","local_activity_agent = Agent(\n"," name=\"ActivitySuggester\",\n"," model=OpenAIChat(id=\"gpt-4o\", temperature=0.8),\n"," tools=[local_flavor],\n"," instructions=[\n"," \"Group local activities by category: cultural, food, outdoors.\",\n"," \"Include both popular and hidden-gem recommendations.\"\n"," ],\n",")\n","\n","synthesizer = Agent(\n"," name=\"Synthesizer\",\n"," model=OpenAIChat(id=\"gpt-4o\", temperature=0.3),\n"," instructions=[\n"," \"Combine partial responses into a clear, well-structured final answer.\"\n"," ],\n",")\n"],"metadata":{"id":"jaGY7FLO2pEv"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["from opentelemetry import trace\n","from openinference.semconv.trace import SpanAttributes\n","\n","tracer = trace.get_tracer(__name__)\n","\n","# --- Run subagents concurrently and synthesize ---\n","async def plan_trip(destination: str, duration: str, interests: str):\n"," with tracer.start_as_current_span(\"ParallelizationAgent\") as span:\n"," span.set_attribute(SpanAttributes.OPENINFERENCE_SPAN_KIND, \"agent\")\n"," span.set_attribute(\"destination\", destination)\n"," span.set_attribute(\"duration\", duration)\n"," span.set_attribute(\"interests\", interests)\n","\n"," # Run all three subagents concurrently\n"," dest_task = destination_agent.arun(destination)\n"," budget_task = budget_agent.arun(duration)\n"," local_task = local_activity_agent.arun(interests)\n","\n"," dest_info, budget_info, activities = await asyncio.gather(dest_task, budget_task, local_task)\n","\n"," # Combine results via one final LLM call\n"," final_prompt = f\"\"\"\n"," Combine the following into a cohesive, well-structured travel plan for {destination}.\n"," Keep it friendly, natural, and under 1000 words.\n","\n"," [Destination Info]\n"," {dest_info}\n","\n"," [Budget Summary]\n"," {budget_info}\n","\n"," [Local Activities]\n"," {activities}\n"," \"\"\"\n","\n"," final_plan = await synthesizer.arun(final_prompt)\n"," return final_plan"],"metadata":{"id":"DKxb_Mmr2q1-"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["# --- Example usage ---\n","destination = \"Tokyo\"\n","duration = \"5 days\"\n","interests = \"food, culture\"\n","\n","final = await plan_trip(destination, duration, interests)"],"metadata":{"id":"bDUD1fB02xeK"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["![Traces](https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-course-parallel-agent.png)"],"metadata":{"id":"_Ocy3wnfopz9"}}]} \ No newline at end of file +{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[]},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["
\n","
\n","

\n"," \"arize\n","
\n"," Docs\n"," |\n"," GitHub\n"," |\n"," Slack Community\n","

\n","
"],"metadata":{"id":"hrMHoZXRvqnX"}},{"cell_type":"markdown","source":["# **Arize Agent Mastry Course: Agent Architectures**"],"metadata":{"id":"0c726hqfv0s6"}},{"cell_type":"markdown","source":["In this lab, we’ll explore agent architectures by implementing two common frameworks. Understanding different architectures is key to identifying which approach best fits your workflow and use case.\n","\n","We’ll use the same setup as before, then leverage features of the Agno framework to demonstrate both the Orchestrator–Worker architecture and a Parallelization architecture. Finally, we’ll examine the traces of each framework within Arize to better understand their behavior and performance."],"metadata":{"id":"j22ZkUGoke1W"}},{"cell_type":"markdown","source":["# Set Up"],"metadata":{"id":"wVtdGM5Dkbq1"}},{"cell_type":"code","execution_count":null,"metadata":{"id":"1XtfV7qF6HKg"},"outputs":[],"source":["!pip install -qqqqqq arize-otel agno openai openinference-instrumentation-agno openinference-instrumentation-openai httpx"]},{"cell_type":"code","source":["import os\n","from getpass import getpass\n","\n","os.environ[\"ARIZE_SPACE_ID\"] = os.environ.get(\"ARIZE_SPACE_ID\") or getpass(\"🔑 Enter your Arize Space ID: \")\n","\n","os.environ[\"ARIZE_API_KEY\"] = os.environ.get(\"ARIZE_API_KEY\") or getpass(\"🔑 Enter your Arize API Key: \")\n","\n","os.environ[\"OPENAI_API_KEY\"] = os.environ.get(\"OPENAI_API_KEY\") or getpass(\"🔑 Enter your OpenAI API Key: \")\n","\n","os.environ[\"TAVILY_API_KEY\"] = os.environ.get(\"TAVILY_API_KEY\") or getpass(\"🔑 Enter your Tavily API Key: \")"],"metadata":{"id":"hAapTFAi-35H"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["from arize.otel import register\n","from openinference.instrumentation.openai import OpenAIInstrumentor\n","from openinference.instrumentation.agno import AgnoInstrumentor\n","\n","model_id = \"travel-agent-demo\"\n","tracer_provider = register(\n"," space_id=os.getenv(\"ARIZE_SPACE_ID\"),\n"," api_key=os.getenv(\"ARIZE_API_KEY\"),\n"," project_name=model_id,\n"," set_global_tracer_provider=True\n",")\n","OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)\n","AgnoInstrumentor().instrument(tracer_provider=tracer_provider)"],"metadata":{"id":"Y8dSorscJDPT"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["# Define Tools"],"metadata":{"id":"VaNh2t7NkhH5"}},{"cell_type":"code","source":["# --- Helper functions for tools ---\n","import httpx\n","\n","def _search_api(query: str) -> str | None:\n"," \"\"\"Try Tavily search first, fall back to None.\"\"\"\n"," tavily_key = os.getenv(\"TAVILY_API_KEY\")\n"," if not tavily_key:\n"," return None\n"," try:\n"," resp = httpx.post(\n"," \"https://api.tavily.com/search\",\n"," json={\n"," \"api_key\": tavily_key,\n"," \"query\": query,\n"," \"max_results\": 3,\n"," \"search_depth\": \"basic\",\n"," \"include_answer\": True,\n"," },\n"," timeout=8,\n"," )\n"," data = resp.json()\n"," answer = data.get(\"answer\") or \"\"\n"," snippets = [r.get(\"content\", \"\") for r in data.get(\"results\", [])]\n"," combined = \" \".join([answer] + snippets).strip()\n"," return combined[:400] if combined else None\n"," except Exception:\n"," return None\n","\n","\n","def _compact(text: str, limit: int = 200) -> str:\n"," \"\"\"Compact text for cleaner outputs.\"\"\"\n"," cleaned = \" \".join(text.split())\n"," return cleaned if len(cleaned) <= limit else cleaned[:limit].rsplit(\" \", 1)[0]\n"],"metadata":{"id":"H3gLnYV2_KhS"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["from agno.tools import tool\n","\n","@tool\n","def essential_info(destination: str) -> str:\n"," q = f\"{destination} travel essentials weather best time top attractions etiquette\"\n"," s = _search_api(q)\n"," if s:\n"," return f\"{destination} essentials: {_compact(s)}\"\n"," return f\"{destination} is a popular travel destination. Expect local culture, cuisine, and landmarks worth exploring.\"\n","\n","@tool\n","def budget_basics(destination: str, duration: str) -> str:\n"," q = f\"{destination} travel budget average daily costs {duration}\"\n"," s = _search_api(q)\n"," if s:\n"," return f\"{destination} budget ({duration}): {_compact(s)}\"\n"," return f\"Budget for {duration} in {destination} depends on lodging, meals, transport, and attractions.\"\n","\n","@tool\n","def local_flavor(destination: str, interests: str = \"local culture\") -> str:\n"," q = f\"{destination} authentic local experiences {interests}\"\n"," s = _search_api(q)\n"," if s:\n"," return f\"{destination} {interests}: {_compact(s)}\"\n"," return f\"Explore {destination}'s unique {interests} through markets, neighborhoods, and local eateries.\"\n"],"metadata":{"id":"tNgVS4Fk_QRR"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["# Agent Architecture 1: Orchestrator-Worker Framework"],"metadata":{"id":"BkXH80GwxhaV"}},{"cell_type":"markdown","source":["In the Orchestrator–Worker framework, we will structure our system using multiple specialized sub-agents (one for each of our 3 tools).\n","\n","Each sub-agent focuses on a specific capability, such as getting essential information, estimating budgets, or suggesting local experiences.\n","\n","A centralized orchestrator agent coordinates these sub-agents by delegating tasks to the appropriate one and then synthesizing their outputs into a cohesive final response. This approach mirrors how complex workflows can be broken down into smaller, focused tasks that work together seamlessly."],"metadata":{"id":"oaTA4OYFlr9E"}},{"cell_type":"markdown","source":["![Diagram](https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-course-orchestrator-worker-diagram.png)"],"metadata":{"id":"vgO9MBCelctf"}},{"cell_type":"code","source":["from agno.agent import Agent\n","from agno.models.openai import OpenAIChat\n","\n","# --- Define Subagents ---\n","destination_agent = Agent(\n"," name=\"DestinationInfo\",\n"," model=OpenAIChat(id=\"gpt-4o\", temperature=0.2),\n"," description=\"Get basic travel info (weather, best time, attractions, etiquette).\",\n"," instructions=[\"Provide concise, reliable travel info for a destination using the essential_info tool.\"],\n"," tools=[essential_info],\n",")\n","\n","budget_agent = Agent(\n"," name=\"Budget\",\n"," model=OpenAIChat(id=\"gpt-4o\"),\n"," description=\"Summarize travel cost.\",\n"," instructions=[\"Give clear travel budget summaries with hotel, meal, and transport cost ranges; give multiple options with prices and locations.\"],\n"," tools = [budget_basics],\n"," markdown=True,\n",")\n","\n","local_activity_agent = Agent(\n"," name=\"ActivitySuggester\",\n"," model=OpenAIChat(id=\"gpt-4o\", temperature=0.8),\n"," description=\"Suggest authentic local experiences.\",\n"," instructions=[\n"," \"Group local activities by category: cultural, food, outdoors.\",\n"," \"Include both popular and hidden-gem recommendations.\"\n"," ],\n"," tools=[local_flavor],\n"," markdown=True,\n",")\n"],"metadata":{"id":"c5nCZgJANlOu"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["from agno.team import Team\n","\n","travel_team = Team(\n"," name=\"Orchestrator-TripPlanner\",\n"," members=[destination_agent, budget_agent, local_activity_agent],\n"," model=OpenAIChat(id=\"gpt-4o\"),\n"," instructions=[\n"," \"You are a friendly and knowledgeable travel planner. \"\n"," \"Combine coordinate agents to create a trip plan including essentials, budget, and local flavor. \"\n"," \"Keep the tone natural, clear, and under 1000 words.\"\n"," ],\n"," show_members_responses=True,\n"," markdown=True,\n",")\n","\n"],"metadata":{"id":"Sb1jqHcU_R3r"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["# --- Example usage ---\n","destination = \"Tokyo\"\n","duration = \"5 days\"\n","interests = \"food, culture\"\n","\n","query = f\"\"\"\n","Plan a {duration} trip to {destination}.\n","Focus on {interests}.\n","Include essential info, budget breakdown, and local experiences.\n","\"\"\"\n","travel_team.print_response(\n"," query,\n"," stream=True\n",")"],"metadata":{"id":"FVDKZT9x_U-5"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Tracing this agent reveals the Orchestrator–Worker workflow in action. We can observe how tasks are explicitly delegated to individual sub-agents and how their outputs are combined to produce the final response."],"metadata":{"id":"bC13rYIHmTBF"}},{"cell_type":"markdown","source":["![Trace](https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-course-orchestrator-worker-trace.png)"],"metadata":{"id":"c86CFdhfmD_S"}},{"cell_type":"markdown","source":["# Agent Architecture 2: Parallelization Framework"],"metadata":{"id":"qfeZp0lp0P3b"}},{"cell_type":"markdown","source":["In the Parallelization framework, we run all sub-agents concurrently instead of sequentially. Each sub-agent works independently on its assigned task — for example, retrieving essential information, estimating budgets, or finding local experiences — while the main agent waits to gather their results. Once all sub-agents complete their work, the agent synthesizes their outputs into a unified response.\n","\n","This approach offers a significant latency advantage, as parallel execution reduces overall response time without compromising the quality or completeness of the final answer."],"metadata":{"id":"WYsyTGVNn_Xj"}},{"cell_type":"markdown","source":["![Diagram](https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-course-parallelization.png)"],"metadata":{"id":"NGSf4AApoXrN"}},{"cell_type":"code","source":["import asyncio\n","from agno.agent import Agent\n","from agno.models.openai import OpenAIChat\n"],"metadata":{"id":"H5PyEo4NJm0G"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["# --- Define Subagents ---\n","destination_agent = Agent(\n"," name=\"DestinationInfo\",\n"," model=OpenAIChat(id=\"gpt-4o\", temperature=0.2),\n"," tools=[essential_info],\n"," instructions=[\"Provide concise, reliable travel info for a destination using the essential_info tool.\"],\n",")\n","\n","budget_agent = Agent(\n"," name=\"Budget\",\n"," model=OpenAIChat(id=\"gpt-4o\"),\n"," tools=[budget_basics],\n"," instructions=[\"Give clear travel budget summaries with hotel, meal, and transport cost ranges; give multiple options with prices and locations.\"],\n",")\n","\n","local_activity_agent = Agent(\n"," name=\"ActivitySuggester\",\n"," model=OpenAIChat(id=\"gpt-4o\", temperature=0.8),\n"," tools=[local_flavor],\n"," instructions=[\n"," \"Group local activities by category: cultural, food, outdoors.\",\n"," \"Include both popular and hidden-gem recommendations.\"\n"," ],\n",")\n","\n","synthesizer = Agent(\n"," name=\"Synthesizer\",\n"," model=OpenAIChat(id=\"gpt-4o\", temperature=0.3),\n"," instructions=[\n"," \"Combine partial responses into a clear, well-structured final answer.\"\n"," ],\n",")\n"],"metadata":{"id":"jaGY7FLO2pEv"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["from opentelemetry import trace\n","from openinference.semconv.trace import SpanAttributes\n","\n","tracer = trace.get_tracer(__name__)\n","\n","# --- Run subagents concurrently and synthesize ---\n","async def plan_trip(destination: str, duration: str, interests: str):\n"," with tracer.start_as_current_span(\"ParallelizationAgent\") as span:\n"," span.set_attribute(SpanAttributes.OPENINFERENCE_SPAN_KIND, \"agent\")\n"," span.set_attribute(\"destination\", destination)\n"," span.set_attribute(\"duration\", duration)\n"," span.set_attribute(\"interests\", interests)\n","\n"," # Run all three subagents concurrently\n"," dest_task = destination_agent.arun(destination)\n"," budget_task = budget_agent.arun(duration)\n"," local_task = local_activity_agent.arun(interests)\n","\n"," dest_info, budget_info, activities = await asyncio.gather(dest_task, budget_task, local_task)\n","\n"," # Combine results via one final LLM call\n"," final_prompt = f\"\"\"\n"," Combine the following into a cohesive, well-structured travel plan for {destination}.\n"," Keep it friendly, natural, and under 1000 words.\n","\n"," [Destination Info]\n"," {dest_info}\n","\n"," [Budget Summary]\n"," {budget_info}\n","\n"," [Local Activities]\n"," {activities}\n"," \"\"\"\n","\n"," final_plan = await synthesizer.arun(final_prompt)\n"," return final_plan"],"metadata":{"id":"DKxb_Mmr2q1-"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["# --- Example usage ---\n","destination = \"Tokyo\"\n","duration = \"5 days\"\n","interests = \"food, culture\"\n","\n","final = await plan_trip(destination, duration, interests)"],"metadata":{"id":"bDUD1fB02xeK"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["![Traces](https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-course-parallel-agent.png)"],"metadata":{"id":"_Ocy3wnfopz9"}}]} \ No newline at end of file diff --git a/python/llm/agents/agent-mastery-course/labs/lab4_tools.ipynb b/python/llm/agents/agent-mastery-course/labs/lab4_tools.ipynb index 8eb0518..17549ea 100644 --- a/python/llm/agents/agent-mastery-course/labs/lab4_tools.ipynb +++ b/python/llm/agents/agent-mastery-course/labs/lab4_tools.ipynb @@ -101,13 +101,13 @@ "import os\n", "from getpass import getpass\n", "\n", - "os.environ[\"ARIZE_SPACE_ID\"] = globals().get(\"ARIZE_SPACE_ID\") or getpass(\"🔑 Enter your Arize Space ID: \")\n", + "os.environ[\"ARIZE_SPACE_ID\"] = os.environ.get(\"ARIZE_SPACE_ID\") or getpass(\"🔑 Enter your Arize Space ID: \")\n", "\n", - "os.environ[\"ARIZE_API_KEY\"] = globals().get(\"ARIZE_API_KEY\") or getpass(\"🔑 Enter your Arize API Key: \")\n", + "os.environ[\"ARIZE_API_KEY\"] = os.environ.get(\"ARIZE_API_KEY\") or getpass(\"🔑 Enter your Arize API Key: \")\n", "\n", - "os.environ[\"OPENAI_API_KEY\"] = globals().get(\"OPENAI_API_KEY\") or getpass(\"🔑 Enter your OpenAI API Key: \")\n", + "os.environ[\"OPENAI_API_KEY\"] = os.environ.get(\"OPENAI_API_KEY\") or getpass(\"🔑 Enter your OpenAI API Key: \")\n", "\n", - "os.environ[\"TAVILY_API_KEY\"] = globals().get(\"TAVILY_API_KEY\") or getpass(\"🔑 Enter your Tavily API Key: \")" + "os.environ[\"TAVILY_API_KEY\"] = os.environ.get(\"TAVILY_API_KEY\") or getpass(\"🔑 Enter your Tavily API Key: \")" ] }, { diff --git a/python/llm/agents/agent-mastery-course/labs/lab5_RAG.ipynb b/python/llm/agents/agent-mastery-course/labs/lab5_RAG.ipynb index df7fc7f..b33c9b9 100644 --- a/python/llm/agents/agent-mastery-course/labs/lab5_RAG.ipynb +++ b/python/llm/agents/agent-mastery-course/labs/lab5_RAG.ipynb @@ -1 +1 @@ -{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[],"authorship_tag":"ABX9TyOd3iwv05XxBi1lHH6TME+4"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["
\n","
\n","

\n"," \"arize\n","
\n"," Docs\n"," |\n"," GitHub\n"," |\n"," Slack Community\n","

\n","
"],"metadata":{"id":"Rc9Ohodqa_gY"}},{"cell_type":"markdown","source":["# **Arize Agent Mastry Course: RAG & Agentic RAG**"],"metadata":{"id":"V1HXBFLtbEgN"}},{"cell_type":"markdown","source":["In the previous lab, we explored tools in depth and saw how enhancing them can strengthen our agents’ responses. Another powerful way to improve performance is by using **Retrieval-Augmented Generation (RAG)** to give the agent access to specific data sources. In this lab, the agent will retrieve relevant documents from a vector database and use that information to answer queries. We’ll continue building on the agent we created earlier."],"metadata":{"id":"hmf1YyMqbYcx"}},{"cell_type":"markdown","source":["# Set Up"],"metadata":{"id":"U1A2VnzycKYf"}},{"cell_type":"code","execution_count":null,"metadata":{"id":"PieRuLGf7ugU"},"outputs":[],"source":["!pip install -qqqqqqqq arize-otel agno openai openinference-instrumentation-agno openinference-instrumentation-openai httpx chromadb sentence-transformers"]},{"cell_type":"code","source":["import os\n","from getpass import getpass\n","\n","os.environ[\"ARIZE_SPACE_ID\"] = globals().get(\"ARIZE_SPACE_ID\") or getpass(\"🔑 Enter your Arize Space ID: \")\n","\n","os.environ[\"ARIZE_API_KEY\"] = globals().get(\"ARIZE_API_KEY\") or getpass(\"🔑 Enter your Arize API Key: \")\n","\n","os.environ[\"OPENAI_API_KEY\"] = globals().get(\"OPENAI_API_KEY\") or getpass(\"🔑 Enter your OpenAI API Key: \")\n","\n","os.environ[\"TAVILY_API_KEY\"] = globals().get(\"TAVILY_API_KEY\") or getpass(\"🔑 Enter your Tavily API Key: \")"],"metadata":{"id":"mfgANTGsqO6x"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["from arize.otel import register\n","from openinference.instrumentation.openai import OpenAIInstrumentor\n","from openinference.instrumentation.agno import AgnoInstrumentor\n","\n","model_id = \"travel-agent-demo\"\n","tracer_provider = register(\n"," space_id=os.getenv(\"ARIZE_SPACE_ID\"),\n"," api_key=os.getenv(\"ARIZE_API_KEY\"),\n"," project_name=model_id,\n"," set_global_tracer_provider=True\n",")\n","OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)\n","AgnoInstrumentor().instrument(tracer_provider=tracer_provider)"],"metadata":{"id":"3PZNGrszQJ-n"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["# Define Tools"],"metadata":{"id":"Ogbl-o3IQ90I"}},{"cell_type":"markdown","source":["The tool implementation for `essential_info` and `budget_basics` is unchanged."],"metadata":{"id":"dPaFROIZcPeG"}},{"cell_type":"code","source":["# --- Helper functions for tools ---\n","import httpx\n","from opentelemetry import trace\n","\n","tracer = trace.get_tracer(__name__)\n","\n","@tracer.chain(name=\"search-api\")\n","def _search_api(query: str) -> str | None:\n"," \"\"\"Try Tavily search first, fall back to None.\"\"\"\n"," tavily_key = os.getenv(\"TAVILY_API_KEY\")\n"," if not tavily_key:\n"," return None\n"," try:\n"," resp = httpx.post(\n"," \"https://api.tavily.com/search\",\n"," json={\n"," \"api_key\": tavily_key,\n"," \"query\": query,\n"," \"max_results\": 3,\n"," \"search_depth\": \"basic\",\n"," \"include_answer\": True,\n"," },\n"," timeout=8,\n"," )\n"," data = resp.json()\n"," answer = data.get(\"answer\") or \"\"\n"," snippets = [r.get(\"content\", \"\") for r in data.get(\"results\", [])]\n"," combined = \" \".join([answer] + snippets).strip()\n"," return combined[:400] if combined else None\n"," except Exception:\n"," return None\n","\n","def _compact(text: str, limit: int = 200) -> str:\n"," \"\"\"Compact text for cleaner outputs.\"\"\"\n"," cleaned = \" \".join(text.split())\n"," return cleaned if len(cleaned) <= limit else cleaned[:limit].rsplit(\" \", 1)[0]\n"],"metadata":{"id":"zrAqXWtxQLyl"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["# APIs for Essential Info Tool\n","import httpx\n","from urllib.parse import quote\n","from typing import Optional\n","\n","@tracer.chain(name=\"wiki-summary-api\")\n","def _wiki_summary(dest: str) -> str:\n"," if not dest:\n"," return \"\"\n"," encoded_dest = quote(dest)\n","\n"," url = f\"https://en.wikipedia.org/api/rest_v1/page/summary/{encoded_dest}\"\n"," HEADERS = { 'User-Agent': 'MyArizeApp/1.0 (ExampleContac@example.com)'}\n","\n"," try:\n"," r = httpx.get(url, headers = HEADERS, timeout=5)\n"," r.raise_for_status()\n","\n"," data = r.json().get(\"extract\")\n"," return data if data else \"\"\n","\n"," except httpx.HTTPStatusError as e:\n"," if e.response.status_code == 404:\n"," return \"\"\n"," return \"\"\n"," except httpx.RequestError as e:\n"," return \"\"\n"," except Exception as e:\n"," return \"\"\n","\n","@tracer.chain(name=\"weather-api\")\n","def _weather(dest):\n"," g = httpx.get(f\"https://geocoding-api.open-meteo.com/v1/search?name={dest}\")\n"," if g.status_code != 200 or not g.json().get(\"results\"):\n"," return \"\"\n"," lat, lon = g.json()[\"results\"][0][\"latitude\"], g.json()[\"results\"][0][\"longitude\"]\n"," w = httpx.get(f\"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}¤t_weather=true\").json()\n"," cw = w.get(\"current_weather\", {})\n"," return f\"Weather now: {cw.get('temperature')}°C, wind {cw.get('windspeed')} km/h.\""],"metadata":{"id":"JAXZhxYFQNBJ"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["from agno.tools import tool\n","\n","@tool\n","def essential_info(destination: str) -> str:\n"," \"\"\"Get essential info (summary and weather) using APIs\"\"\"\n"," parts = []\n"," wiki = _wiki_summary(destination)\n"," if wiki: parts.append(wiki)\n"," weather = _weather(destination)\n"," if weather: parts.append(weather)\n"," return f\"{destination} essentials:\\n\" + \"\\n\".join(parts)\n","\n","@tool\n","def budget_basics(destination: str, duration: str) -> str:\n"," \"\"\"Summarize travel cost categories.\"\"\"\n"," q = f\"{destination} travel budget average daily costs {duration}\"\n"," s = _search_api(q)\n"," if s:\n"," return f\"{destination} budget ({duration}): {_compact(s)}\"\n"," return f\"Budget for {duration} in {destination} depends on lodging, meals, transport, and attractions.\""],"metadata":{"id":"b99WNnmEXfgU"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["# Create RAG System for Local Flavor Tool"],"metadata":{"id":"uNkrWM89RAA1"}},{"cell_type":"markdown","source":["Now it’s time to make our `local_flavor` tool even smarter by giving it access to a rich database of travel destination insights. We’ll use ChromaDB as the vector database and a Sentence Transformer model to generate embeddings that allow the tool to find and retrieve the most relevant information."],"metadata":{"id":"STcaFYqJcZPM"}},{"cell_type":"code","source":["import chromadb\n","from sentence_transformers import SentenceTransformer\n","\n","chroma_client = chromadb.Client()\n","embedding_model = SentenceTransformer('all-MiniLM-L6-v2')\n","\n","# Create collection for local guides\n","collection = chroma_client.create_collection(\n"," name=\"local_guides\",\n"," metadata={\"hnsw:space\": \"cosine\"}\n",")\n","\n","print(\"✅ RAG system initialized with ChromaDB and sentence-transformers\")"],"metadata":{"id":"xn0R4FHFUpG7"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Download and upload `local_flavor.json` file provided to you here:"],"metadata":{"id":"HKJOkyQ1REek"}},{"cell_type":"code","source":["from google.colab import files\n","guide = files.upload()"],"metadata":{"id":"abGYubKMTdO5"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["import json\n","\n","def load_and_index_guides():\n","\n"," with open('local_guides.json', 'r') as f:\n"," guides = json.load(f)\n","\n"," # Prepare data for ChromaDB\n"," documents = []\n"," metadatas = []\n"," ids = []\n","\n"," for i, guide in enumerate(guides):\n"," # Create a rich text representation for embedding\n"," text = f\"City: {guide['city']}. Interests: {', '.join(guide['interests'])}. Experience: {guide['description']}\"\n","\n"," documents.append(text)\n"," metadatas.append({\n"," \"city\": guide[\"city\"],\n"," \"interests\": \", \".join(guide[\"interests\"]), # ✅ make it a string\n"," \"source\": guide[\"source\"],\n"," \"description\": guide[\"description\"]\n"," })\n"," ids.append(f\"guide_{i}\")\n","\n"," # Add to ChromaDB collection\n"," collection.add(\n"," documents=documents,\n"," metadatas=metadatas,\n"," ids=ids\n"," )\n","\n"," print(f\"✅ Indexed {len(documents)} experiences in vector database\")\n"," return len(documents)\n","\n","# Load the data\n","num_guides = load_and_index_guides()\n"],"metadata":{"id":"IPykxefuRKbP"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["from sentence_transformers import SentenceTransformer\n","from openinference.semconv.trace import SpanAttributes, DocumentAttributes\n","\n","# Initialize embedding model (same one you used for indexing)\n","embedding_model = SentenceTransformer('all-MiniLM-L6-v2')\n","\n","@tool\n","def local_flavor(destination: str, interests: str = \"local culture\") -> str:\n"," \"\"\"Suggest authentic local experiences using vector retrieval from Chroma.\"\"\"\n"," with tracer.start_as_current_span(name=\"RAG\", attributes={SpanAttributes.OPENINFERENCE_SPAN_KIND: \"retriever\"}) as span:\n"," # Construct the query text\n"," query_text = f\"{destination} {interests} authentic experiences\"\n"," span.set_attribute(SpanAttributes.INPUT_VALUE, query_text)\n","\n"," # Embed the query\n"," query_embedding = embedding_model.encode([query_text])\n","\n"," # Search in Chroma collection\n"," results = collection.query(\n"," query_embeddings=query_embedding,\n"," n_results=3 # how many guides to retrieve\n"," )\n","\n"," # If nothing found\n"," if not results or not results.get(\"documents\"):\n"," return f\"Explore {destination}'s unique {interests} through markets, neighborhoods, and local eateries.\"\n","\n"," # Extract retrieved guides\n"," retrieved_docs = results[\"documents\"][0]\n"," retrieved_meta = results[\"metadatas\"][0]\n"," for i, doc in enumerate(retrieved_docs):\n"," span.set_attribute(f\"retrieval.documents.{i}.document.id\", f\"doc_{i}\")\n"," span.set_attribute(f\"retrieval.documents.{i}.document.content\", doc)\n","\n"," # Format a nice summary\n"," suggestions = []\n"," for doc, meta in zip(retrieved_docs, retrieved_meta):\n"," suggestion = f\"📍 **{meta['city']}** — {meta['description']} (Interests: {meta['interests']})\"\n"," suggestions.append(suggestion)\n","\n"," # Combine into one readable response\n"," response = f\"Here are some authentic {interests} experiences near {destination}:\\n\\n\" + \"\\n\\n\".join(suggestions)\n"," span.set_attribute(SpanAttributes.OUTPUT_VALUE, response)\n","\n"," return response\n"],"metadata":{"id":"vdNq15_QXbDF"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["# Define Agent"],"metadata":{"id":"L5NL30n6RJN3"}},{"cell_type":"code","source":["from agno.agent import Agent\n","from agno.models.openai import OpenAIChat\n","\n","# --- Main Agent ---\n","trip_agent = Agent(\n"," name=\"TripPlanner\",\n"," role=\"AI Travel Assistant\",\n"," model=OpenAIChat(id=\"gpt-4.1\"),\n"," instructions=(\n"," \"You are a friendly and knowledgeable travel planner. \"\n"," \"Combine multiple tools to create a trip plan including essentials, budget, and local flavor. \"\n"," \"Keep the tone natural, clear, and under 1000 words.\"\n"," ),\n"," markdown=True,\n"," tools=[essential_info, budget_basics, local_flavor],\n",")"],"metadata":{"id":"gOPVdh4dQOjB"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["# --- Run the Agent ---\n","destination = \"Dubai\"\n","duration = \"5 days\"\n","interests = \"history, wellness\"\n","\n","query = f\"\"\"\n","Plan a {duration} trip to {destination}.\n","Focus on {interests}.\n","Include essential info, budget breakdown, and local experiences.\n","\"\"\"\n","trip_agent.print_response(\n"," query,\n"," stream=True\n",")"],"metadata":{"id":"8Bb5fyY0QPuW"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Now, if we inspect the trace, we can see that the `local_flavor` tool retrieves documents from the vector database. These retrieved documents are then used to generate tailored local recommendations."],"metadata":{"id":"s-FicGzGfrG9"}},{"cell_type":"markdown","source":["![RAG](https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-course-rag-lab.png)"],"metadata":{"id":"fVBVn08NfpO3"}}]} \ No newline at end of file +{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[],"authorship_tag":"ABX9TyOd3iwv05XxBi1lHH6TME+4"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["
\n","
\n","

\n"," \"arize\n","
\n"," Docs\n"," |\n"," GitHub\n"," |\n"," Slack Community\n","

\n","
"],"metadata":{"id":"Rc9Ohodqa_gY"}},{"cell_type":"markdown","source":["# **Arize Agent Mastry Course: RAG & Agentic RAG**"],"metadata":{"id":"V1HXBFLtbEgN"}},{"cell_type":"markdown","source":["In the previous lab, we explored tools in depth and saw how enhancing them can strengthen our agents’ responses. Another powerful way to improve performance is by using **Retrieval-Augmented Generation (RAG)** to give the agent access to specific data sources. In this lab, the agent will retrieve relevant documents from a vector database and use that information to answer queries. We’ll continue building on the agent we created earlier."],"metadata":{"id":"hmf1YyMqbYcx"}},{"cell_type":"markdown","source":["# Set Up"],"metadata":{"id":"U1A2VnzycKYf"}},{"cell_type":"code","execution_count":null,"metadata":{"id":"PieRuLGf7ugU"},"outputs":[],"source":["!pip install -qqqqqqqq arize-otel agno openai openinference-instrumentation-agno openinference-instrumentation-openai httpx chromadb sentence-transformers"]},{"cell_type":"code","source":["import os\n","from getpass import getpass\n","\n","os.environ[\"ARIZE_SPACE_ID\"] = os.environ.get(\"ARIZE_SPACE_ID\") or getpass(\"🔑 Enter your Arize Space ID: \")\n","\n","os.environ[\"ARIZE_API_KEY\"] = os.environ.get(\"ARIZE_API_KEY\") or getpass(\"🔑 Enter your Arize API Key: \")\n","\n","os.environ[\"OPENAI_API_KEY\"] = os.environ.get(\"OPENAI_API_KEY\") or getpass(\"🔑 Enter your OpenAI API Key: \")\n","\n","os.environ[\"TAVILY_API_KEY\"] = os.environ.get(\"TAVILY_API_KEY\") or getpass(\"🔑 Enter your Tavily API Key: \")"],"metadata":{"id":"mfgANTGsqO6x"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["from arize.otel import register\n","from openinference.instrumentation.openai import OpenAIInstrumentor\n","from openinference.instrumentation.agno import AgnoInstrumentor\n","\n","model_id = \"travel-agent-demo\"\n","tracer_provider = register(\n"," space_id=os.getenv(\"ARIZE_SPACE_ID\"),\n"," api_key=os.getenv(\"ARIZE_API_KEY\"),\n"," project_name=model_id,\n"," set_global_tracer_provider=True\n",")\n","OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)\n","AgnoInstrumentor().instrument(tracer_provider=tracer_provider)"],"metadata":{"id":"3PZNGrszQJ-n"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["# Define Tools"],"metadata":{"id":"Ogbl-o3IQ90I"}},{"cell_type":"markdown","source":["The tool implementation for `essential_info` and `budget_basics` is unchanged."],"metadata":{"id":"dPaFROIZcPeG"}},{"cell_type":"code","source":["# --- Helper functions for tools ---\n","import httpx\n","from opentelemetry import trace\n","\n","tracer = trace.get_tracer(__name__)\n","\n","@tracer.chain(name=\"search-api\")\n","def _search_api(query: str) -> str | None:\n"," \"\"\"Try Tavily search first, fall back to None.\"\"\"\n"," tavily_key = os.getenv(\"TAVILY_API_KEY\")\n"," if not tavily_key:\n"," return None\n"," try:\n"," resp = httpx.post(\n"," \"https://api.tavily.com/search\",\n"," json={\n"," \"api_key\": tavily_key,\n"," \"query\": query,\n"," \"max_results\": 3,\n"," \"search_depth\": \"basic\",\n"," \"include_answer\": True,\n"," },\n"," timeout=8,\n"," )\n"," data = resp.json()\n"," answer = data.get(\"answer\") or \"\"\n"," snippets = [r.get(\"content\", \"\") for r in data.get(\"results\", [])]\n"," combined = \" \".join([answer] + snippets).strip()\n"," return combined[:400] if combined else None\n"," except Exception:\n"," return None\n","\n","def _compact(text: str, limit: int = 200) -> str:\n"," \"\"\"Compact text for cleaner outputs.\"\"\"\n"," cleaned = \" \".join(text.split())\n"," return cleaned if len(cleaned) <= limit else cleaned[:limit].rsplit(\" \", 1)[0]\n"],"metadata":{"id":"zrAqXWtxQLyl"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["# APIs for Essential Info Tool\n","import httpx\n","from urllib.parse import quote\n","from typing import Optional\n","\n","@tracer.chain(name=\"wiki-summary-api\")\n","def _wiki_summary(dest: str) -> str:\n"," if not dest:\n"," return \"\"\n"," encoded_dest = quote(dest)\n","\n"," url = f\"https://en.wikipedia.org/api/rest_v1/page/summary/{encoded_dest}\"\n"," HEADERS = { 'User-Agent': 'MyArizeApp/1.0 (ExampleContac@example.com)'}\n","\n"," try:\n"," r = httpx.get(url, headers = HEADERS, timeout=5)\n"," r.raise_for_status()\n","\n"," data = r.json().get(\"extract\")\n"," return data if data else \"\"\n","\n"," except httpx.HTTPStatusError as e:\n"," if e.response.status_code == 404:\n"," return \"\"\n"," return \"\"\n"," except httpx.RequestError as e:\n"," return \"\"\n"," except Exception as e:\n"," return \"\"\n","\n","@tracer.chain(name=\"weather-api\")\n","def _weather(dest):\n"," g = httpx.get(f\"https://geocoding-api.open-meteo.com/v1/search?name={dest}\")\n"," if g.status_code != 200 or not g.json().get(\"results\"):\n"," return \"\"\n"," lat, lon = g.json()[\"results\"][0][\"latitude\"], g.json()[\"results\"][0][\"longitude\"]\n"," w = httpx.get(f\"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}¤t_weather=true\").json()\n"," cw = w.get(\"current_weather\", {})\n"," return f\"Weather now: {cw.get('temperature')}°C, wind {cw.get('windspeed')} km/h.\""],"metadata":{"id":"JAXZhxYFQNBJ"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["from agno.tools import tool\n","\n","@tool\n","def essential_info(destination: str) -> str:\n"," \"\"\"Get essential info (summary and weather) using APIs\"\"\"\n"," parts = []\n"," wiki = _wiki_summary(destination)\n"," if wiki: parts.append(wiki)\n"," weather = _weather(destination)\n"," if weather: parts.append(weather)\n"," return f\"{destination} essentials:\\n\" + \"\\n\".join(parts)\n","\n","@tool\n","def budget_basics(destination: str, duration: str) -> str:\n"," \"\"\"Summarize travel cost categories.\"\"\"\n"," q = f\"{destination} travel budget average daily costs {duration}\"\n"," s = _search_api(q)\n"," if s:\n"," return f\"{destination} budget ({duration}): {_compact(s)}\"\n"," return f\"Budget for {duration} in {destination} depends on lodging, meals, transport, and attractions.\""],"metadata":{"id":"b99WNnmEXfgU"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["# Create RAG System for Local Flavor Tool"],"metadata":{"id":"uNkrWM89RAA1"}},{"cell_type":"markdown","source":["Now it’s time to make our `local_flavor` tool even smarter by giving it access to a rich database of travel destination insights. We’ll use ChromaDB as the vector database and a Sentence Transformer model to generate embeddings that allow the tool to find and retrieve the most relevant information."],"metadata":{"id":"STcaFYqJcZPM"}},{"cell_type":"code","source":["import chromadb\n","from sentence_transformers import SentenceTransformer\n","\n","chroma_client = chromadb.Client()\n","embedding_model = SentenceTransformer('all-MiniLM-L6-v2')\n","\n","# Create collection for local guides\n","collection = chroma_client.create_collection(\n"," name=\"local_guides\",\n"," metadata={\"hnsw:space\": \"cosine\"}\n",")\n","\n","print(\"✅ RAG system initialized with ChromaDB and sentence-transformers\")"],"metadata":{"id":"xn0R4FHFUpG7"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Download and upload `local_flavor.json` file provided to you here:"],"metadata":{"id":"HKJOkyQ1REek"}},{"cell_type":"code","source":["from google.colab import files\n","guide = files.upload()"],"metadata":{"id":"abGYubKMTdO5"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["import json\n","\n","def load_and_index_guides():\n","\n"," with open('local_guides.json', 'r') as f:\n"," guides = json.load(f)\n","\n"," # Prepare data for ChromaDB\n"," documents = []\n"," metadatas = []\n"," ids = []\n","\n"," for i, guide in enumerate(guides):\n"," # Create a rich text representation for embedding\n"," text = f\"City: {guide['city']}. Interests: {', '.join(guide['interests'])}. Experience: {guide['description']}\"\n","\n"," documents.append(text)\n"," metadatas.append({\n"," \"city\": guide[\"city\"],\n"," \"interests\": \", \".join(guide[\"interests\"]), # ✅ make it a string\n"," \"source\": guide[\"source\"],\n"," \"description\": guide[\"description\"]\n"," })\n"," ids.append(f\"guide_{i}\")\n","\n"," # Add to ChromaDB collection\n"," collection.add(\n"," documents=documents,\n"," metadatas=metadatas,\n"," ids=ids\n"," )\n","\n"," print(f\"✅ Indexed {len(documents)} experiences in vector database\")\n"," return len(documents)\n","\n","# Load the data\n","num_guides = load_and_index_guides()\n"],"metadata":{"id":"IPykxefuRKbP"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["from sentence_transformers import SentenceTransformer\n","from openinference.semconv.trace import SpanAttributes, DocumentAttributes\n","\n","# Initialize embedding model (same one you used for indexing)\n","embedding_model = SentenceTransformer('all-MiniLM-L6-v2')\n","\n","@tool\n","def local_flavor(destination: str, interests: str = \"local culture\") -> str:\n"," \"\"\"Suggest authentic local experiences using vector retrieval from Chroma.\"\"\"\n"," with tracer.start_as_current_span(name=\"RAG\", attributes={SpanAttributes.OPENINFERENCE_SPAN_KIND: \"retriever\"}) as span:\n"," # Construct the query text\n"," query_text = f\"{destination} {interests} authentic experiences\"\n"," span.set_attribute(SpanAttributes.INPUT_VALUE, query_text)\n","\n"," # Embed the query\n"," query_embedding = embedding_model.encode([query_text])\n","\n"," # Search in Chroma collection\n"," results = collection.query(\n"," query_embeddings=query_embedding,\n"," n_results=3 # how many guides to retrieve\n"," )\n","\n"," # If nothing found\n"," if not results or not results.get(\"documents\"):\n"," return f\"Explore {destination}'s unique {interests} through markets, neighborhoods, and local eateries.\"\n","\n"," # Extract retrieved guides\n"," retrieved_docs = results[\"documents\"][0]\n"," retrieved_meta = results[\"metadatas\"][0]\n"," for i, doc in enumerate(retrieved_docs):\n"," span.set_attribute(f\"retrieval.documents.{i}.document.id\", f\"doc_{i}\")\n"," span.set_attribute(f\"retrieval.documents.{i}.document.content\", doc)\n","\n"," # Format a nice summary\n"," suggestions = []\n"," for doc, meta in zip(retrieved_docs, retrieved_meta):\n"," suggestion = f\"📍 **{meta['city']}** — {meta['description']} (Interests: {meta['interests']})\"\n"," suggestions.append(suggestion)\n","\n"," # Combine into one readable response\n"," response = f\"Here are some authentic {interests} experiences near {destination}:\\n\\n\" + \"\\n\\n\".join(suggestions)\n"," span.set_attribute(SpanAttributes.OUTPUT_VALUE, response)\n","\n"," return response\n"],"metadata":{"id":"vdNq15_QXbDF"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["# Define Agent"],"metadata":{"id":"L5NL30n6RJN3"}},{"cell_type":"code","source":["from agno.agent import Agent\n","from agno.models.openai import OpenAIChat\n","\n","# --- Main Agent ---\n","trip_agent = Agent(\n"," name=\"TripPlanner\",\n"," role=\"AI Travel Assistant\",\n"," model=OpenAIChat(id=\"gpt-4.1\"),\n"," instructions=(\n"," \"You are a friendly and knowledgeable travel planner. \"\n"," \"Combine multiple tools to create a trip plan including essentials, budget, and local flavor. \"\n"," \"Keep the tone natural, clear, and under 1000 words.\"\n"," ),\n"," markdown=True,\n"," tools=[essential_info, budget_basics, local_flavor],\n",")"],"metadata":{"id":"gOPVdh4dQOjB"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["# --- Run the Agent ---\n","destination = \"Dubai\"\n","duration = \"5 days\"\n","interests = \"history, wellness\"\n","\n","query = f\"\"\"\n","Plan a {duration} trip to {destination}.\n","Focus on {interests}.\n","Include essential info, budget breakdown, and local experiences.\n","\"\"\"\n","trip_agent.print_response(\n"," query,\n"," stream=True\n",")"],"metadata":{"id":"8Bb5fyY0QPuW"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["Now, if we inspect the trace, we can see that the `local_flavor` tool retrieves documents from the vector database. These retrieved documents are then used to generate tailored local recommendations."],"metadata":{"id":"s-FicGzGfrG9"}},{"cell_type":"markdown","source":["![RAG](https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-course-rag-lab.png)"],"metadata":{"id":"fVBVn08NfpO3"}}]} \ No newline at end of file diff --git a/python/llm/agents/agent-mastery-course/labs/lab6_evals.ipynb b/python/llm/agents/agent-mastery-course/labs/lab6_evals.ipynb index 45f5885..42e0711 100644 --- a/python/llm/agents/agent-mastery-course/labs/lab6_evals.ipynb +++ b/python/llm/agents/agent-mastery-course/labs/lab6_evals.ipynb @@ -100,13 +100,13 @@ "import os\n", "from getpass import getpass\n", "\n", - "os.environ[\"ARIZE_SPACE_ID\"] = globals().get(\"ARIZE_SPACE_ID\") or getpass(\"🔑 Enter your Arize Space ID: \")\n", + "os.environ[\"ARIZE_SPACE_ID\"] = os.environ.get(\"ARIZE_SPACE_ID\") or getpass(\"🔑 Enter your Arize Space ID: \")\n", "\n", - "os.environ[\"ARIZE_API_KEY\"] = globals().get(\"ARIZE_API_KEY\") or getpass(\"🔑 Enter your Arize API Key: \")\n", + "os.environ[\"ARIZE_API_KEY\"] = os.environ.get(\"ARIZE_API_KEY\") or getpass(\"🔑 Enter your Arize API Key: \")\n", "\n", - "os.environ[\"OPENAI_API_KEY\"] = globals().get(\"OPENAI_API_KEY\") or getpass(\"🔑 Enter your OpenAI API Key: \")\n", + "os.environ[\"OPENAI_API_KEY\"] = os.environ.get(\"OPENAI_API_KEY\") or getpass(\"🔑 Enter your OpenAI API Key: \")\n", "\n", - "os.environ[\"TAVILY_API_KEY\"] = globals().get(\"TAVILY_API_KEY\") or getpass(\"🔑 Enter your Tavily API Key: \")" + "os.environ[\"TAVILY_API_KEY\"] = os.environ.get(\"TAVILY_API_KEY\") or getpass(\"🔑 Enter your Tavily API Key: \")" ] }, { diff --git a/python/llm/agents/agent-mastery-course/requirements-labs.txt b/python/llm/agents/agent-mastery-course/requirements-labs.txt new file mode 100644 index 0000000..b6c72af --- /dev/null +++ b/python/llm/agents/agent-mastery-course/requirements-labs.txt @@ -0,0 +1,10 @@ +# Labs 1-4 + 6 (labs 5 adds chromadb+sentence-transformers via notebook cell) +arize-otel>=0.8.1 +arize>=8.0.0,<9.0.0 +arize-phoenix>=8.0.0 +agno>=1.0.0 +openai>=1.0.0 +openinference-instrumentation-agno>=0.1.0 +openinference-instrumentation-openai>=0.1.32 +openinference-instrumentation-mcp>=1.3.0 +httpx>=0.27.0