agentscope-ai / agentscope-ai/agentscope-java
[Bug]: Tool groups activated at build time are lost on first call for new sessions
- Langage dominant
- Java
- Étoiles
- 5.6k
- Forks
- 1.3k
- Merge moyen
- 4 j 12 h
- PR mergées (30 j)
- 77
Description
**Describe the bug**
When creating a `ReActAgent` with a `Toolkit` that has tool groups activated at build time (via `toolkit.createToolGroup(name, desc, true)`), the activated groups are silently lost on the **first call** for any new session. This results in an empty `tools` array being sent to the LLM — the model cannot call any tools.
**To Reproduce**
Steps to reproduce the behavior:
1. Create a `Toolkit` and register a tool group with `active=true`:
```java
Toolkit toolkit = new Toolkit();
toolkit.createToolGroup("myTools", "My tool group", true);
toolkit.registration().tool(myToolObject).group("myTools").apply();
// At this point, toolkit.getActiveGroups() returns ["myTools"] ✓
```
2. Build a `ReActAgent` with this toolkit and an `AgentStateStore`:
```java
ReActAgent agent = ReActAgent.builder()
.name("testAgent")
.sysPrompt("You are a helpful assistant.")
.model(someModel)
.toolkit(toolkit)
.maxIters(10)
.stateStore(agentStateStore) // ← key: stateStore is configured
.build();
```
3. Call the agent with a **new** session (no prior state in the store):
```java
RuntimeContext ctx = RuntimeContext.builder()
.sessionId("new-session-001")
.userId("user-001")
.build();
agent.call("Please use myTool to do something", ctx);
```
4. Observe that the LLM request body has an empty `tools` array — the model cannot call any tools.
**What happens internally:**
Inside `ReActAgent.activateSlotForContext()` (line 479-481):
```java
if (toolkit != null) {
toolkit.setActiveGroups(loaded.getToolContext().getActivatedGroups());
}
```
For a new session, `loadOrCreateAgentStateForSlot()` returns a `freshState()` where `ToolContextState.activatedGroups` defaults to an **empty list**. This empty list then **overwrites** the toolkit's build-time active groups via `setActiveGroups([])`.
Then in `reasoning()` (line 1947-1949):
```java
List tools = toolkit.getToolSchemas(
state.getToolContext().getActivatedGroups() // ← empty list
);
// → all grouped tools are filtered out → tools = []
```
The LLM receives a request with no `tools` field, so it cannot invoke any tool.
**Expected behavior**
Tool groups activated at build time (`active=true`) should remain active for new sessions. The initial activation state should be treated as a template, similar to how `initialPermissionContext` is handled.
**Error messages**
No exception is thrown. The bug manifests as:
- The LLM request body has an empty (or missing) `tools` array
- The model responds without calling any tools, often saying it doesn't have access to tools
**Environment (please complete the following information):**
- AgentScope-Java Version: 2.0.0-SNAPSHOT (master branch)
- Java Version: 21
- OS: Windows
**Additional context**
**Root cause:**
The constructor saves `initialPermissionContext` as a template for new sessions, but does **not** save the toolkit's initial active groups:
```java
// ReActAgent constructor (line 315):
this.initialPermissionContext = builder.permissionContext; // ✓ saved
// ⚠️ No equivalent for toolkit active groups
```
When `freshState()` creates a new `AgentState`, it builds a default `ToolContextState` with an empty `activatedGroups` list. Then `activateSlotForContext()` uses this empty list to call `toolkit.setActiveGroups([])`, clearing all build-time activations.
**Suggested fix:**
1. Save the toolkit's initial active groups in the constructor:
```java
// In constructor:
this.initialActiveGroups = agentToolkit != null
? List.copyOf(agentToolkit.getActiveGroups())
: List.of();
```
2. Use them when creating fresh state:
```java
// In freshState():
ToolContextState toolCtx = ToolContextState.builder().build();
if (!initialActiveGroups.isEmpty()) {
toolCtx.setActivatedGroups(new ArrayList<>(initialActiveGroups));
}
asb.toolContext(toolCtx);
```
This ensures that new sessions inherit the build-time tool group activation, and `activateSlotForContext()` will correctly restore the toolkit's active groups instead of clearing them.
Guide de contribution
Ouvrir le guide de contribution
Évaluation
Cette issue n'a pas encore été évaluée.