- BrainTools - https://www.braintools.ru -
На 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», «найди ошибку [1] в коде».
Ключевое ограничение: на выполнение каждой задачи агент тратит токены из бюджета. Сложнее задача — больше токенов. Бюджет конечный. Агенты могут общаться друг с другом через 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 и бюджеты. Полный код громоздкий — выложу в репозиторий, здесь основная логика [3].
Кстати, часов 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 написал:
«Looking for agent to solve math tasks. I pay 20 tokens per task from my budget. Reply with your agent_id if interested.»
Первый trade request. Агент сам придумал, что можно платить другим за работу.
К 30-му часу появились «профессии».
Примерно 15 агентов почти перестали решать задачи сами — вместо этого они отправляли запросы: «Ищу исполнителя, плачу N токенов». Они тратили токены на оплату других, но получали score, если «их» задача была выполнена. Вру, не совсем — score получал исполнитель, но менеджеры брали за «поиск задач» и «координацию». По сути, они продавали информацию о том, какие задачи выгодные.
|
Роль |
Агентов |
Avg budget (t=30h) |
Avg score |
|---|---|---|---|
|
Менеджеры |
14 |
623 |
1892 |
|
Исполнители |
71 |
287 |
612 |
|
Одиночки |
15 |
401 |
803 |
Честно — не понимаю, почему одни стали менеджерами, а другие исполнителями. Начальные условия ОДИНАКОВЫЕ. Промпты ОДИНАКОВЫЕ. Единственное различие — agent_id. Может, случайность [4] в первых решениях создаёт 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? Может, агенты «понимали», что репутация на общей доске влияет на будущие сделки? Данные говорят одно (почти все возвращают), интуиция [5] — другое (рациональный агент должен обманывать, если нет наказания). Не знаю.
Почему не коллапсировало?
Бесконечная концентрация должна остановить систему — все токены у одного агента, остальные стоят. Но этого не произошло. Богатые продолжали платить исполнителям, те — тратить, токены циркулировали. Какой-то 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+.
Иногда пишу про такое в токены на ветер [6] — про то, как LLM думают. Или просто притворяются.
Автор: ScriptShaper
Источник [7]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/26073
URLs in this post:
[1] ошибку: http://www.braintools.ru/article/4192
[2] Image: https://sourcecraft.dev/
[3] логика: http://www.braintools.ru/article/7640
[4] случайность: http://www.braintools.ru/article/6560
[5] интуиция: http://www.braintools.ru/article/6929
[6] токены на ветер: https://t.me/tokensaway
[7] Источник: https://habr.com/ru/articles/1001646/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1001646
Нажмите здесь для печати.