Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions models/llm_negotiation_market/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## How to run
# Option 1: Google Colab (recommended)
Open model.ipynb in Google Colab
https://colab.research.google.com/drive/1yLOkKcxm4rOBbcFjULn_gVbCnVYwaoBP?usp=sharing

# Option 2: Local (requires Linux/WSL due to Windows DLL policy)
pip install mesa mesa-llm groq litellm
python model.py


# LLM Negotiation Market Model

A Mesa-LLM model where buyer and seller agents use Chain-of-Thought
reasoning to negotiate prices over multiple steps.

## What I learned

- `LLMAgent.step()` is empty by default — you must call
`self.reasoning.plan()` manually to trigger LLM thinking
- `CoTReasoning` makes two LLM calls: one for thinking, one for
tool execution. When no tools are registered, the second call
crashes with a Groq 400 error (`tool_choice="required"` with empty tools)
- Fixed by subclassing `CoTReasoning` into `SimpleCoTReasoning` that
skips the tool execution step — only uses the thinking/planning call
- Agents have real memory across steps — in Step 2, agents referenced
their Step 1 decisions without any extra code
- Three reasoning engines available: `CoTReasoning`, `ReActReasoning`,
`ReWOOReasoning`
- `llm_model` needs `"groq/"` prefix for LiteLLM routing

## How to run
pip install mesa mesa-llm groq litellm
export GROQ_API_KEY=your_key
python model.py

## Potential Issues Found in mesa-llm
- `CoTReasoning` crashes when no tools registered (tool_choice="required" bug)
→ This could be a good issue to open on mesa/mesa-llm!
99 changes: 99 additions & 0 deletions models/llm_negotiation_market/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import os
import mesa
from mesa_llm.llm_agent import LLMAgent
from mesa_llm.reasoning.cot import CoTReasoning

os.environ["GROQ_API_KEY"] = "your_groq_api_key_here"


class SimpleCoTReasoning(CoTReasoning):
"""
Subclass of CoTReasoning that skips tool execution.
Only uses the first LLM call (the thinking/planning step).
This avoids the tool_choice='required' error when no tools are registered.
"""

def plan(self, prompt=None, obs=None, ttl=1, selected_tools=None):
if prompt is None:
if self.agent.step_prompt is not None:
prompt = self.agent.step_prompt
else:
raise ValueError("No prompt provided and agent.step_prompt is None.")

if obs is None:
obs = self.agent.generate_obs()

llm = self.agent.llm
obs_str = str(obs)

self.agent.memory.add_to_memory(
type="Observation", content={"content": obs_str}
)

system_prompt = self.get_cot_system_prompt(obs)
llm.system_prompt = system_prompt

rsp = llm.generate(
prompt=prompt,
tool_schema=None,
tool_choice="none",
)

chaining_message = rsp.choices[0].message.content
self.agent.memory.add_to_memory(
type="Plan", content={"content": chaining_message}
)
if hasattr(self.agent, "_step_display_data"):
self.agent._step_display_data["plan_content"] = chaining_message

return chaining_message


class SellerAgent(LLMAgent):
def __init__(self, model, price):
super().__init__(
model=model,
reasoning=SimpleCoTReasoning,
llm_model="groq/llama-3.3-70b-versatile",
system_prompt="You are a seller in a market. Reply in one sentence.",
step_prompt=f"Your price is {price}. Should you lower it to attract buyers?",
)
self.price = price

def step(self):
response = self.reasoning.plan()
print(f"Seller {self.unique_id} (price={self.price}): {response}")


class BuyerAgent(LLMAgent):
def __init__(self, model, budget):
super().__init__(
model=model,
reasoning=SimpleCoTReasoning,
llm_model="groq/llama-3.3-70b-versatile",
system_prompt="You are a buyer in a market. Reply in one sentence.",
step_prompt=f"Your budget is {budget}. What price will you offer?",
)
self.budget = budget

def step(self):
response = self.reasoning.plan()
print(f"Buyer {self.unique_id} (budget={self.budget}): {response}")


class NegotiationMarket(mesa.Model):
def __init__(self, n_buyers=2, n_sellers=2):
super().__init__()
for _ in range(n_sellers):
SellerAgent(self, price=100)
for _ in range(n_buyers):
BuyerAgent(self, budget=150)

def step(self):
self.agents.shuffle_do("step")


model = NegotiationMarket()
for i in range(2):
print(f"\n--- Step {i + 1} ---")
model.step()