Это не canonical шаблон, по которому надо обязательно собирать страницу. Это inspiration — каркас, который однажды собрался под validity-classifier и оказался читаемым. Берите оттуда то, что подходит вашему сервису; не пытайтесь натягивать разделы, для которых у вас нет содержания. Многие пункты ниже специфичны для validity-классификатора (батчи + бисекция + worker-pool в коде агента) и исчезают, если сервис устроен иначе.
Что описывает каркас: LLM-сервис со structured output (generateObject / responseSchema / эквивалент) и обработкой N независимых элементов. Архитектурный паттерн «гранулярность вызовов + трёхуровневая декомпозиция» — см. llm-call-granularity, llm-call-failure-classes.
Решения по интеграции (где в пайплайне, куда output, Inngest-декомпозиция, id-прокидка, стыковка с соседними фильтрами) — в companion integration decision page, не дублируются здесь. Но если интеграция уже не вопрос, отдельная decision-страница может быть лишней.
Скелет страницы
---
type: concept
status: draft
updated: <YYYY-MM-DD>
---
# <Имя сервиса>
<lead-абзац: один абзац — что разбирает страница, зачем (порт / интеграция /
онбординг), где живёт код, ссылка на companion integration decision (если есть).>
Ниже — секции по порядку. «Если применимо» означает буквально это: нет содержания — нет секции.
1. ## Что это и зачем
Один параграф: что сервис отвечает (1 предложение), какой closed-set output, какой input. Сравнить с соседними сервисами («он отвечает на X, не на Y»). Терминологическая записка — если в системе сущность называют по-разному (analyte / биомаркер / параметр / Observation), назвать прямо.
Если в пакете есть слои, которые легко спутать (например, stub’ы / data-tables рядом с типами/валидатором, или mis-naming файлов) — подсветить, чтобы не путать. Не делать этого «на всякий случай».
2. ## I/O-контракт
Таблица полей для input и output симметрично — см. wiki-io-contract-format. Формат столбцов:
- Input:
Поле | Тип | Источник | Зачем модели - Output:
# | Поле | Тип | Что решает | Что НЕ смотрит
TS-shape — в коде, не на странице; § «Где в коде» линкует на типы. JSON-пример — опциональный companion под таблицей, когда shape вложенная (FHIR-объекты внутри bundle, sub-объекты со своими полями) или когда конкретные значения проясняют семантику быстрее описания.
Если есть input-builder (чистая функция, нормализующая источник в LLM-инпут — dedup, drop-условия, нормализация → строки) — отдельный параграф.
Если есть link-back identifier (fhirObservationId / id ресурса) — отдельный параграф: где в input, где в output, как протаскивается; pointer на llm-fhir-linkback.
Что является настоящим output-ом, который читают downstream-потребители (один enum-статус? весь объект? часть его?) — назвать прямо. Производные поля, computed в коде после LLM-ответа, отметить как computed.
3. ## Выходная схема как CoT (если применяется приём)
Только если порядок полей выходной схемы используется как structured-output-field-order-cot. Если приём не применён — секции нет, либо она просто описывает output без CoT-фрейма.
Когда применяется: лид-абзац про порядок-полей-как-CoT с ссылкой на pattern-страницу; таблица полей (# | Поле | Тип | Что решает | Что НЕ смотрит). Подчеркнуть, что является финальным аутпутом (статус / id / etc.), а что — служебные поля рассуждения. Производные computed-поля явно отметить.
4. ## Три уровня обработки (если есть батчинг)
Если сервис делает один LLM-вызов на запрос — этой секции нет, всё описывается в одной § «Один LLM-вызов». Три уровня появляются, когда есть разделение один LLM-вызов ⊂ один батч ⊂ один прогон — типично при N независимых элементов с групповой обработкой.
Подсекции — по факту: что у этого сервиса реально есть.
### Один LLM-вызов— то, что выполняется на этом уровне у этого сервиса. Если используетсяgenerateObject— назвать; если другая библиотека или собственная обёртка — назвать её. Retry/timeout/fallback — назвать там, где это живёт: в коде агента, в SDK-врапере, или в инфра-слое (LLM-прокси). Если retry-policy уже на прокси — не описывать как «есть в коде».### Один батч— preprocess + один вызов + postprocess валидатор. Указать, что именно валидатор проверяет в этом сервисе. Нюансы пустых / опциональных полей — буллитами, если есть.### Один прогон— chunk → fan-out → каждый батч → собрать финальный результат + метаданные. Если worker-pool / бисекция / retry-loop реализованы в коде агента — это анти-паттерн (no-self-rolled-queues), при порте → Inngest; companion integration decision § Inngest-декомпозиция.### Константы— env-override-таблица только для констант, реально использующихся. Альтернативно — описывать константу там, где она используется, без отдельной таблицы. Не выставленные константы упоминать, если это open-вопрос (например,maxOutputTokens— см. gemini-doom-loop).
5. ## Классы сбоев
Применить таксономию llm-call-failure-classes (транспорт / схема / семантика) — но только в той мере, в какой эти классы видны в коде сервиса. Канонически:
- Транспорт должен жить на прокси, не в коде агента. Если в коде есть transport-retry — это карри-овер, отметить как «кандидат на вынос».
- Схема (schema-cleaning, превентивно до вызова) — тоже на прокси.
- Семантика — остаётся в коде агента (validator → reject → retry-with-hint → degrade). Это главный класс, который описывается в секции.
Подчеркнуть: все классы дают partial-результат, прогон не валится.
6. ## Зависимости / самодостаточность (если есть что сказать)
Что импортирует / не импортирует — описывать, если это аргумент за переносимость (мало cross-cuts → легко портится). Port-gotchas (import.meta.url / __dirname под CJS-бандлом, имя файла врёт) — если они есть и реально кусают. Pointer на companion integration page § Следствия для подробного разбора. Не делать секцию, если зависимостей нечего обсуждать.
7. ## Где в коде
Таблица Артефакт | Расположение. Расположение — ссылка на код (на конкретной ветке), не голая строка пути: чтобы из других страниц можно было кликнуть и попасть в файл. Артефакты — те, что есть: оркестрация, публичный orchestrator, input-builder, промпт-файл, типы, валидатор, eval-suite, CLI, модель-резолвер, адаптеры, ветка/автор. Если артефакта нет — не упоминать.
8. ## Диаграммы (если помогают)
Диаграммы — там, где они проясняют. Без шаблонного лида про «ASCII-черновики». Что бывает полезно:
### D1 — control flow одного прогона— control flow от точки входа до финального аутпута. Над диаграммой — короткий лид: какие классы сбоев видны в этом control flow для этого сервиса. Подчеркнуть «partial-результат, прогон не валится» — если это так.### D2 — модель данных— поток от source-of-truth через input-builder через LLM-вызовы в финальный output-тип. TBD-куски (куда персистится, например) — ссылкой на decision-страницу (которая обсуждает вопрос), не голым TBD-маркером.
D3 «где воткнуть в пайплайн» — не сюда. Это архитектурная сшивка → companion integration decision page. Здесь — pointer.
9. ## Открытые вопросы
Только важные открытые вопросы — те, что действительно нерешены и реально кусают. Не делать секцию ради секции. Если интеграционные вопросы не проблемные, отдельная companion integration decision page может не понадобиться; если проблемные — выносить туда, не дублировать здесь.
Если есть смысл — разделить по адресату («к автору сервиса» / «к команде по пайплайну»), но не насильно.
10. ## Связано
Фокусировано, не все связанные страницы подряд. Включать то, что по смыслу стоит посмотреть после этой страницы. Не повторять ссылки, уже процитированные выше в тексте (особенно базовые — failure-classes, granularity и т.п.) — если упомянуты в lead-абзаце или в § 5 «Классы сбоев», нет смысла дублировать в § «Связано».
Соседние сервисы — да, как ориентир. Технические нюансы, на которые уже сослались — нет.
11. ## Источники
Branch + commit HEAD + spec-файлы + plan-файл + автор (того, кто это собрал / писал код / промпт). Если есть MD-файлы рядом с кодом (SPEC.md, DEBUG_METHODOLOGY.md) — линковать. Цитаты из митингов / Slack / fireflies — как footnotes ([^N]) к конкретным claim’ам в теле, определения здесь. См. wiki/CLAUDE.md § «Источники — формат ссылок».
Граница: что — здесь, что — в companion integration decision page
Когда integration действительно вопрос — отдельная companion decision page по wiki/CLAUDE.md § «Граница: что входит в decision, а что в concept». Когда integration не вопрос (сервис готов и работает, всё стыкуется) — companion decision не нужен, всё сидит в concept-странице или вообще нигде. Не плодить decision-страницы для не-проблем.
| Concept-страница (эта) | Companion integration decision (если вопрос) | |
|---|---|---|
| Внутренности (что отвечает, схема output, валидатор, как обрабатывается вызов / батч / прогон) | да | нет |
| Константы | да | нет |
| File paths | да (§ «Где в коде») | нет |
| D1 / D2 | да | нет |
| D3 (где в пайплайне) | нет — pointer | да |
| Где живёт output (ephemeral / FHIR / Postgres) | нет — pointer | да |
| Idempotency между перезапусками | нет — pointer | да |
| Inngest-декомпозиция | нет — pointer | да |
| Стыковка с соседними фильтрами (TTL / triage / retrieval) | нет — pointer | да |
| Companion-страница: каркас | — | Контекст → Вопрос 1 → Вопрос 2 → … → Позиции → Что нужно для разрешения → Следствия → Открытые вопросы → Связано → Источники |
Companion-decision создавать сразу при первом сомнении (status: draft), переходит в active когда фиксируется.
Открытые вопросы
- Один сервис = одна concept-страница vs одна companion decision — норм; что делать, если несколько сервисов делят companion (общая интеграция-история) — пока не встречалось. TBD по факту, если возникнет.
Связано
- validity-classifier — каноничный пример, из которого собран каркас (смотреть если что-то непонятно)
- llm-call-granularity — паттерн «1 / N / батчами» + трёхуровневая декомпозиция
- biomarker-actuality-integration — пример companion integration decision page
Источники
- validity-classifier (
origin/feat/v2-5) — служила образцом при сборе шаблона. Автор сервиса: Артур. wiki/CLAUDE.md§ «Граница: что входит в decision, а что в concept», § «Формат страницы», § «Типы страниц», § «Источники — формат ссылок»- Discussion: chain
6bfe41d1; ревизия 12 мая 2026 — критика «слишком частный кейс / слишком прескриптивный шаблон» (Ильдар).