# 28. AI Chat Agent — ADK Go Integration ## Overview The CV site includes an AI-powered conversational assistant that lets visitors ask natural language questions about the CV content. Built with [Google ADK Go 1.0](https://github.com/google/adk-go) (Agent Development Kit), it provides instant answers by querying the same cached JSON data that renders the site. **Live example:** A visitor can ask *"How many Go projects has Juan built?"* and get an accurate answer drawn directly from the CV data — no hallucination, no stale data. ## Architecture ``` ┌─────────────────────────────────────────────────┐ │ CV Site Server │ │ │ │ ┌─────────────┐ ┌────────────────────────┐ │ │ │ Data Cache │────▶│ ADK Go Agent │ │ │ │ (cv-en.json) │ │ ┌──────────────────┐ │ │ │ │ (cv-es.json) │ │ │ cv_assistant │ │ │ │ └─────────────┘ │ │ (LLM Agent) │ │ │ │ │ │ │ │ │ │ │ │ │ │ Tools: │ │ │ │ │ │ │ ├─ query_cv │ │ │ │ │ │ │ │ (section+query) │ │ │ │ │ │ └──────────────────┘ │ │ │ │ └───────────┬────────────┘ │ │ │ │ │ │ ┌──────▼─────────────────────────▼──────────┐ │ │ │ POST /api/chat │ │ │ │ (chat.Handler) │ │ │ │ ├─ Session management │ │ │ │ ├─ ADK Runner execution │ │ │ │ └─ HTML fragment response (HTMX) │ │ │ └────────────────────────────────────────────┘ │ │ ▲ │ │ │ hx-post │ │ ┌──────────────────────┴─────────────────────┐ │ │ │ Chat Widget (HTMX + Hyperscript) │ │ │ │ ├─ Floating chat icon │ │ │ │ ├─ Expandable panel │ │ │ │ ├─ Message history │ │ │ │ └─ Session persistence │ │ │ └────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────┘ │ ▼ ┌──────────────────┐ │ Gemini 2.5 Flash │ │ (Google AI) │ └──────────────────┘ ``` ## How It Works ### 1. Agent Definition (`internal/chat/agent.go`) A single LLM agent (`cv_assistant`) with one tool (`query_cv`): ```go llmagent.New(llmagent.Config{ Name: "cv_assistant", Model: llm, Instruction: `You answer questions about the CV owner's experience, projects, skills, education, and career. Use the query_cv tool to look up CV data before answering. Answer in the SAME LANGUAGE the user writes in.`, Tools: []tool.Tool{queryTool}, }) ``` **Why a single agent?** The CV data is structured and bounded — there's no need for multi-agent orchestration. One agent with one tool is the right abstraction: simple, fast, predictable. ### 2. The `query_cv` Tool The tool accepts two parameters: - **`section`** — which CV section to search: `experience`, `projects`, `skills`, `education`, `languages`, `certifications`, `courses`, `awards`, `summary`, or `all` - **`query`** — keyword filter (e.g., "Go", "React", "2019", "Olympic") The tool reads from the same `cache.DataCache` that powers the website rendering — zero additional I/O, zero data duplication. **Filtering logic:** Case-insensitive keyword matching across all relevant fields in each section (title, company, technologies, descriptions, responsibilities). ### 3. HTTP Handler (`internal/chat/handler.go`) ``` POST /api/chat Content-Type: application/x-www-form-urlencoded message=How many Go projects has Juan built? session_id= ``` **Response:** HTML fragment for HTMX swap: ```html
How many Go projects has Juan built?

Juan has built 2 projects that use Go:

``` **Session management:** ADK Go's in-memory session service maintains conversation context. The session ID is preserved via a hidden form input, enabling follow-up questions. ### 4. Chat Widget (HTMX + Hyperscript) The UI is a floating chat panel that follows the site's existing widget pattern: ```html
``` **Key HTMX attributes:** - `hx-post="/api/chat"` — sends message to the agent - `hx-target="#chat-messages"` — appends response to chat history - `hx-swap="beforeend scroll:bottom"` — auto-scrolls to latest message - `hx-indicator="#chat-spinner"` — shows loading spinner during request ## Graceful Degradation The chat feature is entirely optional. When `GOOGLE_API_KEY` is not set: 1. `chat.NewHandler()` returns a disabled handler 2. `CVHandler` receives `chatEnabled: false` 3. Template data includes `ChatEnabled: false` 4. The chat widget template renders nothing (`{{if .ChatEnabled}}...{{end}}`) 5. No JavaScript errors, no broken UI, no hidden network requests **Zero impact on the site when disabled.** ## Configuration ### Required ```bash # .env GOOGLE_API_KEY=your-gemini-api-key # From https://aistudio.google.com/apikey ``` ### Optional ```bash MODEL_NAME=gemini-2.5-flash # Default model (free tier) ``` ### Cost Gemini 2.5 Flash free tier: **15 requests/minute**, no credit card needed. Each chat message = 1 request. For a personal CV site, this is more than sufficient. ## Example Conversations ### English | Question | Answer | |----------|--------| | "How many years of experience?" | "Juan has 21 years of professional experience, starting in April 2005." | | "What Go projects has he built?" | Lists Immich Photo Manager and Cmux Resurrect with descriptions | | "Has he worked with React?" | Lists companies where React was used (Olympic Broadcasting, LIV Golf, etc.) | | "Tell me about his time at SAP" | Pulls SAP experience entry with responsibilities and technologies | | "What certifications does he have?" | Lists SAP CDC Full Training, SAP Cloud Platform, GDPR Compliance | ### Spanish | Pregunta | Respuesta | |----------|-----------| | "¿En cuántas empresas ha trabajado?" | Lista las 11 empresas con nombres | | "¿Qué tecnologías domina?" | Categorías de skills con proficiency levels | | "¿Tiene experiencia con autenticación?" | Detalla SAP CDC, Gigya, sistemas de auth | ## File Structure ``` internal/chat/ ├── agent.go # LLM agent + query_cv tool + filter helpers └── handler.go # HTTP handler + session management + response rendering templates/partials/widgets/ └── chat-widget.html # HTMX chat panel template static/css/04-interactive/ └── _chat.css # Chat UI styles (responsive, dark theme) ``` ## Dependencies Added | Package | Purpose | Size Impact | |---------|---------|-------------| | `google.golang.org/adk` | Agent framework (runner, session, tools) | ~2 MB binary increase | | `google.golang.org/genai` | Gemini API client | Included with ADK | ## Security Considerations - **No personal data exposure:** The agent instruction explicitly prohibits revealing email, phone, or other contact details — it directs visitors to the contact form instead - **Input sanitization:** User messages are HTML-escaped before rendering - **Response sanitization:** Agent responses go through `formatResponse()` which escapes HTML then applies safe markdown-to-HTML conversion - **Rate limiting:** The `/api/chat` endpoint inherits the site's middleware chain (recovery, logging, security headers) - **Session isolation:** Each visitor gets an independent in-memory session; sessions are ephemeral and not persisted to disk ## ADK Go Concepts Used | Concept | Usage | |---------|-------| | `llmagent.New` | Creates the CV assistant agent with instruction and tools | | `functiontool.New` | Wraps the `query_cv` Go function as an agent-callable tool | | `runner.Runner` | Executes the agent within the HTTP handler | | `session.InMemoryService` | Maintains conversation context per visitor | | `genai.NewContentFromText` | Converts user message to ADK content format | | `event.IsFinalResponse()` | Extracts the agent's final answer from the event stream | | `agent.RunConfig{}` | Default run configuration (non-streaming) | ## Relation to Other Documentation - **[01-ARCHITECTURE.md](01-ARCHITECTURE.md)** — Overall system design - **[03-API.md](03-API.md)** — HTTP API reference (includes `/api/chat`) - **[14-BACKEND-HANDLERS.md](14-BACKEND-HANDLERS.md)** — Handler patterns - **[23-DATA-CACHE.md](23-DATA-CACHE.md)** — How CV data is cached and accessed