docs(i18n): translate all docs to English (with .es.md as Spanish alternative)

- README.md: full English translation, .es.md preserved
- docs/architecture.md: full translation (1039 lines)
- configs/portfolio-bot.yaml: full English translation, .es.yaml preserved
- data/projects/README.md + example-project.md: translated with banners

Default language is now English (standard for OSS). Spanish remains
available via .es.* suffix files.
This commit is contained in:
Victor Hugo Vargas 2026-06-30 13:27:00 -07:00
parent fcf0e6251a
commit b9769fac41
10 changed files with 1658 additions and 316 deletions

109
README.es.md Normal file
View file

@ -0,0 +1,109 @@
# Rony Chat Bot — Portfolio Bot HTTP
> 🌐 **Idioma:** [English](README.md) | [Español](README.es.md)
> 🤖 **Chatbot HTTP que presenta tu portfolio y responde preguntas sobre tus proyectos.**
**Rony Chat Bot** es un chatbot basado en [`rony-llm-agent`](https://github.com/VictorVargas/rony-llm-agent) que se integra con un sitio Astro/React para responder preguntas sobre Victor Hugo Vargas y sus proyectos, usando **RAG sobre archivos markdown**.
## ✨ Features
- 🌐 **HTTP server** con streaming SSE (Server-Sent Events)
- 🧠 **RAG sobre markdown** — indexa automáticamente los `.md` en `data/projects/`
- 🎭 **Persona customizable** — responde como "asistente de Victor"
- ⚡ **Self-hosted** con Ollama o llama.cpp (no requiere API key de cloud)
- 🔌 **Integrable** con Astro/React via proxy HTTP
- 🛡️ **Rate limiting** y logging estructurado
- 📦 **Portable** — se puede adaptar a otros contextos (clientes, productos, etc.)
## 🚀 Quick start
```bash
# 1. Instalar
git clone https://github.com/VictorVargas/rony-chat-bot.git
cd chat-bot
# 2. Resolver dependencias (crea go.sum con hashes)
go mod tidy
# 3. Configurar provider (ejemplo: Ollama)
# Asegúrate de tener Ollama corriendo: ollama serve
# Modelo descargado: ollama pull qwen2.5:1.5b
# 4. Cargar tus proyectos en data/projects/
echo "# Mi Proyecto Cool\nDescripción..." > data/projects/mi-proyecto.md
# 5. Build
go build -o bin/chat-bot ./cmd/chat-bot
# 6. Run
./bin/chat-bot serve
# → Sirve en http://localhost:7331
```
## 📁 Estructura
```
chat-bot/
├── cm./rony-chat-bot/ # Entry point (CLI)
├── internal/
│ ├── server/ # HTTP handlers + SSE
│ ├── portfolio/ # Data loader (markdown → RAG)
│ ├── persona/ # Persona override
│ └── streaming/ # SSE helpers
├── data/projects/ # ← TUS PROYECTOS EN MARKDOWN
│ ├── rony-tui.md
│ ├── rony-llm-agent.md
│ └── ...
├── configs/
│ └── portfolio-bot.yaml # Provider config
├── docs/
│ └── architecture.md # ← Especificación técnica completa
└── go.mod # require rony-llm-agent
```
## 🎯 Uso desde Astro
Ver [`docs/architecture.md`](./docs/architecture.md) §5 — patrón recomendado de proxy.
```typescript
// portfolio/src/pages/api/chat.ts
export const POST: APIRoute = async ({ request }) => {
const body = await request.json();
const resp = await fetch('http://localhost:7331/api/chat', {
method: 'POST',
body: JSON.stringify(body),
});
return new Response(resp.body, {
headers: { 'Content-Type': 'text/event-stream' },
});
};
```
## 🔄 Adaptar a otro cliente
Este bot está diseñado para ser **atómico** y reusable. Para adaptarlo (ej. chatbot para un concesionario):
1. Fork/clone este repo
2. Reemplaza `data/projects/` con `data/inventory/` (u otro dominio)
3. Actualiza `configs/portfolio-bot.yaml` con la nueva persona
4. Deploy
La librería `rony-llm-agent` no cambia.
## 📚 Documentación
- [**Architecture doc**](./docs/architecture.md) — Especificación técnica completa
- [Library: `rony-llm-agent`](https://github.com/VictorVargas/rony-llm-agent) — Core reutilizable
- [Harness](https://github.com/VictorVargas/rony-harness) — El otro proyecto que usa la misma librería
## 📄 Licencia
MIT — ver [`LICENSE`](./LICENSE).
## 🔗 Proyectos del workspace
- [`rony-llm-agent`](https://github.com/VictorVargas/rony-llm-agent) — Librería core
- [`harness`](https://github.com/VictorVargas/rony-harness) — AI agent harness (TUI)
- [`portfolio`](https://github.com/VictorVargas/portfolio) — Astro + React site (integra este bot)

View file

@ -1,68 +1,70 @@
# Rony Chat Bot — Portfolio Bot HTTP
# Rony Chat Bot — HTTP Portfolio Bot
> 🤖 **Chatbot HTTP que presenta tu portfolio y responde preguntas sobre tus proyectos.**
> 🌐 **Language:** [English](./README.md) | [Español](./README.es.md)
>
> 🤖 **HTTP chatbot that presents your portfolio and answers questions about your projects.**
**Rony Chat Bot** es un chatbot basado en [`rony-llm-agent`](https://github.com/VictorVargas/rony-llm-agent) que se integra con un sitio Astro/React para responder preguntas sobre Victor Hugo Vargas y sus proyectos, usando **RAG sobre archivos markdown**.
**Rony Chat Bot** is a chatbot based on [`rony-llm-agent`](https://github.com/VictorVargas/rony-llm-agent) that integrates with an Astro/React site to answer questions about Victor Hugo Vargas and his projects, using **RAG over markdown files**.
## ✨ Features
- 🌐 **HTTP server** con streaming SSE (Server-Sent Events)
- 🧠 **RAG sobre markdown** — indexa automáticamente los `.md` en `data/projects/`
- 🎭 **Persona customizable** — responde como "asistente de Victor"
- ⚡ **Self-hosted** con Ollama o llama.cpp (no requiere API key de cloud)
- 🔌 **Integrable** con Astro/React via proxy HTTP
- 🛡️ **Rate limiting** y logging estructurado
- 📦 **Portable**se puede adaptar a otros contextos (clientes, productos, etc.)
- 🌐 **HTTP server** with SSE (Server-Sent Events) streaming
- 🧠 **RAG over markdown** — automatically indexes `.md` in `data/projects/`
- 🎭 **Customizable persona** — responds as "Victor's assistant"
- ⚡ **Self-hosted** with Ollama or llama.cpp (no cloud API key required)
- 🔌 **Integrable** with Astro/React via HTTP proxy
- 🛡️ **Rate limiting** and structured logging
- 📦 **Portable**adaptable to other contexts (clients, products, etc.)
## 🚀 Quick start
```bash
# 1. Instalar
# 1. Install
git clone https://github.com/VictorVargas/rony-chat-bot.git
cd chat-bot
cd rony-chat-bot
# 2. Resolver dependencias (crea go.sum con hashes)
# 2. Resolve dependencies (creates go.sum with hashes)
go mod tidy
# 3. Configurar provider (ejemplo: Ollama)
# Asegúrate de tener Ollama corriendo: ollama serve
# Modelo descargado: ollama pull qwen2.5:1.5b
# 3. Configure provider (e.g., Ollama)
# Make sure Ollama is running: ollama serve
# Downloaded model: ollama pull qwen2.5:1.5b
# 4. Cargar tus proyectos en data/projects/
echo "# Mi Proyecto Cool\nDescripción..." > data/projects/mi-proyecto.md
# 4. Load your projects in data/projects/
echo "# My Cool Project\nDescription..." > data/projects/my-project.md
# 5. Build
go build -o bin/chat-bot ./cmd/chat-bot
# 6. Run
./bin/chat-bot serve
# → Sirve en http://localhost:7331
# → Serves on http://localhost:7331
```
## 📁 Estructura
## 📁 Structure
```
chat-bot/
├── cm./rony-chat-bot/ # Entry point (CLI)
├── cmd/chat-bot/ # Entry point (CLI)
├── internal/
│ ├── server/ # HTTP handlers + SSE
│ ├── portfolio/ # Data loader (markdown → RAG)
│ ├── persona/ # Persona override
│ └── streaming/ # SSE helpers
├── data/projects/ # ← TUS PROYECTOS EN MARKDOWN
│ ├── rony-tui.md
├── data/projects/ # ← YOUR PROJECTS IN MARKDOWN
│ ├── rony-harness.md
│ ├── rony-llm-agent.md
│ └── ...
├── configs/
│ └── portfolio-bot.yaml # Provider config
├── docs/
│ └── architecture.md # ← Especificación técnica completa
│ └── architecture.md # ← Complete technical specification
└── go.mod # require rony-llm-agent
```
## 🎯 Uso desde Astro
## 🎯 Use from Astro
Ver [`docs/architecture.md`](./docs/architecture.md) §5 — patrón recomendado de proxy.
See [`docs/architecture.md`](./docs/architecture.md) §5 — recommended proxy pattern.
```typescript
// portfolio/src/pages/api/chat.ts
@ -70,37 +72,43 @@ export const POST: APIRoute = async ({ request }) => {
const body = await request.json();
const resp = await fetch('http://localhost:7331/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
return new Response(resp.body, {
headers: { 'Content-Type': 'text/event-stream' },
status: 200,
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
});
};
```
## 🔄 Adaptar a otro cliente
## 🔄 Adapt to another client
Este bot está diseñado para ser **atómico** y reusable. Para adaptarlo (ej. chatbot para un concesionario):
This bot is designed to be **atomic** and reusable. To adapt it (e.g., chatbot for a car dealership):
1. Fork/clone este repo
2. Reemplaza `data/projects/` con `data/inventory/` (u otro dominio)
3. Actualiza `configs/portfolio-bot.yaml` con la nueva persona
1. Fork/clone this repo
2. Replace `data/projects/` with `data/inventory/` (or another domain)
3. Update `configs/portfolio-bot.yaml` with the new persona
4. Deploy
La librería `rony-llm-agent` no cambia.
The `rony-llm-agent` library doesn't change.
## 📚 Documentación
## 📚 Documentation
- [**Architecture doc**](./docs/architecture.md) — Especificación técnica completa
- [Library: `rony-llm-agent`](https://github.com/VictorVargas/rony-llm-agent) — Core reutilizable
- [Harness](https://github.com/VictorVargas/rony-harness) — El otro proyecto que usa la misma librería
- [**Architecture doc**](./docs/architecture.md) — Complete technical specification
- [Library: `rony-llm-agent`](https://github.com/VictorVargas/rony-llm-agent) — Reusable core
- [Rony Harness](https://github.com/VictorVargas/rony-harness) — The other project using the same library
## 📄 Licencia
## 📄 License
MIT — ver [`LICENSE`](./LICENSE).
MIT — see [`LICENSE`](./LICENSE).
## 🔗 Proyectos del workspace
## 🔗 Workspace projects
- [`rony-llm-agent`](https://github.com/VictorVargas/rony-llm-agent) — Librería core
- [`harness`](https://github.com/VictorVargas/rony-harness) — AI agent harness (TUI)
- [`portfolio`](https://github.com/VictorVargas/portfolio) — Astro + React site (integra este bot)
- [`rony-llm-agent`](https://github.com/VictorVargas/rony-llm-agent) — Core library
- [`rony-harness`](https://github.com/VictorVargas/rony-harness) — AI agent harness (TUI)
- [`portfolio`](https://github.com/VictorVargas/portfolio) — Astro + React site (integrates this bot)

View file

@ -0,0 +1,82 @@
# Configuración del Portfolio Bot
# Documentación: https://github.com/VictorVargas/rony-llm-agent/pkg/llm
server:
host: "0.0.0.0"
port: 7331
read_timeout_ms: 30000
cors_origins:
- "http://localhost:4321" # Astro dev server
- "https://victorvargas.dev" # Producción (cuando exista)
rate_limit:
requests_per_minute: 30 # Por IP
burst: 5
# Providers LLM (al menos uno configurado)
providers:
# === Ollama (recomendado para desarrollo) ===
- name: ollama-local
type: ollama
model: qwen2.5:1.5b # Modelo pequeño para Q&A
endpoint: http://localhost:11434
default: true
# === llama.cpp directo (GGUF) ===
- name: llamacpp-local
type: llamacpp
model_path: ${RONY_MODELS_PATH}/qwen2.5-1.5b-instruct-q5_k_m.gguf
context_size: 4096
n_gpu_layers: 999
# === Anthropic (si quieres calidad > privacidad) ===
- name: anthropic-api
type: anthropic
model: claude-haiku-4 # Modelo barato
api_key_env: ANTHROPIC_API_KEY
# RAG: cómo se indexan los proyectos
rag:
enabled: true
data_path: ./data/projects # Directorio con .md
chunk_size: 500 # caracteres por chunk
chunk_overlap: 50
embedding_provider: ollama # o llamacpp
embedding_model: nomic-embed-text
vector_db_path: ./chroma # Persistencia local
top_k: 5 # Documentos a recuperar por query
rerank: false # Phase 2
# Persona: quién es el bot
persona:
name: "Rony Chat Bot"
tone: "Profesional, conocedor, amable"
language: "Español"
constraints:
- "Solo responder sobre Victor y sus proyectos"
- "Si no sabes, decir 'No tengo esa información'"
- "Ser conciso pero informativo"
- "Usar formato markdown para listas y código"
intro: "¡Hola! Soy Rony, el asistente virtual de Victor Hugo Vargas. Pregúntame sobre sus proyectos, skills o experiencia."
# System prompt base (concatenado con el contenido RAG)
system_prompt: |
Eres Rony Chat Bot, el asistente virtual de Victor Hugo Vargas, un ingeniero de software mexicano.
Tu trabajo es responder preguntas sobre:
- Los proyectos de Victor (ver archivos en data/projects/)
- Su experiencia y skills técnicas
- Su enfoque de trabajo
Responde en español, con tono profesional pero accesible.
Si te preguntan algo que no está en tu contexto, dilo honestamente.
Formato recomendado:
- Usa markdown para listas, código, y énfasis
- Sé conciso (máximo 2-3 párrafos por respuesta)
- Incluye links a repos cuando sea relevante
# Logging
logging:
level: info # debug | info | warn | error
format: json # json | text
output: stderr

View file

@ -1,5 +1,5 @@
# Configuración del Portfolio Bot
# Documentación: https://github.com/VictorVargas/rony-llm-agent/pkg/llm
# Portfolio Bot Configuration
# Documentation: https://github.com/VictorVargas/rony-llm-agent/pkg/llm
server:
host: "0.0.0.0"
@ -7,73 +7,73 @@ server:
read_timeout_ms: 30000
cors_origins:
- "http://localhost:4321" # Astro dev server
- "https://victorvargas.dev" # Producción (cuando exista)
- "https://victorvargas.dev" # Production (when it exists)
rate_limit:
requests_per_minute: 30 # Por IP
requests_per_minute: 30 # Per IP
burst: 5
# Providers LLM (al menos uno configurado)
# LLM providers (at least one configured)
providers:
# === Ollama (recomendado para desarrollo) ===
# === Ollama (recommended for development) ===
- name: ollama-local
type: ollama
model: qwen2.5:1.5b # Modelo pequeño para Q&A
model: qwen2.5:1.5b # Small model for Q&A
endpoint: http://localhost:11434
default: true
# === llama.cpp directo (GGUF) ===
# === llama.cpp direct (GGUF) ===
- name: llamacpp-local
type: llamacpp
model_path: ${RONY_MODELS_PATH}/qwen2.5-1.5b-instruct-q5_k_m.gguf
context_size: 4096
n_gpu_layers: 999
# === Anthropic (si quieres calidad > privacidad) ===
# === Anthropic (if you want quality > privacy) ===
- name: anthropic-api
type: anthropic
model: claude-haiku-4 # Modelo barato
model: claude-haiku-4 # Cheap model
api_key_env: ANTHROPIC_API_KEY
# RAG: cómo se indexan los proyectos
# RAG: how projects are indexed
rag:
enabled: true
data_path: ./data/projects # Directorio con .md
chunk_size: 500 # caracteres por chunk
data_path: ./data/projects # Directory with .md
chunk_size: 500 # characters per chunk
chunk_overlap: 50
embedding_provider: ollama # o llamacpp
embedding_provider: ollama # or llamacpp
embedding_model: nomic-embed-text
vector_db_path: ./chroma # Persistencia local
top_k: 5 # Documentos a recuperar por query
vector_db_path: ./chroma # Local persistence
top_k: 5 # Documents to retrieve per query
rerank: false # Phase 2
# Persona: quién es el bot
# Persona: who the bot is
persona:
name: "Rony Chat Bot"
tone: "Profesional, conocedor, amable"
language: "Español"
tone: "Professional, knowledgeable, friendly"
language: "English"
constraints:
- "Solo responder sobre Victor y sus proyectos"
- "Si no sabes, decir 'No tengo esa información'"
- "Ser conciso pero informativo"
- "Usar formato markdown para listas y código"
intro: "¡Hola! Soy Rony, el asistente virtual de Victor Hugo Vargas. Pregúntame sobre sus proyectos, skills o experiencia."
- "Only answer about Victor and his projects"
- "If you don't know, say 'I don't have that information'"
- "Be concise but informative"
- "Use markdown format for lists and code"
intro: "Hi! I'm Rony, Victor Hugo Vargas's virtual assistant. Ask me about his projects, skills or experience."
# System prompt base (concatenado con el contenido RAG)
# Base system prompt (concatenated with RAG content)
system_prompt: |
Eres Rony Chat Bot, el asistente virtual de Victor Hugo Vargas, un ingeniero de software mexicano.
You are Rony Chat Bot, the virtual assistant of Victor Hugo Vargas, a Mexican software engineer.
Tu trabajo es responder preguntas sobre:
- Los proyectos de Victor (ver archivos en data/projects/)
- Su experiencia y skills técnicas
- Su enfoque de trabajo
Your job is to answer questions about:
- Victor's projects (see files in data/projects/)
- His experience and technical skills
- His work approach
Responde en español, con tono profesional pero accesible.
Si te preguntan algo que no está en tu contexto, dilo honestamente.
Respond in English, with professional but accessible tone.
If you're asked something not in your context, say it honestly.
Formato recomendado:
- Usa markdown para listas, código, y énfasis
- Sé conciso (máximo 2-3 párrafos por respuesta)
- Incluye links a repos cuando sea relevante
Recommended format:
- Use markdown for lists, code, and emphasis
- Be concise (max 2-3 paragraphs per response)
- Include links to repos when relevant
# Logging
logging:

View file

@ -0,0 +1,52 @@
# Proyectos del Portfolio
> 🌐 **Idioma:** [English](README.md) | [Español](README.es.md)
Coloca aquí un archivo `.md` por cada proyecto que quieras que el bot pueda responder.
## Convención de nombres
- Un archivo por proyecto: `nombre-del-proyecto.md`
- Nombre en kebab-case (minúsculas con guiones)
- Ejemplo: `rony-tui.md`, `rony-llm-agent.md`, `portfolio-astro.md`
## Frontmatter (opcional pero recomendado)
```markdown
---
title: "Rony TUI"
date: 2026-06
status: "active" # active | archived | wip
tags: ["go", "ai", "cli"]
repo: "https://github.com/VictorVargas/rony-harness"
demo: "https://..." # opcional
---
# Rony TUI
AI agent harness para desarrollo de software...
```
## Cómo se procesan
1. El bot escanea este directorio al arrancar
2. Cada `.md` se divide en chunks de ~500 caracteres
3. Cada chunk se convierte a embedding con Ollama
4. Los embeddings se guardan en ChromaDB
5. Cuando alguien pregunta, se buscan los top-5 chunks más relevantes
6. Esos chunks se inyectan al contexto del LLM
## Re-indexar
Si modificas los `.md`, ejecuta:
```bash
./bin/chat-bot reindex
```
Esto reconstruye ChromaDB desde cero.
## Ejemplo de proyecto
Ver [`example-project.md`](./example-project.md) para una plantilla.

View file

@ -1,49 +1,49 @@
# Proyectos del Portfolio
# Portfolio Projects
Coloca aquí un archivo `.md` por cada proyecto que quieras que el bot pueda responder.
Place a `.md` file here for each project you want the bot to be able to answer about.
## Convención de nombres
## File naming convention
- Un archivo por proyecto: `nombre-del-proyecto.md`
- Nombre en kebab-case (minúsculas con guiones)
- Ejemplo: `rony-tui.md`, `rony-llm-agent.md`, `portfolio-astro.md`
- One file per project: `project-name.md`
- Name in kebab-case (lowercase with hyphens)
- Example: `rony-harness.md`, `rony-llm-agent.md`, `portfolio-astro.md`
## Frontmatter (opcional pero recomendado)
## Frontmatter (optional but recommended)
```markdown
---
title: "Rony TUI"
title: "Rony Harness"
date: 2026-06
status: "active" # active | archived | wip
tags: ["go", "ai", "cli"]
repo: "https://github.com/VictorVargas/rony-harness"
demo: "https://..." # opcional
demo: "https://..." # optional
---
# Rony TUI
# Rony Harness
AI agent harness para desarrollo de software...
AI agent harness for software development...
```
## Cómo se procesan
## How they're processed
1. El bot escanea este directorio al arrancar
2. Cada `.md` se divide en chunks de ~500 caracteres
3. Cada chunk se convierte a embedding con Ollama
4. Los embeddings se guardan en ChromaDB
5. Cuando alguien pregunta, se buscan los top-5 chunks más relevantes
6. Esos chunks se inyectan al contexto del LLM
1. The bot scans this directory on startup
2. Each `.md` is split into chunks of ~500 characters
3. Each chunk is converted to embedding with Ollama
4. Embeddings are stored in ChromaDB
5. When someone asks a question, the top-5 most relevant chunks are searched
6. Those chunks are injected into the LLM context
## Re-indexar
## Re-index
Si modificas los `.md`, ejecuta:
If you modify the `.md` files, run:
```bash
./bin/chat-bot reindex
```
Esto reconstruye ChromaDB desde cero.
This rebuilds ChromaDB from scratch.
## Ejemplo de proyecto
## Project example
Ver [`example-project.md`](./example-project.md) para una plantilla.
See [`example-project.md`](./example-project.md) for a template.

View file

@ -0,0 +1,45 @@
---
title: "Proyecto de Ejemplo"
date: 2026-06
status: "active"
tags: ["ejemplo", "plantilla"]
---
> 🌐 **Idioma:** [English](example-project.md) | [Español](example-project.es.md)
repo: ""
demo: ""
---
# Proyecto de Ejemplo
> 🌐 **Language:** [English](example-project.md) | [Español](example-project.es.md)
Este es un template. Reemplaza con la información real de tu proyecto.
## Descripción
Qué hace el proyecto, en 1-2 párrafos. Evita jerga innecesaria.
## Stack técnico
- **Lenguaje:** Go 1.26
- **Framework:** Ninguno (stdlib)
- **Base de datos:** SQLite
- **Deployment:** Fly.io
## Features principales
1. Feature uno — descripción breve
2. Feature dos — descripción breve
3. Feature tres — descripción breve
## Aprendizajes
Qué aprendiste, qué challenges tuviste, qué harías diferente.
## Links
- Repo: github.com/VictorVargas/proyecto
- Demo: proyecto.example.com
- Docs: docs.proyecto.example.com

View file

@ -1,39 +1,39 @@
---
title: "Proyecto de Ejemplo"
title: "Example Project"
date: 2026-06
status: "active"
tags: ["ejemplo", "plantilla"]
tags: ["example", "template"]
repo: ""
demo: ""
---
# Proyecto de Ejemplo
# Example Project
Este es un template. Reemplaza con la información real de tu proyecto.
This is a template. Replace with the real information about your project.
## Descripción
## Description
Qué hace el proyecto, en 1-2 párrafos. Evita jerga innecesaria.
What the project does, in 1-2 paragraphs. Avoid unnecessary jargon.
## Stack técnico
## Tech stack
- **Lenguaje:** Go 1.26
- **Framework:** Ninguno (stdlib)
- **Base de datos:** SQLite
- **Language:** Go 1.26
- **Framework:** None (stdlib)
- **Database:** SQLite
- **Deployment:** Fly.io
## Features principales
## Main features
1. Feature uno — descripción breve
2. Feature dos — descripción breve
3. Feature tres — descripción breve
1. Feature one — brief description
2. Feature two — brief description
3. Feature three — brief description
## Aprendizajes
## Learnings
Qué aprendiste, qué challenges tuviste, qué harías diferente.
What you learned, what challenges you faced, what you'd do differently.
## Links
- Repo: github.com/VictorVargas/proyecto
- Demo: proyecto.example.com
- Docs: docs.proyecto.example.com
- Repo: github.com/VictorVargas/project
- Demo: project.example.com
- Docs: docs.project.example.com

1044
docs/architecture.es.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,93 +1,96 @@
# 📋 Rony Chat Bot — Technical Design Document
**Versión:** 1.0
**Autor:** Victor Hugo Vargas
**Fecha:** 2026-06-28
**Estado:** Especificación completa para implementación
**Version:** 1.0
**Author:** Victor Hugo Vargas
**Date:** 2026-06-28
**Status:** Complete specification for implementation
**Path:** `rony-chat-bot/docs/architecture.md`
> 📚 **Workspace:** Este proyecto es parte del workspace `Rony/`. Ver [`../README.md`](../../README.md).
> 🌐 **Language:** [English](./architecture.md) | [Español](./architecture.es.md)
>
> 🔑 **Depende de:** [`rony-llm-agent`](https://github.com/VictorVargas/rony-llm-agent) — librería core que provee agent loop, LLM clients, RAG, persona system.
> 📚 **Workspace:** This project is part of the `Rony/` workspace. See [`../README.md`](../../README.md).
>
> 📐 **Metodología:** Este proyecto sigue el enfoque **SDD + DDD + Hexagonal Architecture**. Los Requisitos Funcionales se numeran como `CRF-XXX`. Ver [`../../METHODOLOGY.md`](../../METHODOLOGY.md).
> 🔑 **Depends on:** [`rony-llm-agent`](https://github.com/VictorVargas/rony-llm-agent) — core library that provides agent loop, LLM clients, RAG, persona system.
>
> 📐 **Methodology:** This project follows the **SDD + DDD + Hexagonal Architecture** approach. Functional Requirements are numbered as `CRF-XXX`. See [`../../METHODOLOGY.md`](../../METHODOLOGY.md).
---
## 🎯 1. Visión del Proyecto
## 🎯 1. Project Vision
### 1.1 ¿Qué es Chat-Bot?
### 1.1 What is Chat-Bot?
Un **chatbot HTTP** que responde preguntas sobre Victor Hugo Vargas y sus proyectos. Usa **RAG (Retrieval-Augmented Generation)** sobre archivos markdown que describen cada proyecto, y un LLM local (o cloud) para generar respuestas.
An **HTTP chatbot** that answers questions about Victor Hugo Vargas and his projects. Uses **RAG (Retrieval-Augmented Generation)** over markdown files describing each project, and a local LLM (or cloud) to generate responses.
### 1.2 Caso de uso primario
### 1.2 Primary use case
Victor tiene un portfolio web (Astro + React). En el sitio hay un widget de chat donde visitantes pueden preguntar:
- "¿Qué proyectos ha hecho Victor?"
- "¿Cuál es su experiencia con Go?"
- "¿Cómo funciona Rony TUI?"
- "¿Victor ha trabajado con PostgreSQL?"
Victor has a portfolio website (Astro + React). On the site there's a chat widget where visitors can ask:
El bot responde con información precisa extraída de los archivos markdown de proyectos + bio + skills.
- "What projects has Victor done?"
- "What's his experience with Go?"
- "How does Rony Harness work?"
- "Has Victor worked with PostgreSQL?"
### 1.3 Casos de uso secundarios (futuro)
The bot responds with accurate information extracted from the projects' markdown files + bio + skills.
- **Adaptación a clientes:** El mismo bot, con otra data y otra persona, sirve para concesionarios, restaurantes, etc.
- **Standalone CLI:** `./chat-bot ask "¿qué sabes de X?"` para uso desde terminal.
- **Slack/Discord bot:** Wrapper que consume el HTTP API.
### 1.3 Secondary use cases (future)
### 1.4 Filosofía
- **Client adaptation:** The same bot, with other data and another persona, serves car dealerships, restaurants, etc.
- **Standalone CLI:** `./chat-bot ask "what do you know about X?"` for terminal use.
- **Slack/Discord bot:** Wrapper that consumes the HTTP API.
- **Self-hosted por defecto** — funciona 100% local con Ollama + modelos 1-3B
- **Cloud opcional** — si se necesita más calidad, swap a Anthropic API
- **Portable** — fácil de fork/customizar para otros contextos
- **Streaming** — respuestas token-por-token con SSE (no espera a respuesta completa)
- **Reutiliza `rony-llm-agent`** — no reinventar el agent loop
### 1.4 Philosophy
- **Self-hosted by default** — works 100% local with Ollama + 1-3B models
- **Cloud optional** — if you need more quality, swap to Anthropic API
- **Portable** — easy to fork/customize for other contexts
- **Streaming** — token-by-token responses with SSE (no waiting for complete response)
- **Reuses `rony-llm-agent`** — doesn't reinvent the agent loop
---
## 🏗️ 2. Arquitectura
## 🏗️ 2. Architecture
### 2.1 Vista general
### 2.1 Overview
```
┌─────────────────────────────────────────────────────────────────┐
│ Browser (Astro site) │
│ ↓ HTTP POST /api/chat │
│ Astro SSR (proxy) ←────────── Sirve portfolio + proxy chat │
│ Astro SSR (proxy) ←────────── Serves portfolio + proxy chat │
│ ↓ HTTP POST /api/chat │
│ Chat-Bot HTTP server (:7331) │
│ ↓ │
│ Agent loop (rony-llm-agent) │
│ ↓ │
│ RAG retrieval → ChromaDB sobre data/projects/*.md │
│ RAG retrieval → ChromaDB over data/projects/*.md │
│ ↓ │
│ LLM (Ollama local / Anthropic cloud) │
└─────────────────────────────────────────────────────────────────┘
```
### 2.2 Componentes principales
### 2.2 Main components
| Componente | Path | Responsabilidad |
| Component | Path | Responsibility |
|---|---|---|
| **HTTP server** | `internal/server/` | Gin/chi handlers, SSE streaming |
| **Agent runner** | `internal/agent/` | Wrapper sobre `rony-llm-agent` con config específica |
| **Portfolio loader** | `internal/portfolio/` | Lee `data/projects/*.md`, indexa en ChromaDB |
| **Persona** | `internal/persona/` | Carga persona desde `configs/portfolio-bot.yaml` |
| **CLI** | `cm./rony-chat-bot/` | Comandos: `serve`, `reindex`, `ask`, `version` |
| **Agent runner** | `internal/agent/` | Wrapper over `rony-llm-agent` with specific config |
| **Portfolio loader** | `internal/portfolio/` | Reads `data/projects/*.md`, indexes in ChromaDB |
| **Persona** | `internal/persona/` | Loads persona from `configs/portfolio-bot.yaml` |
| **CLI** | `cmd/chat-bot/` | Commands: `serve`, `reindex`, `ask`, `version` |
### 2.3 Stack tecnológico
### 2.3 Tech stack
| Capa | Tecnología | Razón |
| Layer | Technology | Reason |
|---|---|---|
| **Lenguaje** | Go 1.26+ | Mismo que `harness`, aprovechar `os.Root`, `iter.Seq` |
| **HTTP router** | `net/http` + `chi` | Stdlib + chi para middleware (CORS, logging) |
| **SSE** | `net/http` Flusher | Stdlib es suficiente, no necesita librería externa |
| **Config** | `gopkg.in/yaml.v3` | Mismo que harness |
| **Language** | Go 1.26+ | Same as rony-harness, leverage `os.Root`, `iter.Seq` |
| **HTTP router** | `net/http` + `chi` | Stdlib + chi for middleware (CORS, logging) |
| **SSE** | `net/http` Flusher | Stdlib is enough, no external library needed |
| **Config** | `gopkg.in/yaml.v3` | Same as harness |
| **RAG backend** | ChromaDB embedded via `chroma-go` | Self-hosted, simple API |
| **Embeddings** | Ollama (nomic-embed-text) | Local, gratis, buena calidad |
| **LLM** | Ollama (qwen2.5:1.5b) o llama.cpp | Self-hosted por defecto |
| **Tests** | stdlib + testify | Consistencia con el resto |
| **Embeddings** | Ollama (nomic-embed-text) | Local, free, good quality |
| **LLM** | Ollama (qwen2.5:1.5b) or llama.cpp | Self-hosted by default |
| **Tests** | stdlib + testify | Consistency with the rest |
---
@ -95,13 +98,13 @@ El bot responde con información precisa extraída de los archivos markdown de p
### 3.1 Endpoints
#### `POST /api/chat` — Chat con streaming SSE
#### `POST /api/chat` — Chat with SSE streaming
**Request:**
```json
{
"messages": [
{"role": "user", "content": "¿Qué proyectos tiene Victor?"}
{"role": "user", "content": "What projects does Victor have?"}
],
"stream": true
}
@ -112,29 +115,29 @@ El bot responde con información precisa extraída de los archivos markdown de p
data: {"type":"start","conversation_id":"abc123"}
data: {"type":"chunk","content":"Victor"}
data: {"type":"chunk","content":" tiene"}
data: {"type":"chunk","content":" varios"}
data: {"type":"chunk","content":" proyectos"}
data: {"type":"chunk","content":" has"}
data: {"type":"chunk","content":" several"}
data: {"type":"chunk","content":" projects"}
data: {"type":"sources","documents":["rony-tui.md","rony-llm-agent.md"]}
data: {"type":"sources","documents":["rony-harness.md","rony-llm-agent.md"]}
data: {"type":"done","usage":{"input_tokens":245,"output_tokens":38}}
```
**Sin streaming** (`"stream": false`):
**Without streaming** (`"stream": false`):
```json
{
"content": "Victor tiene varios proyectos...",
"sources": ["rony-tui.md", "rony-llm-agent.md"],
"content": "Victor has several projects...",
"sources": ["rony-harness.md", "rony-llm-agent.md"],
"usage": {"input_tokens": 245, "output_tokens": 38}
}
```
#### `POST /api/reindex` — Re-indexar portfolio
#### `POST /api/reindex` — Re-index portfolio
Útil cuando se modifican archivos en `data/projects/`.
Useful when files in `data/projects/` are modified.
**Request:** vacío
**Request:** empty
**Response:**
```json
{
@ -159,14 +162,14 @@ data: {"type":"done","usage":{"input_tokens":245,"output_tokens":38}}
}
```
#### `GET /api/info`Metadata del bot
#### `GET /api/info`Bot metadata
```json
{
"name": "Asistente de Victor Hugo Vargas",
"name": "Rony Chat Bot",
"model": "qwen2.5:1.5b",
"persona": "...",
"topics": ["proyectos", "experiencia", "skills técnicas"]
"topics": ["projects", "experience", "technical skills"]
}
```
@ -184,7 +187,7 @@ import (
)
func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) {
// Headers SSE
// SSE headers
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
@ -192,7 +195,7 @@ func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) {
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "SSE no soportado", http.StatusInternalServerError)
http.Error(w, "SSE not supported", http.StatusInternalServerError)
return
}
@ -208,7 +211,7 @@ func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) {
"conversation_id": generateConvID(),
})
// Run agent con streaming
// Run agent with streaming
sources := []string{}
for chunk, err := range s.agent.RunStream(r.Context(), req.Messages) {
if err != nil {
@ -246,7 +249,7 @@ package server
func (s *Server) loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// Wrap response writer para capturar status
// Wrap response writer to capture status
rw := &statusRecorder{ResponseWriter: w, status: 200}
next.ServeHTTP(rw, r)
@ -295,7 +298,7 @@ func (s *Server) rateLimitMiddleware(next http.Handler) http.Handler {
## 🧠 4. RAG (Retrieval-Augmented Generation)
### 4.1 Pipeline de indexación
### 4.1 Indexing pipeline
```
data/projects/*.md
@ -309,26 +312,26 @@ Vectors [][]float32
Indexed corpus
```
**Cuándo se ejecuta:**
- Al arrancar el bot (si `--reindex-on-start` flag)
- Manualmente: `./chat-bot reindex`
- Vía HTTP: `POST /api/reindex`
**When it runs:**
- On bot startup (if `--reindex-on-start` flag)
- Manually: `./chat-bot reindex`
- Via HTTP: `POST /api/reindex`
### 4.2 Pipeline de retrieval
### 4.2 Retrieval pipeline
```
User query "¿qué proyectos tiene Victor?"
User query "what projects does Victor have?"
↓ (embed query)
Query vector
↓ (cosine similarity search en ChromaDB, top_k=5)
Top 5 chunks relevantes
↓ (cosine similarity search in ChromaDB, top_k=5)
Top 5 relevant chunks
↓ (format as context block)
System prompt += chunks relevantes
System prompt += relevant chunks
↓ (send to LLM)
LLM generates answer
```
### 4.3 Implementación
### 4.3 Implementation
```go
// internal/portfolio/indexer.go
@ -404,8 +407,8 @@ func (i *Indexer) indexFile(ctx context.Context, path string) (int, error) {
}
func splitIntoChunks(text string, size, overlap int) []string {
// Implementación simple: split por tamaño con overlap
// Versión production usa tokenizer-aware chunking
// Simple implementation: split by size with overlap
// Production version uses tokenizer-aware chunking
var chunks []string
for i := 0; i < len(text); i += size - overlap {
end := i + size
@ -418,7 +421,7 @@ func splitIntoChunks(text string, size, overlap int) []string {
}
```
### 4.4 Retrieval en el agent loop
### 4.4 Retrieval in the agent loop
```go
// internal/agent/runner.go
@ -437,9 +440,9 @@ func (r *Runner) buildSystemPrompt(ctx context.Context, query string) (string, e
// 3. Format as context
var contextBlock strings.Builder
contextBlock.WriteString(basePrompt)
contextBlock.WriteString("\n\n## Contexto relevante\n\n")
contextBlock.WriteString("\n\n## Relevant context\n\n")
for idx, frag := range fragments {
contextBlock.WriteString(fmt.Sprintf("### Fuente: %s\n%s\n\n",
contextBlock.WriteString(fmt.Sprintf("### Source: %s\n%s\n\n",
frag.Metadata["source_file"], frag.Content))
}
@ -471,31 +474,30 @@ func (r *Runner) RunStream(ctx context.Context, messages []llm.Message) iter.Seq
---
## 🌐 5. Integración con Astro (Portfolio)
## 🌐 5. Integration with Astro (Portfolio)
### 5.1 Patrón recomendado: Astro proxy
### 5.1 Recommended pattern: Astro proxy
```
[Browser] ←→ [Astro SSR :4321] ←→ [Chat-Bot :7331]
```
**Por qué proxy y no llamada directa del browser al chat-bot:**
**Why proxy and not direct browser call to chat-bot:**
- ✅ Single domain (no CORS)
- ✅ Astro maneja auth/sesión si se necesita
- ✅ Puede haber rate limiting centralizado en Astro
- ✅ El chat-bot queda en red privada (no expuesto a internet directamente)
- ✅ Astro handles auth/session if needed
- ✅ There can be centralized rate limiting in Astro
- ✅ The chat-bot stays on private network (not exposed to internet directly)
### 5.2 Astro: API route del proxy
### 5.2 Astro: API route of the proxy
```typescript
// portfolio/src/pages/api/chat.ts
import type { APIRoute } from 'astro';
const CHAT_BOT_URL = process.env.CHAT_BOT_URL || 'http://localhost:7331';
const CHAT_BOT_URL = import.meta.env.CHAT_BOT_URL || 'http://localhost:7331';
export const POST: APIRoute = async ({ request }) => {
const body = await request.json();
const resp = await fetch(`${CHAT_BOT_URL}/api/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@ -506,7 +508,7 @@ export const POST: APIRoute = async ({ request }) => {
return new Response('Chat bot error', { status: resp.status });
}
// Stream SSE de vuelta al browser
// Stream SSE back to browser
return new Response(resp.body, {
status: 200,
headers: {
@ -518,7 +520,7 @@ export const POST: APIRoute = async ({ request }) => {
};
```
### 5.3 React: Componente del chat
### 5.3 React: Chat component
```tsx
// portfolio/src/components/Chat.tsx
@ -543,7 +545,7 @@ export default function Chat() {
setInput('');
setStreaming(true);
// Placeholder para streaming
// Placeholder for streaming
const assistantMsg: Message = { role: 'assistant', content: '' };
setMessages(prev => [...prev, assistantMsg]);
@ -611,7 +613,7 @@ export default function Chat() {
value={input}
onChange={e => setInput(e.target.value)}
onKeyDown={e => e.key === 'Enter' && send()}
placeholder="Pregunta sobre Victor..."
placeholder="Ask about Victor..."
disabled={streaming}
/>
{streaming ? (
@ -627,39 +629,39 @@ export default function Chat() {
---
## 🤖 6. Self-hosting con Ollama
## 🤖 6. Self-hosting with Ollama
### 6.1 Setup
```bash
# 1. Instalar Ollama
# 1. Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
# 2. Descargar modelo de chat
# 2. Download chat model
ollama pull qwen2.5:1.5b
# 3. Descargar modelo de embeddings
# 3. Download embeddings model
ollama pull nomic-embed-text
# 4. Verificar
# 4. Verify
ollama list
```
### 6.2 Configuración por defecto
### 6.2 Default configuration
`configs/portfolio-bot.yaml` ya viene con Ollama como default. Solo necesitas:
`configs/portfolio-bot.yaml` already comes with Ollama as default. You only need:
```bash
# Asegurar que Ollama está corriendo
# Make sure Ollama is running
ollama serve
# Arrancar el bot
# Start the bot
./bin/chat-bot serve
```
### 6.3 Alternativa: llama.cpp directo
### 6.3 Alternative: llama.cpp direct
Para más control o si Ollama no funciona en tu setup:
For more control or if Ollama doesn't work in your setup:
```yaml
providers:
@ -667,42 +669,42 @@ providers:
type: llamacpp
model_path: ${RONY_MODELS_PATH}/qwen2.5-1.5b-instruct-q5_k_m.gguf
context_size: 4096
n_gpu_layers: 999 # offload todo a GPU
n_gpu_layers: 999 # offload all to GPU
default: true
```
El adapter `llamacpp` se importa desde `rony-llm-agent/pkg/llm/providers/llamacpp` y se compila contra `llama.cpp` vía CGO o binario externo.
The `llamacpp` adapter is imported from `rony-llm-agent/pkg/llm/providers/llamacpp` and is compiled against `llama.cpp` via CGO or external binary.
---
## 📦 7. CLI del bot
## 📦 7. Bot CLI
### 7.1 Comandos
### 7.1 Commands
```bash
# Arrancar servidor HTTP
# Start HTTP server
chat-bot serve [--port 7331] [--host 0.0.0.0] [--reindex-on-start]
# Re-indexar portfolio (lee data/projects/*.md → ChromaDB)
# Re-index portfolio (reads data/projects/*.md → ChromaDB)
chat-bot reindex
# Pregunta única (sin servidor, útil para tests)
chat-bot ask "¿Qué proyectos tiene Victor?" [--no-rag]
# Single question (no server, useful for tests)
chat-bot ask "What projects does Victor have?" [--no-rag]
# Validar config
# Validate config
chat-bot config validate
# Health check (útil para monitoring)
# Health check (useful for monitoring)
chat-bot health
# Versión
# Version
chat-bot version
```
### 7.2 Implementación con Cobra
### 7.2 Implementation with Cobra
```go
// cm./rony-chat-bot/main.go
// cmd/chat-bot/main.go
package main
import (
@ -756,10 +758,10 @@ func serveCmd() *cobra.Command {
## 🚀 8. Deployment
### 8.1 Recomendación: Self-hosted en VPS
### 8.1 Recommendation: Self-hosted on VPS
```bash
# 1. Instalar dependencias
# 1. Install dependencies
sudo apt install golang-go ollama
ollama pull qwen2.5:1.5b
ollama pull nomic-embed-text
@ -800,7 +802,7 @@ chat.victorvargas.dev {
### 8.3 Monitoring
```bash
# Health check periódico
# Health check periodic
curl -s http://localhost:7331/api/health | jq
# Logs
@ -821,7 +823,7 @@ func TestHandleChat_ValidRequest(t *testing.T) {
s := newTestServer(t)
req := httptest.NewRequest("POST", "/api/chat", strings.NewReader(`{
"messages": [{"role": "user", "content": "hola"}]
"messages": [{"role": "user", "content": "hello"}]
}`))
req.Header.Set("Content-Type", "application/json")
@ -838,31 +840,31 @@ func TestHandleChat_RateLimit(t *testing.T) {
})
// First request OK
req1 := newChatRequest("hola")
req1 := newChatRequest("hello")
w1 := httptest.NewRecorder()
s.handleChat(w1, req1)
assert.Equal(t, 200, w1.Code)
// Second request denied
req2 := newChatRequest("hola de nuevo")
req2 := newChatRequest("hello again")
w2 := httptest.NewRecorder()
s.handleChat(w2, req2)
assert.Equal(t, 429, w2.Code)
}
```
### 9.2 Integration tests con mock LLM
### 9.2 Integration tests with mock LLM
```go
// internal/agent/runner_test.go
func TestRunner_RAGContextIsInjected(t *testing.T) {
mockLLM := mock.New(mock.Responses{
{Match: "proyectos", Response: "Victor tiene varios proyectos..."},
{Match: "projects", Response: "Victor has several projects..."},
})
memory := newMockMemoryWithDocs(t, []rag.Fragment{
{Content: "Rony TUI: AI agent harness...", ProjectID: "rony-tui"},
{Content: "rony-llm-agent: librería Go...", ProjectID: "rony-llm-agent"},
{Content: "Rony Harness: AI agent harness...", ProjectID: "rony-harness"},
{Content: "go-llm-agent: Go library...", ProjectID: "rony-llm-agent"},
})
runner := agent.NewRunner(agent.Config{
@ -872,36 +874,36 @@ func TestRunner_RAGContextIsInjected(t *testing.T) {
})
resp, _ := runner.Run(context.Background(), []llm.Message{
{Role: llm.RoleUser, Content: "¿qué proyectos tiene Victor?"},
{Role: llm.RoleUser, Content: "what projects does Victor have?"},
})
// Verify LLM received context chunks in system prompt
lastReq := mockLLM.LastRequest()
assert.Contains(t, lastReq.Messages[0].Content, "Rony TUI")
assert.Contains(t, lastReq.Messages[0].Content, "rony-llm-agent")
assert.Contains(t, lastReq.Messages[0].Content, "Rony Harness")
assert.Contains(t, lastReq.Messages[0].Content, "go-llm-agent")
}
```
### 9.3 E2E test con Astro
### 9.3 E2E test with Astro
```bash
# 1. Arrancar chat-bot en :7331
# 1. Start chat-bot on :7331
./bin/chat-bot serve &
# 2. Arrancar Astro en :4321
# 2. Start Astro on :4321
cd ../portfolio && npm run dev &
# 3. Hacer request al proxy de Astro
# 3. Make request to Astro's proxy
curl -X POST http://localhost:4321/api/chat \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"hola"}]}'
-d '{"messages":[{"role":"user","content":"hello"}]}'
# 4. Verificar SSE stream
# 4. Verify SSE stream
```
---
## 📂 10. Estructura del Proyecto
## 📂 10. Project Structure
```
chat-bot/
@ -918,21 +920,21 @@ chat-bot/
│ │ ├── middleware.go # logging, CORS, rate limit
│ │ └── sse.go # SSE helpers
│ │
│ ├── agent/ # Wrapper sobre rony-llm-agent
│ │ ├── runner.go # RunStream con RAG injection
│ ├── agent/ # Wrapper over rony-llm-agent
│ │ ├── runner.go # RunStream with RAG injection
│ │ └── prompts.go # System prompt builder
│ │
│ ├── portfolio/ # Data loader
│ │ ├── indexer.go # Lee .md, chunks, embed, store
│ │ ├── indexer.go # Reads .md, chunks, embed, store
│ │ ├── retriever.go # Query → top-k chunks
│ │ └── chunker.go # Text splitting
│ │
│ └── persona/ # Persona override
│ └── loader.go # Carga persona desde YAML
│ └── loader.go # Loads persona from YAML
├── data/
│ └── projects/ # ← Markdown por proyecto
│ ├── rony-tui.md
│ └── projects/ # ← Markdown per project
│ ├── rony-harness.md
│ ├── rony-llm-agent.md
│ └── example-project.md
@ -940,7 +942,7 @@ chat-bot/
│ └── portfolio-bot.yaml # Provider + RAG + persona config
├── docs/
│ └── architecture.md # ← ESTE ARCHIVO
│ └── architecture.md # ← THIS FILE
├── go.mod
└── README.md
@ -950,83 +952,83 @@ chat-bot/
## 📅 11. Roadmap
### Fase 1: MVP (2-3 semanas)
### Phase 1: MVP (2-3 weeks)
- [ ] Setup proyecto (`go mod init`, estructura)
- [ ] HTTP server básico con un endpoint `/api/chat`
- [ ] SSE streaming funcional
- [ ] RAG indexer (lee `data/projects/*.md` → ChromaDB)
- [ ] Project setup (`go mod init`, structure)
- [ ] Basic HTTP server with `/api/chat` endpoint
- [ ] Functional SSE streaming
- [ ] RAG indexer (reads `data/projects/*.md` → ChromaDB)
- [ ] RAG retriever (query → top-k chunks)
- [ ] Persona loader desde YAML
- [ ] Integración con Ollama (qwen2.5:1.5b)
- [ ] Persona loader from YAML
- [ ] Ollama integration (qwen2.5:1.5b)
- [ ] CLI: `serve`, `reindex`, `ask`
- [ ] Tests básicos
- [ ] Basic tests
### Fase 2: Integración con Astro (1 semana)
### Phase 2: Integration with Astro (1 week)
- [ ] Astro API route del proxy
- [ ] React component del chat widget
- [ ] E2E test: Astro → chat-bot → respuesta
- [ ] Styling del widget (TailwindCSS)
- [ ] Astro API route of the proxy
- [ ] React component of the chat widget
- [ ] E2E test: Astro → chat-bot → response
- [ ] Widget styling (TailwindCSS)
### Fase 3: Polish (1 semana)
### Phase 3: Polish (1 week)
- [ ] Rate limiting robusto
- [ ] Logging estructurado (JSON)
- [ ] Health checks para monitoring
- [ ] Robust rate limiting
- [ ] Structured logging (JSON)
- [ ] Health checks for monitoring
- [ ] systemd service file
- [ ] README + docs de deployment
- [ ] README + deployment docs
### Fase 4: Opcionales
### Phase 4: Optionals
- [ ] Soporte para múltiples conversaciones (session ID)
- [ ] Historial de chats persistido
- [ ] Análisis de preguntas frecuentes
- [ ] Multi-idioma (EN/ES switch)
- [ ] Versión standalone CLI más pulida (`chat-bot ask`)
- [ ] Support for multiple conversations (session ID)
- [ ] Persisted chat history
- [ ] Analysis of frequent questions
- [ ] Multi-language (EN/ES switch)
- [ ] More polished standalone CLI version (`chat-bot ask`)
---
## 📐 12. Especificaciones de Calidad
## 📐 12. Quality Specifications
### 12.1 Métricas de rendimiento
### 12.1 Performance metrics
| Métrica | Target |
| Metric | Target |
|---|---|
| TTFT (Time-to-first-token) | <500ms con Ollama local |
| End-to-end (pregunta → respuesta completa) | <3s para respuestas típicas |
| Memoria en reposo | <150MB |
| RAG indexing speed | ~100 docs/segundo |
| Retrieval latency | <50ms para top-5 |
| TTFT (Time-to-first-token) | <500ms with Ollama local |
| End-to-end (question → complete response) | <3s for typical responses |
| Memory at rest | <150MB |
| RAG indexing speed | ~100 docs/second |
| Retrieval latency | <50ms for top-5 |
### 12.2 Pruebas requeridas
### 12.2 Required tests
- Unit tests: cobertura ≥70%
- Integration tests: con mock LLM + mock ChromaDB
- E2E: al menos un flujo completo Astro → chat-bot
- Unit tests: coverage ≥70%
- Integration tests: with mock LLM + mock ChromaDB
- E2E: at least one complete Astro → chat-bot flow
---
## 🔒 13. Seguridad
## 🔒 13. Security
### 13.1 Implementado
### 13.1 Implemented
- **Rate limiting** por IP (default 30 req/min)
- **CORS restrictivo** — solo origins configurados
- **Input validation** — JSON schema validation en requests
- **No PII storage**no guardamos conversaciones por default
- **Local-only por default** — sin llamadas a APIs cloud
- **Rate limiting** per IP (default 30 req/min)
- **Restrictive CORS** — only configured origins
- **Input validation** — JSON schema validation on requests
- **No PII storage**we don't save conversations by default
- **Local-only by default** — no calls to cloud APIs
### 13.2 Diferido / Opcional
### 13.2 Deferred / Optional
- Auth con API key (para uso privado)
- Logging de queries para analytics
- Anonymization de IPs en logs
- Auth with API key (for private use)
- Query logging for analytics
- IP anonymization in logs
- HTTPS via reverse proxy (Caddy/nginx)
---
## 📚 14. Referencias
## 📚 14. References
- **SSE Spec:** https://html.spec.whatwg.org/multipage/server-sent-events.html
- **Ollama API:** https://github.com/ollama/ollama/blob/main/docs/api.md
@ -1038,4 +1040,4 @@ chat-bot/
---
**Documento listo para implementación. 🚀**
**Document ready for implementation. 🚀**