О проекте
Команда: Menelai
Кейс HackAlem.ai: Финансы — Freedom
Стадия: Доведённый продукт
Задача кейса: «Граф денег»: восстановить структуру организованной группы по сети транзакций (AML)
Суть
Moneylai: объяснимый анализ сети переводов для AML-аналитика. Шесть ролей клиентов по правилам, кластеры, очередь приоритетной проверки, схема сети, аналитика цепочек, циклов и времени, ассистент с вызовом инструментов графа и экспорт трёх CSV.
Как устроено
Python-пайплайн проверяет схему трёх Parquet, строит направленный граф и считает признаки: суммы, степени, PageRank, betweenness, короткие циклы, seed_share (пропорциональное смешивание потоков), fast_out_share (выход в течение 2 дней). Роли coordinator/consolidator/distributor/transit/terminal/peripheral назначаются каскадом правил с порогами по перцентилям. priority_score равен среднему перцентильных рангов, умноженному на вес роли и поправку за слабую связь с seed. Кластеры строятся Louvain. Сервер на TS отдаёт результаты в React-клиент, ассистент отвечает через вызовы инструментов графа. Правила доработаны по итогам LLM-симуляции жюри.
Ключевые решения
- PageRank
- betweenness centrality
- Louvain
- percentile thresholds
- rule cascade
- short cycle detection
- flow-mixing seed share
- sensitivity analysis
- LLM jury simulation
- tool calling
Метрики
Симуляция жюри на v3: 65/0/0 (66 gid); 2248 клиентов → 24 coordinator, 63 consolidator, 20 distributor, 20 transit, 193 terminal; 91 кластер
Особенности
Правила ролей проверялись LLM-симуляцией жюри (gpt-5.6-sol): критика правил и проверка evidence во всех 2248 строках, потом переработка до v3. Для каждого порога указан перцентильный источник. Есть анализ устойчивости при удалении топ-узлов и синтетические наборы для сверки.
Стек
Python · Parquet · TypeScript · React · Vite · Bun · shadcn · Docker · Railway
Модели
gpt-5.6-sol
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-a5bc9531-menelai.
README
README.mdMoneylai
Объяснимый анализ транзакционной сети для AML-аналитика. Moneylai помогает разобраться в графе банковских переводов: кто собирает средства, кто передаёт их дальше, какие группы связаны между собой и кого стоит проверить первым. Для каждого клиента решение показывает роль, приоритет и численное обоснование, которое можно сопоставить с исходными переводами.
Проект создан для кейса HackAlem AI «Граф денег». Встроенный пример — обезличенная выгрузка организаторов: 2 248 клиентов, 3 119 направленных связей, 4 840 переводов, 81 исходный клиент (seed) за июль 2026 года.
Роли и приоритеты — гипотезы для аналитической проверки. Структура переводов сама по себе не доказывает причастность клиента к противоправной деятельности.
Развёрнутая версия: moneylai.up.railway.app.
Возможности · Метод · Запуск · Сценарий для жюри · Тесты и эвалы · Риски
Что реализовано
| Возможность | Что получает аналитик |
|---|---|
| Загрузка данных | Три файла Parquet → проверка схемы → расчёт → новая активная выгрузка. Можно переключаться между наборами и удалять загруженные наборы |
| Роли клиентов | Одна из шести ролей по явным правилам: coordinator, consolidator, distributor, transit, terminal, peripheral |
| Очередь проверки | До 30 клиентов по priority_score, фильтр по роли, объяснение why, переход к карточке клиента |
| Карточка клиента | Значения метрик рядом с порогами правила, суммы и количество переводов, плательщики и получатели, связь с seed, циклы и временные признаки |
| Схема сети | Направленные связи, поиск по полному gid или его окончанию, фильтры, раскраска, масштабирование и выбор узла |
| Группы клиентов | Кластеры, их состав, внутренний оборот, крупнейшие узлы и гипотезы; карта потоков между группами |
| Аналитические проверки | Повторяющиеся цепочки, короткие циклы, активность по дням, синхронные поступления, быстрый вывод, нетипичные входящие суммы, изменение связности при удалении приоритетных узлов |
| Полнота данных | Видимые границы выгрузки и подсказки, какие дополнительные операции запросить |
| Ассистент | Вопрос на русском → вызовы инструментов графа → ответ и видимая цепочка вызовов. Формы поиска путей и общих получателей работают без LLM |
| Экспорт | Три обязательных CSV для жюри и дальнейшего анализа |
Основные экраны: /, /top, /nodes/:gid, /graph, /network, /clusters, /analysis, /assistant, /upload, /method/rules, /method/architecture. В разделе /analysis доступны вкладки routes, time, anomalies, resilience, completeness.
Реализация интерфейса — client/src/pages, вычислений API — server/src/data, назначения ролей — pipeline/run.py.
Как работает решение
- Выбрать входные данные. При запуске открывается демо организаторов. Для своей выгрузки аналитик передаёт
nodes.parquet,edges.parquetиtransactions.parquetчерез экран «Данные». - Проверить и рассчитать. Python-пайплайн сверяет схему и согласованность файлов, строит направленный граф, вычисляет структурные и временные признаки, назначает роли, кластеры и приоритеты.
- Получить очередь проверки. Аналитик открывает приоритетного клиента и видит фактические значения рядом с условиями сработавшего правила.
- Проверить контекст. На схеме и в карточке можно проследить контрагентов, суммы и число переводов; в аналитике — цепочки, циклы, синхронность и границы наблюдения.
- Сформировать следующую гипотезу. Поиск путей и ассистент помогают задавать вопросы к уже рассчитанным данным. Аналитик решает, какие операции или периоды запросить дополнительно, и скачивает CSV.
Повторная загрузка тех же трёх файлов определяется по хешу содержимого и активирует существующий набор без пересчёта. Кнопка обновления интерфейса повторно запрашивает API; расчёт запускается при загрузке нового набора либо отдельной командой пайплайна.
Данные и результаты
Вход
Источник демо — файлы организаторов в task/data. Описание полей — task/README.md.
| Файл | Поля | Объём демо |
|---|---|---|
nodes.parquet | gid, depth, is_seed | 2 248 клиентов |
edges.parquet | src, dst, sum_kzt, n_tx, depth | 3 119 пар плательщик → получатель |
transactions.parquet | src, dst, date, sum_kzt | 4 840 переводов |
Период демо — 1–31 июля 2026 года, общий объём видимых переводов — 365 890 012,01 ₸. Граф собран по исходящим переводам от 81 seed на четыре колена, только внутри одного банка и только для сумм от 5 000 ₸. Число клиентов по коленам 0–4: 81 / 472 / 462 / 789 / 444.
gid — идентификатор, а не число для арифметики: в TypeScript и JSON он хранится строкой, в Parquet и Python — как int64. Это сохраняет точность значений, превышающих Number.MAX_SAFE_INTEGER.
Внешнее обогащение клиентских данных не используется. task/starter содержит исходный пример организаторов; рабочий расчёт выполняет собственный pipeline/run.py.
Выход
Результаты демо сохранены в out и пересчитываются из исходных Parquet одной командой.
| Файл | Схема | Строк данных в демо, без заголовка |
|---|---|---|
| nodes_roles.csv | gid, role, role_score, cluster_id, priority_score, evidence | 2 248 |
| clusters.csv | cluster_id, n_nodes, n_seed, sum_kzt_internal, top_gids, hypothesis | 91 |
| top_nodes.csv | rank, gid, role, priority_score, why | 30 |
evidence содержит численные основания и занимает не более 200 символов. role_score и priority_score лежат в диапазоне 0–1. В результат включены все клиенты входного набора, в том числе 19 seed без рёбер.
Дополнительно создаются node_metrics.csv, edge_metrics.csv и node_metrics.json. JSON содержит подробные признаки каждого клиента и _meta: рассчитанные пороги, веса, распределение ролей, период и параметры выгрузки. Сервер использует этот JSON вместе с clusters.csv и top_nodes.csv; при скачивании отдаёт три обязательных CSV. Подробная схема — docs/PIPELINE_CONTRACT.md.
Как назначаются роли и приоритет
Метрики и правила
Роли вычисляются кодом, без LLM. Пайплайн рассчитывает отдельные показатели суммы и количества переводов, числа контрагентов, PageRank, посредничества (betweenness), коротких циклов, связи с seed и времени операций. Роль назначается по условиям таблицы ниже; PageRank и количество переводов служат дополнительным контекстом в карточках и объяснениях и не входят в каскад ролей или формулу приоритета.
pass_through = out_kzt / in_kzt — отношение видимого выхода к видимому входу. seed_share — оценка доли средств от seed в модели пропорционального смешивания потоков. fast_out_share — доля исходящей суммы, перед которой был входящий перевод в пределах двух дней; это временной признак, а не сопоставление конкретных денежных единиц.
Правила проверяются сверху вниз, назначается первая подходящая роль. Часть порогов определяется перцентилями входных данных с нижней границей; долевые и временные пороги заданы явно. Ниже — значения для встроенной выгрузки, воспроизведённые текущим кодом.
| Порядок | Роль | Условие | Клиентов |
|---|---|---|---|
| 0 | peripheral с no_data | Последнее колено без исходящих либо seed без единого ребра: экономическая роль не определяется | 463 |
| 1 | coordinator | ≥3 плательщика, ≥3 получателя, посредничество ≥0,00115965; дополнительно цикл длиной ≤5, либо ≥3 seed выше по потоку, либо сам seed с поступлением от другого seed | 24 |
| 2 | consolidator | ≥3 плательщика, пропуск <0,3, вход ≥215 000 ₸, доля крупнейшего плательщика <80% | 63 |
| 3 | distributor | ≥16 получателей и число получателей ≥3 × число плательщиков | 20 |
| 4 | transit | Есть вход и выход, пропуск 0,8–1,2, вход ≥100 000 ₸, быстрый выход ≤2 дней составляет ≥50%; клиент не seed | 20 |
| 5 | terminal | Колено 1–3, нет исходящих либо пропуск <0,3, вход ≥215 000 ₸; клиент не seed | 193 |
| 6 | peripheral | Остальные узлы: ни одно из перечисленных правил не выполнено | 1 465 |
Итого peripheral — 1 928 клиентов, включая 463 случая no_data. Последний наблюдаемый hop определяется из данных; для демо 444 узла четвёртого колена не получают роль terminal только из-за отсутствия исходящих.
Источники порогов: число плательщиков — p95; исходящая степень coordinator — p70, distributor — p95; посредничество coordinator — p95 среди узлов с входом и выходом; вход consolidator/terminal — p80; вход transit — p60 с нижней границей 100 000 ₸. Полные условия и формулы — pipeline/run.py и pipeline/RULES.md.
Что означают оценки
role_score показывает запас выполнения правила. Для назначенной роли используется 0,5 + 0,5 × средний нормированный запас по числовым условиям. Для обычной периферии оценка зависит от доли выполненных условий ближайшей роли (nearest_role). Для no_data значение 1 означает уверенность в отсутствии достаточных данных для назначения экономической роли.
priority_score определяет порядок ручной проверки:
base = mean(
percentile_rank(seed_money_in),
percentile_rank(n_seed_upstream),
percentile_rank(in_kzt; для seed — out_kzt),
percentile_rank(betweenness)
)
priority_score = base × вес_роли × поправка_на_слабую_связь_с_seed
Веса: coordinator — 1,0; consolidator — 0,9; distributor — 0,7; transit — 0,6; terminal — 0,5; peripheral — 0,2; no_data — 0,5. Для coordinator, consolidator, distributor и terminal с seed_share < 10% устанавливается weak_seed_link, приоритет умножается на 0,7, а объяснение допускает легального контрагента. Обе оценки являются техническими показателями правил, а не вероятностью виновности.
Кластеры
Louvain применяется к неориентированной проекции графа: вес пары — сумма переводов в обе стороны, случайное зерно фиксировано (seed=42). Для встроенных данных получен 91 кластер, из них 72 содержат более одного узла. Направление сохраняется при расчёте ролей, потоков и путей, но не используется самим Louvain. Принадлежность к одному кластеру означает связанность в наблюдаемой сети, а не установленную принадлежность к одной организации.
Технологии и архитектура
| Компонент | Технологии и назначение |
|---|---|
| Расчёт | Python ≥3.12, pandas, NumPy, PyArrow, NetworkX, SciPy; окружение и lock-файл — uv |
| API | TypeScript, Bun, Hono; hyparquet читает Parquet, ZSTD распаковывается через Bun.zstdDecompressSync; d3-force рассчитывает расположение узлов |
| Интерфейс | React 19, React Router 8, Vite 8, TypeScript 6, Tailwind CSS 4, shadcn/ui на Base UI, Lucide, Sonner, Canvas 2D |
| AI-ассистент | OpenAI Responses API; модель по умолчанию gpt-5.6-sol, настраивается через OPENAI_MODEL |
| Проверки | pytest, встроенные проверки Python, анализ чувствительности, отдельные скрипты LLM-эвальвации, TypeScript и oxlint |
| Развёртывание | Docker, Railway; Bun workspaces для клиента и сервера |
flowchart LR
D[Три Parquet] --> P[Python: метрики, правила, кластеры, приоритет]
P --> O[CSV и node_metrics.json]
D --> S[Bun + Hono]
O --> S
S -->|REST API| U[React: очередь, граф, карточки, проверки]
U -->|Загрузка Parquet| S
S -->|Подпроцесс uv| P
U -->|Вопрос аналитика| A[Ассистент]
A <--> L[OpenAI Responses API]
A <--> T[Локальные инструменты графа]
S --> T
client/ интерфейс, маршруты страниц, типизированные запросы API
server/src/routes/ HTTP API: граф, клиенты, результаты, аналитика, наборы, ассистент
server/src/data/ загрузка наборов, реестр, раскладка графа, аналитика, инструменты
pipeline/run.py полный расчёт из Parquet в CSV/JSON
pipeline/tests/ проверки на небольшом графе с известными ожидаемыми ролями
pipeline/sensitivity.py изменение порогов и сравнение ролей/топ-20
pipeline/eval/ снимки v1/v3, LLM-эвалы, сырые ответы и журналы расхода
synthetic/ два синтетических набора, генератор, обход и аудит
experiments/ исследовательский notebook probe-0.ipynb
task/data/ неизменяемые входные данные организаторов
out/ сохранённые результаты для демо
data/datasets/ пользовательские загрузки; каталог исключён из Git
docs/ контракт данных, методология, требования и схема решения
Dockerfile, railway.json конфигурация развёртывания
Пайплайн — единственный компонент, назначающий роли и приоритет. Сервер читает готовые результаты, строит раскладку, аналитику и индексы запросов. store.ts держит реестр наборов и один активный набор. При загрузке новые файлы сохраняются в data/datasets/<id>/raw, результаты — в out внутри того же каталога, после чего набор становится активным.
API и внешние интеграции
| Метод и путь | Назначение |
|---|---|
GET /api/health, GET /api/stats | Проверка сервиса и сводка активного набора |
GET /api/graph, GET /api/transactions, GET /api/nodes/:gid | Граф, переводы, карточка клиента |
GET /api/top, GET /api/clusters, GET /api/meta | Приоритеты, кластеры, правила и пороги |
GET /api/export/:file | Один из трёх обязательных CSV |
GET /api/analytics/:key | time, routes, anomalies, resilience, completeness |
GET /api/datasets, POST /api/datasets | Список наборов и загрузка трёх Parquet |
POST /api/datasets/:id/activate, DELETE /api/datasets/:id | Переключение и удаление пользовательского набора |
GET /api/assistant/status | Доступность LLM и список инструментов |
POST /api/assistant/tools/:name | Прямой запрос к инструменту без LLM |
POST /api/assistant/ask | Свободный вопрос через OpenAI |
Ассистент может вызывать семь инструментов: node_metrics, neighbors, common_successors, paths, sync_events, query_nodes, cluster_summary. Модель получает вопрос и результаты вызовов, формулирует ответ; сохранённые роли и CSV она не изменяет. Код задаёт требование брать числа из инструментов, а UI показывает цепочку вызовов для проверки ответа.
Ключ OpenAI нужен только для свободных вопросов и повторных внешних LLM-эвальваций. Расчёт, экспорт, граф и прямые инструменты работают локально без ключа. Шрифты Unbounded и IBM Plex Sans/Mono подключены через Google Fonts. Подключения к банковской системе и базы данных в текущем приложении нет: источником служат файлы.
Установка и запуск
Что требуется
- Bun для клиента и API. При подготовке README команды проверены на Bun 1.4.2; сервер использует Bun API и не запускается как обычное Node.js-приложение.
- uv и Python ≥3.12 для пересчёта и загрузки новых наборов. Проверки ниже выполнены на Python 3.13.13; Docker-образ устанавливает Python 3.12.
- Сеть для первоначальной установки зависимостей. Версии зафиксированы в
bun.lockиpipeline/uv.lock.
Локальный запуск
git clone https://github.com/BAITC-Hacks/hack-a5bc9531-menelai.git
cd hack-a5bc9531-menelai
bun install --frozen-lockfile
Подготовить окружение пайплайна и пересчитать демо одной командой из корня репозитория:
(cd pipeline && uv sync --frozen && uv run python run.py)
Команда читает task/data, создаёт out и в конце выводит ПРОВЕРКИ: все assert'ы пройдены, распределение ролей и время. Для просмотра демо уже сохранённых файлов out достаточно; повторный запуск позволяет проверить воспроизводимость. Для загрузок через UI uv должен быть доступен серверу в PATH.
Запустить API и интерфейс:
bun run dev
Открыть http://localhost:5173. API работает на http://localhost:3001, Vite проксирует к нему /api.
Для свободных вопросов ассистенту создайте корневой .env по образцу .env.example и заполните OPENAI_API_KEY; при необходимости задайте OPENAI_MODEL. Если .env уже существует, редактируйте его без перезаписи. Файл исключён из Git. Без ключа чат отключён, прямые инструменты доступны.
Пересчёт другой выгрузки:
cd pipeline
uv run python run.py --data /путь/к/parquet --out /путь/для/результатов
Сборка и запуск в одном сервисе
Из корня репозитория:
bun run build
bun run start
Сервер отдаёт /api и собранный client/dist на http://localhost:3001, включая переходы по адресам страниц SPA.
| Переменная | Назначение / значение по умолчанию |
|---|---|
PORT | Порт API, 3001 |
DATA_DIR | Исходные Parquet демо, task/data |
OUT_DIR | Рассчитанные результаты демо, out |
DATASETS_DIR | Каталог загрузок, data/datasets |
API_PROXY | Адрес API для Vite при разработке, http://localhost:3001 |
OPENAI_API_KEY | Серверный ключ для свободных вопросов |
OPENAI_MODEL | Модель ассистента, gpt-5.6-sol |
Пути к данным по умолчанию вычисляются относительно файлов сервера. При изменении используйте абсолютные пути. Встроенный набор всегда имеет id demo.
Как проверить решение
Демонстрация для жюри
- Открыть главную. Выбрать демо и сверить сводку: 2 248 клиентов, 3 119 связей, 4 840 переводов, 81 seed. Скачать три CSV.
- Открыть первого клиента очереди —
100000003684369100. Рольcoordinator, приоритет 0,9917, 24 плательщика и 62 получателя. В карточке сопоставить порог посредничества с фактом и показать оговорку о неполном входе у seed. - Открыть
100000003115284100. Рольconsolidator, приоритет 0,8849: вход 2 160 500 ₸ от 8 плательщиков, выход 517 000 ₸ двум получателям, пропуск 0,24, доля крупнейшего плательщика 29%. Проверить каждое условие правила. - В «Ассистенте» открыть форму поиска путей. Указать первый gid как источник, второй — как получателя. Среди результатов есть прямое направленное ребро на 193 000 ₸, 2 перевода. Этот сценарий не требует OpenAI.
- Проверить границу наблюдения —
100000003220685100. Это узел четвёртого колена с входом 1 306 000 ₸. Он помеченperipheralс объяснением «нет данных» и запросом исходящих, поскольку обход остановлен на этом колене. - Открыть «Проверки» → «Полнота данных» и «Маршруты и циклы». Сопоставить гипотезы с охватом данных и циклами. Затем на экране «Данные» загрузить три исходных Parquet, дождаться расчёта и проверить новую активную выгрузку.
Проверка через API
Команды выполняются из корня при запущенном сервере:
curl -fsS http://localhost:3001/api/health
# {"ok":true}
curl -fsS http://localhost:3001/api/stats
# nodes=2248, edges=3119, transactions=4840, seeds=81, clusters=91
curl -fsS http://localhost:3001/api/nodes/100000003684369100
curl -fsS http://localhost:3001/api/assistant/tools/paths \
-H 'Content-Type: application/json' \
-d '{"src":"100000003684369100","dst":"100000003115284100","max_len":4}'
curl -fsS http://localhost:3001/api/export/nodes_roles.csv -o /tmp/nodes_roles.csv
Загрузка и возврат к демо:
curl -fsS http://localhost:3001/api/datasets \
-F nodes=@task/data/nodes.parquet \
-F edges=@task/data/edges.parquet \
-F transactions=@task/data/transactions.parquet \
-F name=jury-check
curl -fsS -X POST http://localhost:3001/api/datasets/demo/activate
Успешная загрузка возвращает { dataset, log } и активирует набор. log содержит последние 40 строк вывода пайплайна. Ошибки входных файлов возвращаются как HTTP 400 с пояснением; без результатов расчёта API ролей, кластеров и аналитики возвращает 503.
Тесты, прогоны и эвалы
Проверки отвечали на разные вопросы: соблюдается ли контракт выгрузки; правильно ли реализованы правила; повторяется ли расчёт; насколько результат зависит от порогов; понятны ли объяснения; насколько синтетические данные пригодны для дальнейших испытаний. Их результаты не объединяются в одну «оценку точности».
1. Полный прогон и проверки контракта
Зачем. Проверить путь от исходных Parquet до обязательных артефактов и убедиться, что интерфейс получает согласованные данные.
Как. load() в pipeline/run.py проверяет обязательные файлы и колонки, уникальность клиентов, наличие seed, концы рёбер и совпадение пар и сумм edges с агрегированными transactions. После расчёта checks() проверяет количество и уникальность строк, словарь ролей, диапазоны оценок, длину evidence, принадлежность к кластерам, обработку отсутствующих данных, порядок топа и ссылки между CSV/JSON.
Что получили. При повторной проверке текущей версии 23.09.2026 два запуска run.py завершились сообщением ПРОВЕРКИ: все assert'ы пройдены за 4,3 и 4,5 секунды по внутреннему таймеру программы. Получены 2 248 строк ролей, 91 кластер и 30 строк приоритета. Установка зависимостей в эти времена не входит.
Как использовали. Проверки встроены в обычный запуск, включая загрузку через API: вычисление должно завершиться успешно прежде, чем новый набор будет зарегистрирован и показан пользователю.
2. Регрессионные тесты на небольшом графе
Зачем. Проверять конкретные экономические паттерны и случаи неполных данных с заранее известным ожидаемым результатом.
Как. pipeline/tests/test_synthetic.py строит искусственный граф на 62 узлах. Три теста проверяют назначение ролей, признаки паттернов и веса приоритета. В сценариях есть быстрый транзит и похожий на него медленный вывод, сборщик, веер, координатор с циклом, конечный получатель, легальный магазин с аналогичной структурой, доминирующий плательщик, обрезанный узел и изолированный seed.
(cd pipeline && uv run pytest tests -q)
Что получили. Повторный запуск при подготовке README: 3 passed in 1.28s. Проверяются, в частности, нулевая seed-доля у изолированного seed, текст «0 вход., 0 исход.», запрос исходящих у обрезанного узла, max_payer_share, поправка 0,7 для слабой связи с seed и отдельный вес no_data.
Как использовали. Эти проверки закрепляют поведение правил и изменения после разбора эвалов. Это регрессионная проверка реализации на сконструированных примерах; она не заменяет оценку на независимо размеченных банковских данных.
3. Повторяемость расчёта
Зачем. Убедиться, что одна и та же выгрузка и версия кода дают один результат для демонстрации и проверки жюри.
Как. Пайплайн дважды запущен в разные временные каталоги; затем сравнили содержимое файлов. Louvain использует фиксированный seed=42.
(cd pipeline && uv run python run.py --out /tmp/moneylai-run-1)
(cd pipeline && uv run python run.py --out /tmp/moneylai-run-2)
for file in nodes_roles.csv clusters.csv top_nodes.csv node_metrics.csv edge_metrics.csv node_metrics.json
do
cmp "/tmp/moneylai-run-1/$file" "/tmp/moneylai-run-2/$file"
done
Что получили. Все шесть файлов совпали побайтно между двумя прогонами и с текущими файлами out в репозитории. cmp при совпадении ничего не печатает.
Как использовали. Жюри может пересоздать те же артефакты и проверить объяснение любого gid по тем же метрикам. Повторяемость относится к зафиксированным данным, коду и окружению; сама по себе она не доказывает экономическую истинность роли.
4. Чувствительность к порогам
Зачем. Оценить риск изменения роли или очереди проверки при выборе близкого порога.
Как. pipeline/sensitivity.py меняет 15 параметров по одному в обе стороны на 20%, затем все пороги одновременно в сторону более мягких и более строгих условий. Получается 30 одиночных и 2 совместных варианта, плюс базовый. Степенные пороги сдвигаются минимум на единицу: переход 3 → 4 плательщика фактически равен +33%, что явно отмечено в отчёте. Метрики графа сохраняются, пересчитываются роли и приоритет.
До прогона заданы критерии: Jaccard состава топ-20 ≥0,80 и не более 2% клиентов со сменой роли в каждом одиночном варианте. Jaccard — размер пересечения списков, делённый на размер их объединения.
mkdir -p /tmp/moneylai-sensitivity
(cd pipeline && uv run python sensitivity.py --out /tmp/moneylai-sensitivity)
Что получили. Повторный расчёт совпал с out/sensitivity.md побайтно.
| Изменение | Jaccard топ-20 | Сменили роль |
|---|---|---|
| Минимум плательщиков consolidator: 3 → 4 | 0,67 | 32 |
| Минимальный вход terminal: 215 000 → 172 000 ₸ | 1,00 | 49 |
| Все условия мягче | 0,67 | 134 |
| Все условия строже | 0,54 | 102 |
Как использовали. Критерий выполнен не для всех одиночных вариантов: выявлен риск на границе consolidator/terminal. При переходе 3 → 4 четыре сборщика становятся terminal и покидают топ-20. Для проверки таких случаев интерфейс показывает факты рядом с порогами, а приоритет следует использовать как очередь ручной проверки. Анализ не устанавливает новые пороги автоматически; базовые правила и сам отчёт доступны для воспроизведения.
5. LLM-эвалы: симуляция жюри и проверка объяснений
Зачем. Проверить, можно ли защитить роль конкретного клиента числами, найти неоднозначности методологии и подготовить вопросы для демонстрации. Модель выступала критиком решения; назначение ролей оставалось детерминированным.
Как организовали. Скрипты pipeline/eval/jury.py, evidence_check.py и llm.py используют OpenAI Responses API. В сохранённых журналах вызовов указана gpt-5.6-sol; код предусматривает резервную gpt-5.5. Правила и данные заморожены в snapshot_v1 и snapshot_v3, ответы и расход сохранены отдельно.
Проводились три задачи:
- Критика методологии. Модель получила правила, разделы ТЗ и сводку данных и сформулировала замечания к порогам, интерпретации неполной выгрузки и доказательности ролей. Этот этап выполнен на v1; в v3 его не повторяли. Оценка «5/10» в исходном ответе — мнение симулированного жюри, а не оценка реального жюри и не метрика классификации.
- Защита отдельного gid. Модель получила карточку: роль, метрики, evidence, крупнейших контрагентов, циклы и синхронные дни; для топа — ещё
whyи ранг. Ответ — «да / частично / нет» на вопрос об обоснованности роли по правилам и числам, альтернативная роль и уточняющий вопрос. Затем вопросы сгруппированы по темам для подготовки защиты. - Проверка evidence по всей выгрузке. Первый слой — код: наличие цифр, длина ≤200, префикс роли, нежелательные утверждения о виновности и сверка отдельных чисел с метриками. Второй — модель: соответствие правилам, числам и гипотезной формулировке. В каждом прогоне обработано 2 248 строк, 57 пакетов максимум по 40 строк, с восемью параллельными исполнителями.
Выборка защиты. В v1 взяли топ-30, по пять случайных узлов каждой роли (random_state=42) и 12 специальных случаев: обрезанные узлы, изолированные seed, сборщики со слабой seed-связью и terminal с малым числом плательщиков. После удаления пересечений — 66 gid. В v3 использованы топ-30, по пять случайных на роль и девять случаев no_data, weak_seed_link и terminal с доминирующим плательщиком — 65 gid после удаления пересечений.
Что получили. Итоги ниже пересчитаны из сохранённых JSON/CSV, включая sim_results.json и sim_results_v3.json.
| Проверка | v1 | v3 |
|---|---|---|
| Уникальных gid в защите | 66 | 65 |
| Роль обоснована: «да» | 54 | 65 |
| «Частично» | 12 | 0 |
| «Нет» | 0 | 0 |
| Строк evidence с замечаниями кодовой проверки | 19 | 0 |
| Замечаний модели к evidence | 21 по 21 gid | 1 по 1 gid |
В v1 код нашёл 19 объяснений изолированных seed без чисел. Модель также указала эти строки и два случая округления: отображение пропуска 0,2988 как 0,30 рядом с условием <0,3 и доли 0,875 как 87%. В v3 осталось одно замечание модели: у 100000007170072100 пропуск 0,995 отображён как 0,99; кодовая проверка допускает такое расхождение в пределах установленной погрешности.
Между выборками v1 и v3 25 общих gid. Результат 65/65 означает согласие модели с обоснованиями выбранных узлов по переданным правилам. Это не «100% точность»: выборки различаются, независимой истинной разметки реальных клиентов нет.
Как использовали замечания. Связь с доработками зафиксирована в истории Git, в частности в коммите b74023e «pipeline v3: правки по симуляции жюри».
| Находка при проверке | Что сделано в коде и объяснениях |
|---|---|
| Порог посредничества coordinator не был виден в evidence | В evidence печатаются численный порог p95 и фактическое значение |
| Несколько плательщиков могли скрывать один доминирующий источник | Для consolidator введено условие max_payer_share < 0.8; четыре прежних сборщика с долями крупнейшего плательщика ≥80% перешли в terminal |
| Обрезанные узлы получали вес обычной периферии | Выделен no_data, задан вес 0,5 и явный запрос исходящих переводов |
| Изолированный seed имел seed-долю 1 при отсутствии денег | Установлена доля 0; evidence содержит «0 вход., 0 исход.» |
| Структурный паттерн не обязательно связан с seed | В v2 введены weak_seed_link и оговорка о возможном легальном контрагенте; в v3 добавлена поправка приоритета ×0,7 |
| Ближайшая роль периферии и role_score требовали уточнения | Формализованы условия, nearest_role сохраняется в метриках |
| Округление визуально противоречило строгому порогу | Добавлена дополнительная точность у границ и уточнено отображение процентов |
Поведение доминирующего плательщика, изолированного seed и весов закреплено регрессионными тестами. Между полными снимками v1 и v3 роли изменились у 38 клиентов — это совокупный эффект изменений, а не результат одной поправки. Предложения модели о неттинге встречных потоков, точной атрибуции средств и дополнительных флагах остаются предложениями, если их нет в текущем коде.
Контроль самого эксперимента. Первоначальный запуск задач защиты и evidence смешал карточки v1 с изменёнными в другой сессии правилами v2. Он исключён из содержательных итогов, сохранён в raw/mismatch_v2rules; последующие запросы читают правила из замороженного снимка. Так результаты привязаны к конкретной версии правил.
Отчёты: jury_report.md, jury_report_v3.md, critique_theses.md. Сырые ответы — raw и raw_v3; замечания — evidence_*_issues*.csv в том же каталоге.
Уточнение к архивным текстам: в таблице сравнения
jury_report_v3.mdзнаменатель v1 ошибочно указан как 54; исходные результаты содержат 66 gid, доля «частично» — 12/66 = 18,18%. Вpipeline/README.mdосталась историческая фраза, что v3 не перезапускалась; сохранённые артефакты подтверждают повтор задач защиты и evidence, но не общей критики методологии.
Расход по журналам. v1 включает и исключённый запуск. Указанная стоимость рассчитана скриптом при допущении $5/$40 за миллион входных/выходных токенов; это не подтверждённый счёт провайдера. Reasoning уже входит в выходные токены.
| Журнал | Вызовов | Входных токенов | Выходных токенов | Расчётная стоимость |
|---|---|---|---|---|
| usage.jsonl, v1 | 250 | 1 339 718 | 205 270 | $14,91 |
| usage_v3.jsonl, v3 | 123 | 870 066 | 99 643 | $8,34 |
Как воспроизвести. Для изучения сохранённых ответов достаточно открыть JSON/CSV; ключ и сеть не нужны. Скрипты исторического прогона можно запустить из pipeline:
cd pipeline
EVAL_SNAP=v3 uv run --with openai --with python-dotenv --with tabulate python eval/jury.py sim
EVAL_SNAP=v3 uv run --with openai --with python-dotenv --with tabulate python eval/jury.py agg
EVAL_SNAP=v3 uv run --with openai --with python-dotenv --with tabulate python eval/evidence_check.py
Для v1 используется EVAL_SNAP=v1; отдельная задача критики — eval/jury.py critique. Скрипты требуют OPENAI_API_KEY в корневом .env даже при использовании сохранённых ответов. При наличии raw/*.json или raw_v3/*.json ответ берётся из кеша, при отсутствии — выполняется внешний платный запрос. Архивные снимки имеют расширенную схему для эвала: текущий шестиколоночный out/nodes_roles.csv не следует подставлять вместо них. При подготовке этого README внешние LLM-запросы не выполнялись.
6. Два независимых синтетических набора
Зачем. Создать дополнительные миры с известным авторским замыслом и проверить, насколько генераторы воспроизводят структуру наблюдаемой банковской выгрузки. Это отдельный эксперимент над данными, не измерение точности классификатора на реальных клиентах.
Как. До генерации зафиксированы synthetic/SPEC.md, публичный бриф и критерии проверки. По протоколу генераторы не получали реальные транзакции и реализацию пайплайна. Оба мира проходят один crawl.py: фильтр внутрибанковских операций ≥5 000 ₸ за июль, обход исходящих на четыре колена, сохранение seed без рёбер и обрыв исходящих на границе.
| Метод | Как создан мир | Что осталось в наблюдаемой выгрузке |
|---|---|---|
| A — LLM-журналы | Семь авторских частей: шесть ячеек и легальная экономика, CSV участников и операций, пояснения замысла | 785 узлов, 2 273 ребра, 3 284 перевода, 69 seed |
| B — стохастическая агентная модель | B_abm/gen.py, фиксированный seed=7, случайное поведение участников по архетипам | 2 153 узла, 4 826 рёбер, 8 364 перевода, 87 seed |
Что проверяли и получили.
- Схема и инварианты. Проверены типы полей, агрегирование переводов в рёбра, сумма/период/направление, отсутствие исходящих у четвёртого колена и пересечений gid с реальными. При повторной проверке сохранённых A и B оба набора прошли
crawl.check. - Сходство распределений. profile.py сравнивает степени, суммы, повторяемость переводов, активность, компоненты, циклы, временные признаки и статистики KS. В отчёте по 45 свойствам B ближе к реальным данным в 24 случаях, A — в 19, в двух — равенство. При подготовке README профиль пересчитан во временном каталоге и побайтно совпал с PROFILE.md.
- Наблюдаемость авторской роли. Проверяются отдельные мягкие сигнатуры, а не полный каскад
run.py. Среди размеченных авторами участников с вовлечённостьюcriminalна коленах 1–3, без seed, сигнатура transit видна у 80% из 45 примеров A и примерно 6% из 16 примеров B; consolidator — у 0% из 6 примеров A и 10% из 10 примеров B. Авторская «должность» и наблюдаемая функция счёта могут различаться: сборщик может почти всё передавать дальше, а вход транзитного счёта может исчезнуть за межбанковской границей. - Ручной аудит. В REPORT.md разобрано по пять случайно выбранных размеченных узлов: авторская роль согласована с видимыми деньгами у 3 из 5 в A и 2 из 5 в B. audit.py выводит метрики и сырые переводы выбранных клиентов для такого разбора.
Как использовали. Сопоставление показало, для каких дальнейших проверок пригоден каждый набор: A — для разбора смысла роли по журналу и пояснениям автора, B — для изменения масштаба и повторных случайных генераций. Перед расчётом accuracy потребовалась бы отдельная разметка наблюдаемой функции, поскольку метка автора описывает также скрытый от банка мир. Синтетические данные не добавляются к демо и не заменяют исходную выгрузку организаторов.
Пересборка сохранённых наборов и профиля из корня, в отдельной рабочей копии, если нужно сохранить текущие артефакты:
cd synthetic
uv run --project ../pipeline python crawl.py A_llm
(cd B_abm && uv run --project ../../pipeline python gen.py)
uv run --project ../pipeline python crawl.py B_abm
uv run --project ../pipeline python profile.py
uv run --project ../pipeline python audit.py A_llm
uv run --project ../pipeline python audit.py B_abm
Для A используются уже сохранённые журналы, новых LLM-вызовов эти команды не делают. Для B сначала восстанавливаются world_*.csv, затем выполняется общий обход. Команды перезаписывают соответствующие синтетические Parquet и профиль.
7. Проверка приложения при подготовке README
Повторно выполнено на версии 49dfb93 от 23.09.2026; исходные данные и сохранённые результаты не изменялись.
| Команда / проверка | Результат |
|---|---|
bun install --frozen-lockfile | Зависимости установлены |
(cd pipeline && uv sync --frozen) | Checked 12 packages |
bun run build | TypeScript клиента и production-сборка завершились успешно |
(cd client && bun run lint) | Код завершения 0, два предупреждения Fast Refresh в badge.tsx и button.tsx |
(cd server && bunx tsc --noEmit -p .) | Код завершения 0 |
| Тесты, полный расчёт, сравнение файлов и sensitivity | Результаты приведены выше |
Локальные запросы через app.request Hono | 20 GET-маршрутов API/SPA вернули 200; проверены CSV, аналитика, карточка и граф |
| Путь, загрузка, повторная загрузка, переключение, удаление тестовой загрузки | Успешно; пересчёт запущен в отдельном временном каталоге |
| Ожидаемые ответы ошибок | Неизвестный /api — 404; вопрос ассистенту без ключа — 503 |
Сборщик Vite сообщил о JavaScript-фрагменте больше 500 kB. Это предупреждение сохранено как ориентир для оценки загрузки интерфейса. Проверка API не является браузерным end-to-end тестом; внешний чат и доступность удалённого развёртывания в эти локальные проверки не входили.
Риски и границы применения
| Риск | Как учитывать при работе |
|---|---|
| Неполная видимость переводов | В демо отсутствуют межбанковские операции, суммы ниже 5 000 ₸, другие месяцы и исходящие за четвёртым коленом. «Деньги остались» означает отсутствие сопоставимого видимого выхода; для проверки нужны дополнительные данные |
| Заниженный вход у seed | Seed исключены из правил transit/terminal, в компоненте масштаба их приоритета используется выход. Соотношение входа и выхода seed не следует трактовать как полный денежный баланс |
| Легальные двойники паттернов | Магазин, работодатель или семейные переводы могут соответствовать структурной роли. Проверять связь с seed, контрагентов и контекст; weak_seed_link снижает приоритет, но не устанавливает правовую оценку |
| Зависимость от порогов и порядка правил | На границе consolidator/terminal состав ролей и топа меняется. Использовать таблицу условий и результаты чувствительности; одна назначенная роль не исключает других возможных интерпретаций |
| Приближённое представление движения денег | seed_share основан на пропорциональном смешивании, быстрый вывод — на временном окне. Нет точной попереводной атрибуции, неттинга возвратов и проверки хронологии каждого цикла; даты не содержат времени суток |
| Интерпретация кластеров и LLM | Louvain не учитывает направление, модель может предложить неподтверждённую интерпретацию. Сверять ответ с инструментами и метриками; оценки LLM-эвала не заменяют независимую разметку |
| Обработка других схем и размеров | Загрузка принимает три Parquet заданной схемы, суммарно до 100 MiB; одна обработка за раз, тайм-аут пять минут. Период и последний hop читаются из данных, но правило terminal и часть подписей рассчитаны на колена 1–3/4; встроенная проверка требует минимум 20 строк топа |
| Общий серверный контекст | Активный набор один для всех пользователей; при перезапуске выбирается demo. Пользовательские наборы хранятся на диске, постоянный том в конфигурации репозитория не объявлен |
| Использование реальных банковских данных | В текущем прототипе нет авторизации и разграничения рабочих пространств. При включённом чате вопрос и результаты инструментов передаются внешнему OpenAI API; для чувствительных данных нужно отдельно определить условия доступа и обработки |
| Рост нагрузки | Данные и аналитика хранятся в памяти; точное посредничество, перебор циклов и отрисовка всего графа требуют проверки на существенно больших объёмах |
Масштабирование до примерно миллиона узлов
Текущий замер относится к 2 248 узлам; время работы на миллионе узлов не измерялось. Для такого объёма потребуется перенести агрегации в колоночную обработку, считать приближённое посредничество, ограничивать циклы компонентами и кандидатами, пересчитывать затронутые части графа и отдавать интерфейсу выбранные подграфы. Возможные инструменты для этих изменений — DuckDB/Polars и igraph/graph-tool; в текущую реализацию они не входят. Направление развития подробнее описано в pipeline/README.md.
Развёртывание
Ссылка на приложение, сохранённая в репозитории: https://moneylai.up.railway.app.
Dockerfile собирает один сервис: Bun, uv, Python 3.12, зависимости пайплайна и production-клиент. Hono отдаёт и API, и интерфейс. railway.json выбирает Dockerfile и проверку здоровья /api/health. Python-окружение собирается заранее, поэтому для пересчёта внутри готового контейнера установка зависимостей не требуется.
Локальный запуск такого образа:
docker build -t moneylai .
docker run --rm -p 3001:3001 -e PORT=3001 moneylai
Для воспроизводимого расчёта и демонстрации через локальный интерфейс облачное развёртывание не требуется.
Авторы коммитов
История коммитов
- 2026-09-23 17:50cleanupoljaskrut
- 2026-09-23 17:48upload: один тост об успехе (id) вместо двух в StrictModeoljaskrut
- 2026-09-23 17:48docs: правила v4, словарь терминов, масштабирование в READMEДаурен (401kdaniel)
- 2026-09-23 17:47docs: подробный README с тестами, эвалами и рискамиRakhat Shukirkhan
- 2026-09-23 17:47ui: правила v4 и направление потоков поверх нового интерфейсаДаурен (401kdaniel)
- 2026-09-23 17:43pipeline и сервер: круг 2 ревью, аналитика и инструменты ассистентаДаурен (401kdaniel)
- 2026-09-23 17:43pipeline v4: запуск на Windows, приоритет без обрыва, порог оборота координатораДаурен (401kdaniel)
- 2026-09-23 17:42readmeДаурен (401kdaniel)
Все коммиты (48)
- 2026-09-23 17:34toast: убрать кнопку закрытия и её стили, стандартный вид sonneroljaskrut
- 2026-09-23 17:32toast: кнопка закрытия справа сверхуoljaskrut
- 2026-09-23 17:31upload: спиннер по центру кнопкиoljaskrut
- 2026-09-23 17:29upload: без поля названия, имя выгрузки — локальное время dd.mm.yy hh:mmoljaskrut
- 2026-09-23 17:28upload: три строки-дропзоны вместо общей области, тосты sonner об ошибке и успехеoljaskrut
- 2026-09-23 17:26rename: продукт называется Moneylai, «Граф денег» остаётся только как имя кейсаoljaskrut
- 2026-09-23 17:25ui: вернуть нейтральную метку «Координатор», убрать число групп из READMEoljaskrut
- 2026-09-23 17:25Merge remote-tracking branch 'origin/codex/ui-ux-review'oljaskrut
- 2026-09-23 17:23env exampleoljaskrut
- 2026-09-23 17:23docs: название Moneylai вместо «Граф денег»; заметка о task/starter в README; удалены шаблонный client/README.md и неиспользуемый solution-diagram.pngoljaskrut
- 2026-09-23 17:20feat(ui): implement light analysis workspace from design handoffRakhat Shukirkhan
- 2026-09-23 17:10Merge remote-tracking branch 'origin/feat/synthetic-v2'oljaskrut
- 2026-09-23 17:04synthetic: два синтетических набора (A — ИИ пишет журнал, B — стохастическая модель), общий обход, профиль и сверка с реальнымиRakhat Shukirkhan
- 2026-09-23 17:03feat: загрузка своей выгрузки и переключение датасетов; пайплайн без констант датасета; Dockerfile для Railwayoljaskrut
- 2026-09-23 16:24service urloljaskrut
- 2026-09-23 16:19docs: METHODOLOGY.md и схема решения из 3523e8a (release/final), сверены с текущим out/; pipeline/.gitignoreoljaskrut
- 2026-09-23 16:15docs: сервер читает out/ через results.ts; убраны устаревшие API и out.ts из контракта и AGENTS.mdoljaskrut
- 2026-09-23 16:13fix: убран ложный сигнал «дробление у порога 5 000 ₸», JSON 404 для /api/*, мёртвый код, READMEoljaskrut
- 2026-09-23 16:13pipeline: nodes_roles.csv — ровно 6 колонок схемы жюри; метрики правил и role_rule перенесены в node_metrics.csvoljaskrut
- 2026-09-23 16:07Merge feat/ui-redesign into main: UI со схемой сети + пайплайн v3 + docsRakhat Shukirkhan
- 2026-09-23 16:06merge: feat/ui-redesign в main (новый UI + маршруты results/analytics/assistant)oljaskrut
- 2026-09-23 16:06pipeline: уборка кода, eval v3 (65/0/0), docs/METHODOLOGY.md и схема решенияRakhat Shukirkhan
- 2026-09-23 16:00eval: перепроверка жюри на v3 (65/0/0), README пайплайнаRakhat Shukirkhan
- 2026-09-23 15:57pipeline v3 в main: правила с источниками, тесты, чувствительность, симуляция жюри, выходы по контрактуRakhat Shukirkhan
- 2026-09-23 15:57graph-money: Python-пайплайн ролей, кластеров и приоритетаДаурен (401kdaniel)
- 2026-09-23 15:56pipeline: выходы по docs/PIPELINE_CONTRACT.md (node_metrics.csv, edge_metrics.csv, role_rule, true/false)Rakhat Shukirkhan
- 2026-09-23 15:48pipeline: README пайплайна (запуск, правила, проверка, ограничения, масштабирование)Rakhat Shukirkhan
- 2026-09-23 15:38pipeline v3: правки по симуляции жюриRakhat Shukirkhan
- 2026-09-23 15:33deploy to railway: serve client from serveroljaskrut
- 2026-09-23 15:25eval: симуляция жюри на gpt-5.6-sol (критика правил, 66 gid, evidence всех 2248 строк)Rakhat Shukirkhan
- 2026-09-23 15:22ui: экраны анализа, ассистент, схема решенияRakhat Shukirkhan
- 2026-09-23 15:14pipeline v2: пороги-перцентили, transit по времени, флаг слабой связи с seed, синтетический тест, чувствительностьRakhat Shukirkhan
- 2026-09-23 15:10server: аналитика и инструменты ассистента; правила v2 в UIRakhat Shukirkhan
- 2026-09-23 15:02ui: новый дизайн клиента и API результатов пайплайнаRakhat Shukirkhan
- 2026-09-23 14:56pipeline: роли, кластеры, приоритет, выгрузки по ТЗ (v1)Rakhat Shukirkhan
- 2026-09-23 14:40pipeline backend splitoljaskrut
- 2026-09-23 14:19agents.mdoljaskrut
- 2026-09-23 14:10skeletonoljaskrut
- 2026-09-23 13:38taskoljaskrut
- 2026-09-23 13:17initoljaskrut
Структура репозитория (554 файлов)
.dockerignore .env.example .gitignore AGENTS.md Dockerfile README.md bun.lock client/.gitignore client/.oxlintrc.json client/components.json client/index.html client/package.json client/public/favicon.svg client/src/components/NetworkCanvas.tsx client/src/components/ReviewWorkspace.css client/src/components/ReviewWorkspace.tsx client/src/components/kit.tsx client/src/components/ui/badge.tsx client/src/components/ui/button.tsx client/src/components/ui/card.tsx client/src/components/ui/dropdown-menu.tsx client/src/components/ui/input.tsx client/src/components/ui/sonner.tsx client/src/index.css client/src/layouts/AppLayout.tsx client/src/layouts/SectionLayout.tsx client/src/lib/api.ts client/src/lib/depth.ts client/src/lib/format.ts client/src/lib/roles.ts client/src/lib/use-api.ts client/src/lib/utils.ts client/src/main.tsx client/src/pages/AnomaliesPage.tsx client/src/pages/ArchitecturePage.tsx client/src/pages/AssistantPage.tsx client/src/pages/ChecksOverviewPage.tsx client/src/pages/ClustersPage.tsx client/src/pages/CompletenessPage.tsx client/src/pages/GraphPage.tsx client/src/pages/NetworkOverviewPage.tsx client/src/pages/NodePage.tsx client/src/pages/NotFoundPage.tsx client/src/pages/OverviewPage.tsx client/src/pages/ResiliencePage.tsx client/src/pages/RoutesPage.tsx client/src/pages/RulesPage.tsx client/src/pages/TimePage.tsx client/src/pages/TopPage.tsx client/src/pages/UploadPage.tsx client/src/router.tsx client/tsconfig.app.json client/tsconfig.json client/tsconfig.node.json client/vite.config.ts docs/METHODOLOGY.md docs/PIPELINE_CONTRACT.md docs/README_REQUIREMENTS.md docs/solution-diagram.svg experiments/probe-0.ipynb out/clusters.csv out/edge_metrics.csv out/node_metrics.csv out/node_metrics.json out/nodes_roles.csv out/sensitivity.md out/top_nodes.csv package.json pipeline/.gitignore pipeline/README.md pipeline/RULES.md pipeline/eval/agg_stdout.txt pipeline/eval/critique_stdout.txt pipeline/eval/critique_theses.md pipeline/eval/evidence_check.py pipeline/eval/evidence_code_issues.csv pipeline/eval/evidence_code_issues_v3.csv pipeline/eval/evidence_llm_issues.csv pipeline/eval/evidence_llm_issues_v3.csv pipeline/eval/evidence_stdout.txt pipeline/eval/jury.py pipeline/eval/jury_report.md pipeline/eval/jury_report_v3.md pipeline/eval/llm.py pipeline/eval/question_themes.json pipeline/eval/question_themes_v3.json pipeline/eval/raw/critique.json pipeline/eval/raw/evid_000.json pipeline/eval/raw/evid_001.json pipeline/eval/raw/evid_002.json pipeline/eval/raw/evid_003.json pipeline/eval/raw/evid_004.json pipeline/eval/raw/evid_005.json pipeline/eval/raw/evid_006.json pipeline/eval/raw/evid_007.json pipeline/eval/raw/evid_008.json pipeline/eval/raw/evid_009.json pipeline/eval/raw/evid_010.json pipeline/eval/raw/evid_011.json pipeline/eval/raw/evid_012.json pipeline/eval/raw/evid_013.json pipeline/eval/raw/evid_014.json pipeline/eval/raw/evid_015.json pipeline/eval/raw/evid_016.json pipeline/eval/raw/evid_017.json pipeline/eval/raw/evid_018.json pipeline/eval/raw/evid_019.json pipeline/eval/raw/evid_020.json pipeline/eval/raw/evid_021.json pipeline/eval/raw/evid_022.json pipeline/eval/raw/evid_023.json pipeline/eval/raw/evid_024.json pipeline/eval/raw/evid_025.json pipeline/eval/raw/evid_026.json pipeline/eval/raw/evid_027.json pipeline/eval/raw/evid_028.json pipeline/eval/raw/evid_029.json pipeline/eval/raw/evid_030.json pipeline/eval/raw/evid_031.json pipeline/eval/raw/evid_032.json pipeline/eval/raw/evid_033.json pipeline/eval/raw/evid_034.json pipeline/eval/raw/evid_035.json pipeline/eval/raw/evid_036.json pipeline/eval/raw/evid_037.json pipeline/eval/raw/evid_038.json pipeline/eval/raw/evid_039.json pipeline/eval/raw/evid_040.json pipeline/eval/raw/evid_041.json pipeline/eval/raw/evid_042.json pipeline/eval/raw/evid_043.json pipeline/eval/raw/evid_044.json pipeline/eval/raw/evid_045.json pipeline/eval/raw/evid_046.json pipeline/eval/raw/evid_047.json pipeline/eval/raw/evid_048.json pipeline/eval/raw/evid_049.json pipeline/eval/raw/evid_050.json pipeline/eval/raw/evid_051.json pipeline/eval/raw/evid_052.json pipeline/eval/raw/evid_053.json pipeline/eval/raw/evid_054.json pipeline/eval/raw/evid_055.json pipeline/eval/raw/evid_056.json pipeline/eval/raw/mismatch_v2rules/evid_000.json pipeline/eval/raw/mismatch_v2rules/evid_001.json pipeline/eval/raw/mismatch_v2rules/evid_002.json pipeline/eval/raw/mismatch_v2rules/evid_003.json pipeline/eval/raw/mismatch_v2rules/evid_004.json pipeline/eval/raw/mismatch_v2rules/evid_005.json pipeline/eval/raw/mismatch_v2rules/evid_006.json pipeline/eval/raw/mismatch_v2rules/evid_007.json pipeline/eval/raw/mismatch_v2rules/evid_008.json pipeline/eval/raw/mismatch_v2rules/evid_009.json pipeline/eval/raw/mismatch_v2rules/evid_010.json pipeline/eval/raw/mismatch_v2rules/evid_011.json pipeline/eval/raw/mismatch_v2rules/evid_012.json pipeline/eval/raw/mismatch_v2rules/evid_013.json pipeline/eval/raw/mismatch_v2rules/evid_014.json pipeline/eval/raw/mismatch_v2rules/evid_015.json pipeline/eval/raw/mismatch_v2rules/evid_016.json pipeline/eval/raw/mismatch_v2rules/evid_017.json pipeline/eval/raw/mismatch_v2rules/evid_018.json pipeline/eval/raw/mismatch_v2rules/evid_019.json pipeline/eval/raw/mismatch_v2rules/evid_020.json pipeline/eval/raw/mismatch_v2rules/evid_021.json pipeline/eval/raw/mismatch_v2rules/evid_022.json pipeline/eval/raw/mismatch_v2rules/evid_023.json pipeline/eval/raw/mismatch_v2rules/evid_024.json pipeline/eval/raw/mismatch_v2rules/evid_025.json pipeline/eval/raw/mismatch_v2rules/evid_026.json pipeline/eval/raw/mismatch_v2rules/evid_027.json pipeline/eval/raw/mismatch_v2rules/evid_028.json pipeline/eval/raw/mismatch_v2rules/evid_029.json pipeline/eval/raw/mismatch_v2rules/evid_030.json pipeline/eval/raw/mismatch_v2rules/evid_031.json pipeline/eval/raw/mismatch_v2rules/evid_032.json pipeline/eval/raw/mismatch_v2rules/evid_033.json pipeline/eval/raw/mismatch_v2rules/evid_034.json pipeline/eval/raw/mismatch_v2rules/evid_035.json pipeline/eval/raw/mismatch_v2rules/evid_036.json pipeline/eval/raw/mismatch_v2rules/evid_037.json pipeline/eval/raw/mismatch_v2rules/evid_038.json pipeline/eval/raw/mismatch_v2rules/evid_039.json pipeline/eval/raw/mismatch_v2rules/evid_040.json pipeline/eval/raw/mismatch_v2rules/evid_041.json pipeline/eval/raw/mismatch_v2rules/evid_042.json pipeline/eval/raw/mismatch_v2rules/evid_043.json pipeline/eval/raw/mismatch_v2rules/evid_044.json pipeline/eval/raw/mismatch_v2rules/evid_045.json pipeline/eval/raw/mismatch_v2rules/evid_046.json pipeline/eval/raw/mismatch_v2rules/evid_047.json pipeline/eval/raw/mismatch_v2rules/evid_048.json pipeline/eval/raw/mismatch_v2rules/evid_049.json pipeline/eval/raw/mismatch_v2rules/evid_050.json pipeline/eval/raw/mismatch_v2rules/evid_051.json pipeline/eval/raw/mismatch_v2rules/evid_052.json pipeline/eval/raw/mismatch_v2rules/evid_053.json pipeline/eval/raw/mismatch_v2rules/evid_054.json pipeline/eval/raw/mismatch_v2rules/evid_055.json pipeline/eval/raw/mismatch_v2rules/evid_056.json pipeline/eval/raw/mismatch_v2rules/evidence_llm_issues.csv pipeline/eval/raw/mismatch_v2rules/evidence_stdout.txt pipeline/eval/raw/mismatch_v2rules/question_themes.json pipeline/eval/raw/mismatch_v2rules/sim_100000000331309100.json pipeline/eval/raw/mismatch_v2rules/sim_100000000343175100.json pipeline/eval/raw/mismatch_v2rules/sim_100000000437046100.json pipeline/eval/raw/mismatch_v2rules/sim_100000000456947100.json pipeline/eval/raw/mismatch_v2rules/sim_100000000594783100.json pipeline/eval/raw/mismatch_v2rules/sim_100000000896111100.json pipeline/eval/raw/mismatch_v2rules/sim_100000001285440100.json pipeline/eval/raw/mismatch_v2rules/sim_100000001330685100.json pipeline/eval/raw/mismatch_v2rules/sim_100000001857829100.json pipeline/eval/raw/mismatch_v2rules/sim_100000001899164100.json pipeline/eval/raw/mismatch_v2rules/sim_100000002028158100.json pipeline/eval/raw/mismatch_v2rules/sim_100000002398779100.json pipeline/eval/raw/mismatch_v2rules/sim_100000002433697100.json pipeline/eval/raw/mismatch_v2rules/sim_100000002443664100.json pipeline/eval/raw/mismatch_v2rules/sim_100000002527114100.json pipeline/eval/raw/mismatch_v2rules/sim_100000002578405100.json pipeline/eval/raw/mismatch_v2rules/sim_100000002838861100.json pipeline/eval/raw/mismatch_v2rules/sim_100000002894076100.json pipeline/eval/raw/mismatch_v2rules/sim_100000002952756100.json pipeline/eval/raw/mismatch_v2rules/sim_100000002964966100.json pipeline/eval/raw/mismatch_v2rules/sim_100000003016635100.json pipeline/eval/raw/mismatch_v2rules/sim_100000003057225100.json pipeline/eval/raw/mismatch_v2rules/sim_100000003071930100.json pipeline/eval/raw/mismatch_v2rules/sim_100000003111305100.json pipeline/eval/raw/mismatch_v2rules/sim_100000003115284100.json pipeline/eval/raw/mismatch_v2rules/sim_100000003684369100.json pipeline/eval/raw/mismatch_v2rules/sim_100000003701161100.json pipeline/eval/raw/mismatch_v2rules/sim_100000003736419100.json pipeline/eval/raw/mismatch_v2rules/sim_100000003880331100.json pipeline/eval/raw/mismatch_v2rules/sim_100000004015047100.json pipeline/eval/raw/mismatch_v2rules/sim_100000004070318100.json pipeline/eval/raw/mismatch_v2rules/sim_100000004156082100.json pipeline/eval/raw/mismatch_v2rules/sim_100000004174835100.json pipeline/eval/raw/mismatch_v2rules/sim_100000004299488100.json pipeline/eval/raw/mismatch_v2rules/sim_100000004351795100.json pipeline/eval/raw/mismatch_v2rules/sim_100000004394765100.json pipeline/eval/raw/mismatch_v2rules/sim_100000004400305100.json pipeline/eval/raw/mismatch_v2rules/sim_100000004403675100.json pipeline/eval/raw/mismatch_v2rules/sim_100000004798515100.json pipeline/eval/raw/mismatch_v2rules/sim_100000004820260100.json pipeline/eval/raw/mismatch_v2rules/sim_100000004892144100.json pipeline/eval/raw/mismatch_v2rules/sim_100000004948177100.json pipeline/eval/raw/mismatch_v2rules/sim_100000005382566100.json pipeline/eval/raw/mismatch_v2rules/sim_100000005514161100.json pipeline/eval/raw/mismatch_v2rules/sim_100000005550592100.json pipeline/eval/raw/mismatch_v2rules/sim_100000005910114100.json pipeline/eval/raw/mismatch_v2rules/sim_100000005933757100.json pipeline/eval/raw/mismatch_v2rules/sim_100000006274203100.json pipeline/eval/raw/mismatch_v2rules/sim_100000006866783100.json pipeline/eval/raw/mismatch_v2rules/sim_100000007034089100.json pipeline/eval/raw/mismatch_v2rules/sim_100000007055802100.json pipeline/eval/raw/mismatch_v2rules/sim_100000007347733100.json pipeline/eval/raw/mismatch_v2rules/sim_100000008086440100.json pipeline/eval/raw/mismatch_v2rules/sim_100000008144528100.json pipeline/eval/raw/mismatch_v2rules/sim_100000008304139100.json pipeline/eval/raw/mismatch_v2rules/sim_100000008324800100.json pipeline/eval/raw/mismatch_v2rules/sim_100000008346837100.json pipeline/eval/raw/mismatch_v2rules/sim_100000008465789100.json pipeline/eval/raw/mismatch_v2rules/sim_100000008477350100.json pipeline/eval/raw/mismatch_v2rules/sim_100000008560717100.json pipeline/eval/raw/mismatch_v2rules/sim_100000008561016100.json pipeline/eval/raw/mismatch_v2rules/sim_100000008566737100.json pipeline/eval/raw/mismatch_v2rules/sim_100000008603629100.json pipeline/eval/raw/mismatch_v2rules/sim_100000008666075100.json pipeline/eval/raw/mismatch_v2rules/sim_100000008686313100.json pipeline/eval/raw/mismatch_v2rules/sim_100000008745676100.json pipeline/eval/raw/mismatch_v2rules/sim_results.json pipeline/eval/raw/mismatch_v2rules/sim_stdout.txt pipeline/eval/raw/mismatch_v2rules/sim_table.csv pipeline/eval/raw/probe.json pipeline/eval/raw/question_themes.json pipeline/eval/raw/sim_100000000331309100.json pipeline/eval/raw/sim_100000000343175100.json pipeline/eval/raw/sim_100000000437046100.json pipeline/eval/raw/sim_100000000456947100.json pipeline/eval/raw/sim_100000000594783100.json pipeline/eval/raw/sim_100000000896111100.json pipeline/eval/raw/sim_100000001285440100.json pipeline/eval/raw/sim_100000001330685100.json pipeline/eval/raw/sim_100000001857829100.json pipeline/eval/raw/sim_100000001899164100.json pipeline/eval/raw/sim_100000002028158100.json pipeline/eval/raw/sim_100000002398779100.json pipeline/eval/raw/sim_100000002433697100.json pipeline/eval/raw/sim_100000002443664100.json pipeline/eval/raw/sim_100000002527114100.json pipeline/eval/raw/sim_100000002578405100.json pipeline/eval/raw/sim_100000002838861100.json pipeline/eval/raw/sim_100000002894076100.json pipeline/eval/raw/sim_100000002952756100.json pipeline/eval/raw/sim_100000002964966100.json pipeline/eval/raw/sim_100000003016635100.json pipeline/eval/raw/sim_100000003057225100.json pipeline/eval/raw/sim_100000003071930100.json pipeline/eval/raw/sim_100000003111305100.json pipeline/eval/raw/sim_100000003115284100.json pipeline/eval/raw/sim_100000003684369100.json pipeline/eval/raw/sim_100000003701161100.json pipeline/eval/raw/sim_100000003736419100.json pipeline/eval/raw/sim_100000003880331100.json pipeline/eval/raw/sim_100000004015047100.json pipeline/eval/raw/sim_100000004070318100.json pipeline/eval/raw/sim_100000004156082100.json pipeline/eval/raw/sim_100000004174835100.json pipeline/eval/raw/sim_100000004299488100.json pipeline/eval/raw/sim_100000004351795100.json pipeline/eval/raw/sim_100000004394765100.json pipeline/eval/raw/sim_100000004400305100.json pipeline/eval/raw/sim_100000004403675100.json pipeline/eval/raw/sim_100000004798515100.json pipeline/eval/raw/sim_100000004820260100.json pipeline/eval/raw/sim_100000004892144100.json pipeline/eval/raw/sim_100000004948177100.json pipeline/eval/raw/sim_100000005382566100.json pipeline/eval/raw/sim_100000005514161100.json pipeline/eval/raw/sim_100000005550592100.json pipeline/eval/raw/sim_100000005910114100.json pipeline/eval/raw/sim_100000005933757100.json pipeline/eval/raw/sim_100000006274203100.json pipeline/eval/raw/sim_100000006866783100.json pipeline/eval/raw/sim_100000007034089100.json pipeline/eval/raw/sim_100000007055802100.json pipeline/eval/raw/sim_100000007347733100.json pipeline/eval/raw/sim_100000008086440100.json pipeline/eval/raw/sim_100000008144528100.json pipeline/eval/raw/sim_100000008304139100.json pipeline/eval/raw/sim_100000008324800100.json pipeline/eval/raw/sim_100000008346837100.json pipeline/eval/raw/sim_100000008465789100.json pipeline/eval/raw/sim_100000008477350100.json pipeline/eval/raw/sim_100000008560717100.json pipeline/eval/raw/sim_100000008561016100.json pipeline/eval/raw/sim_100000008566737100.json pipeline/eval/raw/sim_100000008603629100.json pipeline/eval/raw/sim_100000008666075100.json pipeline/eval/raw/sim_100000008686313100.json pipeline/eval/raw/sim_100000008745676100.json pipeline/eval/raw_v3/evid_000.json pipeline/eval/raw_v3/evid_001.json pipeline/eval/raw_v3/evid_002.json pipeline/eval/raw_v3/evid_003.json pipeline/eval/raw_v3/evid_004.json pipeline/eval/raw_v3/evid_005.json pipeline/eval/raw_v3/evid_006.json pipeline/eval/raw_v3/evid_007.json pipeline/eval/raw_v3/evid_008.json pipeline/eval/raw_v3/evid_009.json pipeline/eval/raw_v3/evid_010.json pipeline/eval/raw_v3/evid_011.json pipeline/eval/raw_v3/evid_012.json pipeline/eval/raw_v3/evid_013.json pipeline/eval/raw_v3/evid_014.json pipeline/eval/raw_v3/evid_015.json pipeline/eval/raw_v3/evid_016.json pipeline/eval/raw_v3/evid_017.json pipeline/eval/raw_v3/evid_018.json pipeline/eval/raw_v3/evid_019.json pipeline/eval/raw_v3/evid_020.json pipeline/eval/raw_v3/evid_021.json pipeline/eval/raw_v3/evid_022.json pipeline/eval/raw_v3/evid_023.json pipeline/eval/raw_v3/evid_024.json pipeline/eval/raw_v3/evid_025.json pipeline/eval/raw_v3/evid_026.json pipeline/eval/raw_v3/evid_027.json pipeline/eval/raw_v3/evid_028.json pipeline/eval/raw_v3/evid_029.json pipeline/eval/raw_v3/evid_030.json pipeline/eval/raw_v3/evid_031.json pipeline/eval/raw_v3/evid_032.json pipeline/eval/raw_v3/evid_033.json pipeline/eval/raw_v3/evid_034.json pipeline/eval/raw_v3/evid_035.json pipeline/eval/raw_v3/evid_036.json pipeline/eval/raw_v3/evid_037.json pipeline/eval/raw_v3/evid_038.json pipeline/eval/raw_v3/evid_039.json pipeline/eval/raw_v3/evid_040.json pipeline/eval/raw_v3/evid_041.json pipeline/eval/raw_v3/evid_042.json pipeline/eval/raw_v3/evid_043.json pipeline/eval/raw_v3/evid_044.json pipeline/eval/raw_v3/evid_045.json pipeline/eval/raw_v3/evid_046.json pipeline/eval/raw_v3/evid_047.json pipeline/eval/raw_v3/evid_048.json pipeline/eval/raw_v3/evid_049.json pipeline/eval/raw_v3/evid_050.json pipeline/eval/raw_v3/evid_051.json pipeline/eval/raw_v3/evid_052.json pipeline/eval/raw_v3/evid_053.json pipeline/eval/raw_v3/evid_054.json pipeline/eval/raw_v3/evid_055.json pipeline/eval/raw_v3/evid_056.json pipeline/eval/raw_v3/question_themes.json pipeline/eval/raw_v3/sim_100000000343175100.json pipeline/eval/raw_v3/sim_100000000437046100.json pipeline/eval/raw_v3/sim_100000000552584100.json pipeline/eval/raw_v3/sim_100000000706545100.json pipeline/eval/raw_v3/sim_100000001072022100.json pipeline/eval/raw_v3/sim_100000001164648100.json pipeline/eval/raw_v3/sim_100000001169666100.json pipeline/eval/raw_v3/sim_100000001609499100.json pipeline/eval/raw_v3/sim_100000002173161100.json pipeline/eval/raw_v3/sim_100000002433697100.json pipeline/eval/raw_v3/sim_100000002443664100.json pipeline/eval/raw_v3/sim_100000002738256100.json pipeline/eval/raw_v3/sim_100000002753146100.json pipeline/eval/raw_v3/sim_100000002838861100.json pipeline/eval/raw_v3/sim_100000002872399100.json pipeline/eval/raw_v3/sim_100000002883402100.json pipeline/eval/raw_v3/sim_100000002894076100.json pipeline/eval/raw_v3/sim_100000002939838100.json pipeline/eval/raw_v3/sim_100000002956769100.json pipeline/eval/raw_v3/sim_100000003016635100.json pipeline/eval/raw_v3/sim_100000003071930100.json pipeline/eval/raw_v3/sim_100000003097324100.json pipeline/eval/raw_v3/sim_100000003115284100.json pipeline/eval/raw_v3/sim_100000003134474100.json pipeline/eval/raw_v3/sim_100000003221384100.json pipeline/eval/raw_v3/sim_100000003404627100.json pipeline/eval/raw_v3/sim_100000003684369100.json pipeline/eval/raw_v3/sim_100000003701161100.json pipeline/eval/raw_v3/sim_100000003747133100.json pipeline/eval/raw_v3/sim_100000003798151100.json pipeline/eval/raw_v3/sim_100000003880331100.json pipeline/eval/raw_v3/sim_100000004015047100.json pipeline/eval/raw_v3/sim_100000004070318100.json pipeline/eval/raw_v3/sim_100000004071080100.json pipeline/eval/raw_v3/sim_100000004140994100.json pipeline/eval/raw_v3/sim_100000004159526100.json pipeline/eval/raw_v3/sim_100000004232712100.json pipeline/eval/raw_v3/sim_100000004299488100.json pipeline/eval/raw_v3/sim_100000004394765100.json pipeline/eval/raw_v3/sim_100000004399686100.json pipeline/eval/raw_v3/sim_100000004400305100.json pipeline/eval/raw_v3/sim_100000004403675100.json pipeline/eval/raw_v3/sim_100000004679953100.json pipeline/eval/raw_v3/sim_100000005382566100.json pipeline/eval/raw_v3/sim_100000005498992100.json pipeline/eval/raw_v3/sim_100000005514161100.json pipeline/eval/raw_v3/sim_100000005578559100.json pipeline/eval/raw_v3/sim_100000005778283100.json pipeline/eval/raw_v3/sim_100000005933757100.json pipeline/eval/raw_v3/sim_100000006518161100.json pipeline/eval/raw_v3/sim_100000006788200100.json pipeline/eval/raw_v3/sim_100000006866783100.json pipeline/eval/raw_v3/sim_100000006972210100.json pipeline/eval/raw_v3/sim_100000007055802100.json pipeline/eval/raw_v3/sim_100000007653094100.json pipeline/eval/raw_v3/sim_100000008074304100.json pipeline/eval/raw_v3/sim_100000008104567100.json pipeline/eval/raw_v3/sim_100000008165763100.json pipeline/eval/raw_v3/sim_100000008477350100.json pipeline/eval/raw_v3/sim_100000008547844100.json pipeline/eval/raw_v3/sim_100000008675322100.json pipeline/eval/raw_v3/sim_100000008686313100.json pipeline/eval/raw_v3/sim_100000008712991100.json pipeline/eval/raw_v3/sim_100000008730371100.json pipeline/eval/raw_v3/sim_100000008748408100.json pipeline/eval/report.py pipeline/eval/report_v3.py pipeline/eval/sample_gids.json pipeline/eval/sample_gids_v3.json pipeline/eval/sim_code_checks.md pipeline/eval/sim_results.json pipeline/eval/sim_results_v3.json pipeline/eval/sim_stdout.txt pipeline/eval/sim_table.csv pipeline/eval/sim_table_v3.csv pipeline/eval/snapshot_v1/RULES_v1.md pipeline/eval/snapshot_v1/RULES_v2_seen_midrun.md pipeline/eval/snapshot_v1/clusters.csv pipeline/eval/snapshot_v1/node_metrics.json pipeline/eval/snapshot_v1/nodes_roles.csv pipeline/eval/snapshot_v1/top_nodes.csv pipeline/eval/snapshot_v3/RULES_v3.md pipeline/eval/snapshot_v3/clusters.csv pipeline/eval/snapshot_v3/node_metrics.json pipeline/eval/snapshot_v3/nodes_roles.csv pipeline/eval/snapshot_v3/top_nodes.csv pipeline/eval/tz_5_9.txt pipeline/eval/usage.jsonl pipeline/eval/usage_v3.jsonl pipeline/pyproject.toml pipeline/run.py pipeline/sensitivity.py pipeline/tests/test_synthetic.py pipeline/uv.lock railway.json server/package.json server/src/app.ts server/src/data/analytics.ts server/src/data/dataset.ts server/src/data/load.ts server/src/data/results.ts server/src/data/store.ts server/src/data/tools.ts server/src/data/types.ts server/src/index.ts server/src/routes/analytics.ts server/src/routes/assistant.ts server/src/routes/datasets.ts server/src/routes/graph.ts server/src/routes/health.ts server/src/routes/index.ts server/src/routes/nodes.ts server/src/routes/results.ts server/src/routes/stats.ts server/src/routes/transactions.ts server/tsconfig.json synthetic/.gitignore synthetic/A_llm/REGISTRY.md synthetic/A_llm/c1/actors.csv synthetic/A_llm/c1/tx.csv synthetic/A_llm/c2/actors.csv synthetic/A_llm/c2/tx.csv synthetic/A_llm/c3/actors.csv synthetic/A_llm/c3/tx.csv synthetic/A_llm/c4/actors.csv synthetic/A_llm/c4/tx.csv synthetic/A_llm/c5/actors.csv synthetic/A_llm/c5/tx.csv synthetic/A_llm/c6/actors.csv synthetic/A_llm/c6/tx.csv synthetic/A_llm/data/edges.parquet synthetic/A_llm/data/nodes.parquet synthetic/A_llm/data/transactions.parquet synthetic/A_llm/ground_truth.csv synthetic/A_llm/h/actors.csv synthetic/A_llm/h/tx.csv synthetic/B_abm/data/edges.parquet synthetic/B_abm/data/nodes.parquet synthetic/B_abm/data/transactions.parquet synthetic/B_abm/gen.py synthetic/B_abm/ground_truth.csv synthetic/PROFILE.md synthetic/REPORT.md synthetic/SPEC.md synthetic/audit.py synthetic/crawl.py synthetic/profile.py task/HackAlem AI_ Граф денег_ восстановление финансовой структуры организованной группы по транзакционной сети.pdf task/README.md task/data/edges.parquet task/data/nodes.parquet task/data/transactions.parquet task/starter/README.md task/starter/requirements.txt task/starter/starter.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.