Skip to main content
Glama
cesarreynab

agente-traduccion-mcp

by cesarreynab
README.md
# Asistente de Traducción y Localización (Agente MCP + Memoria + Streamlit)

Proyecto final — César Reyna · Curso *Estrategias de Integración LLM* (BSG)

## Problema
- **Usuario principal:** traductor / gestor de proyectos de localización.
- **Necesidad:** responder rápido preguntas recurrentes de un flujo de traducción:
  ¿cuál es el término aprobado?, ¿ya tradujimos algo parecido?, traducir respetando la
  terminología, y revisar (alinear) una traducción contra su original.
- **Qué NO cubre (límites):** no es un traductor automático de producción; la tool
  `traducir_multicloud` es una **demo sin claves reales** (usa la memoria/glosario). La
  memoria del agente es de corto plazo y se reinicia al reiniciar el proceso.

## Arquitectura
```text
Usuario
  |
  v
[Streamlit]  --->  [Agente LangChain + modelo]  --MCP client-->  [MCP de dominio]
  |                                                                  |
  |                                                                  +--> 5 tools propias
  v                                                                  +--> SQLite (glosario + memoria)
Memoria por session_id (InMemorySaver + ventana de 8 mensajes)
```
- **Streamlit** (`app_streamlit.py`): interfaz de chat, muestra respuesta y evidencia.
- **Agente** (`agent_core.py`): LangChain + modelo (OpenRouter gratis u OpenAI), memoria.
- **MCP** (`mcp_server.py`): expone las 5 tools; el modelo nunca escribe SQL.
- **Datos** (`data_seed.py`): crea la base SQLite de ejemplo (glosario + memoria).

## Tools MCP (contratos)
| Tool | Necesidad que resuelve | Entrada | Salida | Riesgo | Prueba |
|---|---|---|---|---|---|
| `buscar_termino` | Encontrar el término aprobado | `texto: str`, `idioma_origen: str=""`, `limite: int=10` | Lista de términos (origen, destino, dominio) | Lectura: bajo | "¿cómo se dice 'invoice'?" |
| `buscar_memoria` | Reusar traducciones previas | `texto: str`, `idioma_origen: str`, `idioma_destino: str`, `limite: int=5` | Frases ya traducidas + calidad | Lectura: bajo | "¿tradujimos algo con 'privacy'?" |
| `alinear_segmentos` | Revisar (QA) una traducción | `texto_origen: str`, `texto_destino: str` | Alineación oración por oración + similitud | Lectura: bajo | alinear original vs traducción |
| `traducir_multicloud` | Traducir enrutando a proveedor | `texto: str`, `idioma_destino: str`, `proveedor: str="auto"` | `{proveedor, traduccion, origen}` | Lectura (demo): bajo | traducir con 'google' |
| `estadisticas_memoria` | Ver cuánta memoria hay | (sin parámetros) | Conteo por par de idiomas y dominio | Lectura: bajo | "¿cuánta memoria tengo?" |

Cada tool **valida** entradas (texto no vacío, rango de `limite`, proveedor/idioma válidos) y
devuelve salida estructurada (JSON). No existe una tool genérica tipo `ejecutar_sql`.

## Memoria
- Se usa `session_id` como `thread_id`; cada conversación es independiente.
- Checkpointer `InMemorySaver` + una **ventana** de los últimos 8 mensajes.
- **Límite:** es memoria de corto plazo; se pierde al reiniciar el proceso. Para producción
  habría que persistirla en una base de datos por usuario.

## Instalación local
```bash
python -m venv .venv
.venv\Scripts\activate            # Windows  (macOS/Linux: source .venv/bin/activate)
pip install -r requirements.txt
copy .env.example .env            # y completa OPENROUTER_API_KEY (o OPENAI_API_KEY)
streamlit run app_streamlit.py
```
(También puedes poner la key en `.streamlit/secrets.toml` — ver `secrets.toml.example`.)

## Pruebas
Pruebas automáticas (sin LLM):
```bash
pytest -q
```
Los **5 escenarios** para probar desde la app:
- **A. Directa:** "¿Cómo se dice 'invoice' en el glosario?" → usa `buscar_termino`.
- **B. Compuesta:** "Traduce 'Take one tablet twice a day.' al español y revísala contra el original." → `traducir_multicloud` + `alinear_segmentos`.
- **C. Con memoria:** "Busca el término 'dosage'." y luego "Ahora tradúcelo al español con Azure." → resuelve "lo".
- **D. Dato inexistente:** "¿Cómo se dice 'xyznoexiste' en el glosario?" → explica que no hay dato, sin inventar.
- **E. Fuera de alcance:** "¿Qué clima hace en Lima?" → responde "No tengo disponible esa información".

## Despliegue (GitHub + Streamlit Community Cloud)
1. **GitHub (repo público):** crea un repo y sube el contenido de esta carpeta al nivel raíz
   (`app_streamlit.py` en la raíz). No subas `.env` ni `secrets.toml`.
2. **Streamlit Cloud:** [share.streamlit.io](https://share.streamlit.io) → *Create app* → elige el repo,
   rama `main`, *Main file path* = `app_streamlit.py`.
3. **Secrets** (*Settings → Secrets*), pega:
   ```toml
   OPENROUTER_API_KEY = "tu_api_key"
   OPENROUTER_MODEL = "nvidia/nemotron-3-ultra-550b-a55b:free"
   ```
   (o `OPENAI_API_KEY` + `OPENAI_MODEL` si prefieres OpenAI).
4. **Deploy** y prueba los 5 escenarios en la URL pública.

> **Nota sobre el MCP:** por defecto la app arranca el MCP localmente dentro del contenedor
> (simple, un solo despliegue). Si más adelante publicas el MCP como servicio HTTP aparte,
> solo agrega el secret `MCP_SERVER_URL` con su endpoint y la app se conecta ahí sin tocar código.

## Modelo
El nombre del modelo va en variable de entorno para poder cambiarlo sin editar el código.
Por recomendación del curso se usa **OpenRouter** (tiene modelos gratis). También funciona con
**OpenAI** definiendo `OPENAI_API_KEY` y `OPENAI_MODEL`.

## Enlaces
- App (Streamlit): https://agente-traduccion-mcp-awnsobmfcabxmsgf9jptfx.streamlit.app/
- Repositorio (GitHub): https://github.com/cesarreynab/agente-traduccion-mcp