Skip to content
Carlos Morais.
Work

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

Replay

Answer with the buttons below. Each choice becomes a real call to the FIPE API, and you can watch it on the side.

Renova:

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

The browser lists conversations and sends each message over the REST API. Express rebuilds the history from PostgreSQL, calls GPT-4o with the four tools and runs the ones it asks for against FIPE's public API, until the model answers in text. Everything, tool calls included, goes back to the database.

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.