Машинный перевод
Эта страница была автоматически переведена ИИ и может содержать ошибки. Если что-то неясно, обращайтесь к английскому источнику.
Структура архива¶
Все загрузки pplx-export попадают в единое дерево вывода — по умолчанию ./web_archive/
(переопределяется с помощью --out). Эта страница — путеводитель по этому дереву: что представляет собой каждый каталог и файл,
какие ключи несёт thread.json, и как инструмент сохраняет один каталог на поток, когда
беседа продолжается в течение нескольких дней. Всё это создаётся инструментом; механизм подробно описан в
Модель данных и контракт каталога и
Конвейер экспорта.
a. Дерево вывода¶
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. Каталог потока¶
Каждый поток получает ровно один каталог, вычисляемый с помощью thread_dir_for (fs_writer.py:58-72):
<account display name>/<mode>/<YYYY-MM-DD>_<title-slug>_<uuid8>/
| Компонент | Источник | Примечания |
|---|---|---|
<account display name> |
автор потока, через author_folder → _safe_folder (fs_writer.py:40-51) |
разделители путей и символы, недопустимые в Windows (:*?"<>\|), заменяются на _; ./.. отклоняются (защита от обхода пути для общих пространств); всё остальное, включая пробелы, сохраняется |
<mode> |
detect_mode |
один из пяти режимов; см. Режимы беседы |
<YYYY-MM-DD> |
thread.json lastUpdated префикс даты |
дата последнего обновления платформы, не дата экспорта — она изменяется при обновлении продолженного потока (см. миграцию ниже) |
<title-slug> |
slugify(title) (normalize.py:261-263) |
макс. 40 символов, не-словесные символы → -, пусто → untitled |
<uuid8> |
web_uuid[:8] |
первые 8 символов UUID потока — идентификационный якорь каталога |
c. Файлы в каталоге потока¶
c.1 thread.json — метаданные и реестры¶
Записывается с помощью write_thread (fs_writer.py:224-253). Всегда присутствующие ключи:
| Ключ | Содержимое |
|---|---|
web_uuid |
веб- entryUUID — UUID в URL потока; идентификатор потока |
psc_uuid |
платформенный context_uuid (может быть null; из первого непустого оборота) — двойной идентификатор, используемый индексами пространств |
url |
канонический URL потока |
title |
заголовок потока |
mode |
обнаруженный режим (search / deep-research / computer / council / study) |
author |
отображаемое имя автора аккаунта |
export_via |
имя пользователя аккаунта, выполнившего экспорт — важно для потоков из общих пространств, экспортированных через другой аккаунт |
space |
{"uuid", "title", "slug"} или null |
lastUpdated |
временная метка последнего обновления платформы (контракт машинного сравнения для инкрементальной синхронизации) |
threadAccess |
флаг доступа платформы |
n_turns |
количество оборотов |
n_sources |
общее количество цитирований в потоке |
metadata |
thread_metadata из ответа API, дословно |
report_info |
{"title", "file_name", "url"} или null |
exported_at |
время экспорта (UTC ISO 8601) |
Необязательные ключи — отсутствуют, когда нечего записывать:
| Ключ | Добавляется когда | Содержимое |
|---|---|---|
interruptions |
любой незавершённый рабочий процесс (fs_writer.py:242-244) |
список {location, kind, headline, status}; см. Режимы беседы — Прерывания |
answer_variants |
обнаружен вариант перезаписи ответа (fs_writer.py:247-252) |
суженные side_by_side_metadata поля локации; см. Режимы беседы — Варианты перезаписи ответа |
remote_deleted |
pplx-export sync-deleted --online подтверждает удаление на сервере |
временная метка надгробия, записывается на месте, идемпотентно (существующее значение никогда не перезаписывается; sync_deleted_cmd.py:215-244) — сам локальный архив сохраняется |
c.2 conversation.md — компактное чтение¶
render_conversation (render.py:641): заголовок (режим / автор / обороты / количество цитирований),
затем для каждого оборота пара ### Query + ### Answer с полными ответами, и — при наличии —
приложение фоновых задач уровня потока в конце. Это файл для первого открытия; процессы работы с оборотами
находятся в turns/.
c.3 turns/turn_NNNN.md — полное чтение¶
render_turn (render.py:596): один файл на оборот (turn_0001.md …), каждый с полным
рабочим процессом — шаги, вызовы инструментов, запуски под-агентов, таблицы, цитирования на оборот. Когда количество оборотов
потока уменьшается, устаревшие файлы с большими номерами turn_*.md удаляются, но нетронутые файлы сохраняют
своё mtime (fs_writer.py:287-301).
c.4 sources.json / sources.md¶
Цитирования по всему потоку, дедуплицированные по URL (fs_writer.py:270-278). sources.json — это
{"count", "sources": [{"name", "url", "snippet", "timestamp"}]}; sources.md — тот же
список в виде нумерованного списка ссылок Markdown.
c.5 report.md¶
Продукт отчёта глубокого исследования, записывается только если поток его содержит
(fs_writer.py:308-316): заголовок отчёта, исходное имя файла продукта, затем полный Markdown
отчёта.
c.6 raw_entries.json / raw_blocks.json — сырая точность¶
Ответы API, сохранённые дословно до любого разбора (fs_writer.py:257-266):
raw_entries.json— простой ответ:{"thread_metadata", "entries", "background_entries"}. Всегда присутствует.raw_blocks.json— схематизированный ответ, той же формы. Отсутствует для потоковsearch(нет выборки блоков); выбирается для остальных четырёх режимов, а также как запасной вариант, когда отсутствуют все сигналы определения режима.
Эти два файла являются якорем точности архива: разбор, рендеринг и реестры могут быть перестроены из них офлайн, без сети. См. Офлайн-операции.
c.7 assets/ — продукты и их манифест¶
Загружаемые продукты (файлы компьютерного режима и любые другие ресурсы, перечисленные API) извлекаются
из подписанных URL CloudFront в assets/files/; расширение определяется во время загрузки
по пути URL, магическим байтам содержимого или типу ресурса. assets/assets_manifest.json
(fs_writer.py:320-330) записывает каждую версию:
{"count": 2, "files": [{"filename": "analysis.xlsx", "n_versions": 2,
"versions": [{"uuid": "…", "asset_type": "XLSX_FILE", "version": "v1",
"created_at": "…", "downloaded_to": "…"}]}]}
count всегда общее количество версий (Σ len(versions)), а не количество групп
файлов — используйте len(files) для этого.
d. Слой index/¶
web_archive/index/ содержит состояние и индексы, управляемые инструментом — не редактируйте вручную:
| Файл | Записывается | Семантика |
|---|---|---|
library_<account>.json |
pplx-export index (index_cmd.py:17-43) |
полный индекс потоков аккаунта (GraphQL); входные данные для пакетных / планировщика / индексов пространств |
batch_state.json |
BatchState (state.py) |
возобновляемая контрольная точка: uuid → статус (ok/error/expired/deleted) + lastUpdated; атомарные записи; повреждённые файлы автоматически резервируются как .corrupt-<ts> |
.cookies.json |
кэш cookie (common.py:111, common.py:150) |
кэш cookie со свежестью 12 часов с источником и email аккаунта; записывается 0o600 затем атомарно заменяется (учётные данные сессии, доступно только владельцу) |
space_<slug>.json |
pplx-export space-index (spaces_cmd.py:106-167) |
список потоков по пространству, включая отображение двойного ID context_uuid |
space_meta.json |
pplx-export spaces --fetch-meta (spaces_cmd.py:299-330) |
кэш владельца/участника пространства, повторно используемый при перестройках |
credit_usage_<account>.json |
pplx-export usage-backfill (usage_backfill_cmd.py:17) |
использование кредитов по потокам (идемпотентно, возобновляемо, сбрасывается каждые 25 записей) |
cron_snippet.txt |
pplx-export schedule (scheduler.py:48-78) |
фрагмент вызова cron (абсолютные пути) |
answer_variants_log.jsonl |
variant_log.append_registry (variant_log.py:76) |
центральный реестр вариантов перезаписи ответа, дедуплицированный по (thread, entry), идемпотентен |
logs/ |
--log-file (common.py:218-229) |
полные журналы DEBUG |
e. Слой spaces/¶
pplx-export spaces агрегирует index/library_*.json в индекс пространств (spaces_cmd.py:259-389):
один <slug>.md на пространство (участвующие аккаунты, заголовок владелец/участник, таблица потоков,
обратные ссылки на местоположение экспорта) плюс реестр spaces.json.
Расположение вывода
spaces/ записывается относительно текущего рабочего каталога (spaces_cmd.py:332) — он
не следует за --out. Не редактируйте вручную: следующая перестройка перезапишет.
f. Продолжение через несколько дней: миграция каталога по UUID-идентичности¶
Имя каталога содержит дату lastUpdated, поэтому при продолжении потока в другой день
наивное вычисление даёт новый каталог. Запись предотвращает дубликаты по UUID-идентичности
(thread_dir_for, fs_writer.py:58-72):
- Поиск:
find_thread_dirs(fs_writer.py:74-105) ищет во всём архиве каталоги, оканчивающиеся на_<uuid8>— между аккаунтами и режимами. Кандидат принимается только если егоthread.jsonсуществует, разбирается, и егоweb_uuidточно совпадает; отсутствующие, повреждённые или несовпадающие каталоги никогда не трогаются (лучше пропустить миграцию, чем неправильно объединить). - Слияние:
_merge_into(fs_writer.py:107-178) объединяет старый каталог в новый — объединение файлов (ничего уникального из старого каталога не теряется); одинаковое имя + одинаковое содержимое → пропуск; конфликты одинаковых имён всегда сохраняют целевую сторону (семантически более новую), каждый конфликт регистрируется. Каждый скопированный файл проверяется по sha256 перед удалением старого каталога; любой сбой оставляет старый каталог нетронутым, и повторные попытки идемпотентны. - Очистка исторических дубликатов:
consolidate_uuid(fs_writer.py:180-209) объединяет дублирующиеся каталоги дат одного UUID в масштабе архива, сохраняя каталог с максимальнымlastUpdated— подстраховка для дубликатов, оставленных старыми версиями.
Та же строгость UUID защищает обратные ссылки индекса пространств: каталоги-кандидаты с
отсутствующим/повреждённым/несовпадающим thread.json никогда не связываются.
g. Редактируемые вручную и управляемые инструментом¶
- Управляемые инструментом (не редактировать вручную): всё внутри каталогов потоков, а также
index/,spaces/иrelations/. Если содержимое неверно, исправьте инструмент и перегенерируйте — исправления рендеринга проходят черезpplx-export re-render, исправления данных через соответствующую команду обратного заполнения (см. Команды обслуживания) — так каждый артефакт остаётся воспроизводимым из сырых данных. - Редактируемые вручную: документация и отчёты проверки
web_archive/crosscheck/. Одно исключение на уровне пользователя: вручную сохранённый альтернативный ответ может быть записан какrewritten_answer_variant.mdвнутри каталога потока — см. Режимы беседы — Варианты перезаписи ответа.
h. См. также¶
- Режимы беседы — пять режимов и что каждый из них создаёт
- Инкрементальная синхронизация — как
lastUpdatedуправляет повторными экспортами - Команды обслуживания — перерендеринг, обратные заполнения, синхронизация удалённых
- Модель данных и контракт каталога — базовые классы данных
- Конвейер экспорта — как эти файлы записываются
- Офлайн-операции — перестройка всего из
raw_*.json