4 min read · 1,079 words
Dicas práticas / Gestão de blog / Python · API
Cerca de 2.400 caracteres
Ao escrever posts de blog usando LLMs, o padrão mais comum é digitar "escreva um guia sobre este assunto" no chat do ChatGPT ou do Claude, copiar manualmente o HTML recebido e colá-lo na interface de administração do Blogger. Isso leva cerca de 5 minutos, e você precisa configurar as tags, a meta descrição e a categoria manualmente todas as vezes. Para resolver isso, criamos um endpoint de desvio que envia a saída do chat do LLM diretamente para o rascunho (DRAFT) do Blogger com uma única linha de curl.
Por que criamos isso
Chamar diretamente as APIs de LLM (por exemplo, Anthropic ou Google) acumula custos de tokens, custando cerca de $0,10 a $0,50 por post. Para 100 posts, seriam de $10 a $50. Para um blog administrado por uma única pessoa, como o nosso site, isso pesa no bolso.
Por outro lado, as assinaturas do ChatGPT Plus, Claude Pro ou Gemini Advanced custam um valor fixo de $20 por mês. Mesmo escrevendo 100 posts por mês, o custo adicional é zero. No entanto, o fluxo de trabalho de copiar e colar do chat para o Blogger é tão lento que acabamos voltando a usar as chamadas de API.
Solução: ao enviar o HTML recebido no chat do LLM para o endpoint de publicação do webapp com uma única linha de comando, ele passa por todo o fluxo de validação (injeção automática de tags/meta/gráficos e hook chain de validação de SEO) e vai direto para o rascunho (DRAFT) do Blogger. Tudo o que o usuário precisa fazer é obter a resposta no chat e executar o curl uma vez.
Com uma taxa fixa de $20 por mês, você pode publicar 100 posts passando por toda a hook chain de publicação do webapp (higienização, barreira de qualidade e 5 canais de indexação).
Como funciona
Existe um endpoint separado dentro do webapp: POST /api/writer/external-publish.
Estrutura da requisição
{
"title": "Título do post",
"content_html": "<p>HTML do corpo...</p><h2>Seção</h2>...",
"label": "Dicas práticas",
"post_type": "guide",
"primary_keyword": "palavra-chave",
"meta_description": "Meta descrição de 100 a 160 caracteres"
}
Basta inserir o HTML recebido no chat do LLM diretamente em content_html e preencher os outros 5 campos.
Processamento no servidor
- Autenticação — Limitado ao localhost (127.0.0.1). Sem exposição externa.
- Normalização de tags (Label normalize) — Mapeia automaticamente variações como
["활용 팁", "사용팁", "tips"]para uma das 6 tags canônicas do site. Se não for uma tag conhecida, retorna o erro{"error":"unknown_label","allowed":["..."]}. - Verificação de HTML — Remove tags
e(strip) e converte tabelas brutas (Geração automática de templates de prompt
Ao chamar
GET /api/writer/external-publish/prompt-template?topic=foo&label=활용+팁, o sistema retorna um bloco de prompt de LLM adaptado ao tom e à estrutura do site. Basta colá-lo diretamente no chat do ChatGPT ou do Claude e obter a resposta.Resultados práticos
- Custo de API de LLM: de $0,10 a $0,50 por post × 100 posts por mês → Valor fixo de $20 por mês (ChatGPT Plus)
- Tempo de publicação de 1 post (Resposta do LLM → Rascunho no Blogger): de 5 minutos para 30 segundos
- Taxa de aprovação na hook chain de publicação: 47 de 47 posts gerados por LLMs externos (média de aprovação de 15/15 no
publish_quality_gate) - Erros de tags incorretas: média de 3 casos por mês antes da implementação → 0 casos (graças à normalização canônica)
- Padrão de uso mais comum: Chat no ChatGPT → Copiar resposta → Uma linha de curl no terminal
Processar 100 posts por mês com uma única assinatura do ChatGPT Plus reduz o custo anual de LLM para $240 (assinatura) contra $1.200 a $6.000 (chamadas de API). Para um administrador solo, essa é uma diferença enorme.
Métodos de validação
Três frentes de validação.
Taxa de aprovação na hook chain de publicação
Registramos todos os resultados do
publish_quality_gateapós publicar 47 posts gerados por LLMs externos. Todos os 47 posts obtiveram pontuação ≥ 90, com média de 95. Opublish_sanitizerlimpou automaticamente o HTML malformado gerado pelo LLM (como resquícios de Markdown ou tagsvazias) para atingir a pontuação necessária.Teste de regressão de normalização de tags
Criamos um conjunto de testes unitários com 30 casos de teste (entradas de tags variadas) usando um golden set. Mapeamentos como
"tips"→"활용 팁","사용팁"→"활용 팁","compare"→"비교"funcionaram perfeitamente em 30 de 30 casos. Tags desconhecidas retornaram resposta 400.Verificação pontual de segurança (Spot check)
Chamadas ao endpoint a partir de IPs externos retornaram erro 403. Restrição estrita ao localhost. 5 de 5 testes aprovados.
Como implementar
O núcleo do sistema é uma única função de endpoint no FastAPI.
from fastapi import FastAPI, HTTPException, Request from pydantic import BaseModel app = FastAPI() LABEL_MAP = { "활용 팁": "활용 팁", "tips": "활용 팁", "사용팁": "활용 팁", "비교": "비교", "compare": "비교", "가이드": "가이드", "howto": "가이드", # ... mapeamento de tags canônicas do site } 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): # apenas 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") # normalização de tags canonical = LABEL_MAP.get(body.label.strip().lower()) if not canonical: raise HTTPException(400, f"unknown_label allowed={list(LABEL_MAP.values())}") # encaminha para a hook chain 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)Depois de receber o texto no chat do ChatGPT ou do Claude, execute a chamada curl:
curl -X POST http://127.0.0.1:8766/api/writer/external-publish \ -H "Content-Type: application/json" \ -d '{ "title": "Guia sobre este assunto", "content_html": "<p>HTML do corpo...</p>", "label": "Dicas práticas", "meta_description": "Meta descrição de 100 a 160 caracteres..." }'Resposta:
{"ok": true, "post_id": "1234567...", "blogger_url": "https://..."}Resumo: Assinatura fixa de LLM + endpoint de uma linha de curl = 100 posts publicados por mês passando por toda a hook chain. Apenas 30 segundos do chat à resposta e à
Category Coverage Notice
This article follows our label-specific editorial criteria. Details: