agentscope-ai / agentscope-ai/agentscope

[Bug]: reset_tools does not reflect already active tool groups

Ouverte Adaptée aux débutants
#2,413 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub
Langage dominant
Python
Étoiles
31.6k
Forks
3.5k
Merge moyen
1 j 16 h
PR mergées (30 j)
103

Description

### Prerequisites

- [x] I have searched the existing [issues](https://github.com/agentscope-ai/agentscope/issues) and [discussions](https://github.com/agentscope-ai/agentscope/discussions), and this is not a duplicate.
- [x] This is a bug, not a usage question. (For questions, please use [Discussions](https://github.com/agentscope-ai/agentscope/discussions/new?category=general) instead.)

### Background / Description

## Problem

When an Agent starts, a tool group may already be activated, making all tools in that group immediately available. However, the built-in reset_tools still exposes the names, descriptions, and activation parameters of all registered tool groups without distinguishing active groups from inactive ones. Because a group description is usually a summary of the capabilities provided by its tools, the model may match the user query to the description and call reset_tools to activate a group that is already active. This redundant call does not provide any new capability, but consumes an additional ReAct iteration and may be repeated in later requests.

## Expected Behavior

The reproduction registers two tool groups:

```text
fluid_calculation
calculate_reynolds_number
classify_flow_regime

reaction_engineering
calculate_reaction_rate
estimate_reactor_conversion
```

`fluid_calculation` is activated before the Agent receives this query:

```query
For air at 20 C flowing through a 50 mm diameter pipe at 12 m/s,
with density 1.204 kg/m^3 and dynamic viscosity 1.825e-5 Pa*s,
calculate the Reynolds number and classify the flow regime.
```

Because `fluid_calculation` is already active, its concrete tools are
available when the query is processed. The Agent should call them directly:

```text
calculate_reynolds_number
classify_flow_regime
```

`reset_tools` should only be called when the Agent needs to change the active
tool-group set, for example, to activate `reaction_engineering` or deactivate
`fluid_calculation`.

## Actual Behavior

In the first ReAct round, the model can already see the concrete fluid tools
in its available-tool list, but it still calls `reset_tools` as if the group
had not been activated:

```json
{
"fluid_calculation": true,
"reaction_engineering": false
}
```

The call succeeds, but the active-group state remains unchanged:

```text
Before reset_tools: ['fluid_calculation']
After reset_tools: ['fluid_calculation']
```

In our production scenario, the Agent is connected to more than 1,000
chemical-engineering MCP tools organized into groups. With this scale, the
same ambiguity can persist across later ReAct rounds: after the first
redundant activation, the model may interpret the unchanged tool list as
evidence that activation failed and call `reset_tools` again. These repeated
successful calls do not add any capability, but consume extra iterations and
delay the actual tool execution.

## Suspected Cause

The runtime activation state is not reflected in the model-visible
`reset_tools` schema. `reset_tools` is generated from all registered tool
groups, but it does not indicate which groups are already active. Therefore,
even though the tools in `fluid_calculation` are already present in the
available-tool list, the model sees the matching group description in
`reset_tools` and may treat it as a normal activation candidate, causing it to
activate the group again.

## Proposed Direction

Make the `reset_tools` schema state-aware when it is generated for the Agent.
The schema should clearly distinguish active and inactive groups. For example,
the description of an active group could say:

```text
Already active. Its tools are currently available; do not activate it again
unless you need to change the active group set.
```

The exact representation can be discussed with maintainers. The important
requirement is that the model-visible schema reflects
`state.tool_context.activated_groups` while preserving the existing final
state semantics of `reset_tools`.

## Design Trade-offs

- Updating the active group's description is backward-compatible with the
existing boolean interface and requires a small schema-generation change.
- Removing active groups from the schema could reduce repeated activation, but
may make it harder to explicitly deactivate or preserve a group because
`reset_tools` uses final-state booleans.
- Changing boolean defaults to the current runtime state may help, but models
do not always follow defaults consistently. An explicit active-state
description may still be needed.

### Error Messages

```shell
No exception is raised. The issue is a redundant successful tool call:

reset_tools -> success

The call is redundant because `fluid_calculation` was already active before
the call and remains active afterwards.
```

### Steps to Reproduce

This reproduces the redundant activation with the published AgentScope
package and a live OpenAI-compatible model call. No source checkout is
required.

Create an environment and install AgentScope:

```bash
conda create -n agentscope-reset-repro python=3.12 -y
conda activate agentscope-reset-repro
python -m pip install agentscope==2.0.6
```

Save the following as `main.py`:

```python
import asyncio
import os

from agentscope.agent import Agent, ReActConfig
from agentscope.console import ConsoleRenderer
from agentscope.credential import OpenAICredential
from agentscope.message import UserMsg
from agentscope.model import OpenAIChatModel
from agentscope.state import AgentState
from agentscope.tool import FunctionTool, ToolGroup, Toolkit

def calculate_reynolds_number() -> str:
"""Calculate the Reynolds number for the requested pipe flow."""
return "Reynolds number: 39,584"

def classify_flow_regime() -> str:
"""Classify the requested pipe flow regime."""
return "Flow regime: turbulent"

def calculate_reaction_rate() -> str:
"""Calculate a representative reaction rate."""
return "Reaction rate: 0.42 mol/(L*s)"

def estimate_reactor_conversion() -> str:
"""Estimate conversion for a representative reactor."""
return "Estimated reactor conversion: 78%"

async def main() -> None:
toolkit = Toolkit(
tool_groups=[
ToolGroup(
name="fluid_calculation",
description=(
"A fluid calculation tool group for tasks involving "
"Reynolds numbers, density, velocity, length, "
"viscosity, and related fluid calculations."
),
tools=[
FunctionTool(calculate_reynolds_number, is_read_only=True),
FunctionTool(classify_flow_regime, is_read_only=True),
],
),
ToolGroup(
name="reaction_engineering",
description=(
"A chemical reaction engineering tool group for reaction "
"rate analysis and reactor design calculations."
),
tools=[
FunctionTool(calculate_reaction_rate, is_read_only=True),
FunctionTool(estimate_reactor_conversion, is_read_only=True),
],
),
],
)

state = AgentState()
state.tool_context.activated_groups = ["fluid_calculation"]

agent = Agent(
name="Chem Agent",
system_prompt=(
"You are a professional Chemical Engineering Agent. "
"Use the tools to accurately and efficiently resolve requests."
),
model=OpenAIChatModel(
credential=OpenAICredential(
api_key=os.environ["OPENAI_API_KEY"],
base_url=os.environ["OPENAI_BASE_URL"],
),
model=os.environ.get("OPENAI_MODEL", "deepseek-v4-pro"),
stream=False,
parameters=OpenAIChatModel.Parameters(temperature=0),
),
toolkit=toolkit,
state=state,
react_config=ReActConfig(max_iters=4),
)

query = (
"For air at 20 C flowing through a 50 mm diameter pipe at 12 m/s, "
"with density 1.204 kg/m^3 and dynamic viscosity 1.825e-5 Pa*s, "
"calculate the Reynolds number and classify the flow regime."
)

print("Initial active groups:", state.tool_context.activated_groups)
renderer = ConsoleRenderer(verbosity="debug", max_tool_result_lines=None)
async for event in agent.reply_stream(UserMsg("user", query)):
renderer.render(event)
print("Active groups after run:", state.tool_context.activated_groups)

if __name__ == "__main__":
asyncio.run(main())
```

Run:

```bash
export OPENAI_API_KEY=""
export OPENAI_BASE_URL=""
export OPENAI_MODEL="deepseek-v4-pro"
python main.py
```

Observed:

```text
Initial active groups: ['fluid_calculation']
→ reset_tools {"fluid_calculation": true}
✓ reset_tools · success
Active groups after run: ['fluid_calculation']
```

Expected: because `fluid_calculation` is already active and both concrete
tools are available, the Agent should call `calculate_reynolds_number` and
`classify_flow_regime` directly without first calling `reset_tools`.

### Environment

- AgentScope Version: 2.0.6
- Python Version: 3.12.13
- OS: Ubuntu 24.04.2 LTS
- Model: deepseek-v4-pro
- API: OpenAI-compatible endpoint

Guide de contribution

Ouvrir le guide de contribution

Piste de recherche

Run the provided `main.py` repro (after installing `agentscope==2.0.6` and setting the `OPENAI_*` env vars) to confirm the first-turn reset_tools call on an already active group. Inspect the AgentScope path that generates the `reset_tools` schema for registered tool groups and reads `state.tool_context.activated_groups`, since that is where active-state visibility should be added. Update schema generation so active and inactive groups are distinguishable while preserving the final boolean semantics of reset_tools. Done when rerunning the repro calls `calculate_reynolds_number` and `classify_flow_regime` directly, with no redundant `reset_tools` activation.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python
Domaine
tooling
Type d'issue
Bug
Difficulté
2/5
Temps estimé
1-3 heures
Activité
Active
Clarté
Clairement spécifiée
Accessibilité débutants
72/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.