Cómo funciona

Cómo se escribe de verdad un documento largo

Un informe de cuarenta páginas no es una respuesta muy larga. Es un trabajo en segundo plano con una máquina de estados — esquema, preguntas, investigación, escritura sección a sección — donde cada paso se puede repetir tras una caída sin duplicar trabajo ni quemar presupuesto, donde los desplazamientos de una edición los calcula el código y no los elige el modelo, y donde se conserva cada versión.

Una respuesta corriente es una petición al modelo. Un documento no: dura de minutos a decenas de minutos, tiene que sobrevivir a que cierres la pestaña y tiene que mantenerse coherente a lo largo de cuarenta páginas que ninguna petición podría contener. Por eso se ejecuta como un trabajo en segundo plano con una máquina de estados, y casi todas las decisiones de diseño que siguen salen de una sola pregunta: ¿qué pasa si esto muere a medias?

Los pasos

PasoQué haceSi muere a medias
Elegir el tipoEncajar la petición en uno de tus tipos de documentoSe repite y sobrescribe
EsquemaProducir las seccionesFilas insertadas ignorando conflictos
PreguntarPedirte lo que necesita, una vez, antes de escribirEspera: tu respuesta y el despertar del trabajo son una misma transacción
InvestigarReunir hechos por sección, en rondasContinúa en la ronda siguiente
EscribirUna sección cada vezReclama una sección, la escribe, sigue
CerrarTotales, aviso, exportación disponibleIdempotente

El paso que mejor explica el diseño es investigar, porque es el único que de verdad no puede ser idempotente: repite la misma ronda y las búsquedas pueden devolver páginas distintas y las frases extraídas pueden variar. Así que en lugar de idempotencia usa otras dos cosas: los hechos son únicos por (documento, sección, hecho) tras normalizar, de modo que un repetido no entra dos veces; y el contador de rondas se incrementa en la misma transacción que los hechos de esa ronda.

La consecuencia merece decirse claramente: un trabajo que se cae una y otra vez en la ronda 3 sigue desde la ronda 4. Sin eso, cada reinicio volvería a empezar en la ronda 1, y nada acota cuántas veces puede reiniciarse un proceso. Un bucle de caídas no puede quemarte el presupuesto.

Por qué una sección cada vez

Cada sección se reclama con un compare-and-set (ready → writing) y se escribe en una transacción que guarda a la vez el texto, la versión y los contadores. Eso es lo que convierte «el servidor se reinició a mitad del documento» en un no-evento: las secciones terminadas no se tocan y la que estaba en vuelo se vuelve a reclamar en lugar de quedar escrita a medias dos veces.

También mantiene pequeña la petición. Una sección se escribe desde su propio encargo, sus propios hechos y un contexto corto — no desde el documento entero. Esto importa más de lo que parece: medimos cómo se desploma la fiabilidad de las llamadas a herramientas según crece el material, de 7 de 8 llamadas con poco material a 2 de 8 y luego 0 de 8 pasadas unas 5.000 palabras. Un prompt que lo contiene todo no produce una sección mejor: produce una sección que recita el material en vez de seguir el encargo.

Editar: los desplazamientos vienen del código

Selecciona un pasaje en el lienzo, di qué quieres cambiar y solo ese pasaje se sustituye. El mecanismo es deliberadamente estrecho:

  1. El cliente envía un rango de caracteres — [start, end).
  2. El servidor toma body[start:end] como fragmento a cambiar.
  3. El modelo recibe ese fragmento más un contexto acotado y devuelve solo el texto de reemplazo.
  4. El servidor empalma: body[:start] + reemplazo + body[end:].

Nunca se le pide el rango al modelo, y nunca devuelve un diff. Un modelo al que se le pide un rango de edición no falla en voz alta cuando duda: devuelve un rango que parece del todo razonable, y una frase desaparece en silencio de tu documento. Los rangos vienen del troceador que renderizó el texto, así que son exactos por construcción.

Si la sección cambió por debajo — porque el agente la estaba reescribiendo en ese momento — la edición devuelve un conflicto en vez de sobrescribir. Tú decides qué versión sobrevive; esa decisión te corresponde, y la alternativa es enterarte días después.

Los hechos, y qué pasa con un número sin fuente

Las secciones se escriben desde una tabla de hechos, no desde la memoria del modelo. Cada hecho se guarda con la fuente de la que salió, así que cualquier cifra del documento final se puede rastrear hasta una página o un pasaje.

Tras una reescritura, el código revisa el resultado: cifras, códigos y nombres propios que no aparecen en el material citado quedan marcados. Si aparece alguno, el pasaje se regenera una vez; si siguen ahí, el pasaje vuelve marcado y sin aplicarse automáticamente. Conviene señalar que esta comprobación no se hace preguntándole al modelo: preguntado si un pasaje está respaldado, dirá que sí mientras mira material que lo contradice.

Versiones

Cada cambio en una sección es una versión: el primer borrador, tus ediciones manuales, cada reescritura pedida, cada restauración. Restaurar una versión antigua añade una versión en lugar de borrar nada, de modo que el historial es el registro de lo que ocurrió y no el de lo que sobrevivió.

Qué aparece en el chat

La escritura deja tres tipos de mensaje en la conversación, y ninguno lo escribe el modelo:

  • Empezando — el título y cuántas secciones, publicado cuando el trabajo empieza de verdad a gastar tokens, no cuando se hizo la petición.
  • Actualizado — qué secciones cambiaron y qué les pasó, publicado cuando un turno modificó realmente el documento.
  • Terminado — secciones, palabras, tiempo transcurrido.

Los números de los tres se calculan desde la base de datos. No es una preferencia de estilo: un modelo al que se le pide que informe de lo que acaba de hacer producirá un relato seguro, concreto y a veces falso — incluido «ya he añadido ese gráfico al documento» en un turno en el que el documento no cambió. Lo que hizo el sistema lo cuenta el sistema.

Límites que conviene saber

  • Se ejecuta un worker de escritura por base de datos. Los trabajos se reclaman con FOR UPDATE SKIP LOCKED, así que la semántica ya tolera varios workers, pero el despliegue actual ejecuta uno.
  • La cancelación tiene dos mitades: una en proceso que detiene rápido el paso actual, y una marca persistida en la fila del trabajo para que un trabajo que sobreviva al proceso también pare.
  • Si tu agente tiene las fuentes públicas desactivadas, el paso de investigación no llega a internet en absoluto: se salta, no se degrada. El permiso solo puede estrechar.