🐓 Un micro-framework para agentes de IA. Escribe código que escribe código.
Project description
🐓 pluma
"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
¿Por qué 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 | ✅ | ✅ | ✅ |
Instalación
# 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+
Quickstart — 5 minutos
Chat simple
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())
Agente con herramientas (ReAct)
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())
Agentes predefinidos (zero-config)
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)
Conceptos clave
🪶 Pluma — la herramienta
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)
📜 Manuscrito — el alma
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")
🫙 Tintero — la memoria
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()
🦜 Plumifero — el agente
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()
Plumas predefinidas
| 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,
)
Agentes predefinidos
Plumita — asistente conversacional
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
Don Pío — revisor de código
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
El Madrugador — automatización
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
La Pluma Lírica — poeta
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.")
Proveedores
Ollama (local, gratis)
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")
OpenAI
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-...")
Anthropic
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-...")
CLI
# 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-plumas
Inspeccionar ResultadoVuelo
resultado = 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 paso
Estructura del proyecto
src/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
Desarrollo
git clone https://github.com/casromur/pluma
cd pluma
pip install -e ".[dev,all]"
# Tests
pytest
# Lint
ruff check src/
# Tipos
mypy src/pluma/
Ejemplos
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.py
Licencia
MIT © 2024 Sr Claude
"Todo gran escritor necesita una gran pluma. El Plumífero es ambos." — Don Pío, amanecer de 2024
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pluma_ai_framework-0.1.0.tar.gz.
File metadata
- Download URL: pluma_ai_framework-0.1.0.tar.gz
- Upload date:
- Size: 76.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a915a66ae9a9aeaf5fcd1e2966fd1962c811fdb704e962f5fcaef1e083e7a12
|
|
| MD5 |
7adb94dfe25f1562ba244edaf478e66b
|
|
| BLAKE2b-256 |
b497f029d4af06d82b7b379d5a36b62c967d394a33f2d8885ded8d25fe510fe0
|
File details
Details for the file pluma_ai_framework-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pluma_ai_framework-0.1.0-py3-none-any.whl
- Upload date:
- Size: 45.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f4cc483b2669a9d01d42dcd4ae6d8e54d96470a726bdfa92f1e6be945f57d2a9
|
|
| MD5 |
05b70ebb108fd2e9e5833404d10affcf
|
|
| BLAKE2b-256 |
2020f7e54580b8045149e39ceefc5a65bf5a843dfefcdb59a30b89c8183dd9dc
|