Paul Portfolio Agent
AI-powered chat assistant for ochiengpaul.com
// featured build
Paul Portfolio Agent
Designed with a product mindset and implemented across the stack.
Build profile
What it is
A production-minded FastAPI chatbot that answers questions about Paul's work from a curated local knowledge base. It can search the web when a question needs current information, streams responses via Server-Sent Events to a Next.js widget, and limits each anonymous browser to 15 accepted chat requests per day.
The Problem
Static portfolio sites force visitors to read through pages of content to find specific information about skills, experience, or projects. A conversational interface that can answer questions directly — grounded in curated facts about Paul's work — would let visitors get precise answers without navigating away from the page.
My Role
Designed and built the complete backend: FastAPI server with provider abstraction for five LLM backends (OpenAI, Ollama, OpenRouter, HuggingFace, OpenAI-compatible), SSE streaming, curated knowledge retrieval without a vector database, signed anonymous cookie-based rate limiting with SQLite/Redis stores, Tavily and DuckDuckGo search integration, Docker deployment, and CI. Also built the accompanying Next.js chat widget.
Approach
FastAPI with clean architecture: domain services for agent orchestration, infrastructure for providers and search, REST interfaces for chat and config. Provider abstraction with runtime fallback chains so the agent can survive any single provider going down. Curated portfolio knowledge loaded at startup and retrieved by relevance — no embeddings or vector store needed. Signed HTTP-only browser cookie for anonymous identity, daily quota with secondary IP-based cap, and burst protection. SSE streaming from the backend to the widget with token-by-token reveal. DuckDuckGo as default search with Tavily as optional upgrade.
Hardest Decision
Implementing anonymous rate limiting without invasive fingerprinting. A website cannot reliably identify a physical device without authenticated users or invasive techniques. The solution uses a signed, HTTP-only browser cookie combined with a hashed-IP daily cap — deliberately not presented as perfect device identification. This keeps the system privacy-respecting while still limiting casual abuse. For stronger enforcement later, sign-in, CAPTCHA, or an API gateway can be layered on.
Status
Live at api.ochiengpaul.com, powering the 'Ask Paul's assistant' widget on ochiengpaul.com. Supports five LLM providers with runtime fallback, web search via DuckDuckGo/Tavily, daily quota enforcement, and Docker Compose deployment with Redis for production.
What I'd Change
Would add database-backed conversation history, embeddings-based knowledge retrieval for larger knowledge bases, visitor authentication for persistent sessions, and an analytics dashboard for chat usage patterns.