nikGPT
How I built an AI agent that knows my career, my opinions, and when to say no.
The idea
Every intro call with a recruiter starts the same way. Where are you based? Are you open to relocation? What does your process look like? What tools do you use? These are reasonable questions, but answering them for the twelfth time in a month gets old fast.
A static about page does not really solve this. It is a monologue. You write it once, it covers maybe 60% of what people want to know, and there is no way to ask a follow-up. So I built something that could handle the other 40%.
nikGPT is a custom AI chat agent embedded at nikmargetic.com/ai. Visitors can ask it anything: my background, my process, my opinions on design tools, whether I prefer working in product or agency settings. It answers in third person, in my voice, and it knows when to redirect a question to me directly rather than making something up.
The stack
Three pieces. Each one has a specific job.
Framer
Frontend / UI
Vercel
Proxy / Middleware
Anthropic API
Language Model
Why a proxy? You cannot call the Anthropic API directly from a browser. The API key would be exposed in the client-side bundle, and the request would be blocked by CORS. The Vercel function sits in between: it receives the message from Framer, attaches the key server-side, forwards to Anthropic, and pipes the response back. The frontend never sees the key.
That is the whole architecture. Simple on purpose. No databases, no user accounts, no logging beyond what Vercel provides by default.
Building the component
The component is a React component written in TypeScript, running inside Framer as a code component. A few decisions shaped how it feels:
Typewriter intro
The opening message types itself in on load. Sets the expectation immediately: this is interactive, not static.
Simulated streaming
The API returns the full response in one chunk. A typeOut function renders it character by character, so it feels like the model is thinking rather than dropping a wall of text.
Bouncing dots loader
While the API call is in flight, three dots animate in sequence. Without this, the silence reads as broken.
Staggered chip animations
Suggested questions fade in with an 80ms delay between them. Give first-time visitors somewhere to start, disappear after the first message is sent.
iMessage-style bubble entrance
User messages slide in from the right with a slight spring animation. A familiar pattern that makes the conversation feel more like a real exchange.
Session persistance
Conversation history survives a page refresh via localStorage. Not critical for most visitors, but it matters for anyone who shares the link and comes back to it later.
Restart button with portal tooltip
Resets the conversation. The tooltip renders into document.body via a React portal so it is not clipped by the parent frame overflow. This one took longer than it should have.
The trickiest part wasn't the API integration, it was canvas vs. runtime behaviour in Framer. Property panel values override code defaults, which meant keeping the code file and canvas node in sync manually. And Framer's preview sandbox blocks external fetch requests entirely, so testing required publishing to the live site.
Teaching it about me
The system prompt is a character brief. You are not configuring a chatbot, you are writing a document that tells the model who it is playing, what it knows, how it should sound, and where the limits are.
Version 1 was maybe fifteen bullet points: name, current role, location, a few sentences about background. It worked, barely. The agent could answer basic questions but gave thin answers and occasionally made things up when it ran out of real information.
The knowledge base grew from there. Each section evolved through testing: ask the agent something, notice it handles it badly, go back and fill in the gap.
Identity
Who Nik is, where he is based, what he does, what he is looking for. The foundation everything else builds on.
Career
Roles, companies, timeline. Current role at Novi Digital / Zugerberg Finanz. Prior roles at OTP Banka, CIF / N26.
Working Style
How Nik approaches design problems, what he values in a team, how he handles feedback and ambiguity.
Tools
Figma, Framer, v0.dev, Claude. Learning React. Transitioning toward design engineering.
Cabin Crew
4.5 years at Emirates. 49 countries. How it shaped his perspective on people, service, and detail.
Personal ∙ Food ∙ Music ∙ Culture
The texture that makes the agent feel like a person, not a résumé. Things that do not belong on a LinkedIn profile but make a real impression.
The bigger structural shift was moving the knowledge base out of the Framer canvas entirely. Originally the system prompt lived as a hardcoded property on the canvas node. Every update meant opening Framer, pasting in the new version, and republishing the site.
Now the system prompt is a knowledge-base.json file in a GitHub repository. The Vercel proxy fetches it at request time and injects it into the API call. Updating the knowledge base means editing a JSON file and pushing to main. Vercel picks it up automatically. Framer does not need to be touched.
Personality & guardrails
Tone rules matter as much as factual coverage. A few baked into the system prompt:
do's
Third person only. nikGPT refers to Nik as "Nik," not "I." Keeps the interaction from feeling like a chatbot pretending to be a person.
Lead with the answer. No preamble, no "great question," no throat-clearing.
Calculate age and years of experience at runtime from fixed reference dates. The answer is always accurate without anyone remembering to update it each year.
don'ts
Use em dashes. They are a stylistic tell, and Nik does not use them in his own writing. Hard rule.
Invent. If a question is not in the knowledge base, say so and suggest getting in touch. No hallucinating details to seem complete.
Answer salary expectations, personal contact details, or anything that needs a real conversation. Return with a learned response instead.
Iterating in real time
The current workflow:
1
Edit the knowledge base
Update
knowledge-base.json
locally. Fix a bad answer, fill a gap, adjust the tone of a section.
2
Push to GitHub
One commit to main. No other steps.
3
Vercel auto-deploys
The proxy function picks up the new JSON on its next cold start. No configuration, no manual triggers.
4
Live within 30 seconds
Test on the actual site, not the canvas preview. Framer's sandbox blocks external fetch requests, so the live site is the ground truth.
No Framer canvas changes. No republishing the portfolio site. No copy-paste.
The knowledge base still has gaps: questions that need better answers, sections that are thinner than they should be. But the friction of updating it is low enough that it actually gets done, which is the only thing that matters.
What I'd do differently
Honest notes on what I would change if starting over:
would revisit
The canvas-property approach was the wrong architecture from the start. Migrating to GitHub-hosted JSON later cost more time than doing it right the first time would have.
The GIF easter egg is the most memorable thing in the agent. More of them, earlier.
The knowledge base needed more coverage at launch. Early gaps showed up immediately in real conversations.
what's next
Finishing the outstanding knowledge base answers. There is a list.
A proper case study page documenting the build process end to end.
Further fine-tuning the AI agent
Writing a system prompt is design work. You are making decisions about tone, scope, and personality the same way you make decisions about layout and hierarchy. The medium is different. The thinking is not.