- doc/28-AI-CHAT-AGENT.md: comprehensive technical documentation covering architecture, agent design, query_cv tool, HTMX integration, graceful degradation, security, and example conversations - README.md: add AI Chat Agent section with examples, ADK Go badge, updated tech stack and documentation index - doc/00-GO-DOCUMENTATION-INDEX.md: add chat agent to doc index
10 KiB
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 (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):
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, orallquery— 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=<optional, auto-assigned>
Response: HTML fragment for HTMX swap:
<div class="chat-message chat-user">How many Go projects has Juan built?</div>
<div class="chat-message chat-agent">
<p>Juan has built 2 projects that use Go:</p>
<ul>
<li>Immich Photo Manager - AI-Powered Photo Library MCP Server</li>
<li>Cmux Resurrect - Terminal Session Persistence Tool</li>
</ul>
</div>
<input type="hidden" name="session_id" value="c06faf66-..." form="chat-form"/>
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:
<!-- Toggle button -->
<button class="fixed-btn chat-toggle-btn"
_="on click toggle .chat-open on #chat-panel">
<iconify-icon icon="mdi:chat-outline"></iconify-icon>
</button>
<!-- Chat panel -->
<form hx-post="/api/chat"
hx-target="#chat-messages"
hx-swap="beforeend scroll:#chat-messages:bottom"
hx-indicator="#chat-spinner">
<input type="text" name="message" />
</form>
Key HTMX attributes:
hx-post="/api/chat"— sends message to the agenthx-target="#chat-messages"— appends response to chat historyhx-swap="beforeend scroll:bottom"— auto-scrolls to latest messagehx-indicator="#chat-spinner"— shows loading spinner during request
Graceful Degradation
The chat feature is entirely optional. When GOOGLE_API_KEY is not set:
chat.NewHandler()returns a disabled handlerCVHandlerreceiveschatEnabled: false- Template data includes
ChatEnabled: false - The chat widget template renders nothing (
{{if .ChatEnabled}}...{{end}}) - No JavaScript errors, no broken UI, no hidden network requests
Zero impact on the site when disabled.
Configuration
Required
# .env
GOOGLE_API_KEY=your-gemini-api-key # From https://aistudio.google.com/apikey
Optional
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/chatendpoint 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 — Overall system design
- 03-API.md — HTTP API reference (includes
/api/chat) - 14-BACKEND-HANDLERS.md — Handler patterns
- 23-DATA-CACHE.md — How CV data is cached and accessed