Module 1 · Part A of 3

The Agent

An agent is an entity that interacts with an environment at any meaningful level. A person going about their day is an agent in this sense, and so is a thermostat holding a room at temperature or a self-driving car making its way through traffic.

All of them perceive their environment through sensors and act on it through actuators. Because each action feeds the next observation, perceiving and acting fold into one continuous cycle, a perceive-reason-act loop, that lasts as long as the agent continues to interact. The agents you will build all adopt the same cycle and are set apart only by how much they can perceive and how far their actions reach.

The three parts on the loop

A single trip around the loop runs through three parts, each with a plain name and a distinct job.

Only perception and action cross the boundary. The decision step between them can change.

Perception gathers what the agent can know, reasoning chooses what to do, and action changes the environment. Perception and action often come from an existing interaction model, whether it controls a vacuum, web automation, a game entity, or a humanoid. The reasoning step can change without changing that boundary, which separates capability from reach.

Deep · ConceptsExplore the loop and classic architectures?

Open this section when you want concrete examples and the fuller architecture taxonomy.

  • Perception gives the agent everything it knows about its environment. A thermostat reads a single number: the current temperature. A self-driving car fuses cameras, radar, and a map into a model of the road. Whatever perception captures is all the agent has to work from, since nothing downstream ever touches the world directly. A gap in perception becomes a gap the rest of the agent cannot see around.
  • Reasoning turns a perception into a decision, which can vary widely between agents. A thermostat compares the temperature against a target and stops there, while a chess engine searches millions of moves and a self-driving car runs a learned policy. The agents you build here reason by calling a language model, and almost everything the course adds later (tools/memory/planning) hangs off this single step.
  • Action changes the environment for itself and other participants. An action can be as small as flipping a relay or as involved as steering a car. Later pages call code exposed to the model a tool, because from the model's side it is just another available action. Calling a tool means invoking that exposed code as the action. It also marks the outer edge of an agent's reach, because however sharp the reasoning, the agent affects the world only through its action space.
The reasoning step is replaceable. Perception and action do not care which decision function sits between them. A simple rule, a decision tree, a search routine, or a neural network can drive the same loop. Building the loop once as a fixed frame keeps two questions cleanly apart: how capable the reasoning is, and how far the actions reach.

Classic agent designs differ at the decision step. Russell and Norvig's Artificial Intelligence: A Modern Approach arranges them as a spectrum, from a bare reflex agent up to a learning agent that improves its own rules. This course starts with rigid logic, then adds a language model and engineered context to move toward a model-based, goal-oriented loop. Later modules keep that loop running over changing state, which is the pattern learning agents build on.

Four agent architectures in order of growing structure, from a reflex agent that maps inputs straight to actions up to a learning agent that rewrites its own rules. Adapted from Russell and Norvig's Artificial Intelligence: A Modern Approach (Ch 1).
Applied · ReflexRun the smallest rule-based agent loop?

An agent with no language model

The smallest decision function is a single if statement. Here it drives a one-dimensional track bounded by walls at positions 0 and 9, which leaves the whole agent about as stripped down as one can get. Press Run and watch the position number bounce between those two walls as the loop runs.

Two things worth examining in the code above:

  • The environment (wall positions, current position) lives in state.env, while the agent's intrinsics (its direction) live in state.agent. Keeping them separate means a different agent with different intrinsic state can operate in the same environment cleanly.
  • The reasoning step is one if statement. Replacing it with something more powerful, whether a decision tree, a neural network, or a language model, only requires changing what sits between perception and action. The loop structure, the state layout, and the action step all remain identical.

What changes when reasoning calls an LLM

You call the chat-completions interface like any other function: hand it a list of messages and a model name, and it hands back one assistant message plus metadata.

About chat(). It is the course-provided wrapper you run live a little further down this page. The snippet below previews its call shape and the fields it returns, so the names are familiar by the time you reach the runnable cell.
const reply = await chat({
  model:    "nvidia/nemotron-3-nano-30b-a3b",
  messages: [{ role: "user", content: "Hi" }],
});
// reply.choices[0].message  → { content, reasoning_content }
// reply.choices[0].finish_reason → "stop" | "tool_calls" | "length"

Three facts about that function shape how agents built on top of it have to work:

Deep · BuildCompare where APIs keep conversation state?

This course uses the Chat Completions API throughout: you keep the conversation as a messages[] array in your own code and resend the full array on every turn. The newer Responses API can hold the thread server-side instead, so you send only the new input and a reference to the prior turn. Either way the model stays stateless and unchanged between calls. The conversation lives outside it, in state your code or the server keeps.

Both interfaces wrap the same stateless call. They differ only in who keeps the conversation between turns.

A real call, in your browser

The cell below sends one chat-completion to the model. It needs an NVIDIA API key, though the free tier covers every exercise. You enter it once, and the course then keeps it in your browser and reuses it on every page so you never paste it twice.

Press Run to send a single request. It stands entirely on its own, with no loop wrapped around it and nothing from an earlier turn carried along.

Deep · WireInspect the raw HTTP request and response?

What chat() actually sends

chat() is a convenience wrapper around one ordinary HTTP POST in the OpenAI-compatible wire format. That request carries three things:

  • the endpoint to send to,
  • an Authorization header carrying your key,
  • and a JSON body with model and messages.

The cell below makes that call by hand and shows both sides. Open request to see what leaves your browser with the key redacted, and raw response to see the full object chat() hands back.

Every endpoint in this course speaks this same format, including the OpenClaw agent runtime in Module 3.

Two things that call is quietly doing, and why. Neither is magic, and both are worth understanding before you trust them.
  • Your request may go through a proxy. If you selected the learning management system/iframe setting when you put in your API key, your request does not hit build.nvidia.com directly. That endpoint does not return the cross-origin (CORS) headers a browser fetch requires, so the call is routed through a small pass-through proxy that adds them. The proxy also adds a course attribution header so DLI can identify traffic from this resource. The lesson sends your API key with that request without storing it or substituting another key.
  • Streaming is wrapped in a helper. chatStream() sends the same chat request with streaming enabled, then parses the returned event stream into answer text, reasoning text, usage metadata, and the final done signal. The helper also connects those pieces to the lesson panel and Stop button, so the cell can focus on the concept instead of the streaming plumbing. You can open the helper source from the cell menu to inspect exactly what it does.
Applied · LoopCompare maze reasoning inside one fixed loop?

Swapping the reasoning step while the loop stays fixed

You now have both halves of an agent, and the stateless chat() function above will pair nicely with the previous reflex agent's perceive-reason-act loop. Before wiring the model into that loop, it helps to watch the decision step use routines that are not language models first.

Run the nodes in order. Each puts a different decision rule into the same maze-solving loop:

  • The constant "always east" rule reaches the goal on a straight corridor, then stalls at the first wall in the maze, since a fixed heading cannot turn.
  • DFS dives and backtracks out of dead ends.
  • BFS fans out in rings before walking the shortest path.
  • A* biases those same rings toward the goal.
The decision step is the only part that varies. DFS, BFS, and A* all read the current maze state and return a move. One line in the last node chooses the decision function; the loop below runs the same way for each choice. The chat() call you just ran can fill that same step. It changes two edges: your code renders perception as text, and the model returns a tool call that your code applies. The next section zooms in on those two edges.

Using a language model to choose maze moves

The maze loop still samples the world and applies one validated move. Only the decision step changes. Instead of DFS, BFS, or A*, the model reads the rendered maze and asks your code to call choose_direction.

Run the cells in this order, then vary one control at a time:

  1. Run the engine node first. It exports the maze utilities and one shared controller, state.runMaze, that walks corridors automatically and calls the model only at junctions.
  2. Run the editable maze-agent node below. DIRECTIVE is the core system instruction, and the options object controls the model, maze size, text map, image attachment, move history, coordinate trail, and repeated tool schema.
  3. Start with the working defaults. Then turn on includeImage to test the rendered maze, switch model to compare models, or set advanced to grow the maze after the simple case works.
A schema turns the answer into a typed action. The constant rule returned a string. BFS handed back a list. choose_direction gives the model the same contract but enforces it through a JSON Schema, so the loop reads a typed value instead of parsing free prose. With it, a language model can take the decision step that BFS and A* held a moment ago.

The loop, drawn locally

Place the chat() call inside the loop that re-sends history, and a single turn of any LLM agent in this course takes the same four-stage shape. Name the stages now, because every security and reliability question later traces back to one of these four arrows.

  • Locally-sample. Your code reads a slice of the environment, which might be the maze cell and its valid moves, or the latest user message, or the contents of a file. It never reaches for the whole world at once, because all the turn requires is the narrow piece of state the model needs to act on this time around.
  • Locally-perceive. That slice is rendered into a full engineered context: the system prompt, the running messages[] history, prior tool results, and the slice you just sampled. All of it is plain text. For an LLM agent, perception is context engineering.
  • Locally-act. The model maps that text to more text: an intent signal, ideally a typed tool call like { "move": "east" } rather than free prose. The intent only requests an action, and it never performs one. Your code reads it and applies one bounded update to the environment (doMove(...)), validating it first.
  • Locally-impact. The update changes the world. Nothing persisted inside the model. The only durable state is the environment and the context your code will rebuild. The next turn's locally-sample reads the changed world, and the cycle repeats.

Look closely at the middle of that list. The perceive-to-act step is a single stateless text-to-text mapping in which a full engineered context goes in and an intent signal comes back. The model only transforms text, so perceiving the world and turning an intent into a real change remain your code's job on both sides of that mapping.

That leaves the green box below as the only piece you did not write. An agent therefore has just two choices: what you feed into its context, and what you do with the intent it returns.

sample → perceive → (text → text) → act → impact
Each pass of the loop acts only locally. The model reads one small piece of the world such as a single maze cell and hands back one bounded action before the stateless text-to-text step between them forgets everything it just saw. An agent that accomplishes a great deal is only this modest cycle repeated many times while the environment carries the state forward from one pass to the next. Module 1b builds that cycle in code where you can watch the context grow with each turn.

Before you continue

Each question is answerable by editing a knob in the LLM-maze cell above and rerunning it.

  1. Take the text away from the multimodal model. Set includeText:false and includeImage:true, then rerun. The model now sees only the rendered picture of the maze. Does it still reach the goal, and does it take more decisions than the text-plus-image run did?
  2. Remove the strategy hint from the DIRECTIVE. Delete the line that says to prefer unvisited branches over visited cells, then rerun the same cell. Count the decisions against the run with the hint in place. How much of the model's competence came from that one sentence?
  3. Swap the decision model. Change model:"vision" to model:"super" and rerun. The loop, maze, perception, and action do not change. Only the model making the move does. Does the larger text model reach the goal in fewer decisions?

Try it · the model as a pure function

The smallest artifact in the course is a single chat() call with no tools and no memory wrapped around it. Each message you send stands on its own, running the bare stateless function that the loop will later wrap. Type something below and watch the reply stream back.

← Setup