diff --git a/README.md b/README.md index 5918282..384ea5f 100644 --- a/README.md +++ b/README.md @@ -256,6 +256,7 @@ This project includes comprehensive documentation organized by purpose: - **[ARCHITECTURE.md](doc/ARCHITECTURE.md)** - System design, patterns, and technical decisions - **[API.md](doc/API.md)** - Complete HTTP API reference and HTMX integration - **[AI-CHAT-AGENT.md](doc/28-AI-CHAT-AGENT.md)** - ADK Go agent architecture, tool design, and integration details +- **[AI-CHAT-SHOWCASE.md](doc/29-AI-CHAT-SHOWCASE.md)** - Technical showcase: AI-powered CV navigation with ADK Go, dual-provider architecture, and document GPS ### π Policies & Standards - **[SECURITY.md](doc/9-SECURITY.md)** - Complete security architecture, implementation, and testing guide diff --git a/doc/00-GO-DOCUMENTATION-INDEX.md b/doc/00-GO-DOCUMENTATION-INDEX.md index f08647d..8378451 100644 --- a/doc/00-GO-DOCUMENTATION-INDEX.md +++ b/doc/00-GO-DOCUMENTATION-INDEX.md @@ -36,7 +36,7 @@ This documentation covers the core Go systems that power the CV site, with a foc - Coverage gap explanations - Best practices and CI/CD integration -5. **[AI Chat Agent β CV Assistant Mascot](28-AI-CHAT-AGENT.md)** (~500 lines) +5. **[AI Chat Agent β CV Assistant Mascot](28-AI-CHAT-AGENT.md)** (~544 lines) - Complete mascot feature reference: architecture, components, intelligence - ADK Go 1.0 integration with Gemini 2.5 Flash - Agent definition with query_cv tool (11 section types, cross-section search) @@ -47,6 +47,14 @@ This documentation covers the core Go systems that power the CV site, with a foc - Design system integration (CSS tokens, dark theme, responsive) - Graceful degradation, security, and testing (46 Playwright assertions) +6. **[AI Chat Showcase β Technical Writeup](29-AI-CHAT-SHOWCASE.md)** (~250 lines) + - Public-facing technical showcase of the AI chat feature + - Architecture diagram with dual-provider fallback + - 9 key technical decisions explained with code examples + - CV navigation links (GPS for the CV) + - Technology stack and file structure + - What this demonstrates for potential employers/clients + ## Quick Navigation ### By Feature diff --git a/doc/29-AI-CHAT-SHOWCASE.md b/doc/29-AI-CHAT-SHOWCASE.md new file mode 100644 index 0000000..d83b7db --- /dev/null +++ b/doc/29-AI-CHAT-SHOWCASE.md @@ -0,0 +1,243 @@ +# 29. AI-Powered CV Navigation β Technical Showcase + +## What This Is + +This CV site includes an AI assistant that lets visitors navigate and query the CV through natural language conversation. Instead of scanning a dense document, visitors ask questions like *"What Go projects has he built?"* or *"Has he worked with React?"* and get instant, cross-referenced answers with **clickable links that scroll directly to the relevant section**. + +**Live at:** [juan.andres.morenorub.io](https://juan.andres.morenorub.io) + +--- + +## The Problem + +A CV is information-dense. Recruiters and hiring managers have specific questions but must scan every section to find answers. Technologies span multiple sections (a language appears in experience, projects, AND skills). Cross-referencing is manual and slow. + +## The Solution + +An AI agent embedded in the CV page that: +1. **Understands the entire CV** β searches across all sections simultaneously +2. **Answers in natural language** β bilingual (English/Spanish), concise, with bullet points +3. **Navigates the document** β every company, project, and section name in the response is a **clickable link** that closes the chat, scrolls to the target, and highlights it with a green pulse +4. **Degrades gracefully** β no API key? No chat icon. API down? Automatic fallback to local AI. + +--- + +## Technical Architecture + +``` +βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ +β CV Site (Go) β +β β +β Visitor clicks mascot β chat opens β types question β +β β β +β βΌ β +β HTMX POST /api/chat βββ Go Handler β +β β β +β βββββββββββ΄βββββββββββ β +β βΌ βΌ β +β Try Gemini Try Ollama β +β (primary) (auto-fallback) β +β β β β +β ββββββββββ¬ββββββββββββ β +β βΌ β +β ADK Go Agent β +β "cv_assistant" β +β β β +β βΌ β +β query_cv tool β +β (cross-section search) β +β β β +β βΌ β +β Cached CV JSON β +β (same data that renders the page) β +β β β +β βΌ β +β Response with navigation links β +β [Olympic Broadcasting](#exp-olympic) β +β β β +β βΌ β +β HTMX swaps into chat panel β +β Links scroll + highlight on click β +βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ +``` + +## Key Technical Decisions + +### 1. Google ADK Go 1.0 as the Agent Framework + +We chose [ADK Go](https://github.com/google/adk-go) (v1.0, released March 2026) for the agent layer. ADK Go provides: +- **`llmagent.New`** β declarative agent definition with instruction and tools +- **`functiontool.New`** β type-safe Go function β agent tool bridge with auto-generated JSON schema +- **`runner.Runner`** β manages agent execution, sessions, and tool calling loops +- **`session.InMemoryService`** β lightweight session management for conversation context + +Why not a simpler approach (raw API calls)? ADK Go handles the tool-calling protocol automatically β the agent decides which tool to call, the framework executes it, feeds results back, and the agent synthesizes. With raw API calls, we'd need to implement this loop ourselves. + +### 2. Single Agent, Single Tool + +The CV data is bounded and structured. We use one agent (`cv_assistant`) with one tool (`query_cv`). Multi-agent orchestration would be over-engineering here. The intelligence comes from: +- A comprehensive instruction prompt covering 8 question types +- A `search` mode that queries across experience, projects, skills, and courses simultaneously +- Instruction to always include navigation links using CV anchor IDs + +### 3. Cross-Section Search + +When a visitor asks about a technology (e.g., "Go"), the tool searches **all sections at once**: + +```go +case "search": + crossResult := make(map[string]any) + if exp := filterExperience(cv.Experience, q); len(exp) > 0 { + crossResult["experience"] = exp + } + if proj := filterProjects(cv.Projects, q); len(proj) > 0 { + crossResult["projects"] = proj + } + if skills := filterSkills(cv.Skills, q); len(skills) > 0 { + crossResult["skills"] = skills + } + if courses := filterCourses(cv.Courses, q); len(courses) > 0 { + crossResult["courses"] = courses + } +``` + +This prevents the classic problem of "I searched projects but the answer was in experience." + +### 4. CV Navigation Links (GPS for the CV) + +The agent includes markdown links in its responses: + +```markdown +[Olympic Broadcasting](#exp-olympic-broadcasting) β SAP CDC solutions... +[Immich Photo Manager](#proj-immich-photo-manager) β MCP server for... +See the [Skills section](#skills) for full proficiency details. +``` + +The `formatResponse` function converts these to clickable HTML links. When clicked, JavaScript: +1. Closes the chat panel +2. Smooth-scrolls to the target element +3. Pulses a green highlight for 2 seconds + +This turns the chat into a **navigation tool** β like Google Maps for a document. + +### 5. Dual-Provider with Automatic Fallback + +```go +// Handler has primary + fallback runners +type Handler struct { + primary *chatRunner // Gemini (fast, cloud) + fallback *chatRunner // Ollama (local, unlimited) +} + +// Try primary, fall back on any error +response, sessionID, err := h.runAgent(h.primary, message) +if err != nil && h.fallback != nil { + log.Printf("Primary failed, falling back to %s", h.fallback.label) + response, sessionID, err = h.runAgent(h.fallback, message) +} +``` + +- **Primary:** Gemini 2.5 Flash β fast (~2s), pay-as-you-go (~$0.0003/question) +- **Fallback:** Ollama with Mistral Small 3.2 on local Mac Mini via Tailscale β free, unlimited +- **Switching:** Automatic and transparent. If Gemini returns 429/503, Ollama handles the request. +- **No manual intervention** β visitors never see the provider switch. + +### 6. Model Warmup on Chat Open + +Ollama loads models on demand (~10-15s cold start). To hide this latency: + +```javascript +function toggleChatPanel() { + // ... open panel ... + if (!chatWarmedUp) { + chatWarmedUp = true; + fetch('/api/chat/warmup', { method: 'POST' }); // background + } +} +``` + +When the visitor opens the chat, a silent warmup request fires. By the time they type a question, the model is loaded and ready. + +### 7. HTMX + Plain JavaScript + +The chat widget uses HTMX for server communication and plain JavaScript for interactions: + +```html + +