mirror of
https://github.com/microsoft/autogen.git
synced 2025-12-27 15:09:41 +00:00
FIX: resolving_workbench_and_tools_conflict_at_desirialize_assistant_agent (#6407)
## Why are these changes needed? Starting from AutoGen v0.5.5, tools are internally managed through `StaticWorkbench`. However, both tools and workbench were being serialized and deserialized, which caused conflicts during deserialization: • When both are restored, the constructor raises: ``` ValueError: Tools cannot be used with a workbench. ``` The changes address this issue by: 1. Removing tools from serialization/deserialization: • tools are now considered internal state of `StaticWorkbench`, and are no longer serialized. • Only workbench is serialized, ensuring consistency and avoiding duplication. 2. Ensuring logical integrity: • Since tools are not used directly after initialization, persisting them separately serves no functional purpose. • This avoids scenarios where both are populated, violating constructor constraints. Summary: This change prevents tools/workbench conflicts by fully delegating tool management to `StaticWorkbench` and avoiding unnecessary persistence of tools themselves. <!-- Please give a short summary of the change and the problem this solves. --> ## Related issue number Closes #6405 ## Checks - [ ] I've included any doc changes needed for <https://microsoft.github.io/autogen/>. See <https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to build and test documentation locally. - [x] I've added tests (if relevant) corresponding to the changes introduced in this PR. - [x] I've made sure all auto checks have passed. --------- Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
This commit is contained in:
parent
516a211954
commit
99d853a9cb
@ -65,7 +65,7 @@ class AssistantAgentConfig(BaseModel):
|
||||
|
||||
name: str
|
||||
model_client: ComponentModel
|
||||
tools: List[ComponentModel] | None
|
||||
tools: List[ComponentModel] | None = None
|
||||
workbench: ComponentModel | None = None
|
||||
handoffs: List[HandoffBase | str] | None = None
|
||||
model_context: ComponentModel | None = None
|
||||
@ -1342,7 +1342,7 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
|
||||
return AssistantAgentConfig(
|
||||
name=self.name,
|
||||
model_client=self._model_client.dump_component(),
|
||||
tools=[tool.dump_component() for tool in self._tools],
|
||||
tools=None, # versionchanged:: v0.5.5 Now tools are not serialized, Cause they are part of the workbench.
|
||||
workbench=self._workbench.dump_component() if self._workbench else None,
|
||||
handoffs=list(self._handoffs.values()) if self._handoffs else None,
|
||||
model_context=self._model_context.dump_component(),
|
||||
@ -1375,10 +1375,10 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
|
||||
return cls(
|
||||
name=config.name,
|
||||
model_client=ChatCompletionClient.load_component(config.model_client),
|
||||
tools=[BaseTool.load_component(tool) for tool in config.tools] if config.tools else None,
|
||||
workbench=Workbench.load_component(config.workbench) if config.workbench else None,
|
||||
handoffs=config.handoffs,
|
||||
model_context=ChatCompletionContext.load_component(config.model_context) if config.model_context else None,
|
||||
tools=[BaseTool.load_component(tool) for tool in config.tools] if config.tools else None,
|
||||
memory=[Memory.load_component(memory) for memory in config.memory] if config.memory else None,
|
||||
description=config.description,
|
||||
system_message=config.system_message,
|
||||
|
||||
@ -36,6 +36,10 @@ from autogen_core.models._model_client import ModelFamily
|
||||
from autogen_core.tools import BaseTool, FunctionTool, StaticWorkbench
|
||||
from autogen_ext.models.openai import OpenAIChatCompletionClient
|
||||
from autogen_ext.models.replay import ReplayChatCompletionClient
|
||||
from autogen_ext.tools.mcp import (
|
||||
McpWorkbench,
|
||||
SseServerParams,
|
||||
)
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from utils import FileLogHandler
|
||||
|
||||
@ -1399,3 +1403,138 @@ async def test_structured_message_format_string() -> None:
|
||||
|
||||
# Check that the format_string was applied correctly
|
||||
assert message.to_model_text() == "foo - bar"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tools_serialize_and_deserialize() -> None:
|
||||
def test() -> str:
|
||||
return "hello world"
|
||||
|
||||
client = OpenAIChatCompletionClient(
|
||||
model="gpt-4o",
|
||||
api_key="API_KEY",
|
||||
)
|
||||
|
||||
agent = AssistantAgent(
|
||||
name="test",
|
||||
model_client=client,
|
||||
tools=[test],
|
||||
)
|
||||
|
||||
serialize = agent.dump_component()
|
||||
deserialize = AssistantAgent.load_component(serialize)
|
||||
|
||||
assert deserialize.name == agent.name
|
||||
assert await deserialize._workbench.list_tools() == await agent._workbench.list_tools() # type: ignore
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_workbenchs_serialize_and_deserialize() -> None:
|
||||
workbench = McpWorkbench(server_params=SseServerParams(url="http://test-url"))
|
||||
|
||||
client = OpenAIChatCompletionClient(
|
||||
model="gpt-4o",
|
||||
api_key="API_KEY",
|
||||
)
|
||||
|
||||
agent = AssistantAgent(
|
||||
name="test",
|
||||
model_client=client,
|
||||
workbench=workbench,
|
||||
)
|
||||
|
||||
serialize = agent.dump_component()
|
||||
deserialize = AssistantAgent.load_component(serialize)
|
||||
|
||||
assert deserialize.name == agent.name
|
||||
assert deserialize._workbench._to_config() == agent._workbench._to_config() # type: ignore
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tools_deserialize_aware() -> None:
|
||||
dump = """
|
||||
{
|
||||
"provider": "autogen_agentchat.agents.AssistantAgent",
|
||||
"component_type": "agent",
|
||||
"version": 1,
|
||||
"component_version": 1,
|
||||
"description": "An agent that provides assistance with tool use.",
|
||||
"label": "AssistantAgent",
|
||||
"config": {
|
||||
"name": "TestAgent",
|
||||
"model_client":{
|
||||
"provider": "autogen_ext.models.replay.ReplayChatCompletionClient",
|
||||
"component_type": "replay_chat_completion_client",
|
||||
"version": 1,
|
||||
"component_version": 1,
|
||||
"description": "A mock chat completion client that replays predefined responses using an index-based approach.",
|
||||
"label": "ReplayChatCompletionClient",
|
||||
"config": {
|
||||
"chat_completions": [
|
||||
{
|
||||
"finish_reason": "function_calls",
|
||||
"content": [
|
||||
{
|
||||
"id": "hello",
|
||||
"arguments": "{}",
|
||||
"name": "hello"
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 0,
|
||||
"completion_tokens": 0
|
||||
},
|
||||
"cached": false
|
||||
}
|
||||
],
|
||||
"model_info": {
|
||||
"vision": false,
|
||||
"function_calling": true,
|
||||
"json_output": false,
|
||||
"family": "unknown",
|
||||
"structured_output": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"tools": [
|
||||
{
|
||||
"provider": "autogen_core.tools.FunctionTool",
|
||||
"component_type": "tool",
|
||||
"version": 1,
|
||||
"component_version": 1,
|
||||
"description": "Create custom tools by wrapping standard Python functions.",
|
||||
"label": "FunctionTool",
|
||||
"config": {
|
||||
"source_code": "def hello():\\n return 'Hello, World!'\\n",
|
||||
"name": "hello",
|
||||
"description": "",
|
||||
"global_imports": [],
|
||||
"has_cancellation_support": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"model_context": {
|
||||
"provider": "autogen_core.model_context.UnboundedChatCompletionContext",
|
||||
"component_type": "chat_completion_context",
|
||||
"version": 1,
|
||||
"component_version": 1,
|
||||
"description": "An unbounded chat completion context that keeps a view of the all the messages.",
|
||||
"label": "UnboundedChatCompletionContext",
|
||||
"config": {}
|
||||
},
|
||||
"description": "An agent that provides assistance with ability to use tools.",
|
||||
"system_message": "You are a helpful assistant.",
|
||||
"model_client_stream": false,
|
||||
"reflect_on_tool_use": false,
|
||||
"tool_call_summary_format": "{result}",
|
||||
"metadata": {}
|
||||
}
|
||||
}
|
||||
"""
|
||||
agent = AssistantAgent.load_component(json.loads(dump))
|
||||
result = await agent.run(task="hello")
|
||||
|
||||
assert len(result.messages) == 4
|
||||
assert result.messages[-1].content == "Hello, World!" # type: ignore
|
||||
assert result.messages[-1].type == "ToolCallSummaryMessage" # type: ignore
|
||||
assert isinstance(result.messages[-1], ToolCallSummaryMessage) # type: ignore
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user