Building a Resume RAG Chatbot for a Portfolio Assistant
How I turned a static portfolio into an AI-powered assistant using Next.js, the Vercel AI SDK, Gemini embeddings, and pgvector-backed retrieval over resume content.
Tags
Building a Resume RAG Chatbot for a Portfolio Assistant
TL;DR
I built an AI-powered portfolio assistant that answers visitor questions about my experience using retrieval-augmented generation. The system parses my resume into semantic chunks, stores embeddings in PostgreSQL with pgvector, retrieves the most relevant context for each question, and streams grounded answers through the Vercel AI SDK inside a custom chat widget.
The goal was not to add AI for novelty. It was to make the portfolio easier to explore and more useful to visitors who want fast answers about technical background, project experience, and likely team or product impact.
The Challenge
A traditional portfolio gives visitors information, but it does not give them a good way to interrogate relevance.
A hiring manager might want to know whether I have backend depth. A founder might want to know whether I can build AI features safely. A product lead might want to understand whether my past work suggests I can help ship faster or reduce implementation risk. All of those are reasonable questions, but on a normal portfolio site the answers are spread across resume sections, project pages, and blog content.
That creates a discovery problem. The content exists, but the visitor has to assemble the narrative manually.
I wanted to solve that by turning the portfolio into something queryable. Instead of scrolling and guessing, visitors should be able to ask:
- ›What kinds of full-stack systems has Sadam built?
- ›What does his backend experience suggest for platform reliability?
- ›How could this background help a team shipping AI features?
The assistant also needed to avoid the most common failure mode of portfolio chatbots: generic, overconfident answers that sound polished but are not actually grounded in the underlying content.
The Architecture
Resume as the Knowledge Source
I already had the resume stored as MDX, so I treated that file as the canonical content source rather than creating a separate knowledge base.
That mattered for maintainability. I did not want two parallel systems:
- ›one place where I update my resume
- ›another place where I update AI content
Using the existing MDX source meant the assistant could stay synchronized with the portfolio's real content workflow.
Semantic Chunking
The next step was deciding how to break the resume into retrievable units.
Instead of fixed-size token slicing, I split the document using semantic boundaries such as ## and ### headings. That preserved the meaning of each section better because chunks stayed aligned with real concepts like work experience, skills, and projects.
This was a better fit for resume content than naive chunking. A resume is already hierarchically structured, so the retrieval layer should respect that structure.
Embeddings and Retrieval
For embeddings, I used Google's embedding model and stored vectors in PostgreSQL with pgvector. Query-time retrieval uses cosine similarity to find the most relevant chunks for the user's latest question.
I chose pgvector because it matched the scale and constraints of the project:
- ›the corpus is relatively small
- ›Postgres was already in use
- ›I wanted minimal operational overhead
For this kind of feature, simplicity was a product advantage. The architecture stays understandable, and I can still demonstrate a real retrieval pipeline rather than a toy prompt-only chatbot.
AI SDK Chat Route
On the server, the chat route follows the Vercel AI SDK useChat flow:
- ›receive
UIMessage[] - ›validate the incoming messages
- ›extract the latest user text for retrieval
- ›query pgvector for relevant resume chunks
- ›inject those chunks into the system prompt
- ›convert validated messages with
convertToModelMessages(...) - ›stream the response back with
toUIMessageStreamResponse()
That flow matters because it keeps the implementation aligned with the SDK's intended contracts instead of forcing older message formats into a newer UI pipeline.
Key Product Decisions
1. Grounded Answers Over Open-Ended Personality
I wanted the chatbot to feel conversational, but not improvisational.
The system prompt tells the model to answer only from the provided resume context and to explicitly connect the evidence to likely team or product outcomes. That creates a better kind of answer:
- ›what I have done
- ›why that experience could matter
This is different from a generic assistant persona. The real value is not style. The real value is interpretation grounded in evidence.
2. Explain Outcomes, Not Just History
A plain resume restates past work. A useful assistant should help the visitor interpret relevance.
So I changed the prompt style to use an evidence + outcome structure. That means a strong answer does two things:
- ›points to specific experience from the resume
- ›explains what that experience suggests for team execution, product delivery, or engineering outcomes
This makes the chatbot much more useful for decision-makers. It stops sounding like a document reader and starts acting like a contextual guide.
3. Keep the UX Productized
If the chat experience feels clunky, users treat it like an experiment instead of a product feature.
So I treated the UI as part of the system, not decoration:
- ›floating widget pattern
- ›dedicated chat panel
- ›starter questions
- ›contained scrolling
- ›streaming states
- ›markdown rendering for richer answers
This gave the assistant enough polish to feel native to the portfolio.
Technical Challenges
Message Format Mismatch in the AI SDK Flow
One of the first implementation issues came from the boundary between useChat and the server route.
The client sends UIMessage[], but the route initially treated them like ModelMessage[]. That caused a subtle but important mismatch, especially for multi-turn chat.
The fix was to align with the official AI SDK pattern:
- ›accept
UIMessage[] - ›validate them
- ›extract user text from
parts - ›call
convertToModelMessages(...)before generation
This was a good reminder that RAG problems are not only about retrieval quality. Message-shape correctness matters just as much when you want reliable streaming chat behavior.
Retry Behavior and Provider Quota Noise
During debugging, it looked like the chatbot was making too many generation attempts. The root cause was that the AI SDK retries generation and embeddings by default.
That is useful in many applications, but when debugging provider quota behavior it creates confusion because one user message can fan out into multiple attempts. I disabled retries in the relevant code paths so failures became easier to reason about and provider usage became predictable.
Making the Chat UI Actually Readable
Another issue was presentation. The model could generate markdown-like structure, but the chat panel rendered it as plain text.
That made answers feel flatter than they actually were. I added markdown rendering with react-markdown and remark-gfm, then tuned the typography so assistant responses stayed compact enough for a chat UI instead of inheriting oversized prose defaults.
Scroll Behavior Inside the Panel
The chat panel also needed layout fixes so scrolling stayed inside the widget. In a flex layout, missing min-h-0 constraints can break overflow behavior and cause scroll events to leak to the page.
Fixing that made the chat panel feel much more contained and intentional.
The Outcome
The result is a portfolio assistant that does more than restate resume bullets.
It helps visitors move from raw background information to practical interpretation:
- ›what I have built
- ›what technical decisions I have made
- ›what kinds of outcomes that experience may support for teams and products
That is a better portfolio experience for visitors, and it also functions as a live product sample. Instead of merely claiming experience with AI product development, the portfolio now demonstrates it in context.
Why This Project Matters
I think this project is a good example of how small AI features become meaningful when they sit inside a real workflow.
The novelty was never "there is a chatbot on the page." The value came from:
- ›grounding answers in a real content source
- ›making static portfolio material conversational
- ›translating technical history into relevance for visitors
- ›integrating the feature with the same level of care as the rest of the product
That combination is what makes the implementation portfolio-worthy rather than gimmicky.
What I'd Improve Next
If I take this further, the next iterations would be:
- ›expand retrieval beyond the resume into project and case-study content
- ›add visible citations or source links in answers
- ›build an evaluation set of common visitor questions to test retrieval and answer quality over time
- ›add analytics around which questions visitors ask most often so the portfolio can evolve around real interest patterns
Takeaways
This project reinforced a few patterns I would reuse in future AI products:
- ›start with an existing content source instead of creating duplicate admin workflows
- ›keep the retrieval stack simple when the corpus is small and the app already uses Postgres
- ›align server chat routes closely with AI SDK contracts to avoid subtle streaming issues
- ›treat UX details like scrolling, rendering, and readability as part of the AI feature, not afterthoughts
The broader lesson is that good AI product work is often about interpretation and integration, not just model calls. The most useful assistant is not the one that says the most. It is the one that helps people understand the right things faster, with trust.
Collaboration
Need help with a project?
Let's Build It
I help startups and established companies design, build, and scale world-class digital products. From deep technical architecture to pixel-perfect UI — let's bring your vision to life.
Related Articles
Optimizing Core Web Vitals for e-Commerce
Our journey to scoring 100 on Google PageSpeed Insights for a major Shopify-backed e-commerce platform.
Building an AI-Powered Interview Feedback System
How we built an AI-powered system that analyzes mock interview recordings and generates structured feedback on communication, technical accuracy, and problem-solving approach using LLMs.
Migrating from Pages to App Router
A detailed post-mortem on migrating a massive enterprise dashboard from Next.js Pages Router to the App Router.