Перейти к основному содержимому

ADR 0196 — Anamnese снова включена: не работает без субъекта, а принятая гипотеза становится фактом профиля

Статус​

Accepted. 2026-10-01 (AT-277, история HS-072, эпик EP-030, раунд 36; решение владельца от 01.10: "religar a Anamnese com correções" — защита допустимого субъекта, а ПРИНЯТАЯ гипотеза становится фактом профиля в графе и входит в контекст агентов, разговаривающих с человеком; отклонённая лишь регистрируется). Завершает паузу от 2026-08-10 только для Anamnese — Psicólogo остаётся на паузе (PSYCHOLOGIST_ENABLED=false). Использует граф ADR 0099 так, как он был задуман (ПРОИЗВОДНАЯ память event log), и путь эфемерной ориентации языка (RN-622), не редактируя ни то, ни другое.

Контекст​

Измерено в коде dev и в отчёте о реальном использовании от 2026-09-29, до изменения:

  • Шесть раундов, шесть anamnese.run_skipped, написанных МОДЕЛЬЮ с одной и той же причиной, "nenhum membro elegível" (сессии be70, 67f3, d7e9 ×2, 5d66 ×2), на 8 933 micros за 6 вызовов. anamnese_runs = 0, proficiency_profiles = 0.
  • Откуда брался субъект: GetAnamneseContextUseCase читал ProjectRepository.listMembers, то есть ТОЛЬКО project_members. Создание проекта не записывает туда строку (CreateProjectUseCase), а авторизация считает владельца workspace участником проекта через projectRole ?? workspaceRole (RN-471). В проекте одного человека список был пуст всегда.
  • Worker не смотрел на список: AnamneseWorker.maybe_analyze/3 лишь спрашивал триаж (объём окна, очередь гипотез), а гипотеза в очереди ПРИНУЖДАЛА раунд. Prompt говорил "(nenhum membro elegível)", и модели платили за то, чтобы прийти к этому выводу.
  • У принятой гипотезы не было реального потребителя: AcceptHypothesisUseCase ставил в очередь anamnese_queue (3 строки в реальном использовании), а единственным потреблением был propose_instruction_patch одного раунда — который в 5d66 seq 17 ответил "não há arquivo de instrução de agente disponível no contexto". Граф не проецировал psychologist.hypothesis_accepted.
  • У пути чтения графа не было потребителя: QueryUserContextUseCase существовал (гипотезы, профили, handoffs по пользователю и проекту), и никакой код его не вызывал.
  • ANAMNESE_ENABLED имел default false в runtime.exs и в трёх compose с RN-540; отчёт фиксирует engine реального использования с флагом true через окружение.

Решение​

  1. Раунд не выполняется без допустимого субъекта. Engine.Anamnese.Elegibilidade решает ДО триажа, без исключений (даже очередь гипотез его не обходит): субъект — ДЕЙСТВУЮЩИЙ участник проекта, вне opt-out, с СОБСТВЕННЫМ взаимодействием в том, что показывает prompt, — его событие actor_kind: "user" в окне или его решение. Без субъекта: ни одного вызова LLM, ни одного поиска в RAG, названная причина в журнале engine (sem_sujeito_elegivel, с nenhum_membro или nenhuma_interacao_propria и числами) и только в раунде, запрошенном вручную (origem: "manual"), — durable anamnese.run_skipped. Prompt начинает перечислять только субъектов.
  2. Участник проекта — это ДЕЙСТВУЮЩИЙ участник. ProjectRepository получает listEffectiveMembers (membrosEfetivos, мерило RN-471: строка проекта перекрывает строку workspace в обе стороны), используемый тремя use cases Anamnese. listMembers остаётся только project_members — тем, что редактирует экран Участников.
  3. Принятая гипотеза становится ФАКТОМ профиля, производным от event log. psychologist.hypothesis_accepted начинает нести projectId, sujeito (автор анализируемой сессии), hipotese, sugestao и fatoDoPerfil. GraphEventTranslator — ТОТ ЖЕ, что у проектора и grafo:reprojetar, — переводит его в (:FatoDoPerfil {hypothesisId})-[:SOBRE]->(:Usuario) и -[:NO_PROJETO]->(:Projeto) (RecordProfileFactUseCase, MERGE по гипотезе, новое ограничение уникальности). Событие самодостаточно: перепроецирование не читает psychologist_hypotheses.
  4. Факт рождается только из клика самого субъекта. fatoDoPerfil равно true, когда принявший ЯВЛЯЕТСЯ автором сессии. Принятие третьим лицом продолжает действовать для всего прочего (статус, очередь Anamnese) и говорит в payload, почему оно не стало фактом (motivoSemFato). Отклонённая не меняется: лишь регистрируется.
  5. Факт входит в контекст агентов, разговаривающих с человеком. SendAgentMessageUseCase читает факты АВТОРА сообщения через QueryUserContextUseCase — в пределах проекта, 5 самых свежих, с общим числом рядом — и отправляет их в engine как perfilDoAutor, текст, собранный textoDoPerfilDoAutor (не более 2 000 символов, срез объявлен). Engine помещает их в ход ТЕМ ЖЕ путём, что и язык: controller объединяет оба в одну карту, IdiomaDaResposta.com_idioma_do_autor/2 её распознаёт, а фасад EngineApiClient добавляет текст как ЭФЕМЕРНОЕ сообщение system перед ориентацией языка (Engine.Harness.PerfilDoAutor). Ни один сервер агента не меняется. Недоступный граф становится записью в журнале, и ход идёт без фактов.
  6. Включить снова. Default ANAMNESE_ENABLED возвращается к true в runtime.exs и в трёх compose (мерило flags-do-engine-no-compose.spec.ts сохраняется: compose повторяет код). START_ANAMNESE не меняется нигде: true в коде, в dev и в установке; false в продакшен-compose, расхождение уже объявлено. PSYCHOLOGIST_ENABLED не меняется.

Последствия​

  • Реальное использование от 29.09 перестаёт тратить: проект без субъекта не доходит до LLM. А владелец workspace в проекте без собственной строки начинает БЫТЬ субъектом — снова включённая Anamnese начинает что-то делать в самом частом случае, вместо ничего.
  • Объявленная цена: Anamnese теперь профилирует того, кто является участником лишь через workspace. Участник workspace, никогда не взаимодействовавший в проекте, не профилируется (он не субъект), и opt-out продолжает действовать.
  • Объявленная цена: каждое сообщение разговорному агенту делает одно дополнительное чтение графа (четыре запроса QueryUserContextUseCase). Без настроенного Neo4j это немедленный GraphUnavailableError и запись в журнале.
  • Объявленная цена: факты доходят до КАЖДОГО хода автора, в том числе до агента, который не был адресатом гипотезы, — метка говорит, к кому она была обращена, а фильтрация по агенту — открытое решение.
  • Вне, объявлено: три принятия реального использования были записаны до этого изменения, без полей факта, и НЕ становятся фактом при перепроецировании. Они остаются в очереди Anamnese, которая читает их в следующем раунде С субъектом. Ход без автора (kickoff, dev agents, gates) не получает фактов. Staff остаётся без автоматического триггера: Anamnese никогда не запускала его в коде.
  • Раунд по тику без субъекта не становится событием (это было бы предупреждение каждые 15 минут на проект); повествует только запрошенный вручную.