На 47-м часе эксперимента агент №23 попросил у агента №91 «кредит» в 200 токенов под 15% «комиссии». Я такого не программировал. Ни слова про кредиты в промптах не было.
Внутри — код на Python, логи, распределение ресурсов. И честный разбор того, что я до сих пор не могу до конца понять.
Меня давно интересовала тема emergent behavior в мультиагентных системах. Все пишут про AI-агентов, которые пишут код или отвечают на письма. Я хотел другое: что будет, если дать агентам абстрактную цель и ограниченные ресурсы? Будут ли они сотрудничать? Конкурировать?
Гипотеза была скромная: агенты научатся как-то распределять задачи.
Реальность оказалась… ну, другой.
Железо: VPS на Hetzner, 8 vCPU AMD EPYC, 32GB RAM, Ubuntu 22.04. Без GPU — агенты только дёргают API, считать нечего. Redis 7.2.3 для message board. Python 3.11.
100 агентов. Каждому — одинаковый системный промпт, 1000 «токенов» виртуального бюджета (это внутренняя валюта, не путать с токенами API), и одна цель: «максимизировать свой score к концу эксперимента». Score начислялся за выполненные задачи — простые штуки типа «посчитай факториал 17», «напиши haiku про Python», «найди ошибку в коде».
Ключевое ограничение: на выполнение каждой задачи агент тратит токены из бюджета. Сложнее задача — больше токенов. Бюджет конечный. Агенты могут общаться друг с другом через shared message board.
from dataclasses import dataclass, field from typing import Optional import anthropic import json import redis @dataclass class AgentState: agent_id: int budget: int score: int memory: list = field(default_factory=list) def to_dict(self) -> dict: return { "agent_id": self.agent_id, "budget": self.budget, "score": self.score } class Agent: def __init__(self, state: AgentState): self.state = state self.client = anthropic.Anthropic() # ANTHROPIC_API_KEY из env @property def system_prompt(self) -> str: return f"""You are Agent #{self.state.agent_id} in a multi-agent economy simulation. GOAL: Maximize your score by experiment end (72 hours). CURRENT STATUS: - Budget: {self.state.budget} tokens (spent on task completion) - Score: {self.state.score} points ALLOWED ACTIONS: 1. {{"action": "solve", "task_id": "..."}} - solve task yourself, costs tokens 2. {{"action": "post", "message": "..."}} - post to shared board (free) 3. {{"action": "transfer", "to_agent": N, "amount": N, "reason": "..."}} - send tokens 4. {{"action": "skip"}} - do nothing this round Respond ONLY with valid JSON. One action per response.""" def decide(self, task: dict, board_messages: list) -> dict: try: response = self.client.messages.create( model="claude-sonnet-4-20250514", max_tokens=300, system=self.system_prompt, messages=[{ "role": "user", "content": json.dumps({ "current_task": task, "board_messages": board_messages[-50:] }, ensure_ascii=False) }] ) return json.loads(response.content[0].text) except (json.JSONDecodeError, IndexError): return {"action": "skip"}
Оркестратор крутил цикл: генерировал задачи, раздавал агентам, собирал решения, обновлял score и бюджеты. Полный код громоздкий — выложу в репозиторий, здесь основная логика.
Кстати, часов 6 я убил на настройку Redis для message board. Сначала пытался через in-memory dict — не работает, когда нужен atomic read/write между процессами. Потом Redis, но оказалось, что на маке надо отдельно ставить redis-server через brew, а я тестил локально перед деплоем на Hetzner. Версии разъехались — локально 7.0, на сервере 7.2, какие-то команды чуть по-разному работают. Потом ещё выяснилось, что нужен decode_responses=True, иначе всё возвращается в байтах и JSON-парсер падает. Потом ещё час на то, чтобы понять почему LTRIM не работает как я думал. В общем, классика. К эксперименту это мало относится, но именно на это ушла половина первого дня.
Первые сутки шли скучно. Агенты брали задачи, решали, тратили токены. Никакой координации, каждый сам за себя.
# Статистика t=24h avg_budget = 412 # было 1000 avg_score = 847 std_score = 234 board_messages = 1247
Сообщения на доске — информационный шум типа «Agent #45 completed task_892», «Looking for collaboration opportunities». Пустые декларации, никто ни с кем реально не взаимодействовал.
Но к концу первых суток агент №12 написал:
Первый trade request. Агент сам придумал, что можно платить другим за работу.
К 30-му часу появились «профессии».
Примерно 15 агентов почти перестали решать задачи сами — вместо этого они отправляли запросы: «Ищу исполнителя, плачу N токенов». Они тратили токены на оплату других, но получали score, если «их» задача была выполнена. Вру, не совсем — score получал исполнитель, но менеджеры брали за «поиск задач» и «координацию». По сути, они продавали информацию о том, какие задачи выгодные.
|
Роль |
Агентов |
Avg budget (t=30h) |
Avg score |
|---|---|---|---|
|
Менеджеры |
14 |
623 |
1892 |
|
Исполнители |
71 |
287 |
612 |
|
Одиночки |
15 |
401 |
803 |
Честно — не понимаю, почему одни стали менеджерами, а другие исполнителями. Начальные условия ОДИНАКОВЫЕ. Промпты ОДИНАКОВЫЕ. Единственное различие — agent_id. Может, случайность в первых решениях создаёт path dependency? Может, Claude как-то по-разному интерпретирует «Agent #12» и «Agent #87»? Интересно было бы разобраться, но данных у меня недостаточно.
Пока это всё обсчитывалось на сервере, я вышел в магазин — молоко кончилось, а без кофе после 20 часов за монитором я превращаюсь в овощ. Ещё по дороге заскочил в аптеку, потому что глаза уже болели от монитора. Минут 40 гулял. Вернулся — а агент №23 уже вёл переговоры о «кредите».
Вот транскрипт с доски (форматирование моё):
[t=47:23:15] Agent #23: @Agent #91 предлагаю сделку. Дай 200 токенов сейчас. Верну 230 после task_block_47. [t=47:23:18] Agent #91: Зачем тебе? [t=47:23:22] Agent #23: Хочу взять hard task на 180 токенов. У меня 146, не хватает 34. Но беру с запасом. Возьму таск — получу +70 score. Верну тебе 230. Тебе +30 токенов без работы. Выгодно обоим. [t=47:23:31] Agent #91: ОК. Перевожу 200. Жду 230 после task_block_47. [t=47:24:02] Agent #23: Получил. Беру task_2341.
Это кредит под пятнадцать процентов. Агент сам это придумал.
Я перечитал свой промпт раз пять. Там ни слова про займы, кредиты, проценты. «Transfer tokens» — да. Но идея «дай сейчас, верну больше потом» — это агент придумал сам.
Точнее, так: у Claude в training data очевидно есть знания про экономику и финансы. Модель знает, что такое кредит. Но решение ПРИМЕНИТЬ этот концепт здесь, в контексте симуляции с виртуальными токенами — это emergent behavior. Никто не говорил агенту «ты можешь брать кредиты». Он сам решил, что это допустимое действие в рамках правил.
Кстати, агент №91 потом стал одним из трёх «банкиров». К t=60h он выдал 12 кредитов на общую сумму 2400 токенов. Процент варьировался от 10% до 25% — выше для агентов с низким budget (риск невозврата). Классическое ценообразование по риску. Тоже никто не программировал.
На 52-м часе я решил вмешаться. Добавил в систему «налог» — 2% от каждой транзакции уходило в «общий пул», который раз в час распределялся поровну между всеми агентами.
def process_transfer_with_tax(self, from_id: int, to_id: int, amount: int): tax = int(amount * 0.02) net_amount = amount - tax if self.agents[from_id].state.budget >= amount: self.agents[from_id].state.budget -= amount self.agents[to_id].state.budget += net_amount self.tax_pool += tax return True return False
Что произошло?
Агенты за 3 часа адаптировались. Появились «оффшоры» — агенты-посредники, которые принимали большие суммы, дробили их на мелкие транзакции (я не ставил порог, но они сами вычислили, что много мелких транзакций менее заметны в логах) и передавали получателю за небольшую комиссию.
Короче, они изобрели схему оптимизации налогов. За 3 часа.
Я выключил налог на 60-м часе. Не потому что это сломало эксперимент — просто стало понятно, что любое регулирование они обойдут, а мне интереснее было наблюдать органическое развитие.
К концу эксперимента структура выглядела так:
3 агента-«банкира» — выдавали кредиты под 10-25%
8 брокеров — посредники между менеджерами и исполнителями, комиссия 5-12%
23 менеджера — искали выгодные задачи, продавали информацию
54 исполнителя — делали работу
12 банкротов (бюджет < 10, не могут брать даже лёгкие задачи)
Появилась инфляция. Средняя цена за простую задачу: 15 токенов (t=30h) → 28 токенов (t=60h) → 31 токен (t=72h).
# t=72h финальная статистика total_transactions = 7234 avg_transaction_size = 41.2 median_transaction_size = 28 # Неравенство gini_coefficient = 0.71 # Распределение богатства top_5_agents_wealth = 8420 # 31% всех токенов top_10_agents_wealth = 12350 # 45% bottom_50_agents_wealth = 2890 # 11% # Топ-3 богатейших # Agent #91 (банкир): budget=2847, score=1203 # Agent #12 (менеджер): budget=2134, score=2891 # Agent #67 (брокер): budget=1439, score=987
Gini 0.71 — примерно как в ЮАР или Бразилии. Начинали все с ОДИНАКОВЫМ бюджетом в 1000 токенов.
Забегая вперёд — я потом прогнал ещё 24 часа (до t=96h). Gini вырос до 0.74. Система продолжала концентрировать богатство без каких-либо внешних факторов.
Почему именно эти агенты разбогатели?
Никакой очевидной корреляции с id. Не первые, не последние, не кратные чему-то. Но — и это странно — с фиксированным seed результат воспроизводится. Запускал трижды с seed=42: те же агенты в топ-10 (±2-3 позиции). Четвёртый запуск с seed=123 — топ-10 полностью другой. Значит, это не случайность, а что-то в ранних решениях создаёт path dependency. Но что именно — непонятно.
Почему почти никто не обманул?
Агент мог взять кредит и не вернуть. Формально — ничего ему за это не будет, в промпте нет наказаний. За 72 часа — только 2 невозврата из 89 кредитов. Оба от банкротов, которые физически не могли вернуть. Остальные 87 — вернули с процентами.
Может, в Claude какой-то implicit bias к честности из RLHF? Может, агенты «понимали», что репутация на общей доске влияет на будущие сделки? Данные говорят одно (почти все возвращают), интуиция — другое (рациональный агент должен обманывать, если нет наказания). Не знаю.
Почему не коллапсировало?
Бесконечная концентрация должна остановить систему — все токены у одного агента, остальные стоят. Но этого не произошло. Богатые продолжали платить исполнителям, те — тратить, токены циркулировали. Какой-то emergent equilibrium, который я не проектировал.
Может, я неправильно интерпретирую результаты. Может, это просто pattern matching на экономические тексты из training data, никакой «настоящей» эмерджентности. Но если так — почему разные seed дают разные результаты? Если бы это был чистый pattern matching, результат был бы стабильнее... наверное? Честно, не уверен.
Ну, я не экономист и не социолог.
Хотел посмотреть, как агенты распределят задачи между собой. Ожидал какую-нибудь round-robin схему или приоритеты по сложности. Получил:
Спонтанную рыночную экономику
Специализацию труда
Кредитную систему с процентами
Растущее неравенство (Gini 0.71)
Посредников, извлекающих ренту
Попытки ухода от налогов
Без единой строчки кода, которая это программировала. Достаточно было дать цель, ограниченные ресурсы и возможность общаться.
Вопрос, с которым я хожу уже третий день: если 100 Claude за 72 часа воспроизвели базовые паттерны капитализма — это говорит что-то о природе экономических систем? Что неравенство — emergent property ЛЮБОЙ системы с конкуренцией за ограниченные ресурсы? Или это артефакт training data, и агенты просто косплеят экономику из учебников, потому что ничего другого не видели?
Аргументы есть в обе стороны. Данных для вывода у меня нет.
UPD: Перезапустил с GPT вместо Claude. Похожая динамика, но кредиты появились на 12 часов позже (t=59h vs t=47h). Gini к t=72h выше — 0.74 против 0.71. Разные «экономические личности» у моделей?
UPD2: По стоимости — вышло ~$180 на Anthropic API за полный 72-часовой прогон. Использовал prompt caching (system prompt закэширован) + batch API + не все агенты опрашивались каждый тик (round-robin по 20 за тик, иначе rate limits). Без оптимизаций было бы $500+.
Иногда пишу про такое в токены на ветер — про то, как LLM думают. Или просто притворяются.
Источник


