diff --git a/docs/assets/react-prompting.png b/docs/assets/react-prompting.png
new file mode 100644
index 000000000..83d28614b
Binary files /dev/null and b/docs/assets/react-prompting.png differ
diff --git a/docs/tutorials-list.md b/docs/tutorials-list.md
index bcf0c02d4..7a95c6551 100644
--- a/docs/tutorials-list.md
+++ b/docs/tutorials-list.md
@@ -62,6 +62,7 @@
- [Use role prompting with IBM watsonx and Granite](./tutorials/prompt-engineering/role-prompting-tutorial.ipynb)
- [Perform zero-shot classification with a foundation model](./tutorials/generative-ai/zero-shot-classification.ipynb)
- [Prompt Caching](./tutorials/prompt-engineering/Prompt_Caching.ipynb)
+- [ReAct prompting](./tutorials/prompt-engineering/ReAct.ipynb)
## Tooling
diff --git a/docs/tutorials/prompt-engineering/ReAct.ipynb b/docs/tutorials/prompt-engineering/ReAct.ipynb
new file mode 100644
index 000000000..3436ef330
--- /dev/null
+++ b/docs/tutorials/prompt-engineering/ReAct.ipynb
@@ -0,0 +1,662 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# ReAct Prompting for Financial Insight: Classification and Summarization with Granite 4.0 Nano"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Author: Vrunda Gadesha"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This tutorial shows how to build an AI agent using the ReAct (Reasoning and Acting) framework and the IBM Granite 4.0 Nano language model. The agent uses prompt engineering to combine reasoning steps with an action plan. It processes news URLs, retrieves data from external sources, and generates a structured financial analysis in JSON format. This approach ensures clear, transparent, and grounded results."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Before we jumpt into the code, Let's understand some core concepts.\n",
+ "\n",
+ "**What is ReAct Prompting?**\n",
+ "\n",
+ "The ReAct framework enhances large language models (LLMs) by combining internal reasoning with external actions. It integrates Reasoning (Chain-of-Thought, or CoT) and Acting (Tool Use) to create an effective ReAct agent capable of dynamic decision-making.\n",
+ "\n",
+ "- **Thought (Reasoning):** The model generates reasoning traces that break down the task, track progress, and plan the next logical step. These traces can operate in few-shot or zero-shot settings, depending on prompt design.\n",
+ "- **Action (Acting):** The model follows an action plan to execute tool commands, such as web_browser(url), often managed through frameworks like LangChain.\n",
+ "- **Observation:** The agent retrieves external information or data from a knowledge base after executing an action.\n",
+ "\n",
+ "ReAct’s Benefits: It grounds the model in verified external data rather than internal training memory, improving transparency and supporting reliable decision-making in complex tasks."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Introduction to Granite 4.0 Nano\n",
+ "\n",
+ "We use the IBM Granite 4.0 Nano model family, specifically the 1B-instruct variant. This AI model is finetuned for reasoning and instruction-following tasks, making it suitable for building intelligent agents.\n",
+ "\n",
+ "- **Efficiency:** Optimized for fast, low-latency inference through its lightweight architecture and efficient API integration, comparable to how OpenAI models like ChatGPT are deployed.\n",
+ "- **Agentic Capability:** Instruction-tuned to follow complex, multi-step prompts with self-consistency in the model’s reasoning. It handles both structured and verbal reasoning, recognizes tool schemas, and produces reliable, JSON-formatted outputs.\n",
+ "\n",
+ "The Granite 4.0 Nano model demonstrates how compact, finetuned models can achieve strong performance and interpretability similar to larger commercial AI models."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Usecase"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Financial news is rarely straightforward. Articles often mix conflicting signals — for example, a company may report strong sales (positive signal) but its stock may fall due to weak guidance or higher expenses (negative reaction).\n",
+ "\n",
+ "A single-step summary cannot capture this nuance. The ReAct agent improves question answering and reasoning by working step by step, combining reasoning and action in interleaved trajectories. It uses in-context examples and fact verification to ensure each action step aligns with reliable real-world information.\n",
+ "\n",
+ "The agent converts raw news text into structured financial intelligence. It delivers results as one JSON object using Python-based workflows built on machine learning and retrieval techniques like RAG (Retrieval-Augmented Generation).\n",
+ "\n",
+ "Classification:\n",
+ "\n",
+ "- **Sentiment:** Categorizes the market impact (Positive, Negative, Mixed, Neutral).\n",
+ "- **Topic:** Identifies the main business driver (Earnings, Regulatory, HR/Layoffs, Supply Chain, etc.).\n",
+ "- **Summary:** Produces a concise executive summary (3–5 key points) with fact verification, avoiding verbose output.\n",
+ "\n",
+ "The strict JSON schema ensures outputs are instantly consumable by downstream systems and comparable to benchmarks such as HotpotQA or other baseline datasets. This approach supports accurate, real-time financial insight and a grounded final answer."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Prerequisites\n",
+ "\n",
+ "You can execute this tutorial on your local machine using your own Python and Jupyter environment. The local setup gives you full control and flexibility. To run this tutorial, uou must have following setup in your machine. \n",
+ "\n",
+ "To execute the notebook on your local Windows or macOS machine, ensure the following setup:\n",
+ "- Operating system: Windows 10 or later, or macOS 12 or later\n",
+ "- Python: Version 3.10 or newer\n",
+ "- RAM: Minimum 8 GB (16 GB recommended for larger workloads)\n",
+ "- Tools: Jupyter Notebook or JupyterLab installed"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Steps"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 1. Install Dependencies\n",
+ "\n",
+ "To run the Granite 4.0 Nano model and ReAct workflow, you need the core libraries that handle model loading and computation. Installing them ensures your environment can execute reasoning and action steps smoothly."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "!pip install torch torchvision torchaudio --quiet\n",
+ "!pip install accelerate transformers --quiet"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 2. Load the Model and Tokenizer\n",
+ "\n",
+ "This step loads the Granite 4.0 Nano instruction‑tuned model and its tokenizer from Hugging Face. The tokenizer converts text to tokens, and the model generates responses. The code automatically selects GPU if available, otherwise runs on CPU."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Loading model 'ibm-granite/granite-4.0-1b' on cpu...\n",
+ "Model and tokenizer loaded successfully.\n"
+ ]
+ }
+ ],
+ "source": [
+ "import torch\n",
+ "from transformers import AutoModelForCausalLM, AutoTokenizer\n",
+ "\n",
+ "device = \"cpu\" # Use \"cuda\" if GPU is available\n",
+ "MODEL_NAME = \"ibm-granite/granite-4.0-1b\"\n",
+ "\n",
+ "print(f\"Loading model '{MODEL_NAME}' on {device}...\")\n",
+ "\n",
+ "tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)\n",
+ "model = AutoModelForCausalLM.from_pretrained(MODEL_NAME).to(device)\n",
+ "model.eval()\n",
+ "\n",
+ "print(\"Model and tokenizer loaded successfully.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "***Note: Model loading takes about 2–4 minutes on 8 GB RAM and 1–2 minutes on 16 GB RAM; if you see an “out of memory” error on 8 GB systems, restart the kernel and close other applications before retrying.***"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 3. Define the Data\n",
+ "\n",
+ "This step creates a small dataset of five financial news summaries used to test the ReAct agent. The articles are drawn from different sectors — biotechnology (Lenz Therapeutics), payments (Shift4 Payments), lidar technology (Luminar), electric vehicles (Lucid), and consumer goods (Celsius).\n",
+ "\n",
+ "Each article includes mixed market signals such as strong earnings but weak guidance, higher costs, or regulatory challenges. This mix helps evaluate how the agent handles conflicting information and classifies sentiment accurately."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Simulated financial data defined.\n"
+ ]
+ }
+ ],
+ "source": [
+ "SIMULATED_ARTICLE_DATA = {\n",
+ " \"https://ir.lenz-tx.com/news-events/press-releases/detail/43/lenz-therapeutics-reports-third-quarter-2025-financial-results-and-recent-corporate-highlights\": \n",
+ " \"\"\"\n",
+ " LENZ Therapeutics reported Q3 2025 results. The company launched VIZZ, saw 5,000 prescriptions, and received $10M in milestone payments. Operating expenses rose 44%, driven by SG&A. Net loss: $16.7M. Stock fell 9.4% premarket.\n",
+ " \"\"\",\n",
+ " \"https://www.investing.com/news/transcripts/earnings-call-transcript-shift4-payments-q3-2025-shows-strong-growth-93CH-4339020\":\n",
+ " \"\"\"\n",
+ " Shift4 Payments reported EPS of $1.47 (beat) and revenue of $589.2M (miss). Stock up 5.7% premarket after recent weakness.\n",
+ " \"\"\",\n",
+ " \"https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615\":\n",
+ " \"\"\"\n",
+ " Luminar faces SEC investigation, missed payments, CFO exit, and workforce cuts. Bankruptcy risk rising amid liquidity issues.\n",
+ " \"\"\",\n",
+ " \"https://www.cbtnews.com/lucid-trims-production-forecast-misses-q3-expectations/\":\n",
+ " \"\"\"\n",
+ " Lucid missed Q3 targets with $336.6M revenue. Loss widened to $2.65 per share. Production forecast trimmed due to supply chain issues.\n",
+ " \"\"\",\n",
+ " \"https://www.investing.com/news/transcripts/earnings-call-transcript-celsius-q3-2025-earnings-beat-expectations-stock-dips-93CH-4338388\":\n",
+ " \"\"\"\n",
+ " Celsius beat estimates with EPS $0.42 and revenue $725.1M. Stock fell 17.5% on cautious guidance.\n",
+ " \"\"\"\n",
+ "}\n",
+ "\n",
+ "print(\"Simulated financial data defined.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 4. Define the Tool Function\n",
+ "\n",
+ "This step defines a simple function, web_browser(), that simulates how the ReAct agent retrieves text from an external source. It looks up the article text for a given URL from the dataset."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "✅ web_browser tool defined successfully.\n"
+ ]
+ }
+ ],
+ "source": [
+ "def web_browser(url: str) -> str:\n",
+ " \"\"\"Simulates a web lookup by returning article text for a given URL.\"\"\"\n",
+ " return SIMULATED_ARTICLE_DATA.get(url, f\"Error: Could not retrieve content for {url}. URL not found.\")\n",
+ "\n",
+ "print(\"✅ web_browser tool defined successfully.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 5. Define the ReAct Prompt Template\n",
+ "\n",
+ "This step defines the prompt that guides the agent’s reasoning. The template enforces the ReAct pattern — Thought → Action → Observation — and specifies how the model should use the web_browser() tool and format its final output as structured JSON."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "REACT_PROMPT_TEMPLATE = \"\"\"\n",
+ "You are an expert financial analyst agent. Your task is to analyze the provided financial news article URL. Follow the ReAct framework (Thought -> Action -> Observation) until you produce the final structured JSON analysis.\n",
+ "\n",
+ "**AVAILABLE TOOLS:**\n",
+ "web_browser(url: str) -> str: Fetches the full textual content of the article.\n",
+ "\n",
+ "**FINAL OUTPUT INSTRUCTIONS:**\n",
+ "Return a single JSON object with:\n",
+ "1. \"Classification\": {{ \"Sentiment\": Positive/Negative/Mixed/Neutral, \"Topic\": Earnings/Regulatory/Product/Operations/M&A/HR/Layoffs/Supply Chain }}\n",
+ "2. \"Summary\": A concise bulleted list of 3-5 key takeaways.\n",
+ "\n",
+ "---\n",
+ "**INPUT ARTICLE URL:** {url}\n",
+ "---\n",
+ "**START ANALYSIS:**\n",
+ "\"\"\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 6. Tool Execution Function\n",
+ "\n",
+ "This function prepares the initial ReAct prompt, performs the tool call, and returns all intermediate components for the next step."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def prepare_react_context(url: str, verbose: bool = False):\n",
+ " current_prompt = REACT_PROMPT_TEMPLATE.format(url=url)\n",
+ " action_call = f\"Action: web_browser(url='{url}')\"\n",
+ " observation = web_browser(url)\n",
+ " \n",
+ " if verbose:\n",
+ " print(f\"Prompt snippet: {current_prompt[:120]}...\")\n",
+ " print(f\"Observation snippet: {observation[:120]}...\")\n",
+ " \n",
+ " return current_prompt, action_call, observation"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 7. Define the Main ReAct Agent Function\n",
+ "\n",
+ "This function calls the setup function above, performs model generation, and safely parses the JSON output."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def run_granite_react_agent(url: str, max_tokens: int = 512, verbose: bool = False) -> str:\n",
+ " \"\"\"Executes the ReAct loop using Granite model and returns structured text analysis.\"\"\"\n",
+ " \n",
+ " current_prompt, action_call, observation = prepare_react_context(url, verbose)\n",
+ " \n",
+ " # Strong instruction for structured output\n",
+ " system_instruction = (\n",
+ " \"You are an expert financial analyst agent following the ReAct framework. \"\n",
+ " \"Analyze the observation and produce the output in this exact format:\\n\\n\"\n",
+ " \"Classification:\\nSentiment: Positive/Negative/Mixed/Neutral\\nTopic: Earnings/Regulatory/Product/Operations/M&A/HR/Layoffs/Supply Chain\\n\\n\"\n",
+ " \"Summary:\\n- Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n\\n\"\n",
+ " \"Start with Classification, then Summary. Do not include any extra text or role markers. \"\n",
+ " \"Base sentiment and topic ONLY on the observation text. Use actual values from the article.\"\n",
+ " )\n",
+ " \n",
+ " chat = [\n",
+ " {\"role\": \"system\", \"content\": system_instruction},\n",
+ " {\"role\": \"user\", \"content\": f\"{current_prompt}\\nThought: Retrieve article content using web_browser tool.\\n\"\n",
+ " f\"{action_call}\\nObservation: {observation}\\n\\n\"\n",
+ " f\"Thought: Proceed with classification and summarization.\\nAction:\"}\n",
+ " ]\n",
+ " \n",
+ " # Apply Granite chat template\n",
+ " chat_text = tokenizer.apply_chat_template(chat, tokenize=False, add_generation_prompt=True)\n",
+ " input_tokens = tokenizer(chat_text, return_tensors=\"pt\").to(device)\n",
+ " \n",
+ " if verbose:\n",
+ " print(f\"Prompt length: {len(chat_text)} chars | Token count: {input_tokens['input_ids'].shape[-1]}\")\n",
+ " \n",
+ " # Generate output\n",
+ " with torch.no_grad():\n",
+ " output_tokens = model.generate(**input_tokens, max_new_tokens=max_tokens)\n",
+ " \n",
+ " # Decode response\n",
+ " full_response = tokenizer.batch_decode(output_tokens, skip_special_tokens=True)[0]\n",
+ " \n",
+ " return full_response.strip()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 8. Test Single URL\n",
+ "\n",
+ "This step runs the ReAct agent on one example URL to verify the workflow and ensure the model generates a valid JSON response before processing multiple cases."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Testing single ReAct run...\n",
+ "Prompt snippet: \n",
+ "You are an expert financial analyst agent. Your task is to analyze the provided financial news article URL. Follow the ...\n",
+ "Observation snippet: \n",
+ " Luminar faces SEC investigation, missed payments, CFO exit, and workforce cuts. Bankruptcy risk rising amid liquidi...\n",
+ "Prompt length: 1795 chars | Token count: 400\n",
+ "\n",
+ "--- Model Output ---\n",
+ "\n",
+ "systemYou are an expert financial analyst agent following the ReAct framework. Analyze the observation and produce the output in this exact format:\n",
+ "\n",
+ "Classification:\n",
+ "Sentiment: Positive/Negative/Mixed/Neutral\n",
+ "Topic: Earnings/Regulatory/Product/Operations/M&A/HR/Layoffs/Supply Chain\n",
+ "\n",
+ "Summary:\n",
+ "- Key takeaway 1\n",
+ "- Key takeaway 2\n",
+ "- Key takeaway 3\n",
+ "\n",
+ "Start with Classification, then Summary. Do not include any extra text or role markers. Base sentiment and topic ONLY on the observation text. Use actual values from the article.\n",
+ "user\n",
+ "You are an expert financial analyst agent. Your task is to analyze the provided financial news article URL. Follow the ReAct framework (Thought -> Action -> Observation) until you produce the final structured JSON analysis.\n",
+ "\n",
+ "**AVAILABLE TOOLS:**\n",
+ "web_browser(url: str) -> str: Fetches the full textual content of the article.\n",
+ "\n",
+ "**FINAL OUTPUT INSTRUCTIONS:**\n",
+ "Return a single JSON object with:\n",
+ "1. \"Classification\": { \"Sentiment\": Positive/Negative/Mixed/Neutral, \"Topic\": Earnings/Regulatory/Product/Operations/M&A/HR/Layoffs/Supply Chain }\n",
+ "2. \"Summary\": A concise bulleted list of 3-5 key takeaways.\n",
+ "\n",
+ "---\n",
+ "**INPUT ARTICLE URL:** https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615\n",
+ "---\n",
+ "**START ANALYSIS:**\n",
+ "\n",
+ "Thought: Retrieve article content using web_browser tool.\n",
+ "Action: web_browser(url='https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615')\n",
+ "Observation: \n",
+ " Luminar faces SEC investigation, missed payments, CFO exit, and workforce cuts. Bankruptcy risk rising amid liquidity issues.\n",
+ " \n",
+ "\n",
+ "Thought: Proceed with classification and summarization.\n",
+ "Action:\n",
+ "assistantClassification:\n",
+ "Sentiment: Negative\n",
+ "Topic: Operations\n",
+ "\n",
+ "Summary:\n",
+ "- Luminar faces a significant SEC investigation due to missed payments.\n",
+ "- The company has experienced a CFO exit, indicating internal financial challenges.\n",
+ "- Luminar has implemented workforce cuts to address liquidity issues.\n",
+ "- The company is facing bankruptcy risk due to its financial struggles.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Test single URL before batch\n",
+ "test_url = \"https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615\"\n",
+ "print(\"Testing single ReAct run...\")\n",
+ "result = run_granite_react_agent(test_url, verbose=True)\n",
+ "print(\"\\n--- Model Output ---\\n\")\n",
+ "print(result)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 9. Batch Analysis\n",
+ "\n",
+ "This cell applies the ReAct agent to all case study URLs, aggregates the results into a structured DataFrame, and displays the sentiment, topic, and summary for each company."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "--- Starting Batch ReAct Analysis ---\n",
+ "\n",
+ "Analyzing: Article 1: Luminar...\n",
+ "\n",
+ "Analyzing: Article 2: Celsius...\n",
+ "\n",
+ "Analyzing: Article 3: Lucid...\n",
+ "\n",
+ "Analyzing: Article 4: Shift4...\n",
+ "\n",
+ "Analyzing: Article 5: Lenz...\n",
+ "\n",
+ "--- Batch Analysis Results ---\n",
+ "\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Company/Case | \n",
+ " Sentiment | \n",
+ " Topic | \n",
+ " Summary | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " Article 1: Luminar | \n",
+ " Negative | \n",
+ " Operations | \n",
+ " - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Luminar faces a significant SEC investigation due to missed payments.\\n- The company has experienced a CFO exit, indicating internal financial challenges.\\n- Luminar has implemented workforce cuts to address liquidity issues.\\n- The company is facing bankruptcy risk due to its financial struggles. | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " Article 2: Celsius | \n",
+ " Negative | \n",
+ " Earnings | \n",
+ " - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Celsius reported EPS of $0.42, beating estimates.\\n- Revenue of $725.1M, also exceeding expectations.\\n- Stock price fell 17.5% following the earnings call. | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " Article 3: Lucid | \n",
+ " Negative | \n",
+ " Operations | \n",
+ " - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Lucid missed Q3 revenue targets, resulting in a $336.6 million shortfall.\\n- The company experienced a widening loss of $2.65 per share.\\n- Production forecasts were reduced due to supply chain challenges. | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " Article 4: Shift4 | \n",
+ " Positive | \n",
+ " Earnings | \n",
+ " - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- EPS beat expectations with $1.47\\n- Revenue missed the target at $589.2M | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " Article 5: Lenz | \n",
+ " Negative | \n",
+ " Earnings | \n",
+ " - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Q3 2025 financial results show a net loss of $16.7 million.\\n- VIZZ launch and increased prescriptions indicate product performance.\\n- $10 million in milestone payments reflect successful clinical progress.\\n- Operating expenses rose 44% due to increased SG&A costs. | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " Company/Case Sentiment Topic \\\n",
+ "0 Article 1: Luminar Negative Operations \n",
+ "1 Article 2: Celsius Negative Earnings \n",
+ "2 Article 3: Lucid Negative Operations \n",
+ "3 Article 4: Shift4 Positive Earnings \n",
+ "4 Article 5: Lenz Negative Earnings \n",
+ "\n",
+ " Summary \n",
+ "0 - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Luminar faces a significant SEC investigation due to missed payments.\\n- The company has experienced a CFO exit, indicating internal financial challenges.\\n- Luminar has implemented workforce cuts to address liquidity issues.\\n- The company is facing bankruptcy risk due to its financial struggles. \n",
+ "1 - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Celsius reported EPS of $0.42, beating estimates.\\n- Revenue of $725.1M, also exceeding expectations.\\n- Stock price fell 17.5% following the earnings call. \n",
+ "2 - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Lucid missed Q3 revenue targets, resulting in a $336.6 million shortfall.\\n- The company experienced a widening loss of $2.65 per share.\\n- Production forecasts were reduced due to supply chain challenges. \n",
+ "3 - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- EPS beat expectations with $1.47\\n- Revenue missed the target at $589.2M \n",
+ "4 - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Q3 2025 financial results show a net loss of $16.7 million.\\n- VIZZ launch and increased prescriptions indicate product performance.\\n- $10 million in milestone payments reflect successful clinical progress.\\n- Operating expenses rose 44% due to increased SG&A costs. "
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import pandas as pd\n",
+ "\n",
+ "CASE_STUDY_URLS = {\n",
+ " \"Article 1: Luminar\": \"https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615\",\n",
+ " \"Article 2: Celsius\": \"https://www.investing.com/news/transcripts/earnings-call-transcript-celsius-q3-2025-earnings-beat-expectations-stock-dips-93CH-4338388\",\n",
+ " \"Article 3: Lucid\": \"https://www.cbtnews.com/lucid-trims-production-forecast-misses-q3-expectations/\",\n",
+ " \"Article 4: Shift4\": \"https://www.investing.com/news/transcripts/earnings-call-transcript-shift4-payments-q3-2025-shows-strong-growth-93CH-4339020\",\n",
+ " \"Article 5: Lenz\": \"https://ir.lenz-tx.com/news-events/press-releases/detail/43/lenz-therapeutics-reports-third-quarter-2025-financial-results-and-recent-corporate-highlights\"\n",
+ "}\n",
+ "\n",
+ "results = []\n",
+ "print(\"--- Starting Batch ReAct Analysis ---\")\n",
+ "\n",
+ "for name, url in CASE_STUDY_URLS.items():\n",
+ " print(f\"\\nAnalyzing: {name}...\")\n",
+ " analysis_text = run_granite_react_agent(url)\n",
+ " \n",
+ " # Split into sections for display\n",
+ " sentiment = \"\"\n",
+ " topic = \"\"\n",
+ " summary = []\n",
+ " \n",
+ " # Extract Sentiment and Topic from text\n",
+ " for line in analysis_text.splitlines():\n",
+ " if line.startswith(\"Sentiment:\"):\n",
+ " sentiment = line.replace(\"Sentiment:\", \"\").strip()\n",
+ " elif line.startswith(\"Topic:\"):\n",
+ " topic = line.replace(\"Topic:\", \"\").strip()\n",
+ " elif line.startswith(\"- \"):\n",
+ " summary.append(line.strip())\n",
+ " \n",
+ " results.append({\n",
+ " \"Company/Case\": name,\n",
+ " \"Sentiment\": sentiment,\n",
+ " \"Topic\": topic,\n",
+ " \"Summary\": \"\\n\".join(summary)\n",
+ " })\n",
+ "\n",
+ "df = pd.DataFrame(results)\n",
+ "print(\"\\n--- Batch Analysis Results ---\\n\")\n",
+ "\n",
+ "df"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Conclusion\n",
+ "\n",
+ "This tutorial demonstrated how to build a financial insight agent using the ReAct framework and the IBM Granite 4.0 Nano language model.\n",
+ "You learned how the agent performs reasoning and action steps, retrieves external data through a simulated lookup tool, and produces structured financial summaries.\n",
+ "\n",
+ "The ReAct workflow helps reduce hallucination by grounding each decision in retrieved information rather than relying only on internal memory.\n",
+ "By combining transparent reasoning traces with tool use, the agent provides reliable, auditable financial insights.\n",
+ "\n",
+ "**Next Steps**\n",
+ "\n",
+ "You can extend this tutorial by connecting the agent to live financial APIs or a retrieval‑augmented generation (RAG) pipeline for real‑time market analysis.\n",
+ "\n",
+ "Running the model on IBM watsonx.ai improves speed and scalability, while custom prompts or domain‑specific fine‑tuning can further enhance performance for specialized financial use cases.\n"
+ ]
+ }
+ ],
+ "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.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/generative-ai/ReAct.ipynb b/generative-ai/ReAct.ipynb
new file mode 100644
index 000000000..7a18d2540
--- /dev/null
+++ b/generative-ai/ReAct.ipynb
@@ -0,0 +1,662 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# ReAct Prompting for Financial Insight: Classification and Summarization with Granite 4.0 Nano"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Author: Vrunda Gadesha"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This tutorial shows how to build an AI agent using the ReAct (Reasoning and Acting) framework and the IBM Granite 4.0 Nano language model. The agent uses prompt engineering to combine reasoning steps with an action plan. It processes news URLs, retrieves data from external sources, and generates a structured financial analysis in JSON format. This approach ensures clear, transparent, and grounded results."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Before we jumpt into the code, Let's understand some core concepts.\n",
+ "\n",
+ "**What is ReAct Prompting?**\n",
+ "\n",
+ "The ReAct framework enhances large language models (LLMs) by combining internal reasoning with external actions. It integrates Reasoning (Chain-of-Thought, or CoT) and Acting (Tool Use) to create an effective ReAct agent capable of dynamic decision-making.\n",
+ "\n",
+ "- **Thought (Reasoning):** The model generates reasoning traces that break down the task, track progress, and plan the next logical step. These traces can operate in few-shot or zero-shot settings, depending on prompt design.\n",
+ "- **Action (Acting):** The model follows an action plan to execute tool commands, such as web_browser(url), often managed through frameworks like LangChain.\n",
+ "- **Observation:** The agent retrieves external information or data from a knowledge base after executing an action.\n",
+ "\n",
+ "ReAct’s Benefits: It grounds the model in verified external data rather than internal training memory, improving transparency and supporting reliable decision-making in complex tasks."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Introduction to Granite 4.0 Nano\n",
+ "\n",
+ "We use the IBM Granite 4.0 Nano model family, specifically the 1B-instruct variant. This AI model is finetuned for reasoning and instruction-following tasks, making it suitable for building intelligent agents.\n",
+ "\n",
+ "- **Efficiency:** Optimized for fast, low-latency inference through its lightweight architecture and efficient API integration, comparable to how OpenAI models like ChatGPT are deployed.\n",
+ "- **Agentic Capability:** Instruction-tuned to follow complex, multi-step prompts with self-consistency in the model’s reasoning. It handles both structured and verbal reasoning, recognizes tool schemas, and produces reliable, JSON-formatted outputs.\n",
+ "\n",
+ "The Granite 4.0 Nano model demonstrates how compact, finetuned models can achieve strong performance and interpretability similar to larger commercial AI models."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Usecase"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Financial news is rarely straightforward. Articles often mix conflicting signals — for example, a company may report strong sales (positive signal) but its stock may fall due to weak guidance or higher expenses (negative reaction).\n",
+ "\n",
+ "A single-step summary cannot capture this nuance. The ReAct agent improves question answering and reasoning by working step by step, combining reasoning and action in interleaved trajectories. It uses in-context examples and fact verification to ensure each action step aligns with reliable real-world information.\n",
+ "\n",
+ "The agent converts raw news text into structured financial intelligence. It delivers results as one JSON object using Python-based workflows built on machine learning and retrieval techniques like RAG (Retrieval-Augmented Generation).\n",
+ "\n",
+ "Classification:\n",
+ "\n",
+ "- **Sentiment:** Categorizes the market impact (Positive, Negative, Mixed, Neutral).\n",
+ "- **Topic:** Identifies the main business driver (Earnings, Regulatory, HR/Layoffs, Supply Chain, etc.).\n",
+ "- **Summary:** Produces a concise executive summary (3–5 key points) with fact verification, avoiding verbose output.\n",
+ "\n",
+ "The strict JSON schema ensures outputs are instantly consumable by downstream systems and comparable to benchmarks such as HotpotQA or other baseline datasets. This approach supports accurate, real-time financial insight and a grounded final answer."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Prerequisites\n",
+ "\n",
+ "You can execute this tutorial on your local machine using your own Python and Jupyter environment. The local setup gives you full control and flexibility. To run this tutorial, uou must have following setup in your machine. \n",
+ "\n",
+ "To execute the notebook on your local Windows or macOS machine, ensure the following setup:\n",
+ "- Operating system: Windows 10 or later, or macOS 12 or later\n",
+ "- Python: Version 3.10 or newer\n",
+ "- RAM: Minimum 8 GB (16 GB recommended for larger workloads)\n",
+ "- Tools: Jupyter Notebook or JupyterLab installed"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Steps"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 1. Install Dependencies\n",
+ "\n",
+ "To run the Granite 4.0 Nano model and ReAct workflow, you need the core libraries that handle model loading and computation. Installing them ensures your environment can execute reasoning and action steps smoothly."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "!pip install torch torchvision torchaudio --quiet\n",
+ "!pip install accelerate transformers --quiet"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 2. Load the Model and Tokenizer\n",
+ "\n",
+ "This step loads the Granite 4.0 Nano instruction‑tuned model and its tokenizer from Hugging Face. The tokenizer converts text to tokens, and the model generates responses. The code automatically selects GPU if available, otherwise runs on CPU."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Loading model 'ibm-granite/granite-4.0-1b' on cpu...\n",
+ "Model and tokenizer loaded successfully.\n"
+ ]
+ }
+ ],
+ "source": [
+ "import torch\n",
+ "from transformers import AutoModelForCausalLM, AutoTokenizer\n",
+ "\n",
+ "device = \"cpu\" # Use \"cuda\" if GPU is available\n",
+ "MODEL_NAME = \"ibm-granite/granite-4.0-1b\"\n",
+ "\n",
+ "print(f\"Loading model '{MODEL_NAME}' on {device}...\")\n",
+ "\n",
+ "tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)\n",
+ "model = AutoModelForCausalLM.from_pretrained(MODEL_NAME).to(device)\n",
+ "model.eval()\n",
+ "\n",
+ "print(\"Model and tokenizer loaded successfully.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "***Note: Model loading takes about 2–4 minutes on 8 GB RAM and 1–2 minutes on 16 GB RAM; if you see an “out of memory” error on 8 GB systems, restart the kernel and close other applications before retrying.***"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 3. Define the Data\n",
+ "\n",
+ "This step creates a small dataset of five financial news summaries used to test the ReAct agent. The articles are drawn from different sectors — biotechnology (Lenz Therapeutics), payments (Shift4 Payments), lidar technology (Luminar), electric vehicles (Lucid), and consumer goods (Celsius).\n",
+ "\n",
+ "Each article includes mixed market signals such as strong earnings but weak guidance, higher costs, or regulatory challenges. This mix helps evaluate how the agent handles conflicting information and classifies sentiment accurately."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Simulated financial data defined.\n"
+ ]
+ }
+ ],
+ "source": [
+ "SIMULATED_ARTICLE_DATA = {\n",
+ " \"https://ir.lenz-tx.com/news-events/press-releases/detail/43/lenz-therapeutics-reports-third-quarter-2025-financial-results-and-recent-corporate-highlights\": \n",
+ " \"\"\"\n",
+ " LENZ Therapeutics reported Q3 2025 results. The company launched VIZZ, saw 5,000 prescriptions, and received $10M in milestone payments. Operating expenses rose 44%, driven by SG&A. Net loss: $16.7M. Stock fell 9.4% premarket.\n",
+ " \"\"\",\n",
+ " \"https://www.investing.com/news/transcripts/earnings-call-transcript-shift4-payments-q3-2025-shows-strong-growth-93CH-4339020\":\n",
+ " \"\"\"\n",
+ " Shift4 Payments reported EPS of $1.47 (beat) and revenue of $589.2M (miss). Stock up 5.7% premarket after recent weakness.\n",
+ " \"\"\",\n",
+ " \"https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615\":\n",
+ " \"\"\"\n",
+ " Luminar faces SEC investigation, missed payments, CFO exit, and workforce cuts. Bankruptcy risk rising amid liquidity issues.\n",
+ " \"\"\",\n",
+ " \"https://www.cbtnews.com/lucid-trims-production-forecast-misses-q3-expectations/\":\n",
+ " \"\"\"\n",
+ " Lucid missed Q3 targets with $336.6M revenue. Loss widened to $2.65 per share. Production forecast trimmed due to supply chain issues.\n",
+ " \"\"\",\n",
+ " \"https://www.investing.com/news/transcripts/earnings-call-transcript-celsius-q3-2025-earnings-beat-expectations-stock-dips-93CH-4338388\":\n",
+ " \"\"\"\n",
+ " Celsius beat estimates with EPS $0.42 and revenue $725.1M. Stock fell 17.5% on cautious guidance.\n",
+ " \"\"\"\n",
+ "}\n",
+ "\n",
+ "print(\"Simulated financial data defined.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 4. Define the Tool Function\n",
+ "\n",
+ "This step defines a simple function, web_browser(), that simulates how the ReAct agent retrieves text from an external source. It looks up the article text for a given URL from the dataset."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "✅ web_browser tool defined successfully.\n"
+ ]
+ }
+ ],
+ "source": [
+ "def web_browser(url: str) -> str:\n",
+ " \"\"\"Simulates a web lookup by returning article text for a given URL.\"\"\"\n",
+ " return SIMULATED_ARTICLE_DATA.get(url, f\"Error: Could not retrieve content for {url}. URL not found.\")\n",
+ "\n",
+ "print(\"✅ web_browser tool defined successfully.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 5. Define the ReAct Prompt Template\n",
+ "\n",
+ "This step defines the prompt that guides the agent’s reasoning. The template enforces the ReAct pattern — Thought → Action → Observation — and specifies how the model should use the web_browser() tool and format its final output as structured JSON."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "REACT_PROMPT_TEMPLATE = \"\"\"\n",
+ "You are an expert financial analyst agent. Your task is to analyze the provided financial news article URL. Follow the ReAct framework (Thought -> Action -> Observation) until you produce the final structured JSON analysis.\n",
+ "\n",
+ "**AVAILABLE TOOLS:**\n",
+ "web_browser(url: str) -> str: Fetches the full textual content of the article.\n",
+ "\n",
+ "**FINAL OUTPUT INSTRUCTIONS:**\n",
+ "Return a single JSON object with:\n",
+ "1. \"Classification\": {{ \"Sentiment\": Positive/Negative/Mixed/Neutral, \"Topic\": Earnings/Regulatory/Product/Operations/M&A/HR/Layoffs/Supply Chain }}\n",
+ "2. \"Summary\": A concise bulleted list of 3-5 key takeaways.\n",
+ "\n",
+ "---\n",
+ "**INPUT ARTICLE URL:** {url}\n",
+ "---\n",
+ "**START ANALYSIS:**\n",
+ "\"\"\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Step 6. Tool Execution Function\n",
+ "\n",
+ "This function prepares the initial ReAct prompt, performs the tool call, and returns all intermediate components for the next step."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def prepare_react_context(url: str, verbose: bool = False):\n",
+ " current_prompt = REACT_PROMPT_TEMPLATE.format(url=url)\n",
+ " action_call = f\"Action: web_browser(url='{url}')\"\n",
+ " observation = web_browser(url)\n",
+ " \n",
+ " if verbose:\n",
+ " print(f\"Prompt snippet: {current_prompt[:120]}...\")\n",
+ " print(f\"Observation snippet: {observation[:120]}...\")\n",
+ " \n",
+ " return current_prompt, action_call, observation"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 7. Define the Main ReAct Agent Function\n",
+ "\n",
+ "This function calls the setup function above, performs model generation, and safely parses the JSON output."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def run_granite_react_agent(url: str, max_tokens: int = 512, verbose: bool = False) -> str:\n",
+ " \"\"\"Executes the ReAct loop using Granite model and returns structured text analysis.\"\"\"\n",
+ " \n",
+ " current_prompt, action_call, observation = prepare_react_context(url, verbose)\n",
+ " \n",
+ " # Strong instruction for structured output\n",
+ " system_instruction = (\n",
+ " \"You are an expert financial analyst agent following the ReAct framework. \"\n",
+ " \"Analyze the observation and produce the output in this exact format:\\n\\n\"\n",
+ " \"Classification:\\nSentiment: Positive/Negative/Mixed/Neutral\\nTopic: Earnings/Regulatory/Product/Operations/M&A/HR/Layoffs/Supply Chain\\n\\n\"\n",
+ " \"Summary:\\n- Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n\\n\"\n",
+ " \"Start with Classification, then Summary. Do not include any extra text or role markers. \"\n",
+ " \"Base sentiment and topic ONLY on the observation text. Use actual values from the article.\"\n",
+ " )\n",
+ " \n",
+ " chat = [\n",
+ " {\"role\": \"system\", \"content\": system_instruction},\n",
+ " {\"role\": \"user\", \"content\": f\"{current_prompt}\\nThought: Retrieve article content using web_browser tool.\\n\"\n",
+ " f\"{action_call}\\nObservation: {observation}\\n\\n\"\n",
+ " f\"Thought: Proceed with classification and summarization.\\nAction:\"}\n",
+ " ]\n",
+ " \n",
+ " # Apply Granite chat template\n",
+ " chat_text = tokenizer.apply_chat_template(chat, tokenize=False, add_generation_prompt=True)\n",
+ " input_tokens = tokenizer(chat_text, return_tensors=\"pt\").to(device)\n",
+ " \n",
+ " if verbose:\n",
+ " print(f\"Prompt length: {len(chat_text)} chars | Token count: {input_tokens['input_ids'].shape[-1]}\")\n",
+ " \n",
+ " # Generate output\n",
+ " with torch.no_grad():\n",
+ " output_tokens = model.generate(**input_tokens, max_new_tokens=max_tokens)\n",
+ " \n",
+ " # Decode response\n",
+ " full_response = tokenizer.batch_decode(output_tokens, skip_special_tokens=True)[0]\n",
+ " \n",
+ " return full_response.strip()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 8. Test Single URL\n",
+ "\n",
+ "This step runs the ReAct agent on one example URL to verify the workflow and ensure the model generates a valid JSON response before processing multiple cases."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Testing single ReAct run...\n",
+ "Prompt snippet: \n",
+ "You are an expert financial analyst agent. Your task is to analyze the provided financial news article URL. Follow the ...\n",
+ "Observation snippet: \n",
+ " Luminar faces SEC investigation, missed payments, CFO exit, and workforce cuts. Bankruptcy risk rising amid liquidi...\n",
+ "Prompt length: 1795 chars | Token count: 400\n",
+ "\n",
+ "--- Model Output ---\n",
+ "\n",
+ "systemYou are an expert financial analyst agent following the ReAct framework. Analyze the observation and produce the output in this exact format:\n",
+ "\n",
+ "Classification:\n",
+ "Sentiment: Positive/Negative/Mixed/Neutral\n",
+ "Topic: Earnings/Regulatory/Product/Operations/M&A/HR/Layoffs/Supply Chain\n",
+ "\n",
+ "Summary:\n",
+ "- Key takeaway 1\n",
+ "- Key takeaway 2\n",
+ "- Key takeaway 3\n",
+ "\n",
+ "Start with Classification, then Summary. Do not include any extra text or role markers. Base sentiment and topic ONLY on the observation text. Use actual values from the article.\n",
+ "user\n",
+ "You are an expert financial analyst agent. Your task is to analyze the provided financial news article URL. Follow the ReAct framework (Thought -> Action -> Observation) until you produce the final structured JSON analysis.\n",
+ "\n",
+ "**AVAILABLE TOOLS:**\n",
+ "web_browser(url: str) -> str: Fetches the full textual content of the article.\n",
+ "\n",
+ "**FINAL OUTPUT INSTRUCTIONS:**\n",
+ "Return a single JSON object with:\n",
+ "1. \"Classification\": { \"Sentiment\": Positive/Negative/Mixed/Neutral, \"Topic\": Earnings/Regulatory/Product/Operations/M&A/HR/Layoffs/Supply Chain }\n",
+ "2. \"Summary\": A concise bulleted list of 3-5 key takeaways.\n",
+ "\n",
+ "---\n",
+ "**INPUT ARTICLE URL:** https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615\n",
+ "---\n",
+ "**START ANALYSIS:**\n",
+ "\n",
+ "Thought: Retrieve article content using web_browser tool.\n",
+ "Action: web_browser(url='https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615')\n",
+ "Observation: \n",
+ " Luminar faces SEC investigation, missed payments, CFO exit, and workforce cuts. Bankruptcy risk rising amid liquidity issues.\n",
+ " \n",
+ "\n",
+ "Thought: Proceed with classification and summarization.\n",
+ "Action:\n",
+ "assistantClassification:\n",
+ "Sentiment: Negative\n",
+ "Topic: Operations\n",
+ "\n",
+ "Summary:\n",
+ "- Luminar faces a significant SEC investigation due to missed payments.\n",
+ "- The company has experienced a CFO exit, indicating internal financial challenges.\n",
+ "- Luminar has implemented workforce cuts to address liquidity issues.\n",
+ "- The company is facing bankruptcy risk due to its financial struggles.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Test single URL before batch\n",
+ "test_url = \"https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615\"\n",
+ "print(\"Testing single ReAct run...\")\n",
+ "result = run_granite_react_agent(test_url, verbose=True)\n",
+ "print(\"\\n--- Model Output ---\\n\")\n",
+ "print(result)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Step 9. Batch Analysis\n",
+ "\n",
+ "This cell applies the ReAct agent to all case study URLs, aggregates the results into a structured DataFrame, and displays the sentiment, topic, and summary for each company."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "--- Starting Batch ReAct Analysis ---\n",
+ "\n",
+ "Analyzing: Article 1: Luminar...\n",
+ "\n",
+ "Analyzing: Article 2: Celsius...\n",
+ "\n",
+ "Analyzing: Article 3: Lucid...\n",
+ "\n",
+ "Analyzing: Article 4: Shift4...\n",
+ "\n",
+ "Analyzing: Article 5: Lenz...\n",
+ "\n",
+ "--- Batch Analysis Results ---\n",
+ "\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Company/Case | \n",
+ " Sentiment | \n",
+ " Topic | \n",
+ " Summary | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " Article 1: Luminar | \n",
+ " Negative | \n",
+ " Operations | \n",
+ " - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Luminar faces a significant SEC investigation due to missed payments.\\n- The company has experienced a CFO exit, indicating internal financial challenges.\\n- Luminar has implemented workforce cuts to address liquidity issues.\\n- The company is facing bankruptcy risk due to its financial struggles. | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " Article 2: Celsius | \n",
+ " Negative | \n",
+ " Earnings | \n",
+ " - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Celsius reported EPS of $0.42, beating estimates.\\n- Revenue of $725.1M, also exceeding expectations.\\n- Stock price fell 17.5% following the earnings call. | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " Article 3: Lucid | \n",
+ " Negative | \n",
+ " Operations | \n",
+ " - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Lucid missed Q3 revenue targets, resulting in a $336.6 million shortfall.\\n- The company experienced a widening loss of $2.65 per share.\\n- Production forecasts were reduced due to supply chain challenges. | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " Article 4: Shift4 | \n",
+ " Positive | \n",
+ " Earnings | \n",
+ " - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- EPS beat expectations with $1.47\\n- Revenue missed the target at $589.2M | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " Article 5: Lenz | \n",
+ " Negative | \n",
+ " Earnings | \n",
+ " - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Q3 2025 financial results show a net loss of $16.7 million.\\n- VIZZ launch and increased prescriptions indicate product performance.\\n- $10 million in milestone payments reflect successful clinical progress.\\n- Operating expenses rose 44% due to increased SG&A costs. | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " Company/Case Sentiment Topic \\\n",
+ "0 Article 1: Luminar Negative Operations \n",
+ "1 Article 2: Celsius Negative Earnings \n",
+ "2 Article 3: Lucid Negative Operations \n",
+ "3 Article 4: Shift4 Positive Earnings \n",
+ "4 Article 5: Lenz Negative Earnings \n",
+ "\n",
+ " Summary \n",
+ "0 - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Luminar faces a significant SEC investigation due to missed payments.\\n- The company has experienced a CFO exit, indicating internal financial challenges.\\n- Luminar has implemented workforce cuts to address liquidity issues.\\n- The company is facing bankruptcy risk due to its financial struggles. \n",
+ "1 - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Celsius reported EPS of $0.42, beating estimates.\\n- Revenue of $725.1M, also exceeding expectations.\\n- Stock price fell 17.5% following the earnings call. \n",
+ "2 - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Lucid missed Q3 revenue targets, resulting in a $336.6 million shortfall.\\n- The company experienced a widening loss of $2.65 per share.\\n- Production forecasts were reduced due to supply chain challenges. \n",
+ "3 - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- EPS beat expectations with $1.47\\n- Revenue missed the target at $589.2M \n",
+ "4 - Key takeaway 1\\n- Key takeaway 2\\n- Key takeaway 3\\n- Q3 2025 financial results show a net loss of $16.7 million.\\n- VIZZ launch and increased prescriptions indicate product performance.\\n- $10 million in milestone payments reflect successful clinical progress.\\n- Operating expenses rose 44% due to increased SG&A costs. "
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import pandas as pd\n",
+ "\n",
+ "CASE_STUDY_URLS = {\n",
+ " \"Article 1: Luminar\": \"https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615\",\n",
+ " \"Article 2: Celsius\": \"https://www.investing.com/news/transcripts/earnings-call-transcript-celsius-q3-2025-earnings-beat-expectations-stock-dips-93CH-4338388\",\n",
+ " \"Article 3: Lucid\": \"https://www.cbtnews.com/lucid-trims-production-forecast-misses-q3-expectations/\",\n",
+ " \"Article 4: Shift4\": \"https://www.investing.com/news/transcripts/earnings-call-transcript-shift4-payments-q3-2025-shows-strong-growth-93CH-4339020\",\n",
+ " \"Article 5: Lenz\": \"https://ir.lenz-tx.com/news-events/press-releases/detail/43/lenz-therapeutics-reports-third-quarter-2025-financial-results-and-recent-corporate-highlights\"\n",
+ "}\n",
+ "\n",
+ "results = []\n",
+ "print(\"--- Starting Batch ReAct Analysis ---\")\n",
+ "\n",
+ "for name, url in CASE_STUDY_URLS.items():\n",
+ " print(f\"\\nAnalyzing: {name}...\")\n",
+ " analysis_text = run_granite_react_agent(url)\n",
+ " \n",
+ " # Split into sections for display\n",
+ " sentiment = \"\"\n",
+ " topic = \"\"\n",
+ " summary = []\n",
+ " \n",
+ " # Extract Sentiment and Topic from text\n",
+ " for line in analysis_text.splitlines():\n",
+ " if line.startswith(\"Sentiment:\"):\n",
+ " sentiment = line.replace(\"Sentiment:\", \"\").strip()\n",
+ " elif line.startswith(\"Topic:\"):\n",
+ " topic = line.replace(\"Topic:\", \"\").strip()\n",
+ " elif line.startswith(\"- \"):\n",
+ " summary.append(line.strip())\n",
+ " \n",
+ " results.append({\n",
+ " \"Company/Case\": name,\n",
+ " \"Sentiment\": sentiment,\n",
+ " \"Topic\": topic,\n",
+ " \"Summary\": \"\\n\".join(summary)\n",
+ " })\n",
+ "\n",
+ "df = pd.DataFrame(results)\n",
+ "print(\"\\n--- Batch Analysis Results ---\\n\")\n",
+ "\n",
+ "df"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Conclusion\n",
+ "\n",
+ "This tutorial demonstrated how to build a financial insight agent using the ReAct framework and the IBM Granite 4.0 Nano language model.\n",
+ "You learned how the agent performs reasoning and action steps, retrieves external data through a simulated lookup tool, and produces structured financial summaries.\n",
+ "\n",
+ "The ReAct workflow helps reduce hallucination by grounding each decision in retrieved information rather than relying only on internal memory.\n",
+ "By combining transparent reasoning traces with tool use, the agent provides reliable, auditable financial insights.\n",
+ "\n",
+ "**Next Steps**\n",
+ "\n",
+ "You can extend this tutorial by connecting the agent to live financial APIs or a retrieval‑augmented generation (RAG) pipeline for real‑time market analysis.\n",
+ "\n",
+ "Running the model on IBM watsonx.ai improves speed and scalability, while custom prompts or domain‑specific fine‑tuning can further enhance performance for specialized financial use cases.\n"
+ ]
+ }
+ ],
+ "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.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/images/ReAct-prompting.jpg b/images/ReAct-prompting.jpg
new file mode 100644
index 000000000..b447cb15e
Binary files /dev/null and b/images/ReAct-prompting.jpg differ
diff --git a/images/react-prompting.png b/images/react-prompting.png
new file mode 100644
index 000000000..83d28614b
Binary files /dev/null and b/images/react-prompting.png differ