231 lines
10 KiB
Markdown
231 lines
10 KiB
Markdown
|
|
# 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
|