5 min read · 1,130 words
Consejos prácticos / Gestión de blogs / Python · API
Aprox. 2400 caracteres
Al escribir artículos de blog con LLM, el patrón más común es escribir "escribe una guía sobre este tema" en la ventana de chat de ChatGPT o Claude, copiar manualmente el HTML recibido y pegarlo en la interfaz de administración de Blogger. Esto toma unos 5 minutos, y hay que configurar las etiquetas, la meta descripción y las categorías manualmente cada vez. Hemos creado un endpoint de desvío que envía la salida del chat del LLM directamente a un borrador (DRAFT) de Blogger con una sola línea de curl.
Por qué lo creamos
Llamar directamente a las API de los LLM (por ejemplo, Anthropic o Google) acumula costos de tokens, lo que cuesta entre $0.10 y $0.50 por artículo. Para 100 artículos, esto representa entre $10 y $50. Para un blog gestionado por una sola persona, como el nuestro, esto es una carga financiera.
Por otro lado, las suscripciones a ChatGPT Plus, Claude Pro o Gemini Advanced tienen una tarifa plana de $20 al mes. Incluso si escribes 100 artículos al mes, el costo adicional es cero. Sin embargo, el flujo de trabajo de copiar y pegar desde la ventana del chat a Blogger es tan lento que al final terminas volviendo a las llamadas de API.
Solución: Envías el HTML recibido del chat del LLM a un endpoint de publicación de la webapp en una sola línea → pasa por la inyección automática de etiquetas, metadatos y gráficos, y la cadena de hooks de validación SEO → se convierte en un borrador (DRAFT) de Blogger. Lo único que tiene que hacer el usuario es recibir la respuesta en el chat y ejecutar curl una vez.
Con una tarifa plana de $20 al mes, puedes publicar 100 artículos pasando por toda la cadena de hooks de publicación de la webapp (desinfección, control de calidad y 5 canales de indexación).
Cómo funciona
Existe un endpoint independiente dentro de la webapp: POST /api/writer/external-publish.
Estructura de la solicitud (Request shape)
{
"title": "Título del artículo",
"content_html": "<p>HTML del cuerpo...</p><h2>Sección</h2>...",
"label": "Consejos prácticos",
"post_type": "guide",
"primary_keyword": "palabra clave",
"meta_description": "Meta descripción de 100-160 caracteres"
}
Solo tienes que insertar el HTML recibido del chat del LLM directamente en content_html y completar los otros 5 campos.
Procesamiento en el servidor
- Autenticación: Limitado a localhost (127.0.0.1). Sin exposición externa.
- Normalización de etiquetas (Label normalize): Mapea automáticamente variaciones como
["활용 팁", "사용팁", "tips"]a una de las 6 etiquetas canónicas del sitio. Si no es una etiqueta conocida, responde con{"error":"unknown_label","allowed":["..."]}. - Verificación de HTML: Elimina etiquetas
e, y convierte automáticamente tablas sin formato (Generación automática de plantillas de prompts
Al llamar a
GET /api/writer/external-publish/prompt-template?topic=foo&label=활용+팁, se devuelve un bloque de prompt para el LLM adaptado al tono y la estructura del sitio. Solo tienes que pegarlo directamente en la ventana de chat de ChatGPT o Claude y recibir la respuesta.Impacto real
- Costo de la API de LLM: de $0.10-0.50 por artículo × 100 artículos al mes → tarifa plana de $20 al mes (ChatGPT Plus)
- Tiempo de publicación por artículo (Respuesta del LLM → Borrador de Blogger): de 5 minutos a 30 segundos
- Tasa de aprobación de la cadena de hooks de publicación: 47 de 47 artículos de LLM externos (promedio de aprobación de 15/15 en
publish_quality_gate) - Errores de etiquetado incorrecto: promedio de 3 casos al mes antes de la implementación → 0 casos (gracias a la normalización canónica)
- Patrón de uso más común: Chat en ChatGPT → Copiar respuesta → Una línea de curl en la terminal
Procesar 100 artículos al mes con una sola suscripción a ChatGPT Plus reduce el costo anual de LLM a $240 (suscripción) frente a $1,200-$6,000 (llamadas de API). Para un administrador independiente, esta es una diferencia que no se puede ignorar.
Métodos de validación
Tres tipos de validación:
Tasa de aprobación de la cadena de hooks de publicación
Se registraron todos los resultados de
publish_quality_gatetras publicar 47 artículos de LLM externos. 47/47 obtuvieron una puntuación ≥ 90, con un promedio de 95. El hookpublish_sanitizerlimpia automáticamente el HTML descuidado de la salida del LLM (residuos de Markdown o etiquetasvacías) para cumplir con la puntuación requerida.Prueba de regresión de normalización de etiquetas
Se implementó un conjunto de pruebas unitarias con un "golden set" de 30 casos (entradas de etiquetas con variaciones). Mapeo exitoso de 30/30 casos, como
"tips"→"활용 팁","사용팁"→"활용 팁","compare"→"비교", etc. Las etiquetas desconocidas devuelven una respuesta 400.Control de seguridad (Spot check)
Al llamar al endpoint desde una IP externa, devuelve un error 403. Se fuerza la restricción solo a localhost. 5/5 pruebas aprobadas.
Cómo construirlo
La clave está en una sola función de endpoint de FastAPI.
from fastapi import FastAPI, HTTPException, Request from pydantic import BaseModel app = FastAPI() LABEL_MAP = { "활용 팁": "활용 팁", "tips": "활용 팁", "사용팁": "활용 팁", "비교": "비교", "compare": "비교", "가이드": "가이드", "howto": "가이드", # ... mapeo de etiquetas canónicas del sitio } class ExternalPublishBody(BaseModel): title: str content_html: str label: str post_type: str = "guide" primary_keyword: str = "" meta_description: str = "" @app.post("/api/writer/external-publish") async def external_publish(body: ExternalPublishBody, request: Request): # solo localhost client_ip = request.client.host if request.client else "" if client_ip not in ("127.0.0.1", "localhost", "::1"): raise HTTPException(403, "localhost-only") # normalización de etiquetas canonical = LABEL_MAP.get(body.label.strip().lower()) if not canonical: raise HTTPException(400, f"unknown_label allowed={list(LABEL_MAP.values())}") # reenviar a la cadena de hooks publish_post from webapp.routers.blogger import publish_post, PublishPostBody pp = PublishPostBody( title=body.title, content=body.content_html, labels=[canonical], is_draft=True, meta_description=body.meta_description, primary_keyword=body.primary_keyword, ) return await publish_post(pp)Después de recibir el artículo en el chat de ChatGPT o Claude, ejecuta la llamada curl:
curl -X POST http://127.0.0.1:8766/api/writer/external-publish \ -H "Content-Type: application/json" \ -d '{ "title": "Artículo de guía sobre este tema", "content_html": "<p>HTML del cuerpo...</p>", "label": "Consejos prácticos", "meta_description": "Meta de 100-160 caracteres..." }'Respuesta:
{"ok": true, "post_id": "1234567...", "blogger_url": "https://..."}Resumen: Suscripción plana de LLM + endpoint de una sola línea de curl = publicación de 100 artículos al mes pasando por toda la cadena de hooks. Solo toma 30 segundos desde el chat, la respuesta, hasta la publicación. Puedes maximizar la generación de contenido dentro de los límites de presupuesto de un administrador independiente.
Category Coverage Notice
This article follows our label-specific editorial criteria. Details: