Skip to content

BUG: MultiStepAgent.from_dict() passes **kwargs to managed agents, causing child agent configurations to be overridden #1849

@XXSg559

Description

@XXSg559

Problem

When deserializing a CodeAgent (or TreeAgent) with managed agents (sub-agents), the MultiStepAgent.from_dict() method incorrectly passes the parent agent's **kwargs to child agents during deserialization, causing child agent configurations (especially additional_authorized_imports) to be overridden by the parent's configuration.

This results in child agents losing their custom authorized_imports after deserialization, causing runtime errors when they try to execute code that depends on those imports (e.g., sympy).


Steps to reproduce

  1. Create a sub-agent with custom additional_authorized_imports (e.g., ["sympy"])
  2. Create a main agent with the sub-agent as a managed agent
  3. Serialize the main agent using .to_dict()
  4. Deserialize using .from_dict()
  5. Check the sub-agent's authorized_imports - the custom imports are missing
from smolagents import CodeAgent, LiteLLMModel
import os

# Initialize model
model = LiteLLMModel(
    model_id="deepseek/deepseek-chat",
    api_key=os.environ.get("DEEPSEEK_API_KEY"),
    api_base="https://api.deepseek.com",
)

# Create sub-agent with custom authorized_imports
sub_agent = CodeAgent(
    tools=[],
    model=model,
    max_steps=3,
    additional_authorized_imports=["sympy"],  # Custom import
    name="sympy_agent",
    description="Math expert using sympy",
)

# Create main agent with the sub-agent
main_agent = CodeAgent(
    tools=[],
    model=model,
    managed_agents=[sub_agent],
    max_steps=2,
)

# Serialize and check
agent_dict = main_agent.to_dict()
print("=== Serialized data ===")
for ma_dict in agent_dict.get("managed_agents", []):
    print(f"Sub-agent '{ma_dict['name']}' authorized_imports:")
    print(f"  {ma_dict.get('authorized_imports')}")
    print(f"  Has 'sympy': {'sympy' in ma_dict.get('authorized_imports', [])}")

# Deserialize
restored_agent = CodeAgent.from_dict(agent_dict)

# Check restored data
print("\n=== Restored agent ===")
for name, sub_agent_restored in restored_agent.managed_agents.items():
    print(f"Sub-agent '{name}' authorized_imports:")
    print(f"  {sub_agent_restored.authorized_imports}")
    print(f"  Has 'sympy': {'sympy' in sub_agent_restored.authorized_imports}")

Actual behavior and error logs

=== Serialized data ===
Sub-agent 'sympy_agent' authorized_imports:
  ['collections', 'datetime', 'itertools', 'math', 'queue', 'random', 're', 'stat', 'statistics', 'sympy', 'time', 'unicodedata']
  Has 'sympy': True

=== Restored agent ===
Sub-agent 'sympy_agent' authorized_imports:
  ['collections', 'datetime', 'itertools', 'math', 'queue', 'random', 're', 'stat', 'statistics', 'time', 'unicodedata']
  Has 'sympy': False

The custom import 'sympy' is present in the serialized dictionary but missing after deserialization.

When the sub-agent tries to execute code using sympy, it raises:

AgentExecutionError: Import of 'sympy' is not allowed

Expected behavior

The restored sub-agent should retain its authorized_imports from the serialized dictionary, including 'sympy'. The deserialized agent should be functionally identical to the original agent.


Root Cause

In smolagents/agents.py, line 1027 in MultiStepAgent.from_dict():

managed_agent = agent_class.from_dict(managed_agent_dict, **kwargs)

The **kwargs contains the parent agent's code_agent_kwargs (including additional_authorized_imports), which is then passed to child agents.

Then in CodeAgent.from_dict() at line 1774:

code_agent_kwargs.update(kwargs)  # Parent's kwargs override child's config

This causes the parent agent's additional_authorized_imports (without sympy) to override the child agent's own configuration (with sympy).


Proposed Fix

Option 1: Don't pass kwargs to child agents (simplest fix)

In MultiStepAgent.from_dict(), line 1027:

# Current (buggy)
managed_agent = agent_class.from_dict(managed_agent_dict, **kwargs)

# Fixed
managed_agent = agent_class.from_dict(managed_agent_dict)  # Don't pass **kwargs

Option 2: Use a whitelist for shared parameters (better design)

Only pass parameters that should be shared (e.g., model):

# Define which parameters should be shared with child agents
SHARED_PARAMS = {"model", "verbosity_level"}
shared_kwargs = {k: v for k, v in kwargs.items() if k in SHARED_PARAMS}

# Pass only shared parameters
managed_agent = agent_class.from_dict(managed_agent_dict, **shared_kwargs)

Rationale:

  • Agent-specific parameters like additional_authorized_imports, max_steps, tools, etc. should not be passed to child agents
  • Only truly shared resources like model instances should propagate down
  • This preserves child agent independence while allowing resource sharing

Environment:

  • OS: macOS
  • Python version: 3.12
  • Package version:
Name: smolagents
Version: 1.22.0

Additional context

This bug affects any workflow that:

  1. Uses managed agents (multi-agent systems)
  2. Requires child agents to have custom additional_authorized_imports
  3. Needs to serialize/deserialize agents (e.g., for checkpointing, tree search, distributed training)

Temporary workaround: Override from_dict() in a custom agent class to manually load managed agents without passing **kwargs.


Checklist

  • I have searched the existing issues and have not found a similar bug report.
  • I have provided a minimal, reproducible example.
  • I have provided the full traceback of the error.
  • I have provided my environment details.
  • I am willing to work on this issue and submit a pull request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions