Skip to main content
Glama

MARL Cop & Thief — два ИИ-агента на базе MCP

Децентрализованная частично наблюдаемая игра в догонялки между двумя автономными ИИ-агентами — Cop (полицейским) и Thief (вором), — которые общаются на свободном естественном языке через MCP-серверы, выбирают ходы с помощью теоретико-игрового движка minimax + self-play RL, отображаются в реальном времени в веб-панели управления и отправляют по електронной почте согласованный обеими сторонами JSON-отчёт о матче через Gmail API.

Университет Хайфы · Orchestration of AI Agents (ex06) · д-р Йорам Сегал. Одна команда запускает всё; одна вкладка браузера проводит целый матч.


Ключевые особенности

  • Запуск одной командойpython -m cop_thief.app запускает оба MCP-сервера, публичные туннели Cloudflare и браузерную панель управления вместе (никаких осиротевших туннелей, никакой возни с портами).

  • Веб-панель управления — статус узла в реальном времени, публичные URL/токены с копированием, форма вызова оппонента, зеркальный самотест в один клик и живое табло игры 5×5 — всё по адресу http://127.0.0.1:8800.

  • Настоящая стратегия — движок minimax «Ангел–Дьявол» (антагонистическая марковская игра, альфа-бета) с блокирующей игрой Конвея (Cop = стены Дьявола, §4.3) и обучением весов через self-play RL, далеко за пределами базовой табличной Q из задания. См. docs/STRATEGY.md.

  • Децентрализованность и защита от читерства — нет судьи; обе стороны хэшируют результат (SHA-256), и любое расхождение даёт счёт 0/0. Входящий текст считается враждебным: prompt-инъекции и принуждение отсеиваются, фиксируются как улики и не могут изменить исход (действия «сдаться» не существует).

  • Единая граница SDK + API Gatekeeper — вся логика за CopThiefSDK; каждый внешний вызов (LLM, Gmail) проходит через шлюз с FIFO и обратным давлением, с фейловером DeepSeek→Anthropic.

  • Контроль качестваpytest ≥ 85 % покрытия, ruff без нарушений, ≤ 150 строк/файл, только uv.


Related MCP server: Police MCP Server

Быстрый старт

uv sync                                   # install (uv is the ONLY package manager)
cp .env-example .env                      # fill in real values (see "Secrets")
uv run ruff check .                       # zero-violation lint gate
uv run pytest                             # full suite (>=85% coverage gate)
uv run python -m cop_thief.app            # launch the control panel + servers + tunnels

Затем откройте http://127.0.0.1:8800.


Проведение матча (панель управления)

  1. uv run python -m cop_thief.app → открывается панель; дождитесь зелёных индикаторов Серверы ● и Туннели ●.

  2. Карточка статуса показывает два ваших публичных URL …/mcp/ + токены по ролям (кнопки копирования). Отправьте их оппоненту вместе с docs/INTER_GROUP_TREATY_SPEC.md. Используйте формы для заполнения из match_setup/ (RULES.txt, OUR_DETAILS, OPPONENT_DETAILS).

  3. Вставьте два URL …/mcp/ оппонента (и токены, если есть) в форму вызова, затем нажмите START CHALLENGE — 6 под-игр разыгрываются между хостами на табло, а отчёт отправляется по электронной почте.

  4. Партнёра пока нет? Нажмите MIRROR SELF-TEST ⟳ — он подставляет ваши собственные localhost-эндпоинты + токены и играет вас против вас самих (идеально для тестирования стратегии).

Игра = 6 под-игр (по §4.1): мы играем Cop в 3 (домашняя нога) и Thief в 3 (гостевая нога), Thief ходит первым, ≤ 25 ходов в каждой. Система очков неизменна: поимка Cop → 20 / 5; выживание Thief → 5 / 10.


Скриншоты

Панель управления — статус узла (серверы/туннели/игра), наши доступные для передачи URL …/mcp/ + токены с кнопками копирования, форма вызова оппонента и живая игра. Здесь запущен зеркальный самотест; обратите внимание на строку [INTENT: BARRIER] (Cop ставит стену на соседней клетке и остаётся на месте, §4.3), зелёный барьер B и поимку !.

Control panel

Живая доска — сетка 5×5 с Cop C (синий) и Thief T (красный), рядом лента перехвата сообщений, показывающая передачи на естественном языке [INTENT: MOVE] и разделители HOME LEG / Sub-game.

Live board

Переход между ногами — бегун переходит в AWAY LEG (мы играем за Thief) на под-игре 4/6, при этом на доске всё ещё есть барьер B и поимка !.

Away leg and barrier

Запуск узла выводит доступные для передачи эндпоинты в терминал (один процесс: серверы + туннели + панель):

Control panel  >  http://127.0.0.1:8800   (open in a browser)
╔══════════════════════════════════════════════════════════════════╗
║ LIVE PUBLIC MATRIX (Team Alpha)                                   ║
╠══════════════════════════════════════════════════════════════════╣
║ COP   (:8001)  https://acting-tomorrow-yard-raid.trycloudflare.com/mcp/   ║
║ THIEF (:8002)  https://dial-mean-courses-tramadol.trycloudflare.com/mcp/  ║
╚══════════════════════════════════════════════════════════════════╝
Tunnels live and written to config/setup.json. Share these /mcp/ URLs. Ctrl+C to stop.

Архитектура

Слой

Модуль

Обязанность

Домен

domain/

Иммутабельные DecPomdpGameState, Grid, геометрия, язык NL-ходов ([INTENT: …]).

Стратегия

domain/strategy/

minimax (альфа-бета), evaluation/features (Ангел–Дьявол), selfplay (RL), базовая Q-таблица.

SDK

sdk/

Единая точка входа CopThiefSDK; MatchCoordinator (терминальная логика, логика смерти в ловушке); экран защиты от warfare/инъекций.

Gatekeeper

infra/gatekeeper/

FIFO-шлюз для всех вызовов LLM/Gmail; фейловер DeepSeek→Anthropic; телеметрия токенов.

Серверы

servers/

FastMCP-серверы Cop и Thief; авторизация по токенам; инструмент request_moveStrategyResolver.

Транспорт

infra/network/

streamable-HTTP хост /mcp, RemoteMoveClient, коммутатор Cloudflare.

Оркестрация

orchestrator/

ChallengeRunner (кросс-хост, по ногам), reconcile (взаимное согласие / 0-0), серия.

UI

ui/

Бэкенд панели управления (server.py), NodeState, широковещательная SSE-шина, static/panel.html.

Отчётность

reporting/

Gmail OAuth-репортер (имя группы в теме и теле), append-only журнал аудита, предохранитель.

Точки входа

Команда

Что делает

python -m cop_thief.app

Панель управления: серверы + туннели + веб-UI (основная).

python -m cop_thief.challenge

Интерактивный терминальный вызов между хостами (запрашивает URL оппонента).

python -m cop_thief.serve

Только серверы + туннели (без UI).

python -m cop_thief.infra.network.dual_mcp_host

Только два MCP-сервера (:8001/:8002 /mcp).

python -m cop_thief.diagnostic_runner

Офлайн-зонд погони нулевой стоимости (мок-LLM).


Стратегия в одном абзаце

Каждый request_move обрабатывается ограниченным по глубине alpha-beta minimax над антагонистической марковской игрой (Cop максимизирует, Thief минимизирует, предположение об оптимальном противнике). Терминальные оценки, формируемые прогрессом (±WIN ∓ turns), заставляют политику стремиться к поимке/выживанию, поэтому ничьи структурно исключены. Множество действий Cop включает установку стены на соседней клетке (ход «Дьявола» Конвея, §4.3); фича containment в оценке — это залитая (flood-filled) область побега Thief, поэтому планировщик сам находит легальные линии загона в ловушку. Линейные веса оценки настраиваются через self-play TD (selfplay.train_weights). Три варианта профилей (агрессивный / сбалансированный / оборонительный) формируют требуемый состав из 3 агентов. Полное описание: docs/STRATEGY.md.


Формальная модель — Dec-POMDP

Погоня моделируется как децентрализованный частично наблюдаемый марковский процесс принятия решений — кортеж ⟨ n, S, {Aᵢ}, P, R, {Ωᵢ}, O, γ ⟩ (ex06 §11):

Символ

Значение

В этом проекте

n

агенты

2 — Cop и Thief (независимы, без общей памяти).

S

пространство состояний

DecPomdpGameState: cop_pos, thief_pos ∈ сетка 5×5, множество barriers ⊆ G (≤ 5), cop_barriers_left, turn_counter, turn_role. Совместное пространство позиций ограничено (R·C)² = 625; с барьерами достижимое пространство больше, но конечно.

Aᵢ

действия агента

Cop: 8 ходов короля (Чебышев ≤ 1) ∪ поставить барьер на соседнюю свободную клетку (остаться на месте) ∪ HOLD. Thief: 8 ходов короля ∪ HOLD. «Остаться» — вырожденный ход.

P

переход

Детерминированный конечный автомат доски (apply_action): одна мутация за ход; недопустимые ходы (за пределы доски / на барьер / не ход короля) отклоняются; ход с барьером огораживает указанную соседнюю клетку, а Cop остаётся на месте.

R

награда

Иммутабельная таблица 1: поимка → Cop +20 / Thief +5; уклонение → Cop +5 / Thief +10. Планировщик использует терминальные значения, формируемые прогрессом (±WIN ∓ turns), так что партия строго результативна (без ничьих).

Ωᵢ

пространство наблюдений

Субъективный обзор для каждого агента: точные координаты оппонента тогда и только тогда, когда он в радиусе обзора, иначе качественный сектор заслонения (например, THIEF_IN_NORTHWEST_QUADRANT).

O

функция наблюдения

get_subjective_observation(role, radius) — раскрывает оппонента, когда манхэттенское расстояние ≤ vision.radius (по умолчанию 2), иначе только квадрант. Симметрично для обеих ролей (туман войны).

γ

дисконт

rl.gamma = 0.9 для self-play TD / Q-базового уровня; слой minimax вместо этого использует терминальные оценки, формируемые прогрессом, чтобы добиваться поимки/выживания.

Частичная наблюдаемость реальна: каждый агент принимает решение на основе своего представления о доске (последнее наблюдение + разобранная проза оппонента), а не на основе глобальной истинной картины.

Проблемы оркестрации (трудная часть)

Согласно ex06 §14, ценность задания — в оркестрации, а не в победе. Трудные проблемы и способы их решения:

  • Свободный естественный язык, без предопределённого протокола. Агенты общаются прозой. Мы добавляем тонкий детерминированный контракт — каждое сообщение начинается ровно с одного маркера [INTENT: MOVE|BARRIER|HOLD], за которым следует слово-компас, — так что ход машинно разрешим, а тело остаётся свободным естественным языком. Это удерживает два независимо построенных движка в синхронизации без общей кодовой базы.

  • Языковая неоднозначность и недоверенный ввод. Входящая проза разбирается детерминированно (слово направления по самому длинному совпадению, намерение только в квадратных скобках, чтобы декоративный текст не мог его подделать) с опциональным LLM-разбором для неструктурированного текста противника; низкая уверенность → безопасный исследовательский запасной вариант (никогда не падает, никогда не подделывает взятие). Каждое поле рассматривается как враждебное — например, нечисловой variant приводится, а не принимается на веру.

  • Обеспечение взаимопонимания. Обе стороны используют общий детерминированный язык ходов (кодирование/разбор без необходимости в LLM), поэтому партия воспроизводима побайтово. В конце обе стороны хешируют каноническое значение sub_games (SHA-256, K3); любое несовпадение ⇒ 0/0 для обеих — согласованность обеспечивается принудительно, а не предполагается.

  • Живучесть в ненадёжной сети. Межхостовые ходы повторяются с переподключением; длительный сбой или зависший участник (таймаут 20 с на ход) означает поражение в этой под-игре, так что серия всегда завершает все 6 партий, а отчёт всё равно отправляется по почте — недоступный противник никогда не сможет остановить матч.

Визуализация и неопровержимые доказательства (§11)

  • GUIСкриншоты выше показывают живую доску 5×5, ленту перехвата коммуникаций [INTENT: …], барьеры (B), взятия (!) и переходы между этапами.

  • Облачная MCP-коммуникация — загрузочный блок выше выводит актуальные публичные Cloudflare-URL /mcp/, а лента коммуникаций панели транслирует реальные передачи [INTENT:], которыми обмениваются с облачными серверами; каждый ход также добавляется в data/game_audit.jsonl и запечатывается в архив каждой партии с защитой от подделки (data/archive/, SHA-256 набора в отчёте).

  • Обучение — веса стратегии настраиваются методом самоигры TD (selfplay.train_weights); для сравнения сохранена базовая табличная модель Q-learning. Дизайн и кривые: docs/STRATEGY.md.

Безопасность и честная игра

  • Токены — каждый вызов инструмента MCP требует отдельного отзываемого bearer-токена для каждой роли; токены передаются внеполосно и могут ротироваться для отзыва доступа. Серверы закрыты по умолчанию (fail-closed).

  • Защита от инъекций — входящие передачи считаются недоверенными; инъекции/принуждение/имитация/подделка проверяются, помечаются hostile:true в data/game_audit.jsonl, учитываются в отчёте и не влияют на результат, определяемый движком. Для противников это зафиксировано в договоре (§F).

  • Взаимное согласие — обе стороны хешируют каноническое значение sub_games; любое несовпадение ⇒ 0/0 (both_lose).

  • Отчётность — Gmail API через OAuth2 Desktop (scope gmail.modify, без паролей); название группы указывается и в теме письма, и в JSON-теле; предохранитель по умолчанию использует одноразовый почтовый ящик.


Бюджет токенов и стоимость

Каждый внешний вызов учитывается через API GatekeeperTokenTracker, который в реальном времени передаёт данные об использовании в data/token_usage.json (атомарная запись; эти данные исключены из хеша соглашения K3, поэтому стоимость никогда не влияет на результат). Все цифры задаются конфигурацией (config/setup.json → token_budget / economics).

Провайдер (роль)

Вход, $/M

Выход, $/M

DeepSeek deepseek-chat (основной)

0.15

0.60

Anthropic claude-3-5-sonnet (только фолбэк)

3.00

15.00

Статья бюджета

Значение

Фактические расходы на сегодня (суммарно по всем запускам)

≈ $0.01

Бюджет жизненного цикла

200 000 входных + 50 000 выходных токенов

→ прогнозируемая стоимость жизненного цикла (основной)

~$0.06

Оценка на один ход (120 входных / 40 выходных)

~$0.00004

Жёсткий предел (предупреждение при 80 %)

$0.50 (предупреждение $0.40)

Контроль соблюдения

gatekeeper возвращает BudgetExceeded для платных LLM-вызовов при достижении предела — никогда не падает

На практике весь проект обошёлся примерно в $0.01 на LLM. Ходы генерирует локальный движок минимакса, а язык ходов — детерминированный [INTENT: …] encode/parse: для игры и отправки отчёта LLM не нужен (Gmail API, а не LLM). Крошечный бюджет + фолбэк с приоритетом DeepSeek — это предохранители для опционального LLM-ассистированного разбора естественного языка; предел установлен на $0.50 с большим запасом.

Секреты и конфигурация

  • Скопируйте .env-example.env и заполните: DEEPSEEK_API_KEY, ANTHROPIC_API_KEY, COP_MCP_TOKEN, THIEF_MCP_TOKEN, GMAIL_CREDENTIALS_PATH. Автозагрузчик без зависимостей внедряет .env при запуске — export/source не нужны; уже существующие экспортированные переменные окружения всегда имеют приоритет.

  • Все настраиваемые параметры находятся в версионируемых config/*.json (без хардкода). Секреты (.env, credentials.json, token.json) игнорируются git и никогда не попадают в систему контроля версий.

Документация

PRD · PLAN · TODO · STRATEGY · RULES_AND_AGREEMENTS · INTER_GROUP_TREATY_SPEC

Лицензия

MIT.

Maintenance

ActivityMaintained
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/najikay/mcp-marl-pursuit'

If you have feedback or need assistance with the MCP directory API, please join our Discord server