mesa / mesa/mesa-llm

`ToolManager.get_all_tools_schema()` exposes every tool to the LLM regardless of agent state

Open
#148 7 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
73
Forks
89
Avg merge
14d 58m
Merged PRs (30d)
2

Description

`ToolManager.get_all_tools_schema()` exposes every tool to the LLM regardless of agent state.

A wolf with `0` energy still sees `move_one_step`.
An agent with no grid still sees `teleport_to_location`.

The LLM picks from all options — if it picks wrong, you get either a crash or a silent bad state.

### The Core Problem

Right now in `ToolManager.get_all_tools_schema()`:

```python
# Every agent, every step, gets EVERY tool
return [fn.__tool_schema__ for fn in self.tools.values()]
```

A wolf with `0` energy still sees `move_one_step`.
An agent at the grid boundary still sees `teleport_to_location` with no hint that `(5, 99)` is out of bounds.

The LLM selects from **all possible tools** and hopes the choice is valid.

This ignores whether the tool is actually usable in the current state.

### Example Issues

| Tool | Implicit Assumption | What Breaks |
|-----|-----|-----|
| `move_one_step` | `agent.pos` is not `None` | `TypeError: cannot unpack non-iterable NoneType` |
| `move_one_step` | `grid/space` exists on model | `AttributeError` |
| `teleport_to_location` | coordinates in bounds | silent wrong state or `IndexError` |
| `speak_to` | target agents exist | empty list → silent no-op |

The LLM selects from **all possible tools**, even when they are impossible to execute.

---

## Proposed API

Keep the existing method and add two new ones.

```python
class ToolManager:

# Existing (unchanged)
def get_all_tools_schema(self, selected_tools=None) -> list[dict]: ...

# NEW — hard filter (executor safe)
def get_feasible_tools_schema(self, agent, selected_tools=None) -> list[dict]: ...

# NEW — soft annotations with reasons (planner guidance)
def get_annotated_tools_schema(self, agent, selected_tools=None) -> list[dict]: ...
```

### Pros

- Clear API
- Explicit semantics
- No breaking changes
- Executors only receive feasible tools
- Planners can still reason about infeasible tools

---

## Files That Change

| File | Change |
|-----|-----|
| `tool_decorator.py` | Add `@requires` decorator |
| `tool_manager.py` | Add `get_feasible_tools_schema()` and `get_annotated_tools_schema()` |
| `llm_agent.py` | Add `tool_filter()` method |
| `inbuilt_tools.py` | Add `@requires` to built-in tools |
| `cot.py` | Annotated for planner, feasible for executor |
| `react.py` | Annotated for planner |
| `rewoo.py` | Annotated for planner |
| `reasoning.py` | Use feasible tools for execution |

Contributor guide

Open the contributing guide

Research direction

Start by reading tool_decorator.py and tool_manager.py to understand the existing tool schema flow, then trace how llm_agent.py, cot.py, react.py, rewoo.py, and reasoning.py consume it. Review inbuilt_tools.py for the built-in tool assumptions. Done means the proposed decorators, feasible and annotated schema methods, and planner/executor integrations are implemented without changing the existing method's behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
ai, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.