О проекте
Команда: Z
Кейс HackAlem.ai: Финансы — Freedom
Стадия: Доведённый продукт
Задача кейса: «Граф денег»: восстановить структуру организованной группы по сети транзакций (AML)
Суть
Воспроизводимый офлайн-анализ AML-графа: роли для всех 2248 участников по документированным формулам, кластеры Louvain, приоритет проверки, Streamlit-просмотрщик и статическая версия отчёта для GitHub Pages.
Как устроено
Сырые parquet загружаются в направленный взвешенный граф NetworkX; считаются PageRank, betweenness, степени, доля крупнейшего контрагента и out/in, нормированные по 95-му процентилю. Для каждой роли проверяются условия допустимости и считается балл, выбирается максимальный (≥0,5), иначе peripheral. Louvain работает на неориентированной проекции; все пороги вынесены в config.yaml, прогон пишет run_manifest с хешами. Необязательный слой агентов на OpenAI только переписывает тексты поверх готовых чисел.
Ключевые решения
- PageRank
- betweenness centrality
- Louvain
- нормировка по q95
- анализ чувствительности порогов
- кривая устойчивости сети
- хеш-манифест прогона
Метрики
расчёт 4.5 с (лимит ТЗ 300 с); 88 кластеров Louvain, 8 с несколькими seed; 20 тестов pytest
Особенности
Механическая проверка решения против ТЗ (tools/compliance.py), побайтовая повторяемость выгрузок, анализ чувствительности порогов и кривая разрушения сети при изъятии топ-N узлов против случайного изъятия.
Стек
Python · pandas · NumPy · PyArrow · NetworkX · PyYAML · Streamlit · Pyvis · pytest
Модели
OpenAI API
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-3c305a41-z.
README
README.mdГраф денег
Какую задачу решает. Аналитику финансового мониторинга известен 81 клиент — нижний уровень цепочки. Наблюдаемая сеть их переводов — 2 248 участников. Вручную он трассирует её неделями и выбирает, кого проверять, интуитивно.
Инструмент за три секунды строит граф переводов, присваивает каждому из 2 248 участников роль по документированным правилам, разбивает сеть на группы и выдаёт ранжированный список: кого проверять первым и какие именно числа это объясняют.
На выходе — три CSV для работы и локальный экран со схемой сети, поиском по gid и карточкой участника. Данные кейса HackAlem AI, июль 2026.
Роли — гипотезы для проверки, а не утверждение о виновности; скоры не являются вероятностью нарушения. Решение о проверке принимает аналитик.
Технологии
| Слой | Чем сделано | Зачем именно так |
|---|---|---|
| Расчёт | Python 3.12+, pandas, NumPy, PyArrow | колоночное чтение parquet и векторные метрики |
| Граф | NetworkX 3.7 | направленный взвешенный граф, PageRank, betweenness, Louvain |
| Конфигурация | PyYAML, config.yaml | все пороги вынесены из кода и снабжены обоснованием |
| Интерфейс | Streamlit, Pyvis | локальный экран без сервера и внешних CDN |
| Проверки | pytest, 20 тестов | инварианты выгрузок и повторяемость |
| Слой агентов | OpenAI API, необязательно | переписывает тексты поверх готовых чисел |
Зависимости ядра закреплены точными версиями в requirements.txt, полное окружение —
в requirements-lock.txt. Пакет openai вынесен в отдельный requirements-llm.txt:
обязательный расчёт не должен зависеть от внешнего сервиса.
Запуск
Требуется Python 3.12+; проверено на Python 3.13. Зафиксированные NumPy, SciPy и NetworkX требуют минимум 3.12. Данные находятся в data/, исходники организаторов — в starter/.
Windows PowerShell, подготовка окружения:
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
Один запуск от сырых данных до всех результатов:
.\.venv\Scripts\python.exe run.py --data data --out out
Экран просмотра:
.\.venv\Scripts\python.exe -m streamlit run app.py
Открыть http://127.0.0.1:8501. На Linux/macOS вместо .venv\Scripts\python.exe использовать
.venv/bin/python. В активированном окружении команда расчёта — python run.py.
Настройки меняются через --config config.yaml. Для другого набора результатов интерфейса
установить переменную GRAPH_OUT_DIR. Обязательный расчёт не использует API и не требует ключей.
Флаг --llm включает необязательный слой агентов — см. раздел «Слой агентов».
Он переписывает только тексты и требует ключа OpenAI. Обязательный расчёт от него не зависит.
Не реализованы и не имитируются нулями или фиктивными ответами: HITS, поиск повторяющихся маршрутов и циклов, детекция дробления сумм.
Как проверить решение за пять минут
Шаги для проверяющего. Ключи и интернет не нужны ни на одном из них.
- Установить окружение — команды из раздела «Запуск» выше.
- Запустить расчёт:
.\.venv\Scripts\python.exe run.py --data data --out outВ конце печатается время. Ограничение ТЗ — 300 с, фактически 4.5 с. - Проверить выгрузки в
out/:nodes_roles.csv— 2 248 строк, у каждой роль, скор, кластер, приоритет и непустое обоснование;clusters.csv— строка на группу;top_nodes.csv— 20 узлов по убыванию приоритета. - Запустить тесты:
.\.venv\Scripts\python.exe -m pytest -q— 20 тестов. - Проверить повторяемость:
.\.venv\Scripts\python.exe run.py --data data --out out-checkи сравнить файлы сout/— они совпадают побайтово. Хеши входов, конфигурации и каждого артефакта записаны вout/run_manifest.json. - Открыть интерфейс:
.\.venv\Scripts\python.exe -m streamlit run app.py, ввести любой gid изnodes_roles.csvв поиск — видно роль, обоснование, направление потоков и всех контрагентов. - Проверить объяснимость: взять gid из
top_nodes.csv, открыть карточку и сверить обоснование с правилом из раздела «Как считается роль» и порогом вconfig.yaml. - Прогнать проверку против ТЗ:
.\.venv\Scripts\python.exe tools\compliance.py --out outСкрипт механически проверяет пять обязательных требований и требование раздела 9 об осторожности формулировок, и печатает результат построчно.
Результаты на предоставленных данных
| Проверка | Факт |
|---|---|
| Узлы / seed | 2 248 / 81 |
| Направленные рёбра / транзакции | 3 119 / 4 840 |
| Оборот наблюдаемой сети | 365 890 012,01 KZT |
| Изолированные узлы, сохранённые в выгрузке | 19 |
| Обрезанные узлы на глубине 4 | 444 |
| Слабосвязные компоненты с изолятами / без | 35 / 16 |
| Крупнейшая компонента / узлы вне неё | 1 877 / 371 |
| Кластеры Louvain / из них с несколькими seed | 88 / 8 |
В ТЗ указано 352 узла вне крупнейшей компоненты: это число получается без 19 изолятов. Мы сохраняем все узлы, поэтому полное число — 371. Суммы и количество транзакций по каждой паре совпали с edges. Число 8 — фактический результат, не цель настройки алгоритма.
| Гипотеза роли | Узлов |
|---|---|
| consolidator | 85 |
| coordinator | 12 |
| distributor | 56 |
| transit | 42 |
| terminal | 472 |
| peripheral | 1 581 |
Нет размеченных ролей, поэтому accuracy не измерялась. Это распределение эвристических гипотез, а не оценка числа нарушителей. Изменение порогов может изменить результат.
Артефакты
| Файл в out/ | Содержание |
|---|---|
| nodes_roles.csv | Все gid; обязательные role, role_score, cluster_id, priority_score, evidence |
| clusters.csv | cluster_id, n_nodes, n_seed, sum_kzt_internal, top_gids, hypothesis |
| top_nodes.csv | 20 записей: rank, gid, role, priority_score, why |
| node_features.parquet | Полные метрики, допуски ролей, пять скоров, альтернативы, ограничения и слагаемые приоритета |
| viewer_edges.parquet | Направленные связи из того же прогона |
| docs/index.html | Статическая версия отчёта для публикации на GitHub Pages |
| resilience.csv | Кривая разрушения сети: сколько остаётся при изъятии топ-N против случайного изъятия |
| run_manifest.json | Конфигурация, хеши входа/выхода, версии, время этапов, диагностика |
Дополнительные поля nodes_roles включают метрики, eligible_, score_, best_specialized_role, role_runner_up, runner_up_score, score_margin, data_limitation, next_request, priority_* и rank. Неопределённые отношения и средние при нулевом знаменателе остаются пропусками только в дополнительных полях. Обязательные поля заполнены у всех узлов, evidence ≤200 символов.
Интерфейс проверяет хеши всех артефактов и не открывает смешанные или изменённые результаты. Всё читается из одного прогона, без повторного расчёта. Ресурсы графа встроены в HTML, запросов к CDN нет. Большие int64 gid передаются в JavaScript как строки.
Статическая версия отчёта
Streamlit требует запущенного Python, чего статический хостинг не умеет. Поэтому тот же готовый прогон собирается в одну офлайн-страницу: схема сети, топ-лист с разложением приоритета, кривые устойчивости и ссылки на выгрузки.
.\.venv\Scripts\python.exe toolsuild_site.py --out out --site docs
Страница читает опубликованные CSV и ничего не пересчитывает. Внешних ресурсов нет — графики нарисованы inline-SVG, граф встроен целиком, поэтому страница открывается и без сети.
Публикация: Settings → Pages → Source: Deploy from a branch → main → /docs. Локальный предпросмотр без публикации:
cd docs; ..\.venv\Scripts\python.exe -m http.server 8600
Это дополнение к инструменту, а не замена: полноценная работа с поиском по gid, карточкой участника и ассистентом остаётся в локальном Streamlit.
Как считается роль
Обозначим N(x) = min(x / q95, 1), где q95 — 95-й процентиль положительных значений признака во всей входной выборке. Если положительных значений нет, N(x)=0. Масштабы нормировки сохранены в run_manifest.json. Это относительная шкала данной выборки.
C — доля суммы от крупнейшего входящего контрагента; R — наблюдаемый out_kzt / in_kzt. При нулевом входе C и R не определены. Степени — число направленных связей. Посредничество B — точный нормированный betweenness направленного графа без весов длин: сумма денег не считается расстоянием. Для большого графа можно задать betweenness_sample.
Сначала проверяются условия допустимости, затем считается балл только допустимых ролей:
| Роль | Условия | Балл |
|---|---|---|
| consolidator | Не seed, не обрыв; вход >0; ≥3 плательщиков; C≤0,75; R≤0,6 | 0,4·N(in_deg)+0,3·(1−C)+0,3·clip(1−R,0,1) |
| distributor | ≥10 получателей, в том числе допустимо для seed | 0,7·N(out_deg)+0,3·N(out_kzt) |
| transit | Не seed, не обрыв; есть вход и выход; 0,8≤R≤1,2 | 0,7·max(0,1−abs(R−1)/0,2)+0,3·N(in_kzt) |
| terminal | Не seed, не обрыв; исходящих 0; вход ≥50 000 KZT; не выполнены условия consolidator | 0,6+0,4·N(in_kzt) |
| coordinator | Не seed; есть вход и выход; depth≥2; достижим от ≥2 seed; N(B)≥0,7 | 0,7·N(B)+0,3·N(n_seed_sources) |
| peripheral | Ни одна допустимая роль не набрала 0,5 | 0 для изолята, иначе 0,49·(1−лучший специальный скор) |
Если максимальный специальный скор ≥0,5, выбирается его роль. Равенства разрешаются
в порядке coordinator, consolidator, distributor, transit, terminal. Специальное правило
сбора с многих имеет приоритет допустимости над общим признаком отсутствия исходящих.
role_runner_up — второй специальный кандидат с положительным скором; none, если его нет.
Для peripheral отдельно сохраняется лучший специальный кандидат; fallback-скор — не уверенность
в безвредности и не сопоставим с вероятностью. Обоснования порогов находятся в config.yaml.
Достижимость от seed исключает сам seed даже при наличии цикла. Это структурный признак: он не доказывает, что те же деньги прошли весь путь, и не проверяет временной порядок.
Кластеры и приоритет
Louvain применяется к неориентированной проекции: встречные суммы складываются; self-loop не создаёт связь между участниками и исключается из проекции, но сохраняется в оборотах. Изоляты получают собственный кластер. resolution=1, random_seed=42. Кластеры нумеруются по убыванию размера, затем по минимальному gid. Внутренний оборот — сумма исходных направленных рёбер внутри группы, каждое учитывается один раз.
priority = 0,4·role_value + 0,2·N(n_seed_sources)
+ 0,2·N(in_kzt) + 0,2·N(betweenness)
Веса ролей: coordinator 1; consolidator 0,9; distributor 0,7; transit 0,5; terminal 0,4; peripheral 0,1. При равенстве приоритета первым идёт меньший gid. Все четыре вклада видны в карточке и why. Изолят также имеет минимальный ролевой вклад: это элемент очереди обзора, не положительный сигнал о нарушении.
Чувствительность порогов
Пороги — решение команды, а не истина. Чтобы это решение можно было проверить, каждый порог прогоняется по сетке значений и видно, сколько узлов меняют роль:
.\.venv\Scripts\python.exe tools\sensitivity.py --data data
Результат — out/sensitivity.csv. Что он показывает на текущих данных:
| Порог | Значение | Что меняется |
|---|---|---|
consolidator.min_in_degree | 2 / 3 / 4 / 5 / 8 | 176 / 85 / 42 / 23 / 6 точек консолидации |
terminal.min_in_kzt | 5 000 / 50 000 / 250 000 / 1 000 000 | 1037 / 472 / 119 / 4 конечных получателя |
distributor.min_out_degree | 5 / 10 / 20 / 60 | 63 / 56 / 25 / 8 распределителей |
coordinator.min_normalized_betweenness | 0.5 / 0.7 / 0.9 | 13 / 12 / 11 координаторов |
Выводы, которые из этого следуют и которые мы не скрываем:
- Координаторы устойчивы. Сдвиг порога на ±0.2 меняет состав на один узел — эта роль опирается на структуру сети, а не на удачно подобранное число.
- Конечный получатель — самая чувствительная роль. Порог по сумме меняет её численность
в 250 раз. Поэтому
terminalимеет низкий вес в приоритете (0.4) и никогда не присваивается узлу, обрезанному границей обхода. - Порог сбора выбран шире ориентира. В примечании к ТЗ названы узлы с входящими от 8–24 плательщиков; при пороге 8 мы получили бы 6 точек консолидации. Мы взяли 3, потому что сбор от трёх независимых контрагентов уже является наблюдаемым признаком, а приоритет всё равно сортирует их по объёму и охвату. Ориентир ТЗ воспроизводится: при пороге 8 остаются ровно те узлы, о которых говорят организаторы.
Временной профиль участника
transactions.parquet содержит даты, которых нет в агрегированных рёбрах. Два участника
с одинаковым месячным оборотом выглядят одинаково, пока не спросить, задержались ли
деньги. Признаки считаются в src/temporal.py и попадают в nodes_roles.csv:
| Признак | Смысл |
|---|---|
hold_days | медиана срока между поступлением и ближайшим последующим переводом дальше |
fast_pass_share | доля исходящих сумм, ушедших в течение двух дней после поступления |
fast_pass | больше половины исходящих ушло быстро — признак сквозного транзита |
same_day_inflows | максимум разных плательщиков, заплативших в один день |
synchronised_inflow | трое и более плательщиков в один день — синхронность, а не совпадение |
active_days, burst_ratio | сколько дней участник был активен и насколько выделяется пиковый день |
На предоставленных данных: срок удержания удалось наблюдать у 570 участников, медиана — 2 дня; признак сквозного транзита у 275, синхронные поступления у 38.
Эти признаки сообщаются рядом с ролью, но её не меняют. Решение осознанное: критерии ролей должны оставаться стабильными и проверяемыми, а месячная выборка одного банка слишком коротка, чтобы строить на сроках удержания само определение роли. Аналитик видит их в карточке участника как дополнительный сигнал.
Устойчивость сети: проверка самого рейтинга
Рейтинг существует, чтобы отвечать на вопрос «кого проверять первым». Значит, его нужно
проверить: src/resilience.py изымает из сети верх списка и измеряет, что осталось, —
и сравнивает это со случайным изъятием такого же числа участников, усреднённым
по нескольким розыгрышам с фиксированными сидами.
Если бы наш порядок был неинформативным, обе кривые совпали бы. На предоставленных данных изъятие 20 участников из верха списка даёт:
| Показатель | По приоритету | Случайно | Исходно |
|---|---|---|---|
| Крупнейший связный фрагмент | 1351 | 1841 | 1877 |
| Фрагментов сети (от двух участников) | 101 | 18.6 | 16 |
| Доля оборота в уцелевшем ядре | 63% | 91% | 94% |
От ядра сети отрезается на 490 участников больше, чем при случайном изъятии того же размера. Это измеримое подтверждение, что список указывает на структуру, а не просто на крупные суммы.
Полная кривая — в out/resilience.csv и во вкладке «Устойчивость сети». Оценка описывает
наблюдаемую сеть переводов за июль 2026 и не является прогнозом последствий каких-либо
действий в отношении клиентов.
Слой агентов (опционально)
Ядро решения детерминированное: роли, скоры, кластеры и приоритет считает код. Слой агентов включается отдельным флагом и переписывает только тексты поверх уже посчитанных чисел.
.\.venv\Scripts\python.exe -m pip install -r requirements-llm.txt
$env:OPENAI_API_KEY = "..."
.\.venv\Scripts\python.exe run.py --data data --out out --llm
Без флага и без ключа пайплайн полон: те же три CSV, те же роли, тексты из шаблонов. Проверяющему ключ не нужен.
| Агент | Что делает | Получает от |
|---|---|---|
EvidenceWriter | обоснование роли для приоритетных узлов | — |
ClusterAnalyst | гипотеза назначения кластера | — |
PriorityExplainer | объяснение позиции в очереди проверки | EvidenceWriter |
ComplianceCritic | проверка осторожности формулировок, право вернуть текст | все выше |
AnalystAssistant | вопрос на обычном языке → ответ по выгрузке через вызов функций | — |
Почему агентов несколько, а не один запрос: ComplianceCritic оценивает чужой текст со стороны
и отклоняет утверждающие формулировки — требование раздела 9 ТЗ закрыто механизмом, а не
инструкцией в промпте. Каждое отклонение и исправление попадает в out/llm_review.json.
Границы слоя заданы жёстко:
- модель не присваивает роли, не считает и не меняет ни одного числа — после прогона это проверяется сравнением расчётных колонок, расхождение останавливает экспорт;
- текст длиннее лимита ТЗ отбрасывается, остаётся офлайн-вариант — контракт выгрузки важнее красоты формулировки;
- любая ошибка API, отсутствие ключа или пакета означают возврат к офлайн-тексту, а не падение расчёта;
- вызовы идут только для топ-20 узлов и содержательных кластеров, не для всех 2 248 узлов.
Результат живого прогона
Слой проверен на выданном ключе, модель gpt-5.4-mini:
| Показатель | Значение |
|---|---|
| Вызовов писателей | 93 |
| Текстов принято от модели | 91 |
| Осталось офлайн-вариантом | 2 |
| Возвратов от критика формулировок | 47 |
| Время слоя | 40 с, полный прогон 44 с |
Пример возврата, который видно в логе: черновик «Признаки узла консолидации и распределения…»
критик отклонил с замечанием «вывод нужно подать как гипотезу для проверки» и переписал
в «Гипотеза о сети как узле сбора…». Каждый такой случай лежит в
docs/llm-run/llm_review.json — черновик, замечания критика, принятый текст.
Тексты, не уложившиеся в лимит ТЗ в 200 символов, отбрасываются и заменяются офлайн-вариантом: контракт выгрузки важнее формулировки. Поэтому писателю сообщается бюджет ниже жёсткого предела — перебор тогда всё равно укладывается.
В репозитории опубликован детерминированный прогон. out/ содержит результат обычного
запуска без ключа, чтобы проверка побайтовой воспроизводимости из раздела «Как проверить
решение за пять минут» сходилась. Результаты прогона со слоем агентов лежат отдельно,
в docs/llm-run/.
Модель задаётся в config.yaml. Семейства моделей расходятся в параметрах — новые не принимают
max_tokens и требуют max_completion_tokens, некоторые не разрешают своё значение temperature.
src/ai/client.py читает имя отклонённого параметра из ответа API, переименовывает или убирает
его и повторяет вызов, поэтому смена модели в конфигурации не требует правки кода.
AnalystAssistant не видит граф: он вызывает функции по готовой выгрузке
(node_card, neighbours, path_between, top_by_role, collectors_for) и называет gid,
на которых построен ответ. Список вызовов показывается рядом с ответом во вкладке «Ассистент».
Ограничения и следующий запрос
- Граница depth=4 без исходящих: terminal и признаки удержания запрещены; нужен дозапрос исходящих.
- Входящие seed неполны: балансные роли для них запрещены; нужны полные входящие.
- Любой terminal означает только отсутствие исходящих в этой выборке, не реальный остаток на счёте.
- Видны только внутрибанковские переводы за июль от 5 000 KZT. Мелкие, внешние и более поздние переводы неизвестны.
- Маленькая компонента сама по себе не означает нехватку данных. Изоляты явно помечены.
- Обзор показывает максимум 80 узлов. Поиск любого gid показывает соседей первого колена; если их больше лимита, есть уведомление и полный список прямых связей таблицей.
- ИИ, временные признаки, устойчивость к порогам и оценка удаления узлов пока отсутствуют.
Проверки
.\.venv\Scripts\python.exe -m pip install -r requirements-dev.txt
.\.venv\Scripts\python.exe -m pytest -q
.\.venv\Scripts\python.exe run.py --data data --out out-check
20 тестов: неверные суммы/количества, неизвестный конец ребра, дубликаты, отсутствующий файл, цепь, сбор, веер, цикл, seed, обрыв, все изоляты, повторяемость при перестановке входа, целостность артефактов, отсутствие CDN, сценарии Streamlit, кривая разрушения сети, соответствие обязательным требованиям ТЗ, а также слой агентов: деградация без ключа, побайтовое совпадение выгрузок с флагом --llm и без него, обязательный проход через критика формулировок. Реальные три CSV повторного прогона совпали побайтово. Просмотр графа и поиск проверены дополнительно в Chrome. requirements-lock.txt фиксирует всё установленное окружение, включая тестовые зависимости; его можно использовать вместо requirements.txt для повторения этой среды.
Схема
flowchart LR
A[3 файла Parquet] --> B[Проверки схем и агрегатов]
B --> C[Направленный граф со всеми узлами]
C --> D[Метрики и ограничения наблюдения]
D --> E[Допустимость и скоры ролей]
C --> F[Louvain на проекции]
E --> G[Приоритет и объяснения]
F --> G
D --> T[Временной профиль из транзакций]
T --> H
G --> R[Устойчивость: изъятие топ-N против случайного]
R --> H
G --> H[3 CSV и артефакты просмотра]
H --> I[Граф, поиск, карточка и топ-20]
G -. флаг --llm .-> J[Агенты: тексты и критик формулировок]
J -. только тексты .-> H
Масштабирование до миллиона узлов
Текущий NetworkX и точное посредничество предназначены для объёма кейса. При росте: колоночная обработка DuckDB/Polars, графовый движок igraph/graph-tool, сэмплирование посредничества, ограничение визуализации окружением узла. Достижимость от многих seed потребует оптимизации через компоненты сильной связности и пакетные множества достижимости. Нормировки и глобальные метрики изменяются с сетью: инкрементальные расчёты требуют отдельной проверки, простое добавление новых рёбер не сохраняет автоматически старые роли. Это план развития; масштаб на миллионе узлов не тестировался.
Исходное ТЗ, план, решения, задачи. API алгоритмов сверены с NetworkX, проверка интерфейса использует Streamlit AppTest.
Если Chrome блокирует скачивание CSV
По умолчанию расчёт сохраняет nodes_roles.csv, top_nodes.csv и clusters.csv в out/.
Кнопки скачивания в интерфейсе создают дополнительную копию. В боковой панели
«Готовые результаты» → «Где лежат готовые CSV» показан полный путь к папке
на компьютере, где запущено приложение; он учитывает GRAPH_OUT_DIR.
При локальном запуске эту папку можно открыть в Проводнике.
Сообщение «Ваша организация заблокировала файл» означает ограничение скачивания политикой безопасности. Оно не свидетельствует об ошибке расчёта или CSV. Причину ограничения должен проверить администратор браузера или компьютера: Chrome поддерживает блокировку скачивания политиками. Приложение не меняет настройки защиты браузера.
Состояние работ
Все обязательные требования кейса закрыты: воспроизводимый пайплайн, роль и обоснование у каждого из 2 248 узлов, документированные критерии с порогами, кластеризация и топ-лист с экраном просмотра. Из необязательных реализованы учёт границы обхода, оценка нехватки данных, чувствительность порогов и слой агентов с ассистентом. Измерения: Windows, Python 3.13, полный прогон 4.5 с при лимите 300 с. Время импорта интерпретатора в таймер не входит; полный запуск команды — менее 6 секунд.
Авторы коммитов
История коммитов
- 2026-09-23 17:45feat: publishable static copy of the reportzhastunulani
- 2026-09-23 17:41feat: run the agent layer against the live API and pin what it provedzhastunulani
- 2026-09-23 17:17test: check the solution against the brief mechanicallyzhastunulani
- 2026-09-23 17:15feat: timing profile, network resilience check and a redesigned viewerzhastunulani
- 2026-09-23 16:28feat: agent layer, threshold sensitivity and jury-ready READMEzhastunulani
- 2026-09-23 15:16fix: explain blocked downloads and locate existing CSV resultszhastunulani
- 2026-09-23 15:09fix: preserve artifact hashes across Windows checkoutszhastunulani
- 2026-09-23 15:09feat: implement reproducible offline graph analysis and viewerzhastunulani
Все коммиты (12)
- 2026-09-23 14:45Merge remote-tracking branch 'origin/main'zhastunulani
- 2026-09-23 14:45docs: refine implementation plan and analytical contractszhastunulani
- 2026-09-23 14:41Add files via uploadzhastunulani
- 2026-09-23 14:371zhastunulani
Структура репозитория (76 файлов)
.gitattributes .gitignore .streamlit/config.toml AGENTS.md ARCHITECTURE.md CASE-TZ.md CASE.md CODEX-TASKS.md DECISIONS.md DEMO.md PITCH.md PLAN.md PRODUCT.md README.md README.template.md app.py config.yaml data/README.md data/edges.parquet data/nodes.parquet data/transactions.parquet docs/.nojekyll docs/data/clusters.csv docs/data/nodes_roles.csv docs/data/resilience.csv docs/data/top_nodes.csv docs/graph.html docs/index.html docs/llm-run/llm_review.json docs/llm-run/summary.json out/clusters.csv out/node_features.parquet out/nodes_roles.csv out/resilience.csv out/run_manifest.json out/sensitivity.csv out/top_nodes.csv out/viewer_edges.parquet pytest.ini requirements-dev.txt requirements-llm.txt requirements-lock.txt requirements.txt run.py src/__init__.py src/ai/__init__.py src/ai/agents.py src/ai/client.py src/ai/orchestrator.py src/ai/tools.py src/clusters.py src/config.py src/export.py src/features.py src/gaps.py src/graph.py src/load.py src/narrate.py src/priority.py src/resilience.py src/roles.py src/temporal.py src/ui.py src/viewer.py starter/README.md starter/requirements.txt starter/starter.py tests/test_ai_layer.py tests/test_app.py tests/test_compliance.py tests/test_pipeline.py tests/test_result_access.py tools/__init__.py tools/build_site.py tools/compliance.py tools/sensitivity.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.