Skip to content

[Bug]: HarnessAgent.Builder.fromAgent() duplicates GracefulShutdownMiddleware in middleware chain #1950

Description

@JarvisFans

AgentScope-Java is an open-source project. To involve a broader community, we recommend asking your questions in English.

Describe the bug
When using HarnessAgent.Builder.fromAgent(ReActAgent) to wrap a ReActAgent into a HarnessAgent, the GracefulShutdownMiddleware appears twice in the middleware chain. This causes onReasoning and onModelCall completion callbacks to fire
twice, and the graceful-shutdown interruptIfShuttingDown check to run twice at each checkpoint.

To Reproduce

  1. Build a ReActAgent with any middlewares:
ReActAgent agent = ReActAgent.builder()                                                                                                                                                                                                                     
    .name("test-agent")                                                                                                                                                                                                                                     
    .sysPrompt("You are a helpful assistant.")                                                                                                                                                                                                              
    .model(model)                                                                                                                                                                                                                                           
    .build();                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                            
2. Wrap it into a HarnessAgent via fromAgent:                                                                                                                                                                                                               
HarnessAgent harnessAgent = HarnessAgent.Builder.fromAgent(agent)                                                                                                                                                                                           
    .workspace(Path.of("/tmp/workspace"))                                                                                                                                                                                                                   
    .filesystem(new LocalFilesystemSpec())                                                                                                                                                                                                                  
    .build();                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                            
3. Check the middleware list of the resulting HarnessAgent's inner ReActAgent:                                                                                                                                                                              
// The middlewares list will contain two GracefulShutdownMiddleware instances                                                                                                                                                                               
System.out.println(agent.getMiddlewares());                                                                                                                                                                                                                 
                                                                                                                                                                                                                                                            
Expected behavior                                                                                                                                                                                                                                           
GracefulShutdownMiddleware should appear exactly once in the middleware chain, since it is a system-level middleware automatically added by the ReActAgent constructor.                                                                                     
                                                                                                                                                                                                                                                            
Root cause                                                                                                                                                                                                                                                  
The two-phase build in AgentFactory causes the duplication:                                                                                                                                                                                                 
                                                                                                                                                                                                                                                            
1. ReActAgent.builder().build() → constructor (line 301) adds a GracefulShutdownMiddleware to this.middlewares.                                                                                                                                             
2. HarnessAgent.Builder.fromAgent(reactAgent) → filterCopyableMiddlewares() copies it from agent.getMiddlewares() into inner.middlewares.                                                                                                                   
3. HarnessAgent.Builder.build() → inner.build() → ReActAgent constructor (line 301) adds another GracefulShutdownMiddleware.                                                                                                                                
                                                                                                                                                                                                                                                            
filterCopyableMiddlewares only excludes HarnessRuntimeMiddleware, but GracefulShutdownMiddleware is also a system middleware that should not be copiedit is always auto-added by the constructor.                                                        
                                                                                                                                                                                                                                                            
Fix                                                                                                                                                                                                                                                         
In HarnessAgent.java's filterCopyableMiddlewares, exclude GracefulShutdownMiddleware:
                                                                                                                                                                                                                                                            
private static List<MiddlewareBase> filterCopyableMiddlewares(                                                                                                                                                                                              
        List<MiddlewareBase> middlewares) {                                                                                                                                                                                                                 
    List<MiddlewareBase> copyable = new ArrayList<>(middlewares.size());                                                                                                                                                                                    
    for (MiddlewareBase middleware : middlewares) {                                                                                                                                                                                                         
        if (middleware != null                                                                                                                                                                                                                              
                && !(middleware instanceof HarnessRuntimeMiddleware)                                                                                                                                                                                        
                && !(middleware instanceof GracefulShutdownMiddleware)) {                                                                                                                                                                                   
            copyable.add(middleware);                                                                                                                                                                                                                       
        }                                                                                                                                                                                                                                                   
    }                                                                                                                                                                                                                                                       
    return copyable;                                                                                                                                                                                                                                        
}                                                                                                                                                                                                                                                           
                                                                                                                                                                                                                                                            
Error messages                                                                                                                                                                                                                                              
No error thrown. The symptom is duplicate debug log output:
[DEBUG-MW] ononModelCall 完成, mw=GracefulShutdownMiddleware, agent=commodity-operation-agent                                                                                                                                                               
[DEBUG-MW] ononModelCall 完成, mw=GracefulShutdownMiddleware, agent=commodity-operation-agent
[DEBUG-MW] ononReasoning 完成, mw=GracefulShutdownMiddleware, agent=commodity-operation-agent                                                                                                                                                               
[DEBUG-MW] ononReasoning 完成, mw=GracefulShutdownMiddleware, agent=commodity-operation-agent                                                                                                                                                               
                                                                                                                                                                                                                                                            
Environment (please complete the following information):                                                                                                                                                                                                    
- AgentScope-Java Version: 3.0.5-RELEASE                                                                                                                                                                                                                    
- Java Version: 17                                                                                                                                                                                                                                          
- OS: macOS 

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions