feat(short): generación y render de Shorts vía shortsmith
Build & Deploy ResearchOwl / build-and-push (push) Successful in 9s

Añade /generate short_en y /short_spec. El pipeline genera un shot spec
con Haiku, verifica cada cifra, fecha y cita contra los chunks de la
sesión, lo renderiza en shortsmith y entrega el MP4 por Telegram junto
a un informe de claims.

- ShortsmithClient con sondeo y fallback al spec JSON si el render falla
- Contrato de plantillas obtenido de GET /templates, no codificado
- Comprobación de fundamento determinista, sin LLM
- outputs.published_url para enlazar el artículo de Ghost
- Normalización de comillas rectas a tipográficas (ver KNOWN-ISSUES.md)

Lo que no aparece en los chunks se contrasta contra el ejemplo del
prompt: si casa ahí es fuga, no invención, y se informa como tal. El
purgado de sesiones se lleva también su MP4.

La subida a YouTube queda fuera a propósito: fase 3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
ChemaVX
2026-08-01 21:55:42 +00:00
co-authored by Claude Opus 5
parent 8b81ef87e4
commit 20c8d03aa7
27 changed files with 4350 additions and 23 deletions
+44
View File
@@ -81,3 +81,47 @@ Mitigations now in place:
If a research still dies, the scraped sources survive in the DB: `/process`
re-chunks and scores them without re-scraping.
## Un shot spec escrito por Haiku falla de dos maneras concretas
Medido el 2026-08-01 generando Shorts de verdad contra sesiones reales (JAL
1628 #153, Bélgica #158). Las dos están mitigadas, pero conviene saber que
existen porque las dos son silenciosas si nadie mira.
**1. Comillas rectas dentro de una cadena JSON.** El modelo escribe
`"quote_a": ""CREDIBLE PEOPLE. THEY TOLD CLEARLY WHAT THEY SAW.""` y la cadena
se cierra en la segunda comilla: el JSON entero deja de parsear. Se repitió en
los tres intentos aunque el prompt lo prohíbe explícitamente y aunque el error
se le devolvía con el fragmento exacto. **No se arregla insistiendo**: lo
arregla `_typographic_inner_quotes()` en `src/generator/shortspec.py`, que
convierte esas comillas en `“ ”` recorriendo el texto con estado de cadena.
Sólo se ejecuta después de un fallo de parseo, así que un JSON correcto no pasa
por ahí. Además es lo que se quiere dibujar: las citas del canal van con
tipográficas.
**2. Se copian cifras del ejemplo del prompt.** El `examples/jal1628.json` que
va en el prompt como referencia de formato es también una fuente de datos muy
tentadora. En la primera eval dorada, tres claims del spec generado venían del
ejemplo y no de las fuentes: `232 FT` (largo de un 747) y `40 YEARS` no
aparecían en NINGUNO de los 126 chunks de la sesión, y `RARELY, IF EVER` estaba
en 1 chunk que no entró en el top-40 que vio el modelo. La sección 5 del prompt
lo dice ahora en mayúsculas ("FORMAT ONLY … a number copied from here is a
fabrication") y eso bajó de 3 a 1.
El ejemplo se queda con cifras reales a propósito — uno sintético enseña peor
la forma —, así que la defensa es estructural: `check_grounding()` contrasta lo
que no encuentra en los chunks **contra el propio ejemplo**, y lo que casa ahí
sale en el informe como `🧪 copiado del EJEMPLO del prompt (fuga, no
invención)`. Son dos diagnósticos y piden dos acciones: una invención hay que
verificarla, una fuga hay que borrarla.
Corolario: **el informe de claims no es decorativo**. Si algún día se manda el
vídeo sin él, se estará publicando lo que el modelo recuerde del ejemplo.
## El comprobador de fundamento sólo mira los chunks que vio el modelo
`check_grounding()` compara contra los mismos ~40 chunks que se metieron en el
prompt, no contra los 126 de la sesión. Es deliberado — la pregunta es "¿lo
sacó de lo que le dimos?" — pero produce falsos positivos cuando el dato existe
en la sesión y no entró en el top-k. Un falso positivo cuesta un vistazo; un
falso negativo cuesta la credibilidad del canal.