惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V2EX - 技术
V2EX - 技术
P
Privacy International News Feed
Security Latest
Security Latest
H
Hacker News: Front Page
T
Tenable Blog
The Hacker News
The Hacker News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Security @ Cisco Blogs
Project Zero
Project Zero
O
OpenAI News
AI
AI
Spread Privacy
Spread Privacy
C
CERT Recently Published Vulnerability Notes
The Last Watchdog
The Last Watchdog
G
GRAHAM CLULEY
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Scott Helme
Scott Helme
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
CXSECURITY Database RSS Feed - CXSecurity.com
NISL@THU
NISL@THU
A
Arctic Wolf
T
Threat Research - Cisco Blogs
PCI Perspectives
PCI Perspectives
N
News and Events Feed by Topic
C
Cyber Attacks, Cyber Crime and Cyber Security
C
Cybersecurity and Infrastructure Security Agency CISA
Simon Willison's Weblog
Simon Willison's Weblog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Know Your Adversary
Know Your Adversary
Google Online Security Blog
Google Online Security Blog
罗磊的独立博客
L
LINUX DO - 最新话题
U
Unit 42
S
Security Affairs
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
博客园 - 【当耐特】
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
月光博客
月光博客
Engineering at Meta
Engineering at Meta
腾讯CDC
F
Full Disclosure
Cyberwarzone
Cyberwarzone
S
SegmentFault 最新的问题
Recorded Future
Recorded Future
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
The Cloudflare Blog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Заменит ли ИИ настоящих судей? Я скормил ему дело которое арбитры разбирали 3 недели
niktomimo · 2026-05-14 · via Все публикации подряд на Хабре

Спойлер: вердикт совпал слово в слово. Только заняло это не три недели, а 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] = task


async 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.py

EVM_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 банит аккаунты за массовое создание групп. Спасибо что дочитали.