Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Simple ReAct Agent using Hugging Face Transformers - No Agent Framework, No paid APIs

Authoerd by Dr.Tiziana Ligorio for AI Agents - CSCI 395.32 taught at Hunter College of The City University of New York

Adapted from: Large Language Model Agents, Jerin George Mathew & Jacopo Rossi, Springer 2025

Hugging Face is an open-source AI company that provides a platform and tools for building, deploying and sharing machine learning models.

We will build a simple ReAct Agent using Hugging Face’s Transformers. The Agent iteratively alternates between reasoning and acting to accomplish a task. For simplicity, the Agent will have access to a single tool, a calculator that will allow the Agent to perform basic mathematical operations..

Simple ReAct Agent

Import the required libraries

Using device: cpu

Define the language model and tokenizer.

We will use Qwen/Qwen2.5-3B-Instruct, a smaller model in the Qwen family, with good instruction-following and reasoning capabilities. With 3B parameters, requires ~6GB memory of memory.
If using Colab, you will probably need a Colab subscription to reliably access a GPU on which to load and run the LLM. You can cancel the subscription at the end of the course. If you already pay for credits for a different model (ChatGPT, Claude, Gemini, etc.) and plan to use that for the course, you won’t need to pay for Colab Pro to run a model locally.

Loading...
Loading...
Loading...
Loading...
Loading...
Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.

The following cell will load the language model. When running a model locally, you are balancing a tradeoff between model size/capability and available resources. Before running this cell, click on “RAM Disk” in the top-right corner and watch the GPU RAM spike up when loading checkpoint shards (the model parameters).

Loading...
Loading...
Loading...
Loading...
Loading...

Define the calculator tool function

Our agent needs tools to interact with the world. We’ll start with a simple calculator.

We will first define which operations are allowed.

['acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'cbrt', 'ceil', 'copysign']
61

⚠️ Naïvely using eval() in an agent tool is extremely dangerous. When an agent has access to a tool that calls eval(expression), the code being evaluated is no longer “just user input” — it is agent-generated code. If the tool executes eval() without restrictions, the agent is effectively granted the full power of the Python runtime, including file access, imports, and system commands (e.g., open("secret.txt", "r") or import os). This creates a critical security risk: a single tool call can unintentionally (or maliciously) escape its intended purpose. For an agent tool to be safe, we must strictly control which names and capabilities the agent is allowed to use during evaluation, rather than trusting the agent’s outputs.

Define the calculator tool for the Agent

We describe our tools using a structured format that the LLM can understand, the OpenAI function calling format (generally referred to as “tool calling”) that Hugging Face Transformers expects (quite standard).
The key parts are:

name: “calculator”

description: A clear description explaining what the calculator does and what expressions it can evaluate

parameters: This should follow JSON Schema format, defining:

    type: “object”.

    properties: describing the parameters (in our case only the expression parameter)

    required: listing which parameters are mandatory

    additionalProperties: prevent additional parameters

The parameters field uses JSON Schema to describe what arguments the function accepts. For your calculator, it takes a single string parameter called expression that represents the mathematical expression to evaluate.

The OpenAI tool definition format serves as a contract between the LLM and the agent code.

  • For the LLM: It’s documentation - the LLM reads the JSON in the prompt to understand what tools exist and how to call them.

  • For the agent code: It’s a schema - we use the same JSON to know which Python functions to execute and what parameters they expect.

Security note:

Although a structured schema constrains the function’s arguments, the model can still output malformed JSON or hallucinated parameters unless you validate. OpenAI explicitly notes you must validate tool arguments before calling your function.

Define the ReAct Agent as a Python class

Our agent needs to maintain state: the model, tokenizer, and available tools. We’ll define the class structure and initialization here.

We need TOOL_REGISTRY separately because JSON can only contain data (strings, numbers), not executable Python functions. So we maintain a mapping from tool names (strings in JSON) to actual Python function objects.

Define the ReAct system prompt to guie the LLM into generating structured reasoning and use the avilable tools.

'acos, acosh, asin, asinh, atan, atan2, atanh, cbrt, ceil, comb, copysign, cos, cosh, degrees, dist, e, erf, erfc, exp, exp2, expm1, fabs, factorial, floor, fmod, frexp, fsum, gamma, gcd, hypot, inf, isclose, isfinite, isinf, isnan, isqrt, lcm, ldexp, lgamma, log, log10, log1p, log2, modf, nan, nextafter, perm, pi, pow, prod, radians, remainder, sin, sinh, sqrt, sumprod, tan, tanh, tau, trunc, ulp'
You are a ReAct agent capable of using tools to answer questions.
You will think through each problem step-by-step, use tools as necessary, and provide accurate answers.

You have access to the following tools:

[
  {
    "type": "function",
    "function": {
      "name": "calculator",
      "description": "Evaluates a mathematical expression and returns the result. Supports basic arithmetic operations like addition (+), subtraction (-), multiplication (*), division (/), and exponentiation (**).",
      "parameters": {
        "type": "object",
        "properties": {
          "expression": {
            "type": "string",
            "description": "The mathematical expression to evaluate, e.g., '2 + 2' or '(10 * 5) / 2'"
          }
        },
        "required": [
          "expression"
        ],
        "additionalProperties": false
      }
    }
  }
]

You must always use the tools for evaluating mathematical operations. If needed, you may break down a problem into multiple
tool calls to evaluate the final answer.
When a problem requires multiple steps (multiple tool calls), do the following:
    1. make a plan
    2. at each step, review the plan and make sure you are on track
    3. execute all the steps before you answer the question.

IMPORTANT CALCULATOR CONSTRAINTS:
- The calculator can ONLY use these operations: acos, acosh, asin, asinh, atan, atan2, atanh, cbrt, ceil, comb, copysign, cos, cosh, degrees, dist, e, erf, erfc, exp, exp2, expm1, fabs, factorial, floor, fmod, frexp, fsum, gamma, gcd, hypot, inf, isclose, isfinite, isinf, isnan, isqrt, lcm, ldexp, lgamma, log, log10, log1p, log2, modf, nan, nextafter, perm, pi, pow, prod, radians, remainder, sin, sinh, sqrt, sumprod, tan, tanh, tau, trunc, ulp
- The calculator uses Python's math module - use functions like sqrt(x), pow(x,y), sin(x), etc.
- Expression examples: "sqrt(144)", "pow(2, 3)", "sin(pi/2)"
- For rounding: If a result has more than 2 decimal places, round it using this expression: "floor(result * 100 + 0.5) / 100"
  Do not use round(), you can't execute Python builtins
  Example: To round 3.14159 to 2 decimals, use "floor(3.14159 * 100 + 0.5) / 100" which gives 3.14

Use the tools by specifying a json blob with an 'action' key (tool name) and an 'action_input' key (the tool input, matching the parameters schema above).

Valid actions: calculator

The $JSON_BLOB must only contain a SINGLE action and must be formatted as markdown. Do NOT return a list of multiple actions.

Example:
Action:
```json
{
    "action": "calculator",
    "action_input": "5+2"
}
```

ALWAYS use the following format:

Question: the input question you must answer
Thought: you should always think about what action to take. Only one action at a time.
Action:
```json
{JSON_BLOB}
```
Observation: the result of the action

This Thought/Action/Observation cycle can repeat up to 10 times. Take several steps as needed, but use your iterations wisely.

You must always end your output with the following format:
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Now begin! Reminder to ALWAYS use the exact characters 'Final Answer:' when you provide a definitive answer.

Question: What is the square root of 25?

Example

When presented with the question “What is the result of 5 + 2?” we will have the following:

ReAct Reasoning/Thinking

Question: What is the result of 5 + 2?
Thought: To solve this, I need to calculate the value of 5 + 2 using the calculator tool.

Generating the action

The agent specifies the action in JSON (formatted as markdown):

Action:
```json
{
    "action": "calculator",
    "action_input": "5+2"
}
```

Generating the final response

Observation: The result is 7
Thought: I now know the final answer.
Final Answer: 7

Define Stopping Criteria*

To prevent the model from generating the answer without using the tools, we must define a stop criteria. In this case, we want to stop generating right after the model generates the action. Following our ReAct logic, a simple approach is to stop generation right after the model generates the "Observation: " sequence generated right after the action.

Target sequence: Observation:
Prompt length: 3259
Tokenizer EOS token: <|im_end|>

Define the model respone

Now we implement the text generation (a single LLM request) with stopping criteria

Loading...

Define action extraction

{'action': 'calculator', 'action_input': 'sqrt(25)'}

Define the ReAct Workflow

We will add some output to observe the Agent’s reasoning, good for testing.
Some, but not all, thinking output may be desirable for users.

Test

Question: What is the square root of 144?

============================================================

Initial prompt contains 3260 characters
Max iterations: 10


ITERATION 1
------------------------------------------------------------
Generating model response...

Model Output:
Let's start by determining the square root of 144 using the provided mathematical operation capabilities.
Thought: We need to calculate the square root of 144. This falls under the scope of the `calculator` function that we can utilize. Since there's no direct square root function in our available set of functions, we'll rely on taking the exponential of 1/2 power, which is equivalent to finding the square root.
Action:
```json
{"action": "calculator", "action_input": "exp(1/2*log(144))"}
```
Observation:

Extracting and executing action...
   Tool: calculator
   Input: exp(1/2*log(144))

Tool Result: The result is 12.0

Updated context (prompt now contains 3791 characters)
The model will see this history in the next iteration.


ITERATION 2
------------------------------------------------------------
Generating model response...

Model Output:
Thought: I now know the final answer
Final Answer: 12.0

============================================================
AGENT COMPLETED SUCCESSFULLY
============================================================

Final Answer: 12.0
Question: What is (12 + 8) * 5 / 2?

============================================================

Initial prompt contains 3254 characters
Max iterations: 10


ITERATION 1
------------------------------------------------------------
Generating model response...

Model Output:
Let's start by breaking down the given operation and calculate it step-by-step.
Thought: We need to first perform the addition inside the parentheses and then proceed with the multiplication and division. This calculation involves multiple operations so we'll use the calculator function in a sequential manner.
Action:
```json
{"action": "calculator", "action_input": "(12 + 8) * 5 / 2"}
```
Observation:

Extracting and executing action...
   Tool: calculator
   Input: (12 + 8) * 5 / 2

Tool Result: The result is 50.0

Updated context (prompt now contains 3680 characters)
The model will see this history in the next iteration.


ITERATION 2
------------------------------------------------------------
Generating model response...

Model Output:
Thought: I now know the final answer
Final Answer: 50.0

============================================================
AGENT COMPLETED SUCCESSFULLY
============================================================

Final Answer: 50.0
Question: What is the square root of (10 + 8) * 5 / 2?

============================================================

Initial prompt contains 3273 characters
Max iterations: 10


ITERATION 1
------------------------------------------------------------
Generating model response...

Model Output:
Round the result to two decimal places if necessary.
Thought: First, I need to calculate the value inside the parentheses (10 + 8). Then multiply that by 5. Afterward, divide the result by 2. Finally, find the square root of that number. Since the operation might produce results needing rounding to two decimal places, I'll apply the provided method after finding the square root.
Action:
```json
{"action": "calculator", "action_input": "(10 + 8) * 5 / 2"}
```
Observation:

Extracting and executing action...
   Tool: calculator
   Input: (10 + 8) * 5 / 2

Tool Result: The result is 45.0

Updated context (prompt now contains 3769 characters)
The model will see this history in the next iteration.


ITERATION 2
------------------------------------------------------------
Generating model response...

Model Output:
Thought: Now I've calculated that part of the expression. Next, I need to find the square root of 45.0. Once obtained, I will round it according to the instructions given.
Action:
```json
{"action": "calculator", "action_input": "sqrt(45.0)"}
```
Observation:

Extracting and executing action...
   Tool: calculator
   Input: sqrt(45.0)

Tool Result: The result is 6.708203932499369

Updated context (prompt now contains 4062 characters)
The model will see this history in the next iteration.


ITERATION 3
------------------------------------------------------------
Generating model response...

Model Output:
Thought: I now know the final answer
Final Answer: 6.71

============================================================
AGENT COMPLETED SUCCESSFULLY
============================================================

Final Answer: 6.71

Adjustments fo smaller model:

  • Explicitly tell it it can break down a problem into multiple tool calls

  • Tell it to always use tools to answer mathematical expressions, never to evaluate on its own.

  • Allow fo the possibility that the Action json is not written in markdwon

Your turn!

Try adding more tools and test!

Some tool ideas may be:

  • Random-number generator

  • Date calculator

  • Metric converter

And some test prompts:

  • I’m running 5 km tomorrow. How many meters is that, and what’s 10% of that distance?

  • I have a deadline 60 days away. What’s the date, and how many weeks do I have to prepare?

  • Pick a random temperature between freezing and boiling water in celsius, then tell me what it is in fahrenheit