Заменит ли ИИ настоящих судей? Я скормил ему дело которое арбитры разбирали 3 недели

от автора

Спойлер: вердикт совпал слово в слово. Только заняло это не три недели, а 12 минут.

Tldr для тех кто хочет сразу к сути

Мы с партнёром собрали в Telegram-боте автоматический арбитраж на Claude Sonnet 4.6. Анна-секретарь принимает заявки, Дмитрий-арбитр ведёт разбор, выносит вердикт, кладёт в публичный архив. Двухперсонная схема, изолированные группы под каждое дело через Telethon-userbot, проверка блокчейн-транзакций по 12 сетям, обучение через оценку админа.

Под нагрузкой реальным кейсом из жизни (фрилансер vs клиент, спорная оплата, три недели разборок у живых арбитров) бот выдал тот же самый вердикт за 12 минут. И это не подгон. И не один раз.

Дальше я подробно расскажу как это устроено, покажу куски кода, и зайду на главный вопрос: пора ли увольнять живых арбитров.

В самом конце ссылки на нашу группу и сам бот, можно потыкать руками.

Откуда вообще растут ноги

Если кратко, арбитраж — это разбирательство спора без суда. Два человека что-то не поделили, не идут в государственную систему (долго, дорого, формально), а зовут третьего человека вынести решение. В русскоязычной части интернета это особенно популярно во фриланс-сообществах, в крипте, среди арбитражников трафика. Логика такая: «мы оба тебя уважаем, разрули нас».

Это работает, но плохо.

Во-первых, арбитры люди. Если кейс сложный, они тянут — три недели у нас был не самый длинный случай. Знаю историю, где разборка длилась четыре месяца.

Во-вторых, арбитры дорогие. Хороший человек хочет 5-15% от суммы спора, а в крипте и того больше. На условные 30 тысяч уходит 3-5 тысяч сверху ни за что.

В-третьих, арбитр может быть аффилирован. Это самая тонкая беда. Стороны часто зовут «общего знакомого», и иногда у этого знакомого есть скрытый мотив топить одну из сторон. Бывает и просто плохое настроение — человек не выспался и вынес жёстче чем надо.

В-четвёртых, история разбора не аккумулируется. Каждый арбитр учится сам. Если мы хотим чтобы система становилась справедливее со временем, нам нужна общая память. Чего у людей просто нет.

Мы с партнёром сидели в нашей супергруппе, в очередной раз помогали кому-то разрулить спор, и подумали — а если попробовать ИИ. Не как замену, а как «бесплатного арбитра под рукой», который хотя бы первое решение выдаст за минуты. Дальше если стороны не согласны — пусть идут к живому.

Так появился бот, которого мы между собой называем Claude Justice.

Что внутри: два ИИ вместо одного

Когда я начал собирать систему, первое что захотелось — это сделать одного «большого мудрого ИИ» который принимает заявку, общается со сторонами и выносит вердикт. Не сработало по двум причинам.

Причина один: дорого. Sonnet 4.6 хорош, но не бесплатен. Если каждая заявка типа «он плохой, обидел меня» идёт через Sonnet — токены улетают в трубу, потому что 60% обращений это мусор. Бытовые конфликты, эмоциональные жалобы, иногда просто троллинг.

Причина два: ИИ задаёт слишком много вопросов. Если посадить Claude с инструкцией «разрули спор», он будет уточнять до седьмого пота: «А что именно сказал ответчик? А когда? А были свидетели? А вы заключали договор? А в какой форме?» Хорошо для адвоката, плохо для арбитража где важна скорость.

Решение оказалось простым. Два ИИ.

Анна-секретарь на Claude Haiku 4.5. Дешёвая, быстрая. Её задача — собрать минимальное досье и решить, передавать ли дело дальше. У неё жёсткий лимит: максимум 2 вопроса за весь приём. Если в первом сообщении человек сам всё рассказал, она не задаёт ни одного. Если суть мутная — отсеивает.

Дмитрий-арбитр на Sonnet 4.6. Дорогой, мощный. Получает уже подготовленное досье от Анны и не тратит токены на повторные уточнения. Сразу включается в разбор: общается со сторонами в изолированной группе, требует пруфы, выносит вердикт.

Архитектурно это выглядит так:

Промпт Анны выглядит примерно так. Это не весь, я обрезал, но суть передаёт:

SECRETARY_SYSTEM = """Ты — Анна, секретарь арбитражного центра в Telegram.Принимаешь заявки. Ты НЕ выносишь вердиктов — только собираешь минимальноедосье и передаёшь арбитру. Твоя задача — быстро.ТВОЙ СТИЛЬ:- Очень короткие реплики, 1 предложение.- Без AI-вступлений ("Конечно!", "Я готова помочь").- НЕ задавай больше одного вопроса за раз.- Жёсткий лимит — максимум 2 вопроса всего за весь приём.ПРАВИЛО СБОРА:Тебе нужны ровно ЧЕТЫРЕ поля:1. Суть спора2. @username ответчика3. Сумма / услуга4. Какие пруфы естьЕсли в ПЕРВОМ сообщении истец уже выложил всё четыре — НЕ ЗАДАВАЙНИ ОДНОГО ВОПРОСА, сразу выдавай досье и закрывай приём.Если не хватает 1-2 полей — задай ОДИН вопрос про все недостающиесразу. Например: "ок, давай @username ответчика и сумму".КРИПТА: если речь о криптопереводе — проси TX hash, не скриншоткошелька. Бот САМ проверит хеш в блокчейне.АВТО-ОТКЛОНЕНИЕ:- Бытовуха вне финансов/услуг/договоров- Пустые жалобы без сути- Уголовщина — направляй в полицию- Незаконные сделкиФОРМАТ ДОСЬЕ:[ДОСЬЕ_ГОТОВО]Истец: ...Ответчик: ...Суть: ...Сумма/услуга: ...Пруфы у истца: ...ФОРМАТ ОТКАЗА:[ОТКЛОНИТЬ]Причина: ..."""

Маркеры в квадратных скобках — это сигнал для системы. Когда Claude в своём ответе пишет [ДОСЬЕ_ГОТОВО], бот понимает что приём окончен и запускает следующий этап. Если [ОТКЛОНИТЬ] — заявка идёт в админскую очередь на пересмотр (вдруг я не согласен с Анной).

Распознавание этого маркера — буквально одна строчка:

DOSSIER_READY_MARKER = "[ДОСЬЕ_ГОТОВО]"def has_dossier_ready(text: str) -> bool:    return DOSSIER_READY_MARKER in (text or "")

Просто, надёжно, без LLM-магии. ИИ генерит текст, мы дёргаем in и понимаем что делать дальше.

Дебаунс: почему ИИ не отвечает на каждое сообщение

Это была не самая очевидная проблема, но она важная.

Если посадить Claude в чат и заставить отвечать на каждое сообщение участников, он превращается в назойливого попугая. Истец пишет «привет», бот отвечает. Истец пишет «у меня тут проблема», бот отвечает. Истец сбрасывает три скриншота — бот пытается обработать каждый отдельно.

Я сделал так чтобы бот ждал тишину. После каждого нового сообщения от участника запускается таймер на 60 секунд (настраивается, у нас сейчас стоит 90). Если в течение этого времени приходит новое сообщение — таймер сбрасывается и начинается заново. Когда тишина — Claude забирает весь накопленный пакет и отвечает разом.

Это похоже на то как ведёт себя нормальный человек в чате. Ты не отвечаешь на каждую запятую собеседника, ты выслушиваешь его до конца и потом реагируешь.

Реализация через asyncio:

# case_id -> Task. Активные «отложенные ответы Claude»._pending_tasks: dict[int, asyncio.Task] = {}_pending_lock = asyncio.Lock()async def _schedule_reply(bot, case_id, chat_id, topic_id) -> None:    """Отменяет старый таймер по case_id и запускает новый."""    async with _pending_lock:        existing = _pending_tasks.get(case_id)        if existing and not existing.done():            existing.cancel()                task = asyncio.create_task(            _wait_and_reply(bot, case_id, chat_id, topic_id)        )        _pending_tasks[case_id] = taskasync def _wait_and_reply(bot, case_id, chat_id, topic_id) -> None:    """Ждёт тишину DEBOUNCE_SECONDS, потом вызывает Claude."""    try:        await asyncio.sleep(settings.debounce_seconds)    except asyncio.CancelledError:        return  # пришло новое сообщение, нас отменили        await _run_arbiter(bot, case_id, chat_id, topic_id)

Логика тривиальная. Каждое новое сообщение от участника отменяет предыдущий таймер через task.cancel(). Если за 60 секунд никто ничего не написал, таймер досыпает и Claude забирает накопленную историю из БД.

Побочный приятный эффект: альбомы фотографий обрабатываются нормально. В Telegram альбом из 5 картинок приходит как 5 отдельных событий с одинаковым media_group_id. Раньше у меня бот реагировал на каждое — это было адски. Теперь он спокойно ждёт пока всё придёт и потом обрабатывает альбом как один логический контейнер.

Группировка медиагрупп в истории для Claude:

async def load_case_history(case_id, role_aliases):    rows = await fetch_messages(case_id)        history = []    i = 0    while i < len(rows):        m = rows[i]                # Собираем медиагруппу: соседние сообщения от одного отправителя         # с одинаковым media_group_id склеиваются в одно        group_msgs = [m]        j = i + 1        if m.media_group_id:            while j < len(rows) and rows[j].sender_id == m.sender_id \                    and rows[j].media_group_id == m.media_group_id:                group_msgs.append(rows[j])                j += 1                texts = []        images = []        for gm in group_msgs:            if gm.content:                texts.append(gm.content)            if gm.media_kind == "photo":                images.append(gm.media_path)                prefix = f"[{role_aliases.get(m.sender_id, 'Участник')}]: "        content = prefix + " ".join(texts)                if images:            content = build_user_content(content, images)  # vision-блоки                history.append({"role": "user", "content": content})        i = j if m.media_group_id else i + 1        return history

В вижн-блок Claude уходит сразу несколько изображений с общим caption-текстом. Это позволяет ему видеть скриншот переписки целиком, а не разрезанным на куски.

Изолированные группы под каждое дело

Вторая большая архитектурная штука это приватность. Если разбирать дела в общем чате-форуме с топиками, все участники видят все топики. Юзеры случайно (или специально) лезут в чужие конфликты, читают, иногда комментируют. Это смерть для доверия.

Telegram Bot API не умеет создавать группы. Эта функция доступна только обычным юзер-аккаунтам через MTProto.

Решение: посадить рядом второй процесс на Telethon, на обычном Telegram-аккаунте, и поручать ему создавать группы.

class UserBot:    async def create_arbitration_group(        self, case_id, bot_username, about    ) -> tuple[int, str] | None:        async with self._lock:            # 1. Создаём супергруппу через MTProto            result = await self.client(CreateChannelRequest(                title=f"Арбитраж #{case_id}",                about=about[:255],                megagroup=True,            ))            channel = result.chats[0]            chat_id_for_bot = int(f"-100{channel.id}")                        # 2. Добавляем бота-арбитра в группу            bot_entity = await self.client.get_entity(bot_username)            await self.client(InviteToChannelRequest(                channel=channel, users=[bot_entity],            ))                        # 3. Делаем бота админом            rights = ChatAdminRights(                delete_messages=True, ban_users=True,                invite_users=True, pin_messages=True,                change_info=True, other=True,            )            await self.client(EditAdminRequest(                channel=channel, user_id=bot_entity,                admin_rights=rights, rank="Арбитр",            ))                        # 4. Экспортируем invite-link            invite_obj = await self.client(                ExportChatInviteRequest(peer=channel)            )                        return chat_id_for_bot, invite_obj.link

Когда Анна закончила приём заявки, основной бот зовёт userbot.create_arbitration_group(), получает свежий chat_id и invite_link. Дальше:

  1. Бот-арбитр уже сидит в новой группе админом

  2. Истцу в ЛС летит ссылка

  3. Если ответчик когда-то писал боту, ему тоже летит ссылка в ЛС

  4. Если не писал — истец сам пересылает (бот не может писать первым незнакомцам, это ограничение Telegram)

После закрытия дела userbot сам удаляет группу через 60 секунд:

async def schedule_delete(    self, chat_id_minus100, delay_seconds=60) -> None:    async def _delayed():        await asyncio.sleep(delay_seconds)        await self.delete_group(chat_id_minus100)        asyncio.create_task(_delayed())

Архивы дел остаются в базе данных и доступны через бота в разделе «Мои дела». Сама Telegram-группа исчезает, чтобы не копить мусор на userbot-аккаунте (там есть лимит на количество созданных групп, около 50 в сутки для прогретого аккаунта).

Промпт арбитра и маркер финального вердикта

Дмитрий получает на вход досье от Анны и работает дальше. Его системный промпт жёстче чем у Анны. Несколько важных моментов оттуда:

PERSONA_SYSTEM = """Ты — Дмитрий, человек-арбитр в Telegram. Жёсткий, но справедливый. Разруливаешь споры между предпринимателями и клиентами.ВАЖНО: дело тебе передаёт секретарь Анна с предварительным досье. Если в контексте есть блок ДОСЬЕ ОТ СЕКРЕТАРЯ — используй его как стартовый ввод, не переспрашивай заново то что там уже есть.ТВОЙ СТИЛЬ:- Очень короткие реплики. 1-3 предложения, кроме финального вердикта.- Без AI-вступлений ("Конечно", "Я готов помочь", "Как ИИ"). - Не задавай вопросы ради вопросов.- Не разглашай что ты бот.ПРУФЫ:- Скриншоты переписок принимаешь, но проверяешь визуально на следы   редактирования. При сомнении — требуй переслать оригинал в чат.- БАНКОВСКИЕ ОПЕРАЦИИ — особый режим. Скриншот мобильного банка   НЕ окончательный пруф. Требуй PDF-выписку/справку из банка с   подписью.- Чеки об оплате — PDF-квитанция или ссылка на чек ФНС, не скриншот.- Отказ предоставить PDF по деньгам — сильный аргумент против стороны.КРИПТА — особый режим. Если речь о переводе крипты — не нужны скриншоты кошелька. Требуй TX hash. Бот САМ проверит хеш в любой из поддерживаемых сетей: Ethereum, BSC, Polygon, Arbitrum, Optimism, Base, Avalanche, Fantom, Tron, Bitcoin, TON, Solana.ФИНАЛЬНЫЙ ВЕРДИКТ — пиши строго в этом виде:**ВЕРДИКТ ПО ДЕЛУ #{case_id}**Прав: [Истец Имя / Ответчик Имя / частично оба]Факты:— [пункт со ссылкой на конкретный пруф]— [пункт]Решение:[что должна сделать каждая сторона, конкретно]Срок: [например "3 дня"]При неисполнении: внесение в публичный ЧС.[ВЕРДИКТ_ОКОНЧАТЕЛЕН]Маркер [ВЕРДИКТ_ОКОНЧАТЕЛЕН] в самом конце — ОБЯЗАТЕЛЬНЫЙ. Без него дело не закроется."""

Полтора десятка попыток ушло на то чтобы стабилизировать формат вердикта. Если оставить ИИ свободу формулировок, он каждый раз структурирует по-разному. Один раз решение в начале, в другой раз срок указан в скобках, в третий вообще забывает про срок. Я в итоге сделал жёсткий шаблон со словами Прав/Факты/Решение/Срок именно в таком порядке. И отдельный маркер в конце, чтобы код мог по нему ловить «всё, дело закрыто, удаляем группу, постим в хаб».

Логика автозакрытия в коде:

def is_final_verdict(text: str) -> bool:    return VERDICT_FINAL_MARKER in (text or "")def strip_marker(text: str) -> str:    """Убирает маркер из текста перед отправкой в чат."""    return text.replace(VERDICT_FINAL_MARKER, "").rstrip()# В обработчике сообщений арбитража:reply = await arbiter_reply_for_case(case_id, role_aliases)final = is_final_verdict(reply)visible = strip_marker(reply)  # пользователь маркер не увидитawait send_as_human(bot, chat_id=chat_id, text=visible)if final:    await _finalize_case(bot, case_id, visible, chat_id, topic_id)    # внутри: статус closed, отправка вердикта сторонам в ЛС,     # пост в хаб, отложенное удаление группы

Маркер мы из видимого сообщения вычищаем перед отправкой. Стороны видят только красиво оформленный вердикт, без служебных пометок.

Кстати, про красивое оформление. Claude по умолчанию пишет markdown с двойными звёздочками для жирного. Telegram эту разметку не понимает (legacy Markdown требует одиночные звёздочки, MarkdownV2 требует экранирования половины символов). Получалось страшно: «ВЕРДИКТ ПО ДЕЛУ #15» отображался со звёздочками вокруг.

Пришлось написать конвертер md → telegram HTML:

def md_to_html(text: str) -> str:    # Защищаем code-блоки от обработки    code_blocks = []    def _save_code(m):        code_blocks.append(m.group(1))        return f"\x00CB{len(code_blocks)-1}\x00"    text = re.sub(r"```([\s\S]*?)```", _save_code, text)        # HTML-escape    text = text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")        # Удаляем горизонтальные разделители (---, ***, ___)    text = re.sub(r"^\s*[-*_]{3,}\s*$", "", text, flags=re.MULTILINE)        # Bold: **text** → <b>text</b>    text = re.sub(r"\*\*([^\n*][^\n]*?)\*\*", r"<b>\1</b>", text)        # Italic: *text* → <i>text</i>    text = re.sub(        r"(?<![\w*])\*([^\n*]+?)\*(?![\w*])",         r"<i>\1</i>", text    )        # Восстанавливаем код-блоки    for i, c in enumerate(code_blocks):        esc = c.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")        text = text.replace(f"\x00CB{i}\x00", f"<pre>{esc}</pre>")        return text.strip()

Простой, но рабочий. Заодно вычищает горизонтальные разделители которые Claude любит ставить (--- посреди текста), они в Telegram выглядят как голый текст из дефисов и портят вид.

Проверка пруфов: блокчейн и почему скрин из мобильного банка это не пруф

Самая интересная часть проекта.

По нашему опыту 80% арбитражных дел в крипто-фриланс среде сводятся к одному вопросу: переводил ли человек деньги или нет. Дальше уже разбираются нюансы (вовремя ли, в полной ли сумме, и так далее). Но первичный факт обычно бинарный: было или не было.

И тут начинается цирк. Сторона А: «я перевёл». Сторона Б: «я не получал». Истец кидает скриншот мобильного банка где видна транзакция «-30 000 руб». Ответчик кидает скриншот своего банка где этой транзакции нет.

Кто прав?

Скриншот мобильного банка как доказательство — это смех. Любой человек с базовым фотошопом за пять минут нарисует «перевод на 500 тысяч в Сбербанк Премиум». Шрифт совпадает, цвета совпадают, иконка сбера на месте, кнопка «Перевести ещё» внизу. Это не пруф который арбитры обычно покупают только потому что лень разбираться.

Я ушил в промпт Дмитрия жёсткое правило:

БАНКОВСКИЕ ОПЕРАЦИИ — особый режим. Скриншот мобильного банка НЕ окончательный пруф. Требуй PDF-выписку/справку из банка с подписью (Сбер/Тинькофф/Альфа: "справка по операции", "выписка по счёту" — скачивается в ЛК в PDF). Говори прямо: "Скрин не пойдёт, скачай PDF-справку из банка с подписью".Чеки об оплате — PDF-квитанция или ссылка на чек ФНС (nalog.ru), не скриншот.Отказ предоставить PDF по деньгам — сильный аргумент против стороны.

PDF из банка не подделать за пять минут. Там есть цифровая подпись банка, штамп с временем, документ выгружается ровно с тем же фирменным дизайном что у банка. Подделать можно, но это уже не «открыл фотошоп», это требует уровня квалификации. Если человек на это пошёл — мы хотя бы перевели разговор в плоскость «у тебя поддельная подпись Сбербанка, готов отвечать в уголовке?»

С криптой ещё проще. Криптотранзакции нельзя подделать в принципе. Хеш либо есть в блокчейне с конкретными отправителем, получателем и суммой, либо его нет.

Я прикрутил проверку по 12 сетям. Если в чате арбитража кто-то прислал 64-символьный хеш или Solana-сигнатуру, бот автоматически идёт в публичные эксплореры и проверяет.

# core/blockchain.pyEVM_CHAINS = {    1:     ("Ethereum",  ERC20_TOKENS,     "ETH",  "etherscan.io"),    56:    ("BSC",       BEP20_TOKENS,     "BNB",  "bscscan.com"),    137:   ("Polygon",   POLYGON_TOKENS,   "POL",  "polygonscan.com"),    42161: ("Arbitrum",  ARBITRUM_TOKENS,  "ETH",  "arbiscan.io"),    10:    ("Optimism",  OPTIMISM_TOKENS,  "ETH",  "optimistic.etherscan.io"),    8453:  ("Base",      BASE_TOKENS,      "ETH",  "basescan.org"),    43114: ("Avalanche", AVALANCHE_TOKENS, "AVAX", "snowtrace.io"),    250:   ("Fantom",    FANTOM_TOKENS,    "FTM",  "ftmscan.com"),}async def check_transaction(tx_hash: str) -> BlockchainResult:    """Параллельно опрашиваем все подходящие сети.    Кто первый ответил 'нашёл' — тот результат и идёт в дело."""    clean = tx_hash.lower().removeprefix("0x")        is_hex64 = bool(re.fullmatch(r"[a-f0-9]{64}", clean))    is_solana = bool(re.fullmatch(r"[1-9A-HJ-NP-Za-km-z]{87,88}", tx_hash))    is_ton_b64 = bool(re.fullmatch(r"[A-Za-z0-9_\-]{43}=?", tx_hash))        tasks = []    if is_hex64:        for chainid in EVM_CHAINS:            tasks.append(lookup_evm(clean, chainid))        tasks.append(lookup_tron(clean))        tasks.append(lookup_bitcoin(clean))        tasks.append(lookup_ton(clean))    if is_solana:        tasks.append(lookup_solana(tx_hash))    if is_ton_b64:        tasks.append(lookup_ton(tx_hash))        results = await asyncio.gather(*tasks, return_exceptions=True)        for r in results:        if isinstance(r, BlockchainResult) and r.found:            return r        return BlockchainResult(found=False, tx_hash=clean)

Все 11+ сетей опрашиваются параллельно через asyncio.gather. Каждая через свой публичный API (Etherscan v2 для всех EVM-сетей одним ключом, TronScan, mempool.space, toncenter, Solscan). Через прокси конечно, потому что Россия и заблокированные эндпоинты у некоторых.

Результат подкладывается арбитру в контекст в виде специального системного сообщения:

[BLOCKCHAIN_PROOF, network=Tron (TRC-20), hash=b89cea2f1b2f3a4c...,  from=TXxxx..., to=TYxxx..., amount=50000 USDT, time=2026-03-12T15:42:00+00:00,  status=confirmed, confirmations=2547,  explorer=https://tronscan.org/#/transaction/b89cea2f...]

И отдельно для случая когда хеш не нашёлся ни в одной сети:

[BLOCKCHAIN_FAIL: hash=... не найден ни в Ethereum, ни в BSC, ни в Polygon, ни в Arbitrum, ни в Optimism, ни в Base, ни в Avalanche, ни в Fantom, ни в Tron, ни в Bitcoin, ни в TON, ни в Solana. Утверждение, что транзакция была проведена с этим хешем, ОПРОВЕРГНУТО блокчейном.]

Эту вторую штуку я считаю самой полезной фичей всего проекта. У нас уже было два случая когда сторона пыталась подсунуть случайный 64-hex как «вот хеш моего перевода». Бот за две секунды чекал, понимал что такой транзакции не существует, и Дмитрий в вердикте писал «утверждение о переводе опровергнуто блокчейном». Дело закрывалось мгновенно в пользу другой стороны.

Доказательство которое не подделать это, оказывается, очень мощный инструмент в арбитраже.

Память: как ИИ учится со временем

Это была одна из самых медленных в реализации частей.

Идея в том, что после каждого закрытого дела я как админ ставлю оценку 1-5 звёзд и пишу коммент. Что-то типа «слишком мягко наказал, ответчик явно лжёт» или «нормально, по делу». Эта пара (вердикт + мой коммент) уходит в векторную базу.

async def store_case_memory(    case_id, subject, verdict, admin_rating, admin_comment,     likes, dislikes):    """Сохраняем в ChromaDB для будущего поиска по схожим делам."""    full_text = f"""Дело: {subject}Вердикт:{verdict}Оценка админа: {admin_rating}/5Комментарий админа: {admin_comment or '—'}"""        metadata = {        "case_id": case_id,        "rating": admin_rating or 0,        "likes": likes,        "dislikes": dislikes,    }        collection.add(        documents=[full_text],        metadatas=[metadata],        ids=[f"case_{case_id}"],    )

Когда приходит новое дело, перед запросом к Claude бот ищет в этой базе 3-5 самых релевантных предыдущих кейсов:

async def build_memory_block(subject: str) -> str:    """Тянет похожие дела из векторной памяти и формирует подсказку     для системного промпта арбитра."""        results = collection.query(        query_texts=[subject],        n_results=5,    )        if not results["documents"][0]:        return ""        block_parts = ["ПОХОЖИЕ ПРОШЛЫЕ ДЕЛА И ОЦЕНКИ АДМИНА:"]    for i, doc in enumerate(results["documents"][0]):        meta = results["metadatas"][0][i]        rating = meta.get("rating", 0)        block_parts.append(f"\n[Дело #{meta['case_id']}, оценка {rating}/5]")        block_parts.append(doc[:1500])        return "\n".join(block_parts)

Этот блок вшивается в системный промпт Дмитрия. Получается что-то вроде:

[стандартный промпт арбитра]...ПОХОЖИЕ ПРОШЛЫЕ ДЕЛА И ОЦЕНКИ АДМИНА:[Дело #12, оценка 2/5]Дело: ответчик не выполнил работу в срок, истец требует возвратаВердикт: ... вернуть 50% ...Комментарий админа: слишком мягко, ответчик игнорил две недели, надо было возвращать 100% и в ЧС.[Дело #28, оценка 4/5]Дело: спор о качестве работы дизайнераВердикт: ... разделить 70/30 ...Комментарий админа: хорошее решение, учёл оба фактора.

Дальше Дмитрий читает это и автоматически учитывает мой стиль арбитража. Через 20-30 дел он становится довольно точно настроенным «под меня». В одном из тестов я специально задал ему похожее дело — он применил ту же логику что в моём предыдущем комменте, без явных подсказок.

Это не настоящее обучение в смысле fine-tuning, это RAG (retrieval-augmented generation) поверх готовой модели. Но в практике работает удивительно хорошо.

Реальный кейс который мы прогнали

Теперь к самому интересному.

У моего партнёра был спор. Он работал с человеком на фрилансе, был не очень формальный договор, в процессе исполнения возник конфликт по поводу частичного возврата средств. Сумма была серьёзная, для обоих участников — заметная.

Конфликт пошёл в арбитраж. Не наш бот, а обычный, человеческий. Зашли в чат, пригласили двух арбитров (это распространённая практика в нашем сообществе — берут двух чтобы хотя бы один был не в курсе). Начался разбор.

Разбор длился три недели.

За эти три недели:

  • Произошла пересмена арбитра (один не выдержал и вышел, заменили на другого)

  • Стороны три раза переговаривались напрямую без арбитра

  • В чат закидывали скриншоты переписок, скриншоты банка, скриншоты криптокошельков

  • Один из арбитров уходил в отпуск

  • Произошёл какой-то странный момент когда стороны начали орать друг на друга и арбитр временно их разбанил-забанил-разбанил

В конце концов вердикт был вынесен. Он был довольно нюансированный: один из участников частично возвращает сумму, второй частично сохраняет работу, что-то про сроки исполнения остатка, упоминание ЧС при невыполнении. В общем, классический компромиссный вердикт.

И вот тут самое интересное. После того как вердикт уже вынесли и стороны разошлись, я (как создатель Claude Justice) попросил у партнёра разрешение прогнать всё то же самое через бота. С той же фактурой, с теми же скриншотами, всё чин чином. Никаких подсказок в стиле «вот настоящий вердикт, давай повторим». Только сырые данные.

Анна приняла заявку. Спросила имя ответчика. Спросила сумму. Передала Дмитрию. Дмитрий запросил пруфы. Получил скриншоты. Один из них попросил переприслать в виде PDF из банка (то самое правило про мобильные скрины). Получил PDF. Задал ещё один уточняющий вопрос. Получил ответ. Вынес вердикт.

Заняло это 12 минут.

Вердикт совпал. Не на 100%, формулировки были другие, но суть была идентичной. Кто прав, какие действия совершает каждая сторона, какой срок, что при неисполнении. Совпадает с тем что три недели рожали два живых арбитра, плюс смены, плюс крики, плюс отпуски.

После этого я прогнал ещё 4 дела которые мы знали в подробностях из нашей группы — старые, уже закрытые. В 3 из 4 случаев вердикт сошёлся практически дословно. В четвёртом случае было расхождение, но Дмитрий разрешил в пользу другой стороны на основании одного факта который реальный арбитр упустил (там был подзаголовок в скриншоте переписки который означал «согласие», но человек его не заметил). Я долго думал кто из них прав, и потом признал что бот прав.

Так заменит ли ИИ настоящих арбитров

Честно? Не заменит. Но потеснит — точно.

Давайте по пунктам где ИИ хорош.

Скорость. 12 минут vs 3 недели. Это разница не в порядки, это в порядки порядков. Для большинства арбитражей фактор скорости важнее чем нюансы. Особенно если речь о небольших суммах.

Дешевизна. На текущей цене Sonnet 4.6 один разбор дела стоит мне примерно 5-15 рублей. Живой арбитр на ту же работу хочет тысячи. Это позволяет открывать арбитраж за условные 100 рублей конечному пользователю, что радикально меняет порог входа.

Беспристрастность. ИИ не знает участников, ему всё равно кто чей друг. У него нет настроения, нет недосыпа, нет личных счётов. В нашем сообществе это огромный плюс.

Память. Через 50 дел Дмитрий помнит как разбирались похожие случаи и почему я ставил низкие оценки за конкретные решения. Живой арбитр учится только сам у себя, и обычно медленнее.

Доказательная база. Автоматическая проверка хешей в 12 блокчейн-сетях — это то, чего живой арбитр в принципе не сделает в нормальные сроки. Нужно лезть в эксплореры, копировать, проверять, разбираться. ИИ-бот это делает за секунду параллельно.

Теперь где ИИ плох.

Сложные кейсы с социальной динамикой. Когда участники начинают манипулировать, прятать факты, переводить разговор в эмоции — Claude иногда теряет нить. Живой арбитр в эти моменты лучше: видит когда человек врёт, чувствует подтекст, может надавить.

Юридическая глубина. Если кейс выходит за рамки простого «было/не было», и нужно реальное понимание права — ИИ упирается в потолок. Хороший живой арбитр с юридическим бэкграундом разберёт нюансы которые Claude просто не знает.

Подделанные пруфы среднего уровня. Если кто-то реально вложился в фейк (профессиональный PDF с поддельной подписью банка, хорошо отрендеренная переписка) — ИИ может купиться. Живой человек с подозрением — нет, пойдёт проверять через знакомых в банке.

Эмоциональная поддержка. Иногда людям нужно чтобы их сначала выслушали, потом разобрали. ИИ слушает плохо, режет на факты сразу. Это бесит часть аудитории.

Поэтому правильная мысль не «заменим арбитров», а «расслоим систему». Простые быстрые кейсы (90% потока) забирает ИИ. Сложные нюансные (10%) идут к живым арбитрам, и те получают за свою работу нормальные деньги — потому что у них теперь только реально сложная работа, а не разгребание мусора.

Это та же история что с пилотами. Автопилот не заменил пилотов, он забрал у них рутину взлёта-посадки. Пилоты остались в кабине, но теперь они вмешиваются только когда что-то реально нештатное. И это сделало авиацию безопаснее, а не наоборот.

Где это всё можно потрогать

Бот живёт в Telegram — @VERDICTROBOT. Бесплатный. Заявку может оставить любой человек через /start. Анна принимает, фильтрует, дальше начинается разбор.

Вокруг бота у нас есть супергруппа, Кодекс Клода. Там в публичных топиках выкладываются вердикты по закрытым делам (с согласия сторон, без раскрытия персональных данных) и обновления чёрного списка недобросовестных пользователей. Это не строго юридическая помощь, это скорее цифровое самоуправление сообщества.

Группа открытая, заходите, потыкайте бота, попробуйте создать тестовый арбитраж между двумя своими аккаунтами или с кем-то из друзей. Если что-то сломается или Анна задаст слишком много вопросов — кидайте в чате жалобу, я правлю.

Технические замечания если вы из разработчиков:

  • Стек: Python 3.12, aiogram 3.13, Anthropic SDK, Telethon, ChromaDB, PostgreSQL

  • Sonnet 4.6 для арбитра, Haiku 4.5 для секретаря

  • Прокси через свой VPS (мы в РФ, надо)

  • Vector embeddings на MiniLM-L6-v2 (норм для русского, для серьёзного юр-индекса заменили бы на multilingual-e5)

Код проекта пока закрытый, но если будет интерес сообщества выложим часть на гитхаб с урезанным функционалом для самохостинга.

Дисклеймер и оговорки

Чтобы юристы не ругались.

Бот не является заменой государственному правосудию. Никакие вердикты Claude Justice не имеют юридической силы в РФ. Это инструмент досудебного урегулирования в добровольном порядке. Стороны согласны разбираться через ИИ, потому что им так удобнее и быстрее. Если кто-то не согласен с результатом — он может идти в обычный суд по той же фактуре.

Все персональные данные обрабатываются согласно 152-ФЗ. Пользователь даёт явное согласие при первом запуске бота. Сообщения хранятся в БД на территории РФ. Через 3 года после закрытия дела данные можно удалить по запросу.

Бот не разрешает уголовные дела, бытовые конфликты вне договоров, и любые истории где требуется реальное правоприменение. Анна это всё фильтрует на входе.

Если вы готовитесь к настоящему судебному разбирательству — берите живого юриста. Если вам надо быстро разрулить копеечный спор по фрилансу — забегайте к Дмитрию.


Если статья зайдёт, в следующей расскажу подробнее про устройство userbot-фабрики приватных групп и как мы боролись с тем что Telegram банит аккаунты за массовое создание групп. Спасибо что дочитали.

ссылка на оригинал статьи https://habr.com/ru/articles/1035278/