Skip to main content

🐓 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pluma_ai_framework-0.1.0.tar.gz (76.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pluma_ai_framework-0.1.0-py3-none-any.whl (45.4 kB view details)

Uploaded Python 3

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

Hashes for pluma_ai_framework-0.1.0.tar.gz
Algorithm Hash digest
SHA256 4a915a66ae9a9aeaf5fcd1e2966fd1962c811fdb704e962f5fcaef1e083e7a12
MD5 7adb94dfe25f1562ba244edaf478e66b
BLAKE2b-256 b497f029d4af06d82b7b379d5a36b62c967d394a33f2d8885ded8d25fe510fe0

See more details on using hashes here.

File details

Details for the file pluma_ai_framework-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pluma_ai_framework-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f4cc483b2669a9d01d42dcd4ae6d8e54d96470a726bdfa92f1e6be945f57d2a9
MD5 05b70ebb108fd2e9e5833404d10affcf
BLAKE2b-256 2020f7e54580b8045149e39ceefc5a65bf5a843dfefcdb59a30b89c8183dd9dc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page