Traducción automática
Esta página fue traducida automáticamente por IA y puede contener errores. Si algo no está claro, consulte la fuente en inglés.
Diseño del archivo¶
Todo lo que pplx-export descarga termina en un único árbol de salida — ./web_archive/ por defecto
(anular con --out). Esta página es una guía de lectura de ese árbol: qué es cada directorio y archivo,
qué claves lleva thread.json y cómo la herramienta mantiene un directorio por hilo cuando una
conversación continúa a lo largo de varios días. Todo es generado por la herramienta; la profundidad del mecanismo reside en
Modelo de datos y contrato de directorio y
Pipeline de exportación.
a. El árbol de salida¶
web_archive/
├── alice/ # one folder per account (author display name)
│ ├── search/ # mode: search | deep-research | computer | council | study
│ │ └── 2026-07-18_quantum-computing-survey_1a2b3c4d/ # one directory per thread
│ │ ├── thread.json # metadata + optional registries
│ │ ├── conversation.md # compact read: per-turn Query/Answer
│ │ ├── turns/
│ │ │ ├── turn_0001.md # full read: complete work-process detail
│ │ │ └── ...
│ │ ├── sources.json # thread-wide citations (deduped by url)
│ │ ├── sources.md
│ │ ├── report.md # deep-research report (only when one exists)
│ │ ├── raw_entries.json # plain API response, verbatim (always present)
│ │ ├── raw_blocks.json # schematized API response (absent for search)
│ │ └── assets/
│ │ ├── assets_manifest.json # versioned manifest
│ │ └── files/ # downloaded asset bodies
│ ├── deep-research/ ...
│ └── computer/ ...
├── index/ # state files and indexes (see below)
├── relations/ # edges.jsonl + graph.md (rebuilt by `pplx-export relations`)
├── crosscheck/ # cross-validation reports (manual/review artifacts)
└── bob/ ...
b. El directorio del hilo¶
Cada hilo recibe exactamente un directorio, calculado por thread_dir_for (fs_writer.py:58-72):
<account display name>/<mode>/<YYYY-MM-DD>_<title-slug>_<uuid8>/
| Componente | Fuente | Notas |
|---|---|---|
<account display name> |
autor del hilo, mediante author_folder → _safe_folder (fs_writer.py:40-51) |
los separadores de ruta y caracteres ilegales en Windows (:*?"<>\|) se convierten en _; ./.. rechazados (protección de recorrido de ruta para espacios compartidos); todo lo demás — incluidos espacios — se conserva |
<mode> |
detect_mode |
uno de los cinco modos; consulte Modos de conversación |
<YYYY-MM-DD> |
thread.json lastUpdated prefijo de fecha |
la fecha de última actualización de la plataforma, no la fecha de exportación — se mueve cuando se actualiza un hilo continuado (ver migración abajo) |
<title-slug> |
slugify(title) (normalize.py:261-263) |
máximo 40 caracteres, caracteres no alfabéticos → -, vacío → untitled |
<uuid8> |
web_uuid[:8] |
primeros 8 caracteres del UUID del hilo — el ancla de identidad del directorio |
c. Archivos en un directorio de hilo¶
c.1 thread.json — metadatos y registros¶
Escrito por write_thread (fs_writer.py:224-253). Claves siempre presentes:
| Clave | Contenido |
|---|---|
web_uuid |
entryUUID web — el UUID en la URL del hilo; la identidad del hilo |
psc_uuid |
context_uuid de la plataforma (nullable; del primer turno no vacío) — el ID dual usado por los índices de espacio |
url |
URL canónica del hilo |
title |
título del hilo |
mode |
modo detectado (search / deep-research / computer / council / study) |
author |
nombre para mostrar de la cuenta del autor |
export_via |
nombre de usuario de la cuenta que realizó la exportación — importante para hilos de espacio compartido exportados a través de otra cuenta |
space |
{"uuid", "title", "slug"} o null |
lastUpdated |
marca de tiempo de última actualización de la plataforma (contrato de comparación automática para sincronización incremental) |
threadAccess |
indicador de acceso de la plataforma |
n_turns |
recuento de turnos |
n_sources |
recuento de citas a nivel de hilo |
metadata |
thread_metadata de la respuesta de la API, textual |
report_info |
{"title", "file_name", "url"} o null |
exported_at |
hora de exportación (UTC ISO 8601) |
Claves opcionales — ausentes cuando no hay nada que registrar:
| Clave | Añadida cuando | Contenido |
|---|---|---|
interruptions |
cualquier flujo de trabajo no completado (fs_writer.py:242-244) |
lista de {location, kind, headline, status}; consulte Modos de conversación — Interrupciones |
answer_variants |
se detecta una variante de reescritura de respuesta (fs_writer.py:247-252) |
side_by_side_metadata reducido que localiza campos; consulte Modos de conversación — Variantes de reescritura de respuesta |
remote_deleted |
pplx-export sync-deleted --online confirma eliminación remota |
marca de tiempo de tumba, escrita en el lugar, idempotente (el valor existente nunca se sobrescribe; sync_deleted_cmd.py:215-244) — el archivo local en sí se conserva |
c.2 conversation.md — la lectura compacta¶
render_conversation (render.py:641): encabezado de título (modo / autor / turnos / recuento de citas),
luego por turno un par ### Query + ### Answer con respuestas completas y, cuando está presente, el
apéndice de tareas en segundo plano a nivel de hilo al final. Este es el archivo que se debe abrir primero; los procesos
de trabajo por turno están en turns/.
c.3 turns/turn_NNNN.md — la lectura completa¶
render_turn (render.py:596): un archivo por turno (turn_0001.md …), cada uno con el proceso de trabajo
completo — pasos, llamadas a herramientas, ejecuciones de subagentes, tablas, citas por turno. Cuando el recuento de turnos
de un hilo se reduce, los archivos turn_*.md obsoletos de números altos se eliminan, pero los archivos no tocados
conservan su mtime (fs_writer.py:287-301).
c.4 sources.json / sources.md¶
Citas a nivel de hilo, deduplicadas por URL (fs_writer.py:270-278). sources.json es
{"count", "sources": [{"name", "url", "snippet", "timestamp"}]}; sources.md es la misma
lista como una lista de enlaces Markdown numerada.
c.5 report.md¶
El producto del informe de investigación profunda, escrito solo cuando el hilo lleva uno
(fs_writer.py:308-316): título del informe, el nombre de archivo del producto original, luego el informe completo
en Markdown.
c.6 raw_entries.json / raw_blocks.json — fidelidad sin procesar¶
Las respuestas de la API, conservadas textualmente antes de cualquier análisis (fs_writer.py:257-266):
raw_entries.json— la respuesta simple:{"thread_metadata", "entries", "background_entries"}. Siempre presente.raw_blocks.json— la respuesta esquematizada, misma forma. Ausente para hilossearch(sin recuperación de bloques); recuperada para los otros cuatro modos, y también como alternativa cuando cada señal de detección de modo falta.
Estos dos archivos son el ancla de fidelidad del archivo: el análisis, la representación y los registros pueden reconstruirse a partir de ellos sin conexión, con cero red. Consulte Operaciones sin conexión.
c.7 assets/ — productos y su manifiesto¶
Los productos descargables (archivos de modo Computer y cualquier otro activo que la API enumere) se obtienen
de URL firmadas de CloudFront en assets/files/; la extensión se decide en el momento de la descarga
a partir de la ruta URL, bytes mágicos de contenido o tipo de activo. assets/assets_manifest.json
(fs_writer.py:320-330) registra cada versión:
{"count": 2, "files": [{"filename": "analysis.xlsx", "n_versions": 2,
"versions": [{"uuid": "…", "asset_type": "XLSX_FILE", "version": "v1",
"created_at": "…", "downloaded_to": "…"}]}]}
count es siempre el número total de versiones (Σ len(versions)), no el número de grupos de archivos
— use len(files) para eso.
d. La capa index/¶
web_archive/index/ contiene el estado administrado por la herramienta e índices — no editar manualmente:
| Archivo | Escrito por | Semántica |
|---|---|---|
library_<account>.json |
pplx-export index (index_cmd.py:17-43) |
índice completo de hilos de la cuenta (GraphQL); entrada para lotes / programación / índices de espacio |
batch_state.json |
BatchState (state.py) |
punto de control reanudable: uuid → estado (ok/error/expired/deleted) + lastUpdated; escrituras atómicas; archivos corruptos respaldados automáticamente como .corrupt-<ts> |
.cookies.json |
caché de cookies (common.py:111, common.py:150) |
caché de cookies con frescura de 12h con fuente y correo electrónico de la cuenta; escrito 0o600 luego reemplazado atómicamente (credenciales de sesión, solo legible por el propietario) |
space_<slug>.json |
pplx-export space-index (spaces_cmd.py:106-167) |
lista de hilos por espacio, incluyendo la asignación de ID dual context_uuid |
space_meta.json |
pplx-export spaces --fetch-meta (spaces_cmd.py:299-330) |
caché de propietario/miembro del espacio reutilizado en reconstrucciones |
credit_usage_<account>.json |
pplx-export usage-backfill (usage_backfill_cmd.py:17) |
uso de crédito por hilo (idempotente, reanudable, vaciado cada 25 entradas) |
cron_snippet.txt |
pplx-export schedule (scheduler.py:48-78) |
fragmento de invocación cron (rutas absolutas) |
answer_variants_log.jsonl |
variant_log.append_registry (variant_log.py:76) |
registro central de variantes de reescritura de respuesta, deduplicado por (hilo, entrada), idempotente |
logs/ |
--log-file (common.py:218-229) |
registros DEBUG completos |
e. La capa spaces/¶
pplx-export spaces agrega index/library_*.json en un índice de espacio (spaces_cmd.py:259-389):
un <slug>.md por espacio (cuentas participantes, encabezado de propietario/miembro, tabla de hilos,
backlinks de ubicación de exportación) más un registro spaces.json.
Ubicación de salida
spaces/ se escribe en relación con el directorio de trabajo actual (spaces_cmd.py:332) — no
sigue a --out. No editar manualmente: la próxima reconstrucción lo sobrescribe.
f. Continuación entre días: migración de directorio por identidad UUID¶
El nombre del directorio incorpora la fecha lastUpdated, por lo que cuando continúa un hilo en un día posterior,
el cálculo ingenuo produce un directorio nuevo. El escritor evita duplicados por identidad UUID
(thread_dir_for, fs_writer.py:58-72):
- Buscar:
find_thread_dirs(fs_writer.py:74-105) busca en todo el archivo directorios que terminan en_<uuid8>— entre cuentas y modos. Un candidato se acepta solo si suthread.jsonexiste, se analiza y suweb_uuidcoincide exactamente; los directorios faltantes, corruptos o no coincidentes nunca se tocan (es mejor omitir una migración que fusionar incorrectamente). - Fusionar:
_merge_into(fs_writer.py:107-178) fusiona el directorio antiguo en el nuevo — unión de archivos (nada único del directorio antiguo se pierde); mismo nombre + mismo contenido → omitir; conflictos de mismo nombre siempre mantienen el lado de destino (el semánticamente más nuevo), con cada conflicto registrado. Cada archivo copiado se verifica con sha256 antes de eliminar el directorio antiguo; cualquier fallo deja el directorio antiguo intacto y los reintentos son idempotentes. - Limpiar duplicados históricos:
consolidate_uuid(fs_writer.py:180-209) fusiona directorios de fecha duplicados de un UUID en todo el archivo, manteniendo el que tiene el máximolastUpdated— la red de seguridad para duplicados dejados por versiones anteriores.
La misma estrictez de UUID protege los backlinks del índice de espacio: los directorios candidatos con un
thread.json faltante/corrupto/no coincidente nunca se vinculan.
g. Editable manualmente vs. administrado por la herramienta¶
- Administrado por la herramienta (no editar manualmente): todo dentro de los directorios de hilo, más
index/,spaces/yrelations/. Si el contenido es incorrecto, corrija la herramienta y regenere — las correcciones de representación pasan porpplx-export re-render, las correcciones de datos a través del comando de relleno correspondiente (consulte Comandos de mantenimiento) — para que cada artefacto siga siendo reproducible a partir de los datos sin procesar. - Editable manualmente: la documentación y los informes de revisión
web_archive/crosscheck/. Una excepción a nivel de usuario: una respuesta alternativa rescatada manualmente puede registrarse comorewritten_answer_variant.mddentro del directorio del hilo — consulte Modos de conversación — Variantes de reescritura de respuesta.
h. Véase también¶
- Modos de conversación — los cinco modos y lo que produce cada uno
- Sincronización incremental — cómo
lastUpdatedimpulsa las reexportaciones - Comandos de mantenimiento — rerenderizado, rellenos, sincronización de eliminados
- Modelo de datos y contrato de directorio — las dataclasses subyacentes
- Pipeline de exportación — cómo se escriben estos archivos
- Operaciones sin conexión — reconstruir todo desde
raw_*.json