Files
cv-site/doc/28-AI-CHAT-AGENT.md
T

231 lines
10 KiB
Markdown
Raw Normal View History

# 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=<optional, auto-assigned>
```
**Response:** HTML fragment for HTMX swap:
```html
<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:
```html
<!-- 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 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