Test-time self-evolution for multi-agent systems
EvolveMAS pairs every agent with a MemoryAgent. It keeps structured memory while the agents interact, then turns each outcome into knowledge about partners and strategies for the next episode. Model weights stay frozen; your system adds three calls.
The loop
Every agent runs the same three steps in every episode. Below, each step shows the call your system makes and what it actually produced in the recorded episode above.
Agents interact as usual. Before an agent acts, context() hands it its memory and knowledge; after it sees new messages, observe() lets its MemoryAgent update the memory.
memory = plugin.context("agent_c") reply = agent_c.solve(problem, memory) plugin.observe("agent_c", round_messages)
Your task scores the episode its own way. EvolveMAS never sees how; it only receives the result, here the answer check.
result = check(final_answers, answer_key)
# task-native: accuracy, deal value, win/loss…
end_episode() distills the memory and the result into knowledge: interaction strategies and a profile of each partner. Working memory resets; knowledge carries into the next episode.
plugin.end_episode("agent_a", result)
Memory and knowledge
Memory is updated during an episode from what the agent sees. When the next episode starts, working memory and facts marked as belonging to the last episode are cleared; past events and longer-lived facts stay, and a fact that is not seen again for a few episodes expires. Knowledge outlives the episode and is revised by each outcome. Here is Agent A's state across three consecutive problems of the recorded run.
Try it
The real evolvemas package runs here with Pyodide. Scripted agents need no key; switch to a live model to use your own.
Your browser calls the provider directly. The key is kept in this tab's memory, is never stored, and is never sent anywhere else. One problem is about 15 model calls: 6 answers, 6 memory updates and 3 knowledge updates. All three agents and the MemoryAgent use the model you enter.
The first episode starts as soon as the runtime loads.
Nothing yet.
What to watch. After each round, observe gives every agent's MemoryAgent the three solutions, and it updates that agent's MEMORY.md. When the answer is checked, end_episode distills the result into each agent's KNOWLEDGE.md: strategies for itself and a profile of each partner. Before every answer, context hands the agent both. Switch EvolveMAS off and run again to compare.
Simulated mode: the problems and the answer key are real GSM8K, and the plug-in is the real Python package running in your browser, but the three agents and the MemoryAgent are scripted rules, so the numbers only show the mechanics. Agent C's script reads the knowledge the plug-in writes for it, which is why its first answers change. Use Live model to run real agents with your own key, or see a recorded run with real models.
Results
Six configurations across five benchmarks. Each baseline is a method family from one setting, transferred to the others under the same protocol.
| Method | BBH | HumanEval | SOTOPIA | Deliberation | WW-V | WW-W | Avg. |
|---|---|---|---|---|---|---|---|
| Strongest setting-specific baseline | LLM-Deliberation family | 67.5 | |||||
| Self-Memory | 82 | 95.7 | 0.576 | 4.90 | 58.0 | 42.0 | 69.5 |
| EvolveMAS | 83 | 97.0 | 0.613 | 5.40 | 62.0 | 70.0 | 77.2 |
Avg. is the mean of six percentage-scaled metrics. Full table and baselines are in the paper.
Use it
Your system keeps its agents, its message routing and its scoring. EvolveMAS only sees what you hand it, so hidden information stays hidden.
context(agent)Memory and knowledge to add to the agent's prompt before it acts.observe(agent, events)What the agent saw. Its MemoryAgent updates Episodic, Semantic and Working memory.end_episode(agent, result)Your task's own result. Interaction Strategies and Partner Profiles are revised from it.Backends: Claude, any OpenAI-compatible endpoint, or any function llm(system, user) -> str. Turn online or evolve off to ablate either phase.
from evolvemas import EvolveMAS, AnthropicLLM plugin = EvolveMAS("runs/memory", AnthropicLLM()) for a in agents: plugin.register(a.name, profile=a.profile) for problem in problems: for a in agents: plugin.begin_episode(a.name) for rnd in range(2): replies = {a.name: a.solve(problem, plugin.context(a.name)) for a in agents} events = [{"speaker": name, "content": text} for name, text in replies.items()] for a in agents: plugin.observe(a.name, events) result = check(replies, problem.answer) for a in agents: plugin.end_episode(a.name, result)
Paper

Decentralized multi-agent systems let LLM agents interact without central control, across cooperative, mixed-motive and adversarial settings. EvolveMAS supports test-time adaptation within and across episodes with one protocol: a dedicated MemoryAgent keeps each agent's Structured Memory outside its reasoning context, and each episode's outcome is distilled into reusable partner profiles and interaction strategies. Across five benchmarks and six configurations it reaches the highest normalized average score among the compared methods, 77.2.
The plug-in on this site is a compact, host-agnostic implementation of the protocol. It is not the paper's experiment code.
@article{wu2026evolvemas,
title = {EvolveMAS: A Unified Self-Evolution Framework for Decentralized Multi-Agent Systems},
author = {Wu, Yalun and Ma, Qiwei and Xu, Bingbing and Zhang, Dan and Bai, Long and
Wang, Boyang and Li, Jiatong and Lv, Zheqi and Yuan, Shaozu and Wang, Peiying and
Zhang, An and Wang, Jiawei and Chua, Tat-Seng},
journal = {arXiv preprint arXiv:XXXX.XXXXX},
year = {2026}
}