ReAct prompting for financial insight—classification and summarization with Granite 4.0 Nano

This tutorial shows how to build an AI agent by 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.

Before we jump into the code, let’s understand some core concepts.

What is ReAct prompting?

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.

  • 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.
  • Action (acting): The model follows an action plan to execute tool commands, such as web_browser(url), often managed through frameworks like LangChain.
  • Observation: The agent retrieves external information or data from a knowledge base after executing an action.

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.

Figure 1 - ReAct Prompting Figure 1 - ReAct Prompting

Introduction to Granite 4.0 Nano

We use the IBM Granite 4.0 Nano model family, specifically the 1B-instruct variant. This AI model is fine tuned for reasoning and instruction-following tasks, making it suitable for building intelligent agents.

  • Efficiency: Optimized for fast, low-latency inference through its lightweight architecture and efficient application programming interface (API) integration, comparable to how OpenAI models like ChatGPT are deployed.
  • Agentic capability: Instruction-tuned to follow complex, multistep prompts with self-consistency in the model’s agentic reasoning. It handles both structured and verbal reasoning, recognizes tool schemas and produces reliable, JSON-formatted outputs.

The Granite 4.0 Nano model demonstrates how compact, fine tuned models can achieve strong performance and interpretability similar to larger commercial AI models.

Use case

Financial news is rarely straightforward. Articles often mix conflicting signals. For example, a company might report strong sales (positive signal) but its stock can fall due to weak guidance or higher expenses (negative reaction).

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 that each action step aligns with reliable real-world information.

The agent converts raw news text into structured financial intelligence. It delivers results as one DataFrame object by using Python-based workflows built on machine learning and retrieval techniques like RAG (retrieval-augmented generation).

Classification:

  • Sentiment: Categorizes the market impact (positive, negative, mixed, neutral).
  • Topic: Identifies the main business driver (earnings, regulatory, HR, layoffs, supply chain and others).
  • Summary: Produces a concise executive summary (3–5 key points) with fact verification, avoiding verbose output.

The 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.

Prerequisites

You can execute this tutorial on your local machine by using your own Python and Jupyter environment. The local setup gives you full control and flexibility. To run this tutorial and to execute the notebook on your local Windows or macOS machine, you must ensure the following setup.

  • Operating system: Windows 10 or later, or macOS 12 or later
  • Python: Version 3.10 or newer
  • RAM: Minimum 8 GB (16 GB recommended for larger workloads)
  • Tools: Jupyter Notebook or JupyterLab installed

Steps

Step 1. Install dependencies

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 that your environment can execute reasoning and action steps smoothly.

!pip install torch torchvision torchaudio --quiet
!pip install accelerate transformers --quiet

Step 2. Load the model and tokenizer

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 a GPU where available, otherwise it runs on CPU.

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

device = “cpu” # Use “cuda” if GPU is available
MODEL_NAME = “ibm-granite/granite-4.0-1b”

print(f”Loading model ‘{MODEL_NAME}’ on {device}...”)

tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
model = AutoModelForCausalLM.from_pretrained(MODEL_NAME).to(device)
model.eval()

print(“Model and tokenizer loaded successfully.”)

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.

Step 3. Define the data

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).

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.

SIMULATED_ARTICLE_DATA = {
    “https://ir.lenz-tx.com/news-events/press-releases/detail/43/lenz-therapeutics-reports-third-quarter-2025-financial-results-and-recent-corporate-highlights”:
“””
    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.
“””,
    “https://www.investing.com/news/transcripts/earnings-call-transcript-shift4-payments-q3-2025-shows-strong-growth-93CH-4339020”:
“””
    Shift4 Payments reported EPS of $1.47 (beat) and revenue of $589.2M (miss). Stock up 5.7% premarket after recent weakness.
“””,
    “https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615”:
“””
    Luminar faces SEC investigation, missed payments, CFO exit, and workforce cuts. Bankruptcy risk rising amid liquidity issues.
“””,
    “https://www.cbtnews.com/lucid-trims-production-forecast-misses-q3-expectations/”:
“””
    Lucid missed Q3 targets with $336.6M revenue. Loss widened to $2.65 per share. Production forecast trimmed due to supply chain issues.
“””,
    “https://www.investing.com/news/transcripts/earnings-call-transcript-celsius-q3-2025-earnings-beat-expectations-stock-dips-93CH-4338388”:
“””
    Celsius beat estimates with EPS $0.42 and revenue $725.1M. Stock fell 17.5% on cautious guidance.
“””
}

print(“Simulated financial data defined.”)

Step 4. Define the tool function

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 URL from the dataset.

def web_browser(url: str) -> str:
    “””Simulates a web lookup by returning article text for a given URL.”””
    return SIMULATED_ARTICLE_DATA.get(url, f”Error: Could not retrieve content for {url}. URL not found.”)

print(“web_browser tool defined successfully.”)

Step 5. Define the ReAct prompt template

This step defines the prompt that guides the agent’s reasoning. The template enforces the ReAct pattern—thought to action to observation—and specifies how the model should use the web_browser() tool and format its final output as structured JSON.

REACT_PROMPT_TEMPLATE = “””
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.

**AVAILABLE TOOLS:**
web_browser(url: str) -> str: Fetches the full textual content of the article.

**FINAL OUTPUT INSTRUCTIONS:**
Return a single JSON object with:
1. “Classification”: {{ “Sentiment”: Positive/Negative/Mixed/Neutral, “Topic”: Earnings/Regulatory/Product/Operations/M&A/HR/Layoffs/Supply Chain }}
2. “Summary”: A concise bulleted list of 3-5 key takeaways.

---
**INPUT ARTICLE URL:** {url}
---
**START ANALYSIS:**
“””

Step 6. Tool execution function

This function prepares the initial ReAct prompt, performs the tool call and returns all intermediate components for the next step.

def prepare_react_context(url: str, verbose: bool = False):
    current_prompt = REACT_PROMPT_TEMPLATE.format(url=url)
    action_call = f”Action: web_browser(url=’{url}’)”
    observation = web_browser(url)

    if verbose:
        print(f”Prompt snippet: {current_prompt[:120]}...”)
        print(f”Observation snippet: {observation[:120]}...”)

    return current_prompt, action_call, observation

Step 7. Define the main ReAct agent function

This function calls the precedeing setup function, performs model generation and safely parses the output.

def run_granite_react_agent(url: str, max_tokens: int = 512, verbose: bool = False) -> str:
    “””Executes the ReAct loop using Granite model and returns structured text analysis.”””

    current_prompt, action_call, observation = prepare_react_context(url, verbose)

    # Strong instruction for structured output
    system_instruction = (
        “You are an expert financial analyst agent following the ReAct framework. “
        “Analyze the observation and produce the output in this exact format:\n\n”
        “Classification:\nSentiment: Positive/Negative/Mixed/Neutral\nTopic: 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.”
    )

    chat = [
        {“role”: “system”, “content”: system_instruction},
        {“role”: “user”, “content”: f”{current_prompt}\nThought: Retrieve article content using web_browser tool.\n”
        f”{action_call}\nObservation: {observation}\n\n”
        f”Thought: Proceed with classification and summarization.\nAction:”}
    ]

    # Apply Granite chat template
    chat_text = tokenizer.apply_chat_template(chat, tokenize=False, add_generation_prompt=True)
    input_tokens = tokenizer(chat_text, return_tensors=”pt”).to(device)

         if verbose:
        print(f”Prompt length: {len(chat_text)} chars | Token count: {input_tokens[‘input_ids’].shape[-1]}”)

    # Generate output
    with torch.no_grad():
        output_tokens = model.generate(**input_tokens, max_new_tokens=max_tokens)

    # Decode response
    full_response = tokenizer.batch_decode(output_tokens, skip_special_tokens=True)[0]

    return full_response.strip()

Step 8. Test a single URL

This step runs the ReAct agent on one example URL to verify the workflow and ensure that the model generates a valid JSON response before processing multiple cases.

# Test single URL before batch
test_url = “https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615”
print(“Testing single ReAct run...”)
result = run_granite_react_agent(test_url, verbose=True)
print(“\n--- Model Output ---\n”)
print(result)

Output:

Testing single ReAct run...
Prompt snippet: 
You are an expert financial analyst agent. Your task is to analyze the provided financial news article URL. Follow the ...
Observation snippet: 
    Luminar faces SEC investigation, missed payments, CFO exit, and workforce cuts. Bankruptcy risk rising amid liquidi...
Prompt length: 1795 chars | Token count: 400

--- Model Output ---

systemYou are an expert financial analyst agent following the ReAct framework. Analyze the observation and produce the output in this exact format:

Classification:
Sentiment: Positive/Negative/Mixed/Neutral
Topic: Earnings/Regulatory/Product/Operations/M&A/HR/Layoffs/Supply Chain

Summary:
- Key takeaway 1
- Key takeaway 2
- Key takeaway 3

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.
user
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.

**AVAILABLE TOOLS:**
web_browser(url: str) -> str: Fetches the full textual content of the article.

**FINAL OUTPUT INSTRUCTIONS:**
Return a single JSON object with:
1. "Classification": { "Sentiment": Positive/Negative/Mixed/Neutral, "Topic": Earnings/Regulatory/Product/Operations/M&A/HR/Layoffs/Supply Chain }
2. "Summary": A concise bulleted list of 3-5 key takeaways.

---
**INPUT ARTICLE URL:** https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615
---
**START ANALYSIS:**

Thought: Retrieve article content using web_browser tool.
Action: web_browser(url='https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615')
Observation: 
    Luminar faces SEC investigation, missed payments, CFO exit, and workforce cuts. Bankruptcy risk rising amid liquidity issues.
    

Thought: Proceed with classification and summarization.
Action:
assistantClassification:
Sentiment: Negative
Topic: Operations

Summary:
- Luminar faces a significant SEC investigation due to missed payments.
- The company has experienced a CFO exit, indicating internal financial challenges.
- Luminar has implemented workforce cuts to address liquidity issues.
- The company is facing bankruptcy risk due to its financial struggles.

Step 9. Batch analysis

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.

import pandas as pd

CASE_STUDY_URLS = {
    “Article 1: Luminar”: “https://www.photonics.com/Articles/Amid-SEC-Investigation-Luminar-Cuts-Workforce/p5/a71615”,
    “Article 2: Celsius”: “https://www.investing.com/news/transcripts/earnings-call-transcript-celsius-q3-2025-earnings-beat-expectations-stock-dips-93CH-4338388”,
    “Article 3: Lucid”: “https://www.cbtnews.com/lucid-trims-production-forecast-misses-q3-expectations/”,
    “Article 4: Shift4”: “https://www.investing.com/news/transcripts/earnings-call-transcript-shift4-payments-q3-2025-shows-strong-growth-93CH-4339020”,
    “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”
}

results = []
print(“--- Starting Batch ReAct Analysis ---“)

for name, url in CASE_STUDY_URLS.items():
    print(f”\nAnalyzing: {name}...”)
    analysis_text = run_granite_react_agent(url)

    # Split into sections for display
    sentiment = “”
    topic = “”
    summary = []

    # Extract Sentiment and Topic from text
    for line in analysis_text.splitlines():
        if line.startswith(“Sentiment:”):
            sentiment = line.replace(“Sentiment:”, “”).strip()
        elif line.startswith(“Topic:”):
            topic = line.replace(“Topic:”, “”).strip()
        elif line.startswith(“- “):
            summary.append(line.strip())

    results.append({
        “Company/Case”: name,
        “Sentiment”: sentiment,
        “Topic”: topic,
        “Summary”: “\n”.join(summary)
    })

df = pd.DataFrame(results)
print(“\n--- Batch Analysis Results ---\n”)

df

Output:

--- Starting Batch ReAct Analysis ---

Analyzing: Article 1: Luminar...

Analyzing: Article 2: Celsius...

Analyzing: Article 3: Lucid...

Analyzing: Article 4: Shift4...

Analyzing: Article 5: Lenz...

--- Batch Analysis Results ---

 Company/CaseSentimentTopicSummary
0Article 1: LuminarNegativeOperationsLuminar 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.
1Article 2: CelsiusNegativeEarningsCelsius reported EPS of 725.1M, also exceeding expectations.\n- Stock price fell 17.5% following the earnings call.
2Article 3: LucidNegativeOperationsLucid missed Q3 revenue targets, resulting in a 2.65 per share.\n- Production forecasts were reduced due to supply chain challenges.
3Article 4: Shift4PositiveEarningsEPS beat expectations with 589.2M
4Article 5: LenzNegativeEarningsQ3 2025 financial results show a net loss of 10 million in milestone payments reflect successful clinical progress.\n- Operating expenses rose 44% due to increased SG&A costs.

Conclusion

This tutorial demonstrated how to build a financial insight agent by using the ReAct framework and the IBM Granite 4.0 Nano language model. You learned how the agent performs reasoning and action steps, retrieves external data through a simulated lookup tool and produces structured financial summaries.

The ReAct workflow helps reduce AI hallucination by grounding each decision in retrieved information rather than relying only on internal memory. By combining transparent reasoning traces with tool use, the agent provides reliable, auditable financial insights.

Next steps

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.

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.

Author

Vrunda Gadesha

AI Advocate | Technical Content Author

Related solutions
IBM® watsonx Orchestrate™ 

Easily design scalable AI assistants and agents, automate repetitive tasks and simplify complex processes with IBM® watsonx Orchestrate™.

Explore watsonx Orchestrate
AI for developers

Move your applications from prototype to production with the help of our AI development solutions.

Explore AI development tools
AI consulting and services

Reinvent critical workflows and operations by adding AI to maximize experiences, real-time decision-making and business value.

Explore AI services
Take the next step

Whether you choose to customize pre-built apps and skills or build and deploy custom agentic services using an AI studio, the IBM watsonx platform has you covered.

Explore watsonx Orchestrate Explore AI development tools