Solo project · hiring challenge
Renova: a chatbot for Brazil’s vehicle price table
A chatbot that looks up Brazil's FIPE vehicle price table: the model does the talking, and four tools fetch brand, model, year and price from the public API, in the order it demands.
Renova
FIPE price lookup
Answer with the buttons below. Each choice becomes a real call to the FIPE API, and you can watch it on the side.
Hi! I look up vehicle prices in the FIPE table, Brazil’s reference for used vehicle prices. What kind of vehicle are you after?
A replay: the lines follow a script and you choose with buttons. The FIPE lookups are real, made right now by your browser. In Renova, GPT-4o does the talking: it understands free text and decides on its own when to call each tool.
In 30 seconds
- A solo project, built in about 10 days as a hiring challenge.
- A GPT-4o chatbot with four tools that query the FIPE API in the right order, in a tool-calling loop of my own.
- The hiring process ended with me hired. The demo above uses the real FIPE API.
- When
- Feb — Mar 2025 · 10 days
- Role
- Solo project · from idea to code
- Origin
- Hiring process challenge
- Stack
- TypeScript, React, Vite, Tailwind, Node.js, Express, Prisma, PostgreSQL, LangChain, OpenAI (GPT-4o), Docker
Context
In February 2025, Alpha EdTech referred me to the hiring process of a conversational AI startup. Renova was the project I built for it: a chatbot that looks up prices in the FIPE table, Brazil's reference for vehicle prices.
I built it alone, in about 10 days: the idea, the solution design, the prototype and the code, from the database to the interface.
The problem
The FIPE API doesn't answer a question like “how much is a 2012 Palio?”. It is a sequence: vehicle type, brand, model, year and price, and each step needs the exact code returned by the step before. A language model is good at conversation, but it makes up numbers and codes just as confidently.
The bot had to bring both together: let people talk their own way and, underneath, follow the API's order without ever guessing a code or a price.
My part
A solo project: every part below is mine.
- Backend in Express and TypeScript: conversation and message routes, input validation and Prisma on PostgreSQL.
- The bot: GPT-4o through LangChain, four Zod-typed tools and the loop that runs the calls.
- The system prompt and each tool's description.
- Frontend in React, Vite and Tailwind: a conversation list, a chat with Markdown, a waiting animation and each reply's duration.
- Docker Compose with the database, the API and the frontend served by Nginx.
Technical decisions
Tools instead of guesses
Each API step became a tool (getMarcas, getModelos, getAnos and getValor) with Zod-validated arguments. The descriptions spell out the order and forbid made-up codes, and the prompt makes the FIPE table the only source of prices.
A tool loop of my own
In the first version, the bot made one call and answered. Then it became a loop: as long as the model asks for tools, the server runs them and hands back the result, so it can chain steps in a single reply. I used LangChain's bindTools and wrote the loop by hand, without the AgentExecutor.
The history includes the tools
The model keeps nothing from one message to the next. Without FIPE's answers in the history, the bot lost the codes it had just shown. The fix was to store the tool calls and their answers in the database too, as their own message type, and rebuild the whole conversation on every message. The prompt also asks to always show “[code] Name”, so people can answer with the exact code.
Big lists, whole
Some answers are huge: Fiat alone has 586 models, almost 39 thousand characters. I tried cutting every list at 100 items, but then the model a person was looking for could be left out. The cut came out, and lists go in whole.
A prompt of goals, not steps
The first system prompt dictated every action and when to call each tool: the bot was predictable, but stiff. I rewrote it around the goal and the rules that can't break, like the API's order, and let the model pick the path. The conversation got more natural without breaking the lookup.
Errors become answers
If the API fails or a code doesn't exist, the tool doesn't crash the request: it hands the model an error text (“not found, check the identifiers”), and the model can ask the person for something else.
Architecture
Outcome
I delivered it in about 10 days: the chatbot working end to end, with saved conversations, each reply's duration and everything starting with Docker Compose.
The hiring process ended with me being hired: I worked at that startup from April 2025 to January 2026.
The code is public on GitHub. The demo at the top of this page replays the conversation with the same four tools, calling the real FIPE API.