Skip to content

Add: climate negotiation multi-agent LLM example #453

Open
BhoomiAgrawal12 wants to merge 4 commits into
mesa:mainfrom
BhoomiAgrawal12:add/climate-negotiation
Open

Add: climate negotiation multi-agent LLM example #453
BhoomiAgrawal12 wants to merge 4 commits into
mesa:mainfrom
BhoomiAgrawal12:add/climate-negotiation

Conversation

@BhoomiAgrawal12

@BhoomiAgrawal12 BhoomiAgrawal12 commented Mar 30, 2026

Copy link
Copy Markdown

Summary

Adds a new LLM example under llm/climate_negotiation/, a multi-agent simulation of international climate treaty negotiations. Six country agents (USA, EU, China, India, Brazil, Russia), each driven by an LLM, negotiate on a shared emissions-reduction target over multiple rounds until a 2/3 majority treaty is reached.

References issue #454

What makes this different from existing examples

The existing mesa-examples cover classic ABM patterns (opinion dynamic, resource competition, spatial movement). This is llm/ directory that uses LLM agents for structured multi-party
negotiation and a qualitatively different use-case where:

  • Agents hold persistent negotiating positions (pledge %, coalitions, acceptance state) that evolve across rounds through LLM decisions.
  • There is a shared global outcome (the treaty) that emerges only when agents reach consensus and not just individual behaviour.
  • Agents use targeted inter-agent communication (speak_to) and domain-specific tools rather than generic actions.

The closest existing example is deffuant_weisbuch (opinion convergence without LLMs). This model demonstrates how LLM reasoning produces qualitatively richer convergence: coalition-building, diplomatic counters, and conditional acceptance behaviours that rule-based agents cannot replicate.

Mesa-LLM features demonstrated

Feature How it appears
STLTMemory Short-term stores recent proposals; long-term consolidates committed positions across rounds
ReActReasoning Agents reason about economic interests before acting
speak_to (inbuilt tool) Targeted diplomatic messaging between specific countries
Custom @tool functions make_proposal, accept_proposal, form_coalition, reject_and_counter
vision=-1 Full-room awareness as every agent observes all others and no spatial grid needed
DataCollector Tracks TotalProposals, AveragePledge, LargestCoalitionSize, TreatyReached per round

Features

Two LLM hallucination failure modes are explicitly guarded:

  • Phantom agent IDs in form_coalition - partner_ids are filtered against the live agent set; invalid IDs are dropped and logged as WARNING.
  • Invalid proposer_id in accept_proposal - the ID is validated before recording; if wrong, an error listing valid IDs is returned to the LLM for self-correction.

Every agent's step prompt also includes an explicit VALID COUNTRY IDs block to reduce hallucination at the source.

Visualisation (Solara)

  • Pledge bar chart - bars turn green when a country accepts the treaty
  • Coalition status panel - live table of pledge, acceptance, coalition members
  • Pledge trajectories - line chart across rounds
  • Time-series plots - TotalProposals, AveragePledge, LargestCoalitionSize
Screenshot from 2026-03-30 11-35-32 Screenshot from 2026-03-30 11-35-17 Screenshot from 2026-03-30 11-35-02

Supported LLMs

Works with any LiteLLM-compatible model string. Tested with openai/gpt-4o (best emergent behaviour), gemini/gemini-2.0-flash (default), openai/gpt-4o-mini, anthropic/claude-haiku-4-5-20251001, and ollama/llama3.2.

File Structure

llm/climate_negotiation/
├── app.py # Solara visualisation entry point
├── README.md
└── climate_negotiation/
├── init.py # triggers tool registration on import
├── agents.py # CountryAgent + negotiation history helper
├── tools.py # four custom @tool negotiation functions
└── model.py # ClimateNegotiationModel + countries configs

To Try

  • pip install mesa-llm mesa solara python-dotenv rich
  • Set a valid API key in llm/climate_negotiation/.env
  • python -m climate_negotiation.model - confirm rounds print and treaty is reached within ~6 rounds with gpt-4o
  • solara run app.py - confirm all four visualisation panels render and update per round

@sadrasabouri sadrasabouri left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some comments that may be helpful. My overall suggestion is to trim this PR down to an atomic contribution on top of deffuant_weisbuch. One possible direction:

LLM-driven dynamic opinion change: define a few agent types with distinct system prompts reflecting different personalities. Give each agent a personal stance on the topic (e.g., "I believe we should use ..."). Opinion updates come from querying an LLM with something like: "Given that agent X's argument is Y, rate how much do you agree with Z ? Answer between 0 and 1." This setup can simulate the echo-chamber effect by tuning $\epsilon$ from the original model.

  • X: other interacting agents
  • Y: X's opinion statement which gets updated as their opinion gets updated each round
  • Z: the subject that the whole model is going to be about (e.g., climate)

### 1. Install dependencies

```bash
pip install mesa-llm mesa solara python-dotenv rich

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a requirements.txt for this?

Comment on lines +67 to +79
## Robustness Against LLM Hallucinations

Two common failure modes are guarded in code:

- **Phantom agent IDs in `form_coalition`** - `partner_ids` are filtered against
the live agent set before being stored. Invalid IDs are dropped and logged as
`WARNING` in `climate_negotiation.log`.
- **Invalid `proposer_id` in `accept_proposal`** - the ID is validated before
recording an acceptance. If it doesn't match any agent, an error string listing
valid IDs is returned to the LLM so it can self-correct on its next step.

Every agent's step prompt also includes an explicit `VALID COUNTRY IDs` block so
models are less likely to invent IDs in the first place.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the reader does not necessary benefit from this in readme as it's very low-level implementation details. Feel free to remove it.


### 2. Set your API key

Create a `.env` file in `llm/climate_negotiation/`:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: add a local .gitignore file avoid committing .env mistakenly.

Comment on lines +174 to +181
**Try different LLMs**: Change `llm_model` in `app.py`. `gpt-4o` produces richer diplomatic
language; `gemini-2.0-flash` is faster and free.

**Add more countries**: Add a new dict to the `COUNTRIES` list in `model.py` and assign it
a system prompt encoding that country's real-world stance.

**Change the treaty threshold**: Edit `_treaty_reached()` in `model.py`
(currently requires a 2/3 majority).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are nearly trivial edits. Feel free to remove them.


from .agents import country_tool_manager

if TYPE_CHECKING:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't need this if here. for your code as a application script you can just import the type and start using it.

Confirmation of coalition formation and the partners notified.
"""
if isinstance(partner_ids, str):
import json

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move it to the top of the file.

Comment on lines +203 to +206
proposer_id: Unique ID of the country whose proposal you are rejecting.
counter_reduction_percent: Your alternative reduction percentage.
reason: Explanation of why you reject the original and what you offer instead.
agent: Provided automatically.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Comment on lines +211 to +212
counter_reduction_percent = float(counter_reduction_percent or 20.0)
proposer_id = int(proposer_id or 0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Comment on lines +115 to +118
id_to_name = {
a.unique_id: getattr(a, "country_name", f"Agent {a.unique_id}")
for a in self.model.agents
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having this dict as a model's parameter makes more sense.

Comment on lines +116 to +125
Mesa-LLM features demonstrated
- STLTMemory : short-term stores recent proposals; long-term consolidates
committed positions across rounds.
- ReActReasoning: agents reason then act within each simulation step.
- speak_to : inbuilt diplomatic messaging tool between agents.
- Custom tools : make_proposal, accept_proposal, form_coalition,
reject_and_counter.
- vision=-1 : agents observe all other parties (full-room awareness,
no spatial grid needed).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need this here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants