Course Wiki

The Agentic Loop — LLM Zoomcamp Module 1

--- video_url: 'https://www.youtube.com/watch?v=ePlQUcTPPjw&list=PL3MmuxUbc_hLZFNgSad56pDBKK8KO0XIv' --- # The Agentic Loop

LLM Zoomcamp › Module 1: Agentic RAG › The Agentic Loop

Notes


video_url: “https://www.youtube.com/watch?v=ePlQUcTPPjw&list=PL3MmuxUbc_hLZFNgSad56pDBKK8KO0XIv”

The Agentic Loop

In the previous lesson, we did function calling by hand. We sent a message and got back a function call. We ran it, sent the result back, and got the answer.

That works for one function call. It breaks down when the model wants to search several times, or when the first search misses the answer. We don’t know in advance how many calls the model will want. So we need a loop that keeps calling the model and running tools until it’s done. An agent is exactly that.

Anatomy of an agent

With the LLM in the driver’s seat, we have an agent. It’s an AI assistant whose goal is to help the user.

An agent has three parts:

Everything below is the code that wires these three together inside a loop.

A developer prompt

So far we’ve relied on the model to figure out when to search. We make that more reliable with a developer message that spells out how to behave. This is where we give the agent its role. The same message also pushes it toward multiple searches, so we get to watch the loop run more than once.

instructions = """
You're a course teaching assistant.
You're given a question from a course student and your task is to answer it.

If you want to look up information, use the search function. 
Use as many keywords from the user question as possible when making first requests.

Make multiple searches.

Try to expand your search by using new keywords
based on the results you get from the search.

At the end, ask if there are other areas that the user wants to explore.
""".strip()

A function-call helper

We’ll be running function calls repeatedly inside the loop, so let’s wrap that in a small helper. It turns the JSON arguments into a Python dict, calls the right function, and serializes the result. We only have one tool for now, so we dispatch on the function name directly.

def make_call(call):
    args = json.loads(call.arguments)

    if call.name == "search":
        result = search(**args)

    result_json = json.dumps(result, indent=2)

    return {
        "type": "function_call_output",
        "call_id": call.call_id,
        "output": result_json,
    }

The helper returns the exact structure the Responses API expects. When we add more tools later, we’ll extend this with more if branches (or switch to a registry).

Processing one response

Let’s process a single model response. We append each output entry to the conversation, print any messages, and run any function calls. Function-call results get appended too.

question = "I just discovered the course. Can I join it?"
messages = [
    {"role": "developer", "content": instructions},
    {"role": "user", "content": question},
]
response = openai_client.responses.create(
    model="gpt-5.4-mini",
    input=messages,
    tools=[search_tool],
)
messages.extend(response.output)
has_function_calls = False
for item in response.output:
    if item.type == "function_call":
        print("function_call:", item.name, item.arguments)
        call_output = make_call(item)
        messages.append(call_output)
        has_function_calls = True

    elif item.type == "message":
        print("ASSISTANT:")
        print(item.content[0].text)

The has_function_calls flag tells us whether the model needs another API call. If the response contains a function call, the updated messages has tool output the model hasn’t seen yet. We’ll need to send it back.

The full agent loop

We wrap this in a while l

Key concepts

Sources


DataTalks.Club. Hosted on GitHub Pages. Built with Rustkyll. We use cookies.