Why I built this
I'm a CS graduate preparing for a Data Science/AI master's application, and I wanted
to go beyond the usual coursework projects — Kaggle competitions, Coursera
certificates — and actually build something that shows I understand how modern AI
systems work under the hood, not just how to call an API.
So I built three small agents, each demonstrating a different core pattern in how
LLMs interact with the world: web search, retrieval over your own data, and live
code execution. All three run entirely on Google Gemini's free tier — total cost: $0.
Agent 1: Research Assistant (tool-use)
The simplest and most foundational pattern: give the model a tool and let it decide,
on its own, when it actually needs to use it. I used Gemini 2.5 Flash as the LLM and
DuckDuckGo search (via the free ddgs Python package, no API key needed) as the tool.
The agent doesn't search for everything — it only calls the web_search tool when
it judges that it needs current or specific information it isn't confident about,
and answers directly from its own knowledge otherwise. This matters because a naive
"always search" agent wastes calls and can actually produce worse answers by
grounding itself in irrelevant search results for questions it already knew the
answer to. Getting this decision right — teaching the model when not to use a
tool — turned out to be as important as the tool integration itself.
Agent 2: RAG Q&A (retrieval-augmented generation)
The difference between an agent that "knows things" from training and one that
looks them up in real, specific source material. This agent ingests your own
documents (notes, articles, anything in .txt/.md format), splits them into
overlapping chunks, and embeds each chunk into a vector using Gemini's embedding
model. Those vectors get stored locally in ChromaDB — no hosted database, runs
entirely on your own machine.
When you ask a question, it's embedded the same way, and the database finds the
chunks whose meaning is closest to your question. Only those chunks are handed to
Gemini as context, and it's instructed to answer using only that context, citing
which file it came from. This grounding is what reduces hallucination — the model
isn't guessing from general training data, it's answering from your actual source
material, and it's honest when the answer isn't in there.
Agent 3: Data Analysis Agent (code execution)
Instead of trusting the model's own arithmetic (LLMs are notoriously unreliable at
exact calculations), this agent writes real pandas code and executes it against a
CSV you give it, then reports the actual computed result — not a guess. You ask a
question in plain English ("what's the average fare by passenger class?"), the
model translates that into pandas code, the code actually runs, and you get a
verifiable answer rather than a black-box one. This is the same underlying idea as
tools like ChatGPT's Code Interpreter, just built from scratch to understand how it
actually works.
What tied all three together
All three agents share the same underlying pattern: the model decides when to call
a tool, uses it, then reasons over the result. The main thing that surprised me was
how much of the real engineering effort wasn't the "AI" part — it was the boring
plumbing: handling free-tier rate limits gracefully, chunking documents sensibly,
deciding what to exclude from version control. The actual "make an LLM call a tool"
logic was often the easiest part; making it work reliably and cheaply was where the
real learning happened.
Try it yourself
All three repos are public on GitHub, along with an overview repo tying them together:
If you're building something similar on a student budget: Gemini's free tier is
genuinely usable for small projects like this — you don't need to spend anything to
learn how agentic systems actually work.
Top comments (2)
The line that stood out: "teaching the model when not to use a tool turned out to be as important as the tool integration." That's the lesson most tutorials never reach, and you got there on agent #1. An always-search agent doesn't just waste calls — it actively poisons answers it already knew by grounding them in mediocre retrieval, which is a subtle failure because it looks like the tool is "working."
One thing that'll bite once these leave your laptop: the code-execution agent is the one to sandbox hardest. Writing real pandas is the right call over trusting LLM arithmetic, but "execute model-written code" is also the widest blast radius in the whole stack — resource limits, no network, ephemeral filesystem, the works. Worth building that boundary in from the start rather than retrofitting. For the RAG agent, the next thing to measure is chunk retrieval quality itself (recall@k on a few hand-labeled questions) — grounding only helps if the right chunk actually comes back. Genuinely solid set of projects to reason about the patterns from; this is the right way to learn the stack.
Really appreciate this — especially the sandboxing point. You're right that "runs
locally on my own machine" is doing a lot of load-bearing work right now that
wouldn't hold if this ever left my laptop. I'll add it as a documented limitation
for now and look into a proper sandboxed execution environment (resource limits,
no network, ephemeral fs) as a next iteration.
The recall@k suggestion is great too — I've been evaluating the RAG agent by
eyeballing answer quality, which I now realize doesn't actually tell me whether
retrieval itself is working. Going to put together a small hand-labeled test set
this week.
Thanks for actually engaging with the technical details instead of just the surface
level — this is exactly the kind of feedback that's hard to get building solo.