Portafolio profesional
ailocal: Tu asistente de código offline
Cómo construí una herramienta CLI que genera código usando modelos de lenguaje locales de Ollama, sin dependencias en la nube ni integraciones complejas.
Artículo
# ailocal: Tu asistente de código offline
## El problema
Como desarrollador, me encontraba constantemente en situaciones donde necesitaba generar código rápidamente: prototipar una función, crear la estructura de un proyecto nuevo, o incluso escribir código boilerplate repetitivo. Las soluciones existentes como GitHub Copilot o ChatGPT son excelentes, pero tienen un problema fundamental: dependen de servicios en la nube. Esto significa que necesitás conexión a internet, aceptás términos de servicio que pueden no ser ideales para código propietario, y en algunos casos, incluso pagás por uso.
Lo más frustrante era cuando trabajaba en entornos con restricciones de red, en aviones, o simplemente quería mantener mi código completamente local. Además, muchas de estas herramientas vienen con integraciones complejas de herramientas (function calling, tool injection) que, aunque poderosas, a menudo añaden overhead innecesario cuando solo querés código puro y simple.
## Por qué Ollama local
La respuesta parecía obvia: usar modelos de lenguaje locales. Después de evaluar varias opciones, me decidí por Ollama por varias razones concretas. Primero, su ecosistema de modelos optimizados para código como Qwen2.5-Coder o CodeLlama. Segundo, su API HTTP simple y bien documentada. Tercero, y más importante, su capacidad de correr completamente offline una vez descargado el modelo.
Comparado con otras alternativas, Ollama tiene la ventaja de ser multiplataforma (macOS, Linux, Windows) y fácil de instalar ya sea vía Docker o binario nativo. La decisión clave fue evitar completamente cualquier integración de herramientas externas - no hay function calling, no hay tool injection. Solo prompt → respuesta de código → archivo. Esta simplicidad intencional es lo que diferencia a ailocal de otras soluciones más complejas.
## Arquitectura
El diseño de ailocal sigue una arquitectura minimalista pero efectiva. Acá te muestro cómo fluyen los datos a través del sistema:
```mermaid
flowchart LR
User["Terminal
Usuario"] -- comando/interacción --> CLI["cli.py
CLI handler"]
CLI -- parsea argumentos --> Main["__main__.py
Orquestador"]
Main -- obtiene prompt --> User
Main -- envía prompt --> OllamaClient["ollama_client.py
Cliente HTTP asíncrono"]
OllamaClient -- llama API --> OllamaService["Servicio Ollama
Local/Docker"]
OllamaService -- devuelve respuesta --> OllamaClient
OllamaClient -- código generado --> Main
Main -- escribe archivo --> FileWriter["file_writer.py
Manejador de archivos"]
FileWriter -- crea/actualiza --> Filesystem["Sistema de archivos
Proyecto local"]
```
**Decisión 1: CLI primero con Click**
Elegí Click sobre argparse o Typer porque ofrece una experiencia de desarrollo más productiva para herramientas CLI complejas. Su sistema de comandos anidados, grupos, y opciones automáticas me permitió implementar rápidamente modos interactivo, one-shot y pipe sin escribir mucho código boilerplate.
**Decisión 2: Asíncrono para el cliente Ollama**
Aunque el resto de la aplicación es síncrona, el cliente HTTP para Ollama es completamente asíncrono usando aiohttp. Esto permite manejar múltiples solicitudes concurrentes (útil para futuras extensiones) y no bloquear el thread principal durante llamadas que pueden tomar varios segundos.
**Decisión 3: Separación clara de responsabilidades**
Cada módulo tiene una responsabilidad única: cli.py maneja la interfaz de usuario, ollama_client.py la comunicación con el modelo, y file_writer.py la escritura de archivos. Esta separación hace el código más testeable y mantenible.
## El trade-off que nadie te cuenta
El mayor trade-off fue renunciar al contexto del editor. Herramientas como Copilot tienen acceso a todo tu código abierto, pueden analizar imports, entender la estructura del proyecto, y ofrecer sugerencias contextuales. ailocal, al ser una herramienta CLI standalone, solo tiene acceso al prompt que le das y al directorio de trabajo actual.
Esto significa que tenés que ser más explícito en tus prompts. En lugar de decir "agregá una función para calcular el promedio", tenés que decir "en el archivo stats.py, agregá una función calculate_average que tome una lista de números y devuelva el promedio".
La solución fue implementar un sistema de "contexto de directorio" donde ailocal puede leer archivos existentes si se lo pedís explícitamente, pero nunca asume contexto automáticamente. También agregué la opción de usar pipes para pasar contenido existente:
```bash
cat existing_code.py | ailocal "optimiza esta función"
```
No es perfecto, pero mantiene la simplicidad filosófica del proyecto mientras da un camino para trabajos más complejos.
## Resultado
- **Tiempo de respuesta**: Menos de 5 segundos para prompts simples usando qwen2.5-coder:3b en una MacBook M1, comparable a servicios en la nube pero completamente offline
- **Reducción de trabajo manual**: Eliminé aproximadamente el 30% del código boilerplate que escribía manualmente en proyectos nuevos
- **Flexibilidad de modelos**: Podés cambiar entre diferentes modelos de código según la tarea (más rápido vs. más inteligente) con un simple flag `--model`
- **Integración en workflow**: Se convirtió en parte natural de mi flujo de trabajo, especialmente para:
- Crear estructuras de proyecto iniciales
- Generar clases DTO/DataClass
- Escribir tests unitarios repetitivos
- Documentar funciones existentes
## Lo que haría diferente
Si empezara de nuevo, implementaría un sistema de plantillas (templates) para proyectos comunes. Aunque los LLMs son buenos generando código desde cero, para estructuras de proyecto repetitivas (FastAPI con SQLAlchemy, React component library, etc.), una combinación de templates + generación de código específico sería más eficiente.
También reconsideraría la decisión de no incluir ningún análisis de código estático. Un linter básico que revise el código generado antes de escribirlo podría prevenir algunos errores sintácticos que los modelos más pequeños a veces cometen.
---
¿Querés profundizar en algún componente? Contactame o revisá el código en [https://github.com/Albarracin-sg/ailocal](https://github.com/Albarracin-sg/ailocal).