diff --git a/python/packages/autogen-agentchat/src/autogen_agentchat/agents/__init__.py b/python/packages/autogen-agentchat/src/autogen_agentchat/agents/__init__.py index e466164d94..3c521ae7ac 100644 --- a/python/packages/autogen-agentchat/src/autogen_agentchat/agents/__init__.py +++ b/python/packages/autogen-agentchat/src/autogen_agentchat/agents/__init__.py @@ -9,6 +9,7 @@ from ._base_chat_agent import ( ) from ._code_executor_agent import CodeExecutorAgent from ._coding_assistant_agent import CodingAssistantAgent +from ._tool_use_assistant_agent import ToolUseAssistantAgent __all__ = [ "BaseChatAgent", @@ -20,4 +21,5 @@ __all__ = [ "StopMessage", "CodeExecutorAgent", "CodingAssistantAgent", + "ToolUseAssistantAgent", ] diff --git a/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_base_chat_agent.py b/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_base_chat_agent.py index eb3fc875f6..6f90185b3c 100644 --- a/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_base_chat_agent.py +++ b/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_base_chat_agent.py @@ -58,6 +58,8 @@ class BaseChatAgent(ABC): def __init__(self, name: str, description: str) -> None: self._name = name + if self._name.isidentifier() is False: + raise ValueError("The agent name must be a valid Python identifier.") self._description = description @property diff --git a/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_code_executor_agent.py b/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_code_executor_agent.py index 7cdc182f1e..459bbf82fe 100644 --- a/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_code_executor_agent.py +++ b/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_code_executor_agent.py @@ -9,11 +9,15 @@ from ._base_chat_agent import BaseChatAgent, ChatMessage, TextMessage class CodeExecutorAgent(BaseChatAgent): """An agent that executes code snippets and report the results.""" - DESCRIPTION = "A computer terminal that performs no other action than running Python scripts (provided to it quoted in ```python code blocks), or sh shell scripts (provided to it quoted in ```sh code blocks)." - - def __init__(self, name: str, code_executor: CodeExecutor): + def __init__( + self, + name: str, + code_executor: CodeExecutor, + *, + description: str = "A computer terminal that performs no other action than running Python scripts (provided to it quoted in ```python code blocks), or sh shell scripts (provided to it quoted in ```sh code blocks).", + ) -> None: """Initialize the agent with a code executor.""" - super().__init__(name=name, description=self.DESCRIPTION) + super().__init__(name=name, description=description) self._code_executor = code_executor async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> ChatMessage: diff --git a/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_coding_assistant_agent.py b/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_coding_assistant_agent.py index 2fa737c7e5..f12658f807 100644 --- a/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_coding_assistant_agent.py +++ b/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_coding_assistant_agent.py @@ -13,11 +13,18 @@ from ._base_chat_agent import BaseChatAgent, ChatMessage, MultiModalMessage, Sto class CodingAssistantAgent(BaseChatAgent): - """An agent that provides coding assistance using an LLM model client.""" + """An agent that provides coding assistance using an LLM model client. - DESCRIPTION = "A helpful and general-purpose AI assistant that has strong language skills, Python skills, and Linux command line skills." + It responds with a StopMessage when 'terminate' is detected in the response. + """ - SYSTEM_MESSAGE = """You are a helpful AI assistant. + def __init__( + self, + name: str, + model_client: ChatCompletionClient, + *, + description: str = "A helpful and general-purpose AI assistant that has strong language skills, Python skills, and Linux command line skills.", + system_message: str = """You are a helpful AI assistant. Solve tasks using your coding and language skills. In the following cases, suggest python code (in a python coding block) or shell script (in a sh coding block) for the user to execute. 1. When you need to collect info, use the code to output the info you need, for example, browse or search the web, download/read a file, print the content of a webpage or a file, get the current date/time, check the operating system. After sufficient info is printed and the task is ready to be solved based on your language skill, you can solve the task by yourself. @@ -27,12 +34,11 @@ When using code, you must indicate the script type in the code block. The user c If you want the user to save the code in a file before executing it, put # filename: inside the code block as the first line. Don't include multiple code blocks in one response. Do not ask users to copy and paste the result. Instead, use 'print' function for the output when relevant. Check the execution result returned by the user. If the result indicates there is an error, fix the error and output the code again. Suggest the full code instead of partial code or code changes. If the error can't be fixed or if the task is not solved even after the code is executed successfully, analyze the problem, revisit your assumption, collect additional info you need, and think of a different approach to try. When you find an answer, verify the answer carefully. Include verifiable evidence in your response if possible. -Reply "TERMINATE" in the end when everything is done.""" - - def __init__(self, name: str, model_client: ChatCompletionClient): - super().__init__(name=name, description=self.DESCRIPTION) +Reply "TERMINATE" in the end when everything is done.""", + ): + super().__init__(name=name, description=description) self._model_client = model_client - self._system_messages = [SystemMessage(content=self.SYSTEM_MESSAGE)] + self._system_messages = [SystemMessage(content=system_message)] self._model_context: List[LLMMessage] = [] async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> ChatMessage: diff --git a/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_tool_use_assistant_agent.py b/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_tool_use_assistant_agent.py new file mode 100644 index 0000000000..7154e663f2 --- /dev/null +++ b/python/packages/autogen-agentchat/src/autogen_agentchat/agents/_tool_use_assistant_agent.py @@ -0,0 +1,76 @@ +from typing import List, Sequence + +from autogen_core.base import CancellationToken +from autogen_core.components import FunctionCall +from autogen_core.components.models import ( + AssistantMessage, + ChatCompletionClient, + FunctionExecutionResultMessage, + LLMMessage, + SystemMessage, + UserMessage, +) +from autogen_core.components.tools import ToolSchema + +from ._base_chat_agent import ( + BaseChatAgent, + ChatMessage, + MultiModalMessage, + StopMessage, + TextMessage, + ToolCallMessage, + ToolCallResultMessage, +) + + +class ToolUseAssistantAgent(BaseChatAgent): + """An agent that provides assistance with tool use. + + It responds with a StopMessage when 'terminate' is detected in the response. + """ + + def __init__( + self, + name: str, + model_client: ChatCompletionClient, + tool_schema: List[ToolSchema], + *, + description: str = "An agent that provides assistance with ability to use tools.", + system_message: str = "You are a helpful AI assistant. Solve tasks using your tools. Reply 'TERMINATE' in the end when the task is completed.", + ): + super().__init__(name=name, description=description) + self._model_client = model_client + self._system_messages = [SystemMessage(content=system_message)] + self._tool_schema = tool_schema + self._model_context: List[LLMMessage] = [] + + async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> ChatMessage: + # Add messages to the model context. + for msg in messages: + if isinstance(msg, ToolCallResultMessage): + self._model_context.append(FunctionExecutionResultMessage(content=msg.content)) + elif not isinstance(msg, TextMessage | MultiModalMessage | StopMessage): + raise ValueError(f"Unsupported message type: {type(msg)}") + else: + self._model_context.append(UserMessage(content=msg.content, source=msg.source)) + + # Generate an inference result based on the current model context. + llm_messages = self._system_messages + self._model_context + result = await self._model_client.create( + llm_messages, tools=self._tool_schema, cancellation_token=cancellation_token + ) + + # Add the response to the model context. + self._model_context.append(AssistantMessage(content=result.content, source=self.name)) + + # Detect tool calls. + if isinstance(result.content, list) and all(isinstance(item, FunctionCall) for item in result.content): + return ToolCallMessage(content=result.content, source=self.name) + + assert isinstance(result.content, str) + # Detect stop request. + request_stop = "terminate" in result.content.strip().lower() + if request_stop: + return StopMessage(content=result.content, source=self.name) + + return TextMessage(content=result.content, source=self.name) diff --git a/python/packages/autogen-agentchat/src/autogen_agentchat/teams/__init__.py b/python/packages/autogen-agentchat/src/autogen_agentchat/teams/__init__.py index 1a56f4ae7e..e69de29bb2 100644 --- a/python/packages/autogen-agentchat/src/autogen_agentchat/teams/__init__.py +++ b/python/packages/autogen-agentchat/src/autogen_agentchat/teams/__init__.py @@ -1,3 +0,0 @@ -from .group_chat._round_robin_group_chat import RoundRobinGroupChat - -__all__ = ["RoundRobinGroupChat"] diff --git a/python/packages/autogen-agentchat/src/autogen_agentchat/teams/group_chat/__init__.py b/python/packages/autogen-agentchat/src/autogen_agentchat/teams/group_chat/__init__.py index e69de29bb2..0ed1545ad6 100644 --- a/python/packages/autogen-agentchat/src/autogen_agentchat/teams/group_chat/__init__.py +++ b/python/packages/autogen-agentchat/src/autogen_agentchat/teams/group_chat/__init__.py @@ -0,0 +1,3 @@ +from ._round_robin_group_chat import RoundRobinGroupChat + +__all__ = ["RoundRobinGroupChat"] diff --git a/python/packages/autogen-agentchat/src/autogen_agentchat/teams/group_chat/_base_chat_agent_container.py b/python/packages/autogen-agentchat/src/autogen_agentchat/teams/group_chat/_base_chat_agent_container.py index 5938906a07..c9cdcd5f65 100644 --- a/python/packages/autogen-agentchat/src/autogen_agentchat/teams/group_chat/_base_chat_agent_container.py +++ b/python/packages/autogen-agentchat/src/autogen_agentchat/teams/group_chat/_base_chat_agent_container.py @@ -1,9 +1,13 @@ +import asyncio +import sys from typing import List -from autogen_core.base import MessageContext +from autogen_core.base import AgentId, AgentType, MessageContext from autogen_core.components import DefaultTopicId, event +from autogen_core.components.models import FunctionExecutionResult +from autogen_core.components.tool_agent import ToolException -from ...agents import BaseChatAgent, MultiModalMessage, StopMessage, TextMessage +from ...agents import BaseChatAgent, MultiModalMessage, StopMessage, TextMessage, ToolCallMessage, ToolCallResultMessage from ._events import ContentPublishEvent, ContentRequestEvent from ._sequential_routed_agent import SequentialRoutedAgent @@ -16,13 +20,15 @@ class BaseChatAgentContainer(SequentialRoutedAgent): Args: parent_topic_type (str): The topic type of the parent orchestrator. agent (BaseChatAgent): The agent to delegate message handling to. + tool_agent_type (AgentType): The agent type of the tool agent to use for tool calls. """ - def __init__(self, parent_topic_type: str, agent: BaseChatAgent) -> None: + def __init__(self, parent_topic_type: str, agent: BaseChatAgent, tool_agent_type: AgentType) -> None: super().__init__(description=agent.description) self._parent_topic_type = parent_topic_type self._agent = agent self._message_buffer: List[TextMessage | MultiModalMessage | StopMessage] = [] + self._tool_agent_id = AgentId(type=tool_agent_type, key=self.id.key) @event async def handle_content_publish(self, message: ContentPublishEvent, ctx: MessageContext) -> None: @@ -39,7 +45,39 @@ class BaseChatAgentContainer(SequentialRoutedAgent): """Handle a content request event by passing the messages in the buffer to the delegate agent and publish the response.""" response = await self._agent.on_messages(self._message_buffer, ctx.cancellation_token) - # TODO: handle tool call messages. + + # Handle tool calls. + while isinstance(response, ToolCallMessage): + # TODO: use logging instead of print + sys.stdout.write(f"{'-'*80}\n{self._agent.name}:\n{response.content}\n") + # Execute functions called by the model by sending messages to tool agent. + results: List[FunctionExecutionResult | BaseException] = await asyncio.gather( + *[ + self.send_message( + message=call, + recipient=self._tool_agent_id, + cancellation_token=ctx.cancellation_token, + ) + for call in response.content + ] + ) + # Combine the results in to a single response and handle exceptions. + function_results: List[FunctionExecutionResult] = [] + for result in results: + if isinstance(result, FunctionExecutionResult): + function_results.append(result) + elif isinstance(result, ToolException): + function_results.append(FunctionExecutionResult(content=f"Error: {result}", call_id=result.call_id)) + elif isinstance(result, BaseException): + raise result # Unexpected exception. + # Create a new tool call result message. + feedback = ToolCallResultMessage(content=function_results, source=self._tool_agent_id.type) + # TODO: use logging instead of print + sys.stdout.write(f"{'-'*80}\n{self._tool_agent_id.type}:\n{feedback.content}\n") + # Forward the feedback to the agent. + response = await self._agent.on_messages([feedback], ctx.cancellation_token) + + # Publish the response. assert isinstance(response, TextMessage | MultiModalMessage | StopMessage) self._message_buffer.clear() await self.publish_message( diff --git a/python/packages/autogen-agentchat/src/autogen_agentchat/teams/group_chat/_round_robin_group_chat.py b/python/packages/autogen-agentchat/src/autogen_agentchat/teams/group_chat/_round_robin_group_chat.py index 14a4e127f4..e1d50a6cb5 100644 --- a/python/packages/autogen-agentchat/src/autogen_agentchat/teams/group_chat/_round_robin_group_chat.py +++ b/python/packages/autogen-agentchat/src/autogen_agentchat/teams/group_chat/_round_robin_group_chat.py @@ -5,6 +5,8 @@ from typing import Callable, List from autogen_core.application import SingleThreadedAgentRuntime from autogen_core.base import AgentId, AgentInstantiationContext, AgentRuntime, AgentType, MessageContext, TopicId from autogen_core.components import ClosureAgent, TypeSubscription +from autogen_core.components.tool_agent import ToolAgent +from autogen_core.components.tools import Tool from ...agents import BaseChatAgent, TextMessage from .._base_team import BaseTeam, TeamRunResult @@ -14,19 +16,60 @@ from ._round_robin_group_chat_manager import RoundRobinGroupChatManager class RoundRobinGroupChat(BaseTeam): - def __init__(self, participants: List[BaseChatAgent]): + """A team that runs a group chat with participants taking turns in a round-robin fashion. + + If a single participant is in the team, the participant will be the only speaker. + + Args: + participants (List[BaseChatAgent]): The participants in the group chat. + tools (List[Tool], optional): The tools to use in the group chat. Defaults to None. + + Raises: + ValueError: If no participants are provided or if participant names are not unique. + + Examples: + + A team with one participant with tools: + + .. code-block:: python + + from autogen_agentchat.agents import ToolUseAssistantAgent + from autogen_agentchat.teams import RoundRobinGroupChat + + assistant = ToolUseAssistantAgent("Assistant", model_client=..., tool_schema=[...]) + team = RoundRobinGroupChat([assistant], tools=[...]) + await team.run("What's the weather in New York?") + + A team with multiple participants: + + .. code-block:: python + + from autogen_agentchat.agents import CodingAssistantAgent, CodeExecutorAgent + from autogen_agentchat.teams import RoundRobinGroupChat + + coding_assistant = CodingAssistantAgent("Coding Assistant", model_client=...) + executor_agent = CodeExecutorAgent("Code Executor", code_executor=...) + team = RoundRobinGroupChat([coding_assistant, executor_agent]) + await team.run("Write a program that prints 'Hello, world!'") + + """ + + def __init__(self, participants: List[BaseChatAgent], *, tools: List[Tool] | None = None): if len(participants) == 0: raise ValueError("At least one participant is required.") if len(participants) != len(set(participant.name for participant in participants)): raise ValueError("The participant names must be unique.") self._participants = participants self._team_id = str(uuid.uuid4()) + self._tools = tools or [] - def _create_factory(self, parent_topic_type: str, agent: BaseChatAgent) -> Callable[[], BaseChatAgentContainer]: + def _create_factory( + self, parent_topic_type: str, agent: BaseChatAgent, tool_agent_type: AgentType + ) -> Callable[[], BaseChatAgentContainer]: def _factory() -> BaseChatAgentContainer: id = AgentInstantiationContext.current_agent_id() assert id == AgentId(type=agent.name, key=self._team_id) - container = BaseChatAgentContainer(parent_topic_type, agent) + container = BaseChatAgentContainer(parent_topic_type, agent, tool_agent_type) assert container.id == id return container @@ -43,6 +86,12 @@ class RoundRobinGroupChat(BaseTeam): group_topic_type = "round_robin_group_topic" team_topic_type = "team_topic" + # Register the tool agent. + tool_agent_type = await ToolAgent.register( + runtime, "tool_agent", lambda: ToolAgent("Tool agent for round-robin group chat", self._tools) + ) + # No subscriptions are needed for the tool agent, which will be called via direct messages. + # Register participants. participant_topic_types: List[str] = [] participant_descriptions: List[str] = [] @@ -52,7 +101,7 @@ class RoundRobinGroupChat(BaseTeam): topic_type = participant.name # Register the participant factory. await BaseChatAgentContainer.register( - runtime, type=agent_type, factory=self._create_factory(group_topic_type, participant) + runtime, type=agent_type, factory=self._create_factory(group_topic_type, participant, tool_agent_type) ) # Add subscriptions for the participant. await runtime.add_subscription(TypeSubscription(topic_type=topic_type, agent_type=agent_type)) diff --git a/python/packages/autogen-agentchat/tests/test_group_chat.py b/python/packages/autogen-agentchat/tests/test_group_chat.py index 0771207f6b..2008f2c26d 100644 --- a/python/packages/autogen-agentchat/tests/test_group_chat.py +++ b/python/packages/autogen-agentchat/tests/test_group_chat.py @@ -1,54 +1,33 @@ import asyncio +import json import tempfile -from typing import Any, AsyncGenerator, List +from typing import Any, AsyncGenerator, List, Sequence import pytest -from autogen_agentchat.agents import CodeExecutorAgent, CodingAssistantAgent -from autogen_agentchat.teams import RoundRobinGroupChat +from autogen_agentchat.agents import ( + BaseChatAgent, + ChatMessage, + CodeExecutorAgent, + CodingAssistantAgent, + ToolUseAssistantAgent, +) +from autogen_agentchat.teams.group_chat import RoundRobinGroupChat +from autogen_core.base import CancellationToken +from autogen_core.components import FunctionCall from autogen_core.components.code_executor import LocalCommandLineCodeExecutor -from autogen_core.components.models import OpenAIChatCompletionClient +from autogen_core.components.models import FunctionExecutionResult, OpenAIChatCompletionClient +from autogen_core.components.tools import FunctionTool from openai.resources.chat.completions import AsyncCompletions from openai.types.chat.chat_completion import ChatCompletion, Choice from openai.types.chat.chat_completion_chunk import ChatCompletionChunk from openai.types.chat.chat_completion_message import ChatCompletionMessage +from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall, Function from openai.types.completion_usage import CompletionUsage class _MockChatCompletion: - def __init__(self, model: str = "gpt-4o") -> None: - self._saved_chat_completions: List[ChatCompletion] = [ - ChatCompletion( - id="id1", - choices=[ - Choice( - finish_reason="stop", - index=0, - message=ChatCompletionMessage( - content="""Here is the program\n ```python\nprint("Hello, world!")\n```""", - role="assistant", - ), - ) - ], - created=0, - model=model, - object="chat.completion", - usage=CompletionUsage(prompt_tokens=0, completion_tokens=0, total_tokens=0), - ), - ChatCompletion( - id="id2", - choices=[ - Choice( - finish_reason="stop", - index=0, - message=ChatCompletionMessage(content="TERMINATE", role="assistant"), - ) - ], - created=0, - model=model, - object="chat.completion", - usage=CompletionUsage(prompt_tokens=0, completion_tokens=0, total_tokens=0), - ), - ] + def __init__(self, chat_completions: List[ChatCompletion]) -> None: + self._saved_chat_completions = chat_completions self._curr_index = 0 async def mock_create( @@ -60,17 +39,138 @@ class _MockChatCompletion: return completion +class _EchoAgent(BaseChatAgent): + async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> ChatMessage: + return messages[-1] + + +def _pass_function(input: str) -> str: + return "pass" + + @pytest.mark.asyncio async def test_round_robin_group_chat(monkeypatch: pytest.MonkeyPatch) -> None: - mock = _MockChatCompletion(model="gpt-4o-2024-05-13") + model = "gpt-4o-2024-05-13" + chat_completions = [ + ChatCompletion( + id="id1", + choices=[ + Choice( + finish_reason="stop", + index=0, + message=ChatCompletionMessage( + content="""Here is the program\n ```python\nprint("Hello, world!")\n```""", + role="assistant", + ), + ) + ], + created=0, + model=model, + object="chat.completion", + usage=CompletionUsage(prompt_tokens=0, completion_tokens=0, total_tokens=0), + ), + ChatCompletion( + id="id2", + choices=[ + Choice( + finish_reason="stop", + index=0, + message=ChatCompletionMessage(content="TERMINATE", role="assistant"), + ) + ], + created=0, + model=model, + object="chat.completion", + usage=CompletionUsage(prompt_tokens=0, completion_tokens=0, total_tokens=0), + ), + ] + mock = _MockChatCompletion(chat_completions) monkeypatch.setattr(AsyncCompletions, "create", mock.mock_create) with tempfile.TemporaryDirectory() as temp_dir: code_executor_agent = CodeExecutorAgent( "code_executor", code_executor=LocalCommandLineCodeExecutor(work_dir=temp_dir) ) coding_assistant_agent = CodingAssistantAgent( - "coding_assistant", model_client=OpenAIChatCompletionClient(model="gpt-4o-2024-05-13", api_key="") + "coding_assistant", model_client=OpenAIChatCompletionClient(model=model, api_key="") ) team = RoundRobinGroupChat(participants=[coding_assistant_agent, code_executor_agent]) result = await team.run("Write a program that prints 'Hello, world!'") assert result.result == "TERMINATE" + + +@pytest.mark.asyncio +async def test_round_robin_group_chat_with_tools(monkeypatch: pytest.MonkeyPatch) -> None: + model = "gpt-4o-2024-05-13" + chat_completions = [ + ChatCompletion( + id="id1", + choices=[ + Choice( + finish_reason="tool_calls", + index=0, + message=ChatCompletionMessage( + content=None, + tool_calls=[ + ChatCompletionMessageToolCall( + id="1", + type="function", + function=Function( + name="pass", + arguments=json.dumps({"input": "pass"}), + ), + ) + ], + role="assistant", + ), + ) + ], + created=0, + model=model, + object="chat.completion", + usage=CompletionUsage(prompt_tokens=0, completion_tokens=0, total_tokens=0), + ), + ChatCompletion( + id="id2", + choices=[ + Choice(finish_reason="stop", index=0, message=ChatCompletionMessage(content="Hello", role="assistant")) + ], + created=0, + model=model, + object="chat.completion", + usage=CompletionUsage(prompt_tokens=0, completion_tokens=0, total_tokens=0), + ), + ChatCompletion( + id="id2", + choices=[ + Choice( + finish_reason="stop", index=0, message=ChatCompletionMessage(content="TERMINATE", role="assistant") + ) + ], + created=0, + model=model, + object="chat.completion", + usage=CompletionUsage(prompt_tokens=0, completion_tokens=0, total_tokens=0), + ), + ] + mock = _MockChatCompletion(chat_completions) + monkeypatch.setattr(AsyncCompletions, "create", mock.mock_create) + tool = FunctionTool(_pass_function, name="pass", description="pass function") + tool_use_agent = ToolUseAssistantAgent( + "tool_use_agent", + model_client=OpenAIChatCompletionClient(model=model, api_key=""), + tool_schema=[tool.schema], + ) + echo_agent = _EchoAgent("echo_agent", description="echo agent") + team = RoundRobinGroupChat(participants=[tool_use_agent, echo_agent], tools=[tool]) + await team.run("Write a program that prints 'Hello, world!'") + context = tool_use_agent._model_context # pyright: ignore + assert context[0].content == "Write a program that prints 'Hello, world!'" + assert isinstance(context[1].content, list) + assert isinstance(context[1].content[0], FunctionCall) + assert context[1].content[0].name == "pass" + assert context[1].content[0].arguments == json.dumps({"input": "pass"}) + assert isinstance(context[2].content, list) + assert isinstance(context[2].content[0], FunctionExecutionResult) + assert context[2].content[0].content == "pass" + assert context[2].content[0].call_id == "1" + assert context[3].content == "Hello" diff --git a/python/packages/autogen-core/docs/src/agentchat-user-guide/guides/quickstart.ipynb b/python/packages/autogen-core/docs/src/agentchat-user-guide/guides/quickstart.ipynb index 08135a8fd4..8ba79ecbfc 100644 --- a/python/packages/autogen-core/docs/src/agentchat-user-guide/guides/quickstart.ipynb +++ b/python/packages/autogen-core/docs/src/agentchat-user-guide/guides/quickstart.ipynb @@ -311,7 +311,7 @@ ], "source": [ "from autogen_agentchat.agents import CodeExecutorAgent, CodingAssistantAgent\n", - "from autogen_agentchat.teams import RoundRobinGroupChat\n", + "from autogen_agentchat.teams.group_chat import RoundRobinGroupChat\n", "from autogen_core.components.code_executor import DockerCommandLineCodeExecutor\n", "from autogen_core.components.models import OpenAIChatCompletionClient\n", "\n", diff --git a/python/packages/autogen-core/docs/src/agentchat-user-guide/guides/tool_use.ipynb b/python/packages/autogen-core/docs/src/agentchat-user-guide/guides/tool_use.ipynb new file mode 100644 index 0000000000..a77b8b6c78 --- /dev/null +++ b/python/packages/autogen-core/docs/src/agentchat-user-guide/guides/tool_use.ipynb @@ -0,0 +1,103 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tool Use\n", + "\n", + ":::{note}\n", + "See [here](pkg-info-autogen-agentchat) for installation instructions.\n", + ":::\n", + "\n", + ":::{warning}\n", + "🚧 Under construction 🚧\n", + ":::" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "from autogen_agentchat.agents import ToolUseAssistantAgent\n", + "from autogen_agentchat.teams.group_chat import RoundRobinGroupChat\n", + "from autogen_core.components.models import OpenAIChatCompletionClient\n", + "from autogen_core.components.tools import FunctionTool" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "async def get_weather(city: str) -> str:\n", + " return \"Sunny\"\n", + "\n", + "\n", + "get_weather_tool = FunctionTool(get_weather, description=\"Get the weather for a city\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--------------------------------------------------------------------------------\n", + "user:\n", + "What's the weather in New York?\n", + "--------------------------------------------------------------------------------\n", + "Weather_Assistant:\n", + "[FunctionCall(id='call_I8mFF4D73eoC3hhO81ldmIG3', arguments='{\"city\":\"New York\"}', name='get_weather')]\n", + "--------------------------------------------------------------------------------\n", + "tool_agent:\n", + "[FunctionExecutionResult(content='Sunny', call_id='call_I8mFF4D73eoC3hhO81ldmIG3')]\n", + "--------------------------------------------------------------------------------\n", + "Weather_Assistant:\n", + "The weather in New York is sunny. \n", + "\n", + "TERMINATE\n", + "TeamRunResult(result='The weather in New York is sunny. \\n\\nTERMINATE')\n" + ] + } + ], + "source": [ + "assistant = ToolUseAssistantAgent(\n", + " \"Weather_Assistant\",\n", + " model_client=OpenAIChatCompletionClient(model=\"gpt-4o-mini\"),\n", + " tool_schema=[get_weather_tool.schema],\n", + ")\n", + "team = RoundRobinGroupChat([assistant], tools=[get_weather_tool])\n", + "result = await team.run(\"What's the weather in New York?\")\n", + "print(result)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/python/packages/autogen-core/docs/src/agentchat-user-guide/index.md b/python/packages/autogen-core/docs/src/agentchat-user-guide/index.md index 565f9d05e9..1a6a6df00c 100644 --- a/python/packages/autogen-core/docs/src/agentchat-user-guide/index.md +++ b/python/packages/autogen-core/docs/src/agentchat-user-guide/index.md @@ -17,4 +17,5 @@ myst: :hidden: guides/quickstart +guides/tool_use ```