Цифровой портрет владельца: digit portrait
Замысел был про «цифрового двойника»: пусть агент отвечает как владелец, ссылается на его прежние решения и когда-нибудь подключается к созвонам. В одном предложении здесь смешаны три разные вещи, и цена смешения несимметрична: догадка, положенная рядом с фактом, через неделю читается как факт.
Поэтому слои разделены в коде, а не в описании.
| Слой | Что это | Чем проверяется | Кому можно показать |
|---|---|---|---|
| Стиль | как владелец формулирует: длина фраз, лексика, формы отказа и согласия | пересчётом на том же корпусе; у каждой цифры хранится n |
никому, кроме владельца |
| Решение | что он решил: дословная цитата + сессия + время + свидетельство | цитата дословна, источник назван | можно: получатель проверит, а не поверит |
| Догадка | что он мог бы ответить на новый вопрос | ничем: это сочинение модели | только владельцу, всегда под шапкой |
Граница проходит между вторым и третьим слоем. Она и есть смысл этой команды.
Накопление выключено по умолчанию
digit portrait on # включить накопление
digit portrait off # выключить
Профиль речи не должен начать собираться оттого, что человек обновил пакет. Пока накопление не включено, все команды ниже работают и отвечают честно — просто отвечают, что портрет пуст.
Живой прогон на машине, где портрет не включали:
$ digit portrait style
[СТИЛЬ · измерено]
Стиль не измерен: ни одного сообщения владельца не наблюдалось.
$ digit portrait decisions
Ничего не найдено. Поиск лексический — синонимов не понимает; попробуйте
другие слова, прежде чем считать, что решений не было.
Вторая строка — не отговорка, а важное свойство: пустая выдача не доказывает отсутствия решения, и команда говорит об этом сама, вместо того чтобы дать читателю сделать неверный вывод.
Слой 1: стиль — измеренный, а не приписанный
digit portrait style показывает медиану длины сообщения и фразы, долю
вопросов и повелительных, переключение на латиницу посреди русской фразы,
отличительную лексику и — главное — какими словами владелец отказывает,
сомневается и соглашается.
В портрет кладётся дословная поверхность («мимо» ×7), а не имя класса «отказ»: воспроизводить придётся именно её. Отличительность считается лог-шансами к фону из ответов ассистента, поэтому у слова «отличительное» появляется точный смысл — то, что владелец говорит, а модель на его месте не говорит.
У каждой цифры хранится число наблюдений. Пока сообщений меньше двенадцати, отчёт сам называет себя предварительным.
Психотипов здесь нет. Ни MBTI, ни соционики, ни архетипов: у них нет доказанной предсказательной силы, а система, на них построенная, звучит уверенно ровно там, где не знает ничего. «Логик-интроверт» нельзя ни проверить, ни опровергнуть; «отказывает словом „мимо“, 7 раз из 9 отказов» — можно и то, и другое.
Слой 2: решения — факты с происхождением
digit portrait decisions [слова для поиска] — журнал прежних решений
дословно, со ссылкой на сессию и время. Не пересказ: пересказывает модель, и
тогда запись перестаёт отличаться от догадки.
Фраза попадает в журнал, только если в ней есть и маркер обязательства («переходим на», «не будем», «с этого момента»), и конкретный предмет (путь, идентификатор, версия, имя в обратных кавычках). «Давай сделаем красиво» — маркер без предмета, то есть сказано мимоходом. Плюс вето: вопрос, гипотеза, пересказ чужого решения, указательное без предмета, строка структуры данных.
У записи два уровня свидетельства, и разница видна в отчёте:
- сказано — фраза была, действий за ней в этом ходу не последовало;
- сделано — в том же ходу агент вызывал изменяющие инструменты, и их имена записаны.
Читающие инструменты свидетельством не считаются намеренно: с bash и git в
списке «сделано» становилось истинным почти всегда, то есть переставало
что-либо значить.
Позднее решение не стирает прежнее, а помечает его отменённым отдельной
строкой журнала (--all покажет и отменённые). История выбора — часть
портрета: «уходили с X на Y, потом вернулись» знает только тот, кто хранит оба
хода.
Слой 3: догадка — и три ограды вокруг неё
digit portrait draft "вопрос" собирает бриф для ответа в манере владельца.
Сам модуль текста не сочиняет: он выдаёт ограничения стиля, найденные решения
с цитатами и раздел ПРОБЕЛЫ — чего в портрете нет.
Живой прогон на пустом портрете:
$ digit portrait draft "стоит ли переходить на pnpm"
[СТИЛЬ · измерено]
Стиль не измерен: ни одного сообщения владельца не наблюдалось.
[РЕШЕНИЕ · факт со ссылкой]
Ничего: в портрете нет решений по этому вопросу.
Всё, что будет сказано ниже, — сочинено.
[ДОГАДКА · сочинено моделью, не владельцем]
…
[ПРОБЕЛЫ]
— стиль не измерен — воспроизводить нечего
— решений по этому вопросу нет — поиск лексический, попробуйте другие слова
Пустой раздел решений означает, что весь ответ будет сочинён целиком, и владелец обязан увидеть это до того, как прочтёт красивый абзац.
Вся конструкция Digit держится на том, что содержание не сочиняется моделью; двойник по определению сочиняет. Совместить нельзя — можно только огородить, и ограда стоит в трёх местах кода:
- слой — атрибут класса, а не поле: экземпляр не может объявить себя другим слоем;
- наружу ведёт одна дверь, и на догадке она бросает исключение, а не ставит флажок: молчаливое выбрасывание однажды станет молчаливым пропусканием;
- догадка не хранится нигде — у неё нет файла и нет писателя, что закреплено тестом. Записанная догадка через неделю неотличима от записи решения.
digit portrait draft --outside показывает то же глазами не владельца: только
записи со ссылками. Когда их нет, ответ — «сказать от его имени нечего». Это
правильный ответ, а не сбой, который надо чинить.
Где это лежит и что при передаче машины
Портрет — самые чувствительные данные в системе: не «что человек делал», а «как он устроен в речи». Поэтому он живёт отдельно:
$DIGIT_HOME/portrait/— своя директория с правами0700, файлы0600;memories/лежит рядом и попадает вdigit backup, импорт агента и профили — портрет там оказаться не должен;- в системный промпт не уходит ничего. Ни блока промпта, ни предзагрузки. Иначе за портрет пришлось бы платить каждым запросом и вынести профиль речи владельца в каждый лог каждого провайдера модели. Читается портрет явно — этой командой или соответствующим инструментом;
- во внешние провайдеры памяти не передаётся.
Файлов ровно два: style.json (достаточные статистики) и decisions.jsonl
(журнал, только дописывание). Третьего нет и не будет — это и есть отсутствие
файла у догадки.
Посмотреть весь портрет машиночитаемо: digit portrait --json. В этой выдаче
у догадки стоит "stored": false с примечанием «догадка не хранится;
собирается на запрос» — то же свойство, но в виде, который можно проверить
скриптом.
Передача сессии другому человеку портрет не передаёт: записи ссылаются на
сессию, обратной ссылки нет, а другой человек работает под своим
$DIGIT_HOME. Если передаётся сама машина:
digit portrait forget --session <префикс id> # убрать одну сессию
digit portrait forget --all # снести всё
Набрать портрет из архива
Обычно портрет наполняется ходом разговора, и отдельной команды «запомни» нет. Но если переписка уже лежит файлами, её можно прогнать разом:
digit portrait ingest ПУТЬ [ПУТЬ …] # файлы или каталоги с *.jsonl
digit portrait ingest ПУТЬ --dry-run # посчитать, но не писать
По умолчанию сообщение, чей зачин уже встречался дважды, пропускается
(--no-dedupe это отключает). Отсев не косметический: в архивах роль «user»
носит не только человек, а ещё и шаблонные брифы оркестраторов — и портрет,
собранный без отсева, измеряет их, а не владельца.
Что измерено
На живом архиве: 487 транскриптов, 6 146 сообщений роли «user».
2 613 из них отброшены как шаблонная рассылка — в архивах роль «user» носит не только человек, и один и тот же зачин встретился 468 раз. Портрет, собранный без этого отсева, измерял оркестратора: эмодзи 1,40 на сообщение против 0,33 после.
Остаётся 3 533 сообщения владельца → 650 записей решений (18 на 100 сообщений); 519 «сделано», 131 «сказано»; действующих 316, отменённых 334. На коротком разговорном корпусе плотность ниже: 34 сообщения → 5 записей.
Когда портрет становится непустым: первое решение — на 7-м сообщении, устойчивые средние стиля — с 12-го, первая повторившаяся формулировка речевого акта — на 101-м. Последнее и есть честный порог «можно писать в его манере»: одна фраза, сказанная один раз, стилем не является.
Точность извлечения на размеченной вручную случайной выборке 40 записей — 37 из 40. Три промаха: описательная проза, попавшая под маркер.
Доля фактов и догадок в том, что портрет хранит: 100 % — записи с дословной цитатой и ссылкой на источник. Догадок ноль, и не по совпадению — для них нет файла.
Цена хода при портрете из 3 533 сообщений и 650 решений — 17 мс медиана, и считается это в фоновом потоке, не на пути пользователя. Чтение журнала — 7,7 мс, поиск по нему — 26 мс, сборка отчёта стиля — 11 мс.
Границы, названные вслух
Поиск лексический. Синонимов он не понимает, и пустая выдача не доказывает отсутствия решения. Ровно поэтому и черновик, и командная строка говорят об этом в самом ответе.
Вставленные логи видны в лексике. Разметка и блоки кода из речи вычитаются, а вставленный лог — нет, и он проступает в отличительных словах.
Портрет знает одну роль. Набранный на рабочей переписке, он знает, как владелец пишет задачи агенту, и не знает ничего о том, как он говорит с кем-то ещё.
Точность извлечения — 37 из 40, а не 40 из 40. Описательная проза иногда проходит под маркер обязательства. Цитата при этом остаётся дословной: ошибочной оказывается не запись, а её отнесение к решениям.
Код: agent/portrait/, навык skills/productivity/digital-portrait/.
В английской документации этой команды нет — она описана только здесь.