An agent is a loop that lets an AI model decide what to do next, call tools, and keep going until the job is done — and once you see the loop, you'll understand every AI feature in EngineerPrep.
Picture this: your product manager asks you to add a feature where the EngineerPrep tutor automatically builds a student a personalized study plan. You open your IDE and think: "Easy. I'll call the LLM, get a plan back, done." So you write a single API call. You send the student's message. Claude replies with a generic plan. The PM looks at it and says, "But it doesn't know what questions the student has already tried. And it didn't check their weak topics. And it didn't save the plan anywhere." You patch it. You add a database fetch before the LLM call. Then another for weak topics. Then a save step after. Then the requirements change again. Now your "simple" feature is a tangle of hand-stitched steps that breaks whenever one piece moves. The frustrating truth: you were solving the right problem in the wrong way. You were making you the decision-maker…
Think about how you'd handle a complex task at work — say, planning a team offsite. You don't do it all in one move. You check the calendar, look up venues, email a few people, read the replies, adjust the plan, and keep going until it's done. You react to new information at each step. An agent works exactly the same way. An agent is an AI model that is given a goal and a set of tools — small functions it can call, like "search the database" or "save a note" — and then it loops: it thinks about what to do next, calls a tool if it needs to, looks at the result, and repeats until the goal is met. That loop is the whole idea. Think → Act → Observe → Think again. Compare that to a plain LLM call (a one-shot prompt): you send a message, the model replies once, and it's over. The model has no memory of what happened before and no way to fetch additional information mid-response.…
Imagine the following scene unfolding step by step on a whiteboard. Step 1 — The student sends a message. A box labeled Student sends the text "Help me prep for a system design interview" to a box labeled Agent . The agent receives it and wakes up. Step 2 — The agent thinks. Inside the agent box, there's a small thought bubble: "I need to know what this student has already studied before I can help." The agent decides it needs more information. It does not reply to the student yet. Step 3 — The agent calls a tool. An arrow leaves the agent box and points to a box labeled Tool: get student history . The agent passes the student's ID. The tool queries the Postgres database and returns a list of topics the student has covered. Step 4 — The agent observes the result. The result flows back into the agent box.…
Here is a minimal but real Spring AI agent from EngineerPrep's codebase. We'll walk through it a few lines at a time. The tool methods are plain Java methods annotated with @Tool . Spring AI registers them so the LLM can call them by name. getStudentHistory queries JPA for past sessions; searchQuestions runs a pgvector similarity search through the QuestionRepository . The service is a normal @Service bean. Notice constructor injection — no @Autowired field injection. ChatClient is the Spring AI class that wraps the LLM connection (backed here by Amazon Bedrock / Claude). CapsteadBudgetGuard is the Capstead component that checks cost limits before each call so the agent can't run indefinitely and exhaust the budget. The runTutorAgent method is the entry point. It calls .prompt() on the chat client, attaches the two tool methods with .tools(this) , and calls .call().content() .…