NVIDIANVIDIA-Certified Professional: Agentic AI2026-101.2 Implement reasoning and action frameworks (e.g., ReAct).
Bound each ReAct execution path
Completed by the knowledge check
Learning objective
Implement the ReAct thought-action-observation loop with tool constraints and termination controls.
Core idea
A ReAct agent chooses an action, observes its actual result, and uses that observation to choose the next action. The loop earns its inference cost when the next useful operation depends on information unavailable before the previous tool finishes.
Production control requires more than a prompt telling the agent to stop. In NeMo Agent Toolkit, configure the available tools, the text parser’s recovery, the tool-error path, and the maximum tool calls independently. Add an elapsed-time boundary in the caller when the service has a deadline. None of these controls proves that a returned answer is correct.
How it works
Connect reasoning to actual execution
The toolkit’s ReAct implementation requires nvidia-nat[langchain]. Configure workflow._type: react_agent, select a configured model through llm_name, and select configured functions or function groups through tool_names. These names connect the agent to executable components; prose mentioning a tool does not register it.
The model emits a text protocol with Thought, Action, and Action Input. The runtime dispatches the action and supplies the resulting Observation. The next model decision uses that result. Completion uses Final Answer instead of another action. The model must wait for a real observation rather than writing the result it expects a tool to produce.
This distinction matters for a lookup returning no records. An empty result is information that can justify another lookup or an unresolved answer. A plausible invented result cannot support either. In a debugging trace, identify the executed tool and its returned observation before blaming the model’s final synthesis. See 1.1.2 for presenting user-facing status rather than exposing intermediate reasoning.
If you override system_prompt, preserve the {tools} and {tool_names} variables and the ReAct output format required by the documentation. Otherwise the model may produce sensible conversational text that the parser cannot dispatch. Repair the prompt or choose an implementation with a different interface; more retries do not make an incompatible output protocol compatible.
Constrain tool selection and arguments
tool_names defines what the agent can call. Use the smallest set that solves the subtask. A read-only investigation specialist should receive lookup functions, not an operational mutation function merely because both exist in the application. This narrows selection at the agent layer; the tool service still owns its actual authorization rules.
NeMo Agent Toolkit functions use Pydantic BaseModel schemas for runtime validation and conversion. In the ReAct configuration, include_tool_input_schema_in_tool_description defaults to True and adds the input schema to tool descriptions. This helps the model construct arguments; runtime schema validation remains a separate check. A schema-valid identifier can still refer to the wrong business entity.
Quote handling is another explicit choice. normalize_tool_input_quotes defaults to True: if JSON parsing fails, the implementation tries replacing single quotes with double quotes. The docs identify SQL and other quote-sensitive input as reasons to set it to False and pass the raw string. Prefer a well-defined structured input over depending on repair of arbitrary generated query text.
Separate recovery budgets
The documented default for retry_agent_response_parsing_errors is True, and parse_agent_response_max_retries defaults to 1. These govern malformed model output. With raise_on_parsing_failure: true (the default), exhausted parsing retries raise ReActAgentParsingFailedError; setting it to False returns error messages as the answer. An error returned as text must not become a successful answer merely because the transport completed.
Tool errors have different controls. tool_call_max_retries defaults to 1. pass_tool_call_errors_to_agent defaults to True, passing errors back to the agent; set it to False to raise an exception instead. An actionable read error can help the agent choose a valid alternative. A known configuration defect is better surfaced to the application owner than repeatedly restated to the model.
Choose these settings from the failure you want to recover. Parsing retries cannot fix an unavailable database. Tool retries cannot fix a prompt that never emits Action Input. When a run fails, classify it by the component that rejected the operation before increasing any limit.
Bound completion and elapsed time
max_tool_calls defaults to 15 and sets the tool-call budget; reaching the limit does not guarantee an answer. It is not a timeout in seconds. A slow first tool can violate a service deadline even with a small call budget. max_history, also documented with a default of 15, limits retained conversation messages; it is not the tool-call cap.
The v1.8.0 implementation translates this budget into the LangGraph setting recursion_limit = (max_tool_calls + 1) * 2. If the graph exhausts that limit, the single-response entry point re-raises GraphRecursionError. The streaming entry point catches the exception and yields a fixed error message explaining that the agent could not produce a final answer within the configured iterations because tool calls did not converge. The caller must recognize both outcomes as incomplete runs; a completed text stream is not proof of success. This behavior is documented in the release source rather than the configuration page.
For a Python asynchronous caller, asyncio.wait_for(aw, timeout) supplies a timeout in seconds. On timeout it cancels the awaitable and raises TimeoutError. Python documents that it waits for cancellation to complete, so elapsed time can exceed the specified timeout. Treat this as cooperative cancellation, not a hard guarantee that remote work has stopped.
Keep the failure outcome explicit in the caller’s exception handling. Call-budget exhaustion, exhausted parsing recovery, a propagated tool failure, and cancellation are different reasons for an incomplete run. The workflow owner chooses which result the product returns for each. Do not represent the call-limit error text as verified completion of the business task.
Key distinctions and trade-offs
Each control addresses a different boundary:
| Control | What it constrains | What it does not establish |
|---|---|---|
tool_names | Exposed callable components | Business authorization for every argument |
parse_agent_response_max_retries | Recovery from output parsing errors | Recovery from tool availability failures |
tool_call_max_retries | Tool-error retry allowance | A request’s elapsed-time deadline |
max_tool_calls | Tool-call budget | Answer correctness or fixed latency |
asyncio.wait_for | Awaiting an asynchronous operation | Immediate cancellation of external effects |
The iterative loop consumes inference and tool time on every additional step. Lower budgets contain waste but can cut off legitimate investigations. Raise a limit only after identifying successful tasks that need it and failure traces that would otherwise spend it uselessly. verbose defaults to False; enabling it logs input, output, and intermediate steps, so the workflow owner must account for sensitive content before using those traces.
Architecture / decision scenario
A retailer’s customer-support assistant investigates missing refunds using read-only status tools. Its hypothetical service target is 8 seconds. Measured model decisions take 0.7 seconds and each lookup takes 0.5 seconds. Three lookups plus a final model decision consume about 4.3 seconds under those assumptions; fifteen lookups plus a final decision consume about 18.7 seconds before retries.
The tempting change is to retain the default fifteen-call budget and tell the model to finish quickly. The workflow owner instead configures max_tool_calls: 3, exposes only refund and payment lookups through tool_names, and wraps the asynchronous invocation in asyncio.wait_for with an 8-second timeout. That deadline does not replace the call budget: a fast but repetitive run can still waste calls, while a single stalled lookup can still exhaust time.
Malformed action output gets one parsing recovery attempt. For this service, the owner sets pass_tool_call_errors_to_agent: false so unavailable lookup infrastructure raises to the caller rather than inviting alternative guesses. The caller handles the failure as an incomplete investigation. Tests distinguish a valid empty result, malformed model output, unavailable tools, call-budget exhaustion on both entry points, and a cancellation that finishes late. If failures cluster around malformed arguments, the team repairs the tool description or input schema rather than increasing the loop cap.
Common exam traps
- Letting the model invent
Observationtext instead of using the tool result. - Treating
max_tool_calls,max_history, and a timeout as interchangeable. - Assuming parsing retries also recover failed tool execution.
- Removing required prompt variables while customizing
system_prompt. - Interpreting returned error text or an exhausted call budget as business success.
What to remember
Bound access, parsing recovery, tool-error recovery, tool calls, and elapsed time separately. ReAct’s value is adaptation to real observations. Its failure mode is spending more steps on an invalid premise, malformed interface, or unavailable dependency; diagnose that cause before extending the loop.
Knowledge check
A refund-investigation assistant uses the NeMo Agent Toolkit’s react_agent, invoked from an asynchronous Python service with an 8-second response target. Most runs need two or three lookups, but one payment lookup occasionally stalls for 20 seconds. Which configuration puts a separate control on both the number of tool calls and the elapsed time?
max_tool_calls: 3 and treat that call budget as the service’s 8-second response limitmax_history: 3 and treat that history limit as the step boundmax_tool_calls: 3 and wrap the invocation in asyncio.wait_for with an 8-second timeoutmax_tool_calls is a tool-call budget, not a timeout in seconds: one stalled lookup can break the deadline within a small budget. asyncio.wait_for adds the elapsed-time bound in the caller, cancelling the awaitable and raising TimeoutError , though cancellation can finish after the timeout. max_history limits retained messages, and a prompt instruction is not a control.A ReAct agent passes SQL to a reporting tool. When the generated tool input is not valid JSON, queries such as WHERE region = 'EMEA' reach the tool with their quotes rewritten. Which setting addresses this?
normalize_tool_input_quotes to false for this agentparse_agent_response_max_retries to 3 for this agenttool_call_max_retries to 3 for this agentinclude_tool_input_schema_in_tool_description to false for this agentnormalize_tool_input_quotes to false and pass the raw string. Parsing and tool-call retries govern malformed model output and tool errors, not this rewrite, and the schema in the description only helps the model build arguments. A well-defined structured input is still preferable to repairing generated query text.Traces show that a ReAct agent’s order database is unreachable. The default error handling passes each failure back to the model, which keeps trying alternative lookups and guesses. The workflow owner wants infrastructure failures to reach the calling service as incomplete runs. Which change does that?
parse_agent_response_max_retries to 3 and keep the default tool-error handlingpass_tool_call_errors_to_agent: false and handle the exception in the callerraise_on_parsing_failure: false and return the error text as the answermax_tool_calls and keep the default tool-error handlingpass_tool_call_errors_to_agent defaults to True , which returns tool errors to the model; set to False , the agent raises an exception that the caller can treat as an incomplete run. Parsing retries and raise_on_parsing_failure govern malformed model output, not an unavailable dependency, and a larger call budget only spends more steps on the same failure.After a team customises a ReAct agent’s system_prompt, every run fails with ReActAgentParsingFailedError. Traces show sensible conversational replies but no Action or Action Input lines, and the new prompt no longer contains {tools} or {tool_names}. What should the team do?
parse_agent_response_max_retries so that the model gets more attemptsraise_on_parsing_failure: false so that parsing errors are returned as answers{tools} , {tool_names} and the ReAct format so that actions can be parsedmax_tool_calls so that the loop has room to reach a valid action{tools} and {tool_names} variables and instruct the ReAct output format; without them the model produces text the parser cannot dispatch. More retries do not make an incompatible output protocol compatible, and returning parsing-error text as an answer would leave the broken action protocol unresolved.A service uses NeMo Agent Toolkit v1.8.0 ReAct through both its single-response and streaming entry points. Both exhaust the configured call budget: one raises GraphRecursionError, while the other finishes streaming text saying no final answer was produced. How should the caller classify these outcomes?
References
NVIDIA, Configure the ReAct Agent
NVIDIA, Functions
Python Software Foundation, Coroutines and Tasks: Timeouts