Building an AI chatbot for my portfolio

I added a small AI chatbot to my portfolio this week. The idea was simple.
A search bar sits in my hero section, and when someone types a question, it expands into a chat. Visitors can ask about my work, my background, what I am looking for, and get an answer that actually sounds like me.

At first, I thought the hard part would be the AI integration in the Framer but it was not. The hard part was everything around it, and most of that turned out to be writing.

Here is what the process actually looked like, and what I learned along the way.


Starting with the easy part

The UI came together fast.


A rounded input bar, a send button, a few suggestion pills underneath. Type something, the chat window expands above the input, the response streams in.

I borrowed the search bar to chat pattern from a couple of portfolios I had seen, where the chatbot lives right in the hero instead of being tucked away in a corner bubble. It felt like a better fit for a portfolio. Recruiters are already looking at the hero, so the chat is right there instead of being something they have to discover.

Wiring this part up with Claude Code took maybe twenty minutes. Input, state management, a loading animation, done.

Then I had to make the thing actually answer questions about me, and that is where most of my time went.


The part that took the longest: writing the context

A chatbot like this works by sending a system prompt to the model every time someone asks a question. That system prompt is basically a brief about you. The model only knows what you tell it, so if the brief is vague or disorganized, the answers come out vague and disorganized too.


Getting a structure first

I started by asking Claude to help me figure out how this kind of context should be structured, based on what generally works for AI prompts. We landed on a section based format. One paragraph for my background, one for my most recent role, one for selected work, one for how I work with engineers, one for what I am looking for, one for personality and fun facts. Each section covers exactly one topic and nothing else.

Here is the context and structure i gave chatbot:

You are Wen, a product designer. You are talking directly with someone visiting your portfolio — a recruiter, hiring manager, or collaborator. Speak in first person as Wen, in a warm, friendly, and confident tone. Not stiff, not overly casual. Think of it like a great first conversation at a design meetup.

CRITICAL RULES ABOUT LENGTH

STRICT CONTENT RULES

ABOUT WEN

MOST RECENT ROLE

SELECTED WORK

SKILLS AND TOOLS

HOW I WORK WITH ENGINEERS

WHAT I AM LOOKING FOR

CONTACT

PERSONALITY AND FUN FACTS


Writing it section by section

From there I went section by section. I would ask for a first draft of one paragraph, read it, and tell Claude what felt off or what was missing. Sometimes it was tone, sometimes it was a fact that was not quite right, sometimes it was just too long. We went back and forth on each section until it sounded like something I would actually say.

Doing it this way, one labeled section at a time, made the whole thing feel a lot more organized than if I had just tried to write one long paragraph about myself. When someone asks about my background, the model pulls from one focused section instead of trying to extract the relevant bit from one long block of text.

Making every sentence do double duty

The other thing that helped was treating every sentence like it had to do double duty. It needed to sound like something I would actually say, answer the question a hiring manager cares about, and stay short enough that the model would not run out of room. I ended up writing each section almost like I was answering the question out loud in an interview, then cutting it down until only the part that mattered was left.

Setting a hard rule for response length

I also added a hard rule: keep every response to two or three sentences, and always finish the sentence completely, never trail off. Without that rule, longer responses would get cut off mid thought, which looked broken and felt unfinished. With the rule in place, the model wraps up its point within the space it has instead of starting something it cannot finish.


Design Decisions

Why I hardcoded the three most common questions

I put three suggestion pills under the input bar. What sets you apart as a designer, tell me about a project with real impact, how do you work with engineers.

These are the three things almost every recruiter or hiring manager wants to know first. So instead of letting the model generate a fresh answer every time, I wrote out the exact answer for each one and hardcoded it. When someone clicks one of these, there is no API call at all. The answer just appears.

This solved two problems at once. It is faster, since there is no waiting on a model response for the most common questions. And it let me control the framing precisely, since these are the three answers most people will actually read, so I wanted them to be exactly right rather than whatever the model generated that day.

Anything outside those three still goes to the model, using the same context.

Why the suggestion pills disappear one at a time

My readers here are recruiters and hiring managers, and they do not have a lot of time to play around with a chatbot. So I kept it to three options instead of giving them a long list to scroll through.

When someone clicks one, that pill disappears and the other two stay. I thought about generating new follow up suggestions after each answer, but decided against it. New pills would look identical to the old ones, so it would be easy to miss that they changed, and the old pills that never got clicked would just sit there unclicked anyway. Keeping it simple, three options, click one, it goes away, felt more honest about what is actually happening.

Why the title disappears once the chat starts

My hero has a typing animation for the title, "Hi, I'm Wen, a product designer." It is a nice touch on its own, but once someone starts chatting, having a title with its own animation playing in the same area felt distracting. Two things competing for attention in the same space.

So once the first message is sent, the title fades out. The chat takes over the space cleanly, and there is nothing else animating or pulling focus while someone is reading a response.


The bug: scroll position during the typewriter effect

I wanted responses to appear character by character instead of all at once, since it feels more like a real conversation. That part was easy to build.

The bug showed up once responses got long enough to need scrolling. The chat window would not scroll as new characters appeared, so the response kept growing below the visible area while the view stayed put. By the time the response finished, the most recent text was below the fold and you would have to scroll down manually to read it.

The fix was simpler than I expected. Instead of only scrolling once the full message finished loading, I scrolled the chat window to the bottom on every single character as it appeared. Now the view tracks the text as it types, the same way it would if you were watching someone type a message in real time.

Hitting rate limits and switching providers

I was using Gemini for the model, and during testing I kept hitting rate limits. Free tier limits are pretty low, and testing a chatbot means sending a lot of test messages in a short time, so I ran into the limit constantly.

I switched to Groq, which runs open models like Llama on its own hardware built for fast inference. The difference in response speed was noticeable right away, and the free tier limits are much more generous, which matters a lot during testing when you are sending dozens of messages back to back.


What I would tell someone building something similar

If I had to summarize the actual lessons:

Spend more time on the context than the UI.
The UI is the easy part. The context is what makes the chatbot sound like you instead of a generic assistant.

Write the context in sections, not one big block. One topic per paragraph makes it much easier for the model to find and use the right information.

Set hard limits on response length, and tell the model to always finish its sentences. Otherwise responses run long and cut off mid thought.

Hardcode the answers to the questions you know people will ask most. You do not need a model call for every interaction, and it gives you control over the framing of your most important answers.

Add guardrails for off topic questions. A portfolio chatbot does not need to answer general knowledge questions, and without a rule telling it to stay on topic, it will try to. That just burns through your token budget on questions that have nothing to do with why someone is on your site in the first place.

Test it like the person who will actually use it would. I went through this as if I were a recruiter skimming a portfolio, clicking through the obvious questions first, and that is what surfaced most of the issues with response length and tone.

The whole build took an evening, but most of that evening was rewriting the context and testing answers, not writing code. If you are building something like this, budget your time accordingly.