"Flask para agentes de IA. El micro-framework que no te dice cómo pensar."
pluma es un micro-framework Python para construir agentes de IA con personalidad, memoria y herramientas. Sin boilerplate. Sin magia negra. Solo código que vuela.
pip install pluma
| pluma | LangChain | LlamaIndex | |
|---|---|---|---|
| Curva de aprendizaje | 15 minutos | días | días |
| Líneas para un agente básico | ~10 | ~50 | ~40 |
| Dependencias | 4 | 100+ | 80+ |
| Filosofía | Flask | Django | Hibernate |
| Multi-proveedor | ✅ | ✅ | ✅ |
| ReAct loop | ✅ | ✅ | ✅ |
| Streaming | ✅ | ✅ | ✅ |
# Solo el framework
pip install pluma
# Con soporte para Ollama (local)
pip install "pluma[ollama]"
# Con soporte para OpenAI
pip install "pluma[openai]"
# Con soporte para Anthropic
pip install "pluma[anthropic]"
# Todo
pip install "pluma[all]"Requisitos: Python 3.10+
import asyncio
from pluma import Plumifero, Manuscrito
from pluma import ProveedorOllama
async def main():
agente = Plumifero(
proveedor=ProveedorOllama(modelo="llama3.2"),
manuscrito=Manuscrito(
nombre="Cafecito",
proposito="Ayudar a debuggear código antes del primer café.",
personalidad="Paciente, preciso, con olor a café recién hecho.",
),
)
respuesta = await agente.dialogar("¿Qué es un decorador en Python?")
print(respuesta)
asyncio.run(main())import asyncio
from pluma import Plumifero, Manuscrito, ProveedorOllama
from pluma import pluma_buscar_web, pluma_datetime
async def main():
agente = Plumifero(
proveedor=ProveedorOllama(modelo="llama3.2"),
plumas=[pluma_buscar_web, pluma_datetime],
)
resultado = await agente.volar(
"¿Qué día es hoy y qué noticias hay sobre Python?"
)
print(resultado.mensaje_final)
print(f"Pasos: {len(resultado.pasos)} | Tokens: {resultado.tokens_usados}")
asyncio.run(main())from pluma import crear_plumita, crear_don_pio, crear_madrugador, crear_poeta
from pluma import ProveedorAnthropic
proveedor = ProveedorAnthropic(modelo="claude-haiku-4-5-20251001")
# Chat conversacional
plumita = crear_plumita(proveedor)
# Revisor de código implacable
don_pio = crear_don_pio(proveedor)
# Automatización con shell + Python + archivos
madrugador = crear_madrugador(proveedor)
# Poeta que escribe sobre código
poeta = crear_poeta(proveedor)Una Pluma es una función que el agente puede invocar. Define su nombre, descripción y firma para que el LLM sepa cuándo y cómo usarla.
from pluma import Pluma
# Desde una función existente
def obtener_temperatura(ciudad: str, unidad: str = "celsius") -> str:
"""Obtiene la temperatura actual de una ciudad."""
return f"La temperatura en {ciudad} es de 22°{unidad[0].upper()}"
pluma_clima = Pluma.crear(
nombre="obtener_temperatura",
descripcion="Obtiene la temperatura actual de una ciudad.",
funcion=obtener_temperatura,
)
# Con decorador
@Pluma.desde_funcion
def fibonacci(n: int) -> str:
"""Calcula los primeros N números de la secuencia de Fibonacci."""
a, b, seq = 0, 1, []
for _ in range(n):
seq.append(a); a, b = b, a + b
return str(seq)El Manuscrito define la identidad, propósito y personalidad del agente. Se convierte en el prompt de sistema enviado al LLM.
from pluma import Manuscrito
manuscrito = Manuscrito(
nombre="DataBird",
proposito="Analizar datasets y generar insights accionables.",
personalidad="Riguroso con los números, pero accesible en las explicaciones.",
tono="técnico pero claro, como un profesor que sabe de lo que habla",
idioma="español",
conocimiento_base=["pandas", "numpy", "estadística descriptiva"],
restricciones=["No inventes datos. Si no puedes calcular algo, dilo."],
)
# API fluida (inmutable — devuelve nuevo Manuscrito)
manuscrito_v2 = (
manuscrito
.con_conocimiento("matplotlib", "seaborn")
.con_restriccion("No uses datos de usuarios sin anonimizar")
.con_ejemplo(
usuario="¿Qué es la media?",
plumifero="La media es la suma de todos los valores dividida entre el número de elementos."
)
)
# Serialización
manuscrito.guardar("mi_agente.json")
manuscrito_cargado = Manuscrito.cargar("mi_agente.json")El Tintero gestiona el historial de conversación. Cada Plumifero tiene su propio Tintero.
from pluma import Tintero
tintero = Tintero(max_mensajes=50) # mantiene los últimos 50 mensajes
# Escribir mensajes directamente
tintero.escribir_sistema("Eres un asistente útil.")
tintero.escribir_usuario("¿Hola?")
tintero.escribir_plumifero("¡Hola! ¿En qué puedo ayudarte?")
# Inspeccionar
mensajes = tintero.leer_todo() # list[dict[str, str]]
sistema = tintero.leer_por_rol(RolMensaje.SISTEMA)
# Limpiar
tintero.vaciar()El Plumifero orquesta todos los componentes: Proveedor + Manuscrito + Tintero + Plumas + Vuelo.
from pluma import Plumifero, ProveedorOllama
agente = Plumifero(
proveedor=ProveedorOllama(modelo="llama3.2"),
manuscrito=manuscrito,
plumas=[pluma_clima, pluma_datetime],
max_pasos=10, # máximo de pasos ReAct
nombre="Agente", # sobrescribe el nombre del manuscrito
)
# Chat conversacional (una llamada al LLM)
respuesta = await agente.dialogar("¿Qué tiempo hace?")
# Con herramientas (ciclo ReAct completo)
resultado = await agente.volar("Dime la temperatura de Madrid ahora")
# Streaming
async for chunk in agente.dictar("Cuéntame algo interesante"):
print(chunk, end="", flush=True)
# Gestión de herramientas
agente.emplumar(pluma_nueva) # añadir (encadenable)
agente.desplumar("nombre") # quitar (encadenable)
# Memoria
agente.historia() # list[dict] con todos los mensajes
agente.reiniciar() # borra la memoria (encadenable)
# Aliases poéticos
await agente.pensar(msg) # = dialogar()
await agente.emprender_el_vuelo(msg) # = volar()
agente.recordar() # = historia()| Nombre LLM | Tipo | Descripción |
|---|---|---|
obtener_datetime |
sync | Fecha y hora actual con formato personalizable |
leer_archivo |
sync | Lee un archivo de texto en UTF-8 |
escribir_archivo |
sync | Escribe/sobreescribe un archivo (crea directorios intermedios) |
listar_directorio |
sync | Lista el contenido de un directorio con emojis |
ejecutar_shell |
async | Ejecuta comandos de shell y captura la salida |
ejecutar_python |
async | Ejecuta código Python en un subproceso aislado |
buscar_web |
async | Busca en DuckDuckGo (sin API key) |
from pluma import (
pluma_datetime, pluma_leer_archivo, pluma_escribir_archivo,
pluma_listar_directorio, pluma_shell, pluma_python, pluma_buscar_web,
)from pluma import crear_plumita, ProveedorOpenAI
plumita = crear_plumita(ProveedorOpenAI(modelo="gpt-4o-mini"))
respuesta = await plumita.dialogar("¿Cómo funciona async/await?")Herramientas: buscar_web, datetime, leer_archivo
from pluma import crear_don_pio, ProveedorAnthropic
don_pio = crear_don_pio(ProveedorAnthropic())
resultado = await don_pio.volar(f"Revisa este código:\n\n{codigo}")Herramientas: leer_archivo, listar_directorio
from pluma import crear_madrugador, ProveedorOllama
madrugador = crear_madrugador(ProveedorOllama())
resultado = await madrugador.volar(
"Lista todos los .py, cuenta las líneas de cada uno y escribe un informe."
)Herramientas: shell, python, leer_archivo, escribir_archivo, listar_directorio, datetime
from pluma import crear_poeta, ProveedorOpenAI
poeta = crear_poeta(ProveedorOpenAI())
resultado = await poeta.volar("Escribe un poema sobre un bug de producción a las 3am.")from pluma import ProveedorOllama
# Requiere: pip install "pluma[ollama]" y Ollama corriendo en localhost
proveedor = ProveedorOllama(modelo="llama3.2")
proveedor = ProveedorOllama(modelo="llama3.2", base_url="http://mi-servidor:11434")from pluma import ProveedorOpenAI
# Requiere: pip install "pluma[openai]" y OPENAI_API_KEY en el entorno
proveedor = ProveedorOpenAI(modelo="gpt-4o-mini")
proveedor = ProveedorOpenAI(modelo="gpt-4o", api_key="sk-...")from pluma import ProveedorAnthropic
# Requiere: pip install "pluma[anthropic]" y ANTHROPIC_API_KEY en el entorno
proveedor = ProveedorAnthropic(modelo="claude-haiku-4-5-20251001")
proveedor = ProveedorAnthropic(modelo="claude-sonnet-4-6", api_key="sk-ant-...")# Chat interactivo con Plumita
pluma conversar
# Chat con proveedor específico
pluma conversar --proveedor openai --modelo gpt-4o-mini
# Chat con herramientas de sistema (shell, Python, archivos)
pluma conversar --con-herramientas
# Don Pío revisa tu código
pluma revisar src/mi_modulo.py
pluma revisar mi_codigo.py --proveedor anthropic
# El Madrugador ejecuta una tarea con herramientas (ReAct)
pluma volar "lista los archivos .py del directorio y cuéntalos"
pluma volar "busca info sobre Python 3.13" --verbose
# Wizard para crear un Manuscrito
pluma crear
pluma crear --guardar mi_agente.json
# Ver todas las plumas predefinidas
pluma lista-plumasresultado = await agente.volar("tarea compleja")
resultado.exitoso # bool
resultado.mensaje_final # str — la respuesta del agente
resultado.estado # EstadoVuelo.COMPLETADO | .LIMITE_PASOS | .ERROR
resultado.tokens_usados # int — tokens consumidos en total
resultado.duracion_segundos # float — tiempo del vuelo
resultado.error # str | None — mensaje de error si falló
# Inspeccionar cada paso del ciclo ReAct
for paso in resultado.pasos:
paso.pensamiento # str | None — razonamiento interno del agente
paso.accion # str | None — nombre de la herramienta usada
paso.argumentos # dict — argumentos pasados a la herramienta
paso.observacion # str | None — resultado de la herramienta
paso.respuesta_final # str | None — respuesta si es el último pasosrc/pluma/
├── __init__.py ← API pública
├── tipos.py ← Tipos base (Mensaje, Paso, ResultadoVuelo...)
├── pluma.py ← Pluma (herramienta)
├── tintero.py ← Tintero (memoria)
├── manuscrito.py ← Manuscrito + manuscritos predefinidos
├── vuelo.py ← Motor ReAct
├── plumifero.py ← Plumifero (agente)
├── cli.py ← CLI con Typer
└── proveedores/
├── base.py ← Clase abstracta Proveedor
├── ollama.py
├── openai.py
└── anthropic.py
predefinidos/
├── plumas.py ← 7 plumas listas para usar
└── plumiferos.py ← 4 fábricas de agentes
git clone https://github.com/casromur/pluma
cd pluma
pip install -e ".[dev,all]"
# Tests
pytest
# Lint
ruff check src/
# Tipos
mypy src/pluma/Los ejemplos en examples/ están listos para ejecutar:
# Chat básico con Plumita
python examples/01_hola_plumita.py
# Don Pío revisa código (crea un archivo temporal y lo analiza)
python examples/02_don_pio_revisor.py
# El Madrugador en acción: ReAct, streaming y herramientas personalizadas
python examples/03_agente_madrugador.pyMIT © 2024 Sr Claude
"Todo gran escritor necesita una gran pluma. El Plumífero es ambos." — Don Pío, amanecer de 2024