Основы
Статика и динамика
Что разрешается на вашей машине, чему нужен движок и почему граница проходит именно здесь.
Любой содержательный узел бывает либо статическим, либо динамическим. Это различие определяет, откуда берётся его текст и во что он обходится.
Вы никогда не объявляете это сами. На каждый вид есть один хелпер, а режим вытекает из переданных ему опций.
Статические узлы
Дайте абзацу render — и его можно построить из ваших данных, а значит, он
разрешается локально:
paragraph<OfferData>({
id: "offer",
render: (data) =>
`Your place starts on ${data.startDate}, at ${data.college}.`,
});
Он работает в предпросмотре, в тестах и в сборке — офлайн, мгновенно, бесплатно. Если все узлы документа статические, движок вам вообще не нужен.
Динамические узлы
Дайте ему вместо этого промпты, а также заполнитель, чтобы предпросмотр оставался читаемым, — и узел становится динамическим:
paragraph<OfferData>({
id: "tutor-note",
placeholder: (data) => `A note from ${data.interviewer}.`,
generalPrompt: (data) =>
`Write two warm sentences about ${data.applicantName}'s interview.`,
});
Тот же хелпер, другие обязательства. image и graph устроены так же — у них
роль членов локального разрешения играют src и data.
Почему режим выводится, а не задаётся
Узел с render всегда можно построить локально; узел с одними промптами —
никогда. Объявлять режим рядом с этими опциями значило бы завести второй источник
истины, способный им противоречить: staticParagraph с generalPrompt или
наоборот, плюс какое-то правило приоритета, решающее, кто победит.
Вместо этого истина — сами опции, а mode выводится из них. Указать
одновременно render и generalPrompt — это ошибка компиляции, а не
подбрасывание монетки во время выполнения.
mode по-прежнему присутствует в собранном DocumentModel и в document.json:
движку нужно знать, какие узлы разрешать. Это выход, а не вход.
Доступны четыре слота промптов. Обязателен только generalPrompt:
| Слот | Назначение |
|---|---|
generalPrompt | Что узел должен сказать |
infoPrompt | Контекст, который модель должна знать, но не пересказывать |
negativePrompt | Чего следует избегать |
systemPrompt | Указания о роли и тоне |
Что вы видите локально
Сборка для предпросмотра разрешает динамические узлы в их заполнители, а не в сгенерированный текст:
const document = await buildProjectPreviewDocument(project);
// dynamic nodes -> placeholder text
Так задумано. Предпросмотр остаётся детерминированным и бесплатным, так что структуру и оформление можно доводить, не отправляя с машины ни одного запроса. Заполнитель полезен и как дисциплина: если документ с заполнителями невозможно читать, его структура делает слишком мало работы.
Что происходит во время запроса
Артефакт выгрузки сохраняет значения времени запроса в виде токенов вроде
{{data.residentName}}, чтобы их можно было разрешить позже. Отправка его
движку возвращает готовый документ с
заполненными динамическими узлами.
Движок — отдельный бесплатный сервис, который вы размещаете сами. Он намеренно не входит в npm-пакет, так что установка фреймворка никогда не тянет за собой ничего, что требовало бы ключей API или сети. Направьте workspace на свой:
dxcl init my-documents --api-endpoint https://documents.example.com/api/letters
dxcl init my-documents --no-api-endpoint
Значение upload.endpoint в docxcelerate.config.json можно менять в любой
момент.
Почему граница проходит здесь
Разделение по узлу, а не по документу оставляет бесплатную половину инструментария по-настоящему полезной. Полностью статический документ — это законченный работающий документ, за которым не стоит никакого сервиса. К размещённой половине вы обращаетесь только ради тех абзацев, которым нужен сгенерированный текст, — и какие это абзацы, видно прямо в шаблоне.