Диагностика проблем¶
Это не список возможностей трейсинга/observability-инструментов — это observability.md. Это другое направление: у вас конкретный вопрос "почему это произошло/не произошло" и нужен самый быстрый путь к ответу, по симптомам.
Общая нить: в reactifact допустимость — решение по состоянию (§69) — никто не вызывает агента напрямую, поэтому нет стектрейса, указывающего на "строку, которая должна была его вызвать". Все инструменты ниже отвечают на один и тот же вопрос по-разному: что реально содержало состояние в момент, когда runtime решал, что запускать?
"Мой агент вообще не запустился"¶
Допустимость решают Consume/Trigger, сопоставленные с событиями, а не
порядок кода — поэтому "не запустился" почти всегда означает либо что событие,
которое должно было его триггернуть, не произошло, либо что условие
Consume.by_field/by_status оказалось ложным для существующего артефакта.
reactifact graph <module>(например,reactifact graph examples.knowledge.agents) печатает статический blueprint — заявленныеconsumes/producesкаждого агента. Проверьте, что агент реально подписан на нужный тип артефакта; опечатка или устаревшийConsume(WrongType)— самая частая причина.- Частый "тихий no-op", который выглядит как пропущенный триггер, но им не
является:
effects.create_once(..., id=...)/upsert(...)намеренно ничего не делает, если такой id уже существует (§42, идемпотентность) — проверьте, не существовал ли артефакт, который вы ожидали увидеть свежесозданным, уже с прошлого поколения. agent.matches(event, context)— тот самый булев вызов, который делает runtime; можно вызвать напрямую в REPL с реальнымEvent, если граф выглядит верно, а уверенности всё ещё нет.
"Мой агент запустился, но ничего не произвёл"¶
Ранний возврат из produce (return None) — нормальная, честная форма
"пока не готов" или "нечего делать" (§59); сам по себе он не ошибка, поэтому
нигде не отобразится как ошибка.
reactifact trace <path/to/traces.db> [run_id]рисует run как диаграмму Mermaid (trace_to_mermaid, по умолчанию последний run) — у каждого span'а агента видны reads/writes, а любое выброшенное исключение вписано прямо в узел (⚠ <сообщение>), так что это самый быстрый способ увидеть, какой агент реально упал, а какой законно отказался действовать.- Если ничего не упало, а span показывает отсутствие записей — значит гард
внутри вашего
produce()вернулNone. Это случай для breakpoint/print в конкретных местахreturn None, а не для трейсера (трейсер фиксирует, что произошло, а не почему гард решил не действовать). Runtime(isolate_errors=True, on_agent_error=...)— опциональная настройка; по умолчанию reactifact падает вслух, и исключение всплывает изarun()/astream()(§69). Если вы сознательно включили изоляцию, смотритеruntime.last_stats.errors(RunStats.errors, §58) для количества и⚠-метки в трейсе — для того, какой именно агент.
"Мой агент запустился больше раз, чем ожидалось"¶
- Отсутствие идемпотентности:
effects.create(...)без стабильногоid=(или с id, выведенным из счётчика) создаёт новый артефакт на каждое подходящее событие; вместо этого обычно нуженeffects.create_once(..., id=...)(§42). - Самозапуск: агент, который одновременно
consumesиproducesодин и тот же тип артефакта, перезапускает себя на собственном выводе, если гард (if <условие-уже-сделано>: return None) его не останавливает — см. гардыalready planned (§42)/already executedвexamples/plan_execute. - Снова
reactifact trace: повторные записи в один и тот же id артефакта на диаграмме через несколько поколений — прямой симптом.
"Run остановился раньше, чем я ожидал"¶
- Проверьте
runtime.outcome(RunOutcome, §58) послеarun()/astream()(также доступно наruntime.last_stats.outcome):budget_runs_exceeded/budget_time_exceeded/iterations_exhaustedозначают, что сработал лимитBudget, а не баг;completedозначает, что run действительно устаканился (очередное поколение не произвело ничего, на что можно было бы отреагировать). - Если
completedнаступил раньше, чем ожидалось — обычно это реактивная модель работает так, как задумано: больше ничего не стало допустимым. Вернитесь к разделу "Мой агент вообще не запустился" именно для этого агента.
"Шедулер выбрал/отбросил не того агента" (если используете scheduler=)¶
Актуально только если вы передали Runtime(scheduler=...) — без него
выполняются все допустимые кандидаты, в порядке заявленного priority.
- Правила фильтра могут полностью отбросить кандидата ещё до ранжирования;
ранжирование только переупорядочивает, никогда не отбрасывает;
rank_limitобрезает уже ранжированный список после ранжирования. No-starvation fallback означает, что кандидат действительно исчезает только через фильтрrulesилиrank_limit— никогда как сюрприз "метрика оценила слишком низко, чтобы вообще выполниться", потому что само ранжирование никогда не отбрасывает. - См. design-notes/adaptive.md — полный контракт
filter → rank → LLM-tie-break, и
reactifact.scheduler.relation_balance_metric, если используете встроенную uncertainty-driven метрику (§26) — она читает связиsupports/contradicts, так что неожиданный порядок часто означает, что эти связи установлены не так, как вы думаете (context.incoming(artifact_id, relation=...), чтобы проверить напрямую).
"Не понимаю, почему ответ именно такой"¶
Это не сбой — это ровно то, для чего существует provenance (§15, §34):
context.related(answer.id, "supported_by")проходит один шаг исходящих связей от артефакта (пропуская "висящие");context.incoming(id, relation=...)проходит в обратную сторону.context_to_mermaid()/trace_provenance_to_mermaid()(reactifact.viz) рисуют весь граф связей или цепочку provenance трейса как диаграмму — см. comparison.md §3 за разобранным примером.
Детерминированное воспроизведение проблемы¶
Когда понятно, что произошло, reactifact.testing.ScenarioLab позволяет
закрепить это без зависимости от живого LLM или живого run'а: засеять точные
артефакты, прогнать до конца и проверить результат
(.artifacts(...)/.tools/.path/.errors); lab.fail(tool_name, error) и
lab.fail_resource(...) инжектируют конкретный сбой, который вы ловите;
записанный через ReplayLLM ответ модели делает вывод нестабильного
провайдера детерминированным между перезапусками. Отдельного гайда пока нет —
начните с докстринга модуля reactifact.testing и разобранных примеров в
examples/repair/scenarios/ и examples/knowledge/scenarios/.