From 86d45d0816363fa535519b5c7e575d0576984641 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Thu, 19 Dec 2024 14:46:25 -0800 Subject: [PATCH 01/38] readme start --- python/migration_guide/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 python/migration_guide/README.md diff --git a/python/migration_guide/README.md b/python/migration_guide/README.md new file mode 100644 index 000000000000..d1b47d04e857 --- /dev/null +++ b/python/migration_guide/README.md @@ -0,0 +1,10 @@ +# Migration Guide for v0.2.* to v0.4.* + +This is a migration guide for users of the `v0.2.*` version of `autogen-agentchat` +to the `v0.4.*` version, which introduces a new set of APIs and features. + +```{alert} +The `v0.4.*` version contains breaking changes. Please read this guide carefully. +We will still maintain the `v0.2.*` version in the `0.2` branch, however, +we highly recommend you to upgrade to the `v0.4.*` version. +``` \ No newline at end of file From 1074ef466e1b909bdf8d3810e38f6ce584094ff3 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Thu, 19 Dec 2024 17:06:45 -0800 Subject: [PATCH 02/38] Enhance migration guide with organization overview and section breakdown --- python/migration_guide/README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/python/migration_guide/README.md b/python/migration_guide/README.md index d1b47d04e857..78c99db1fbb9 100644 --- a/python/migration_guide/README.md +++ b/python/migration_guide/README.md @@ -7,4 +7,14 @@ to the `v0.4.*` version, which introduces a new set of APIs and features. The `v0.4.*` version contains breaking changes. Please read this guide carefully. We will still maintain the `v0.2.*` version in the `0.2` branch, however, we highly recommend you to upgrade to the `v0.4.*` version. -``` \ No newline at end of file +``` + +## How is this guide organized? + +This guide is organized into the following sections: + +1. [Overview of the new APIs](#overview-of-the-new-apis) +2. [How-tos in v0.4.* for v0.2.* users](#how-tos-in-v0.4.*-for-v0.2.*-users) + a. [Single Agent](#single-agent) + b. [Group Chat](#group-chat) + c. [] \ No newline at end of file From 0f2c84c4ebe31f6389a07f603bd4622946b29693 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Fri, 20 Dec 2024 10:37:23 -0800 Subject: [PATCH 03/38] update toc --- python/migration_guide/README.md | 82 ++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 14 deletions(-) diff --git a/python/migration_guide/README.md b/python/migration_guide/README.md index 78c99db1fbb9..3bb25ec2005f 100644 --- a/python/migration_guide/README.md +++ b/python/migration_guide/README.md @@ -1,20 +1,74 @@ -# Migration Guide for v0.2.* to v0.4.* +# Migration Guide for v0.2 to v0.4 -This is a migration guide for users of the `v0.2.*` version of `autogen-agentchat` -to the `v0.4.*` version, which introduces a new set of APIs and features. +This is a migration guide for users of the `v0.2.*` versions of `autogen-agentchat` +to the `v0.4` version, which introduces a new set of APIs and features. -```{alert} -The `v0.4.*` version contains breaking changes. Please read this guide carefully. -We will still maintain the `v0.2.*` version in the `0.2` branch, however, -we highly recommend you to upgrade to the `v0.4.*` version. +```{note} +The `v0.4` version contains breaking changes. Please read this guide carefully. +We will still maintain the `v0.2` version in the `0.2` branch, however, +we highly recommend you to upgrade to the `v0.4` version. ``` -## How is this guide organized? +## What's in this guide? -This guide is organized into the following sections: +We provide a detailed guide on how to migrate your existing codebase from `v0.2` to `v0.4`. -1. [Overview of the new APIs](#overview-of-the-new-apis) -2. [How-tos in v0.4.* for v0.2.* users](#how-tos-in-v0.4.*-for-v0.2.*-users) - a. [Single Agent](#single-agent) - b. [Group Chat](#group-chat) - c. [] \ No newline at end of file +See each feature below for detailed information on how to migrate. + +- [Model Client](#model-client) +- [Model Client Cache](#model-client-cache) +- [Model Client for OpenAI-Compatible APIs](#model-client-for-openai-compatible-apis) +- [Assistant Agent](#assistant-agent) +- [Multi-Modal Agent](#multi-modal-agent) +- [User Proxy](#user-proxy) +- [Custom Agent and Register Reply](#custom-agent-and-register-reply) +- [Two-Agent Chat](#two-agent-chat) +- [Tool Use](#tool-use) +- [Group Chat](#group-chat) +- [Group Chat with Custom Selector (Stateflow)](#group-chat-with-custom-selector-stateflow) +- [Nested Chat](#nested-chat) +- [Sequential Chat](#sequential-chat) +- [GPTAssistantAgent](#gptassistantagent) +- [Long-Context Handling](#long-context-handling) +- [Observability](#observability) +- [Code Executors](#code-executors) + +The following features currently in `v0.2` +will be providied in the future releases of `v0.4.*` versions: + +- Teacheable Agent +- RAG Agent + +## Model Client + +## Model Client Cache + +## Model Client for OpenAI-Compatible APIs + +## Assistant Agent + +## Multi-Modal Agent + +## User Proxy + +## Custom Agent and Register Reply + +## Two-Agent Chat + +## Tool Use + +## Group Chat + +## Group Chat with Custom Selector (Stateflow) + +## Nested Chat + +## Sequential Chat + +## GPTAssistantAgent + +## Long-Context Handling + +## Observability + +## Code Executors From fe10048479918b163ee5c52c314224c9f5dcb819 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Fri, 20 Dec 2024 13:50:02 -0800 Subject: [PATCH 04/38] Update --- python/migration_guide/README.md | 182 ++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 5 deletions(-) diff --git a/python/migration_guide/README.md b/python/migration_guide/README.md index 3bb25ec2005f..4ece4ca48e28 100644 --- a/python/migration_guide/README.md +++ b/python/migration_guide/README.md @@ -3,11 +3,9 @@ This is a migration guide for users of the `v0.2.*` versions of `autogen-agentchat` to the `v0.4` version, which introduces a new set of APIs and features. -```{note} -The `v0.4` version contains breaking changes. Please read this guide carefully. +> **Note**: The `v0.4` version contains breaking changes. Please read this guide carefully. We will still maintain the `v0.2` version in the `0.2` branch, however, we highly recommend you to upgrade to the `v0.4` version. -``` ## What's in this guide? @@ -16,7 +14,6 @@ We provide a detailed guide on how to migrate your existing codebase from `v0.2` See each feature below for detailed information on how to migrate. - [Model Client](#model-client) -- [Model Client Cache](#model-client-cache) - [Model Client for OpenAI-Compatible APIs](#model-client-for-openai-compatible-apis) - [Assistant Agent](#assistant-agent) - [Multi-Modal Agent](#multi-modal-agent) @@ -36,19 +33,194 @@ See each feature below for detailed information on how to migrate. The following features currently in `v0.2` will be providied in the future releases of `v0.4.*` versions: +- Model Client Cache - Teacheable Agent - RAG Agent ## Model Client -## Model Client Cache +In `v0.2` you configure the model client as follows, and create the `OpenAIWrapper` object. + +```python +from autogen.oai import OpenAIWrapper + +config_list = [ + {"model": "gpt-4o", "api_key": "sk-xxx"}, + {"model": "gpt-4o-mini", "api_key": "sk-xxx"}, +] + +model_client = OpenAIWrapper(config_list=config_list) +``` + +In `v0.4`, we offers two ways to create a model client. + +### Use component config + +TODO: add example + +```python +``` + +### Use model client class directly + +Open AI: + +```python +from autogen_ext.models.openai import OpenAIChatCompletionClient + +model_client = OpenAIChatCompletionClient(model="gpt-4o", api_key="sk-xxx") +``` + +Azure OpenAI: + +```python +from autogen_ext.models.openai import AzureOpenAIChatCompletionClient + +model_client = AzureOpenAIChatCompletionClient( + azure_deployment="gpt-4o", + azure_endpoint="https://.openai.azure.com/", + model="gpt-4o", + api_version="2024-09-01-preview", + api_key="sk-xxx", +) +``` + +Read more on [OpenAI Chat Completion Client Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_ext.models.openai.html) ## Model Client for OpenAI-Compatible APIs +You can use a the `OpenAIChatCompletionClient` to connect to an OpenAI-Compatible API, +but you need to specify the `base_url` and `model_capabilities`. + +```python +from autogen_ext.models.openai import OpenAIChatCompletionClient + +custom_model_client = OpenAIChatCompletionClient( + model="custom-model-name", + base_url="https://custom-model.com/reset/of/the/path", + api_key="placeholder", + model_capabilities={ + "vision": True, + "function_calling": True, + "json_output": True, + }, +) +``` + +> **Note**: We don't test all the OpenAI-Compatible APIs, and many of them +> works differently from the OpenAI API even though they may claim to suppor it. +> Please test them before using them. + +Read about [Model Clients](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/models.html) +in AgentChat Tutorial and more detailed information on [Core API Docs](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/model-clients.html) + +Support for other hosted models will be added in the future. + ## Assistant Agent +In `v0.2`, you create an assistant agent as follows: + +```python +from autogen.agentchat import AssistantAgent + +llm_config = { + "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], + "seed": 42, + "temperature": 0, +} + +assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant.", + llm_config=llm_config, +) +``` + +In `v0.4`, it is similar, but you need to specify `model_client` instead of `llm_config`. + +```python +from autogen_agentchat.agents import AssistantAgent +from autogen_ext.models.openai import OpenAIChatCompletionClient + +model_client = OpenAIChatCompletionClient(model="gpt-4o", api_key="sk-xxx", seed=42, temperature=0) + +assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant.", + model_client=model_client, +) +``` + +However, the usage is somewhat different. In `v0.4`, instead of calling `assistant.send`, +you call `assistant.on_messages` or `assistant.on_messages_stream` to handle incoming messages. +Furthermore, the `on_messages` and `on_messages_stream` methods are asynchronous, +and the latter returns an async generator to stream the inner thoughts of the agent. + +Here is how you can call the assistant agent in `v0.4` directly, continuing from the above example: + +```python +import asyncio +from autogen_agentchat.messages import TextMessage +from autogen_agentchat.agents import AssistantAgent +from autogen_core import CancellationToken +from autogen_ext.models.openai import OpenAIChatCompletionClient + +async def main() -> None: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + + assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant.", + model_client=model_client, + ) + + cancellation_token = CancellationToken() + response = await assistant.on_messages([TextMessage(content="Hello!", source="user")], cancellation_token) + print(response) + +asyncio.run(main()) +``` + +The `CancellationToken` can be used to cancel the request asynchronously +when you call `cancellation_token.cancel()`, which will cause the `await` +on the `on_messages` call to raise a `CancelledError`. + +Read more on [Assistant Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.AssistantAgent). + + ## Multi-Modal Agent +The `AssistantAgent` in `v0.4` supports multi-modal inputs if the model client supports it. +The `vision` capability of the model client is used to determine if the agent supports multi-modal inputs. + +```python +import asyncio +from pathlib import Path +from autogen_agentchat.messages import MultiModalMessage +from autogen_agentchat.agents import AssistantAgent +from autogen_core import CancellationToken, Image +from autogen_ext.models.openai import OpenAIChatCompletionClient + +async def main() -> None: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + + assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant.", + model_client=model_client, + ) + + cancellation_token = CancellationToken() + message = MultiModalMessage( + content=["Here is an image:", Image.from_file(Path("test.png"))], + source="user", + ) + response = await assistant.on_messages([message], cancellation_token) + print(response) + +asyncio.run(main()) +``` + ## User Proxy ## Custom Agent and Register Reply From cdb3e25821bc72cd49096c924465abb3fb8a1622 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Fri, 20 Dec 2024 15:17:24 -0800 Subject: [PATCH 05/38] wip --- python/migration_guide/README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/python/migration_guide/README.md b/python/migration_guide/README.md index 4ece4ca48e28..0c8afc0f8755 100644 --- a/python/migration_guide/README.md +++ b/python/migration_guide/README.md @@ -223,6 +223,35 @@ asyncio.run(main()) ## User Proxy +In `v0.2`, you create a user proxy as follows: + +```python +from autogen.agentchat import UserProxyAgent + +user_proxy = UserProxyAgent( + name="user_proxy", + human_input_mode="NEVER", + max_consecutive_auto_reply=10, + code_execution_config=False, + llm_config=False, +) +``` + +This user proxy would take input from the user through console, and would terminate +if the incoming message ends with "TERMINATE". + +In `v0.4`, a user proxy is simply an agent that takes user input only, there is no +other special configuration needed. You can create a user proxy as follows: + +```python +from autogen_agentchat.agents import UserProxyAgent + +user_proxy = UserProxyAgent("user_proxy") +``` + +See [User Proxy Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.UserProxyAgent) +for more details and how to customize the input function with timeout. + ## Custom Agent and Register Reply ## Two-Agent Chat From 90da99eeb258b986d3774a9285b3562bf992d809 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Fri, 20 Dec 2024 17:14:39 -0800 Subject: [PATCH 06/38] wip --- python/migration_guide/README.md | 133 ++++++++++++++++++++++++++++++- 1 file changed, 132 insertions(+), 1 deletion(-) diff --git a/python/migration_guide/README.md b/python/migration_guide/README.md index 0c8afc0f8755..741f8227c6bb 100644 --- a/python/migration_guide/README.md +++ b/python/migration_guide/README.md @@ -22,6 +22,7 @@ See each feature below for detailed information on how to migrate. - [Two-Agent Chat](#two-agent-chat) - [Tool Use](#tool-use) - [Group Chat](#group-chat) +- [Group Chat with Resume](#group-chat-with-resume) - [Group Chat with Custom Selector (Stateflow)](#group-chat-with-custom-selector-stateflow) - [Nested Chat](#nested-chat) - [Sequential Chat](#sequential-chat) @@ -252,14 +253,144 @@ user_proxy = UserProxyAgent("user_proxy") See [User Proxy Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.UserProxyAgent) for more details and how to customize the input function with timeout. -## Custom Agent and Register Reply +## Conversable Agent and Register Reply + +In `v0.2`, you can create a conversable agent and register a reply function as follows: + +```python +from typing import Any, Dict, List, Optional, Tuple, Union +from autogen.agentchat import ConversableAgent + +llm_config = { + "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], + "seed": 42, + "temperature": 0, +} + +conversable_agent = ConversableAgent( + name="conversable_agent", + system_message="You are a helpful assistant.", + llm_config=llm_config, + code_execution_config={"work_dir": "coding"}, + human_input_mode="NEVER", + max_consecutive_auto_reply=10, +) + +def reply_func( + recipient: ConversableAgent, + messages: Optional[List[Dict]] = None, + sender: Optional[Agent] = None, + config: Optional[Any] = None, +) -> Tuple[bool, Union[str, Dict, None]]: + # Custom reply logic here + return True, "Custom reply" + +# Register the reply function +conversable_agent.register_reply([ConversableAgent], reply_func, position=0) + +# NOTE: An async reply function will only be invoked with async send. +``` + +Rather than guessing what the `reply_func` does, all its parameters, +and what the `position` should be, in `v0.4`, we can simply create a custom agent +and implement the `on_messages` method. + +```python +from typing import Sequence +from autogen_core import CancellationToken +from autogen_agentchat.agents import BaseChatAgent +from autogen_agentchat.messages import TextMessage +from autogen_agentchat.base import Response + +class CustomAgent(BaseChatAgent): + async def on_messages(self, messages: Sequence[TextMessage], cancellation_token: CancellationToken) -> Response: + # Custom reply logic here + return Response(chat_message=TextMessage(content="Custom reply", source=self.name)) +``` + +You can then use the custom agent in the same way as the `AssistantAgent`. ## Two-Agent Chat +In `v0.2`, you can create a two-agent chat for code execution as follows: + +```python +from autogen.coding import LocalCommandLineCodeExecutor +from autogen.agentchat import AssistantAgent, UserProxyAgent + +llm_config = { + "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], + "seed": 42, + "temperature": 0, +} + +assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant. Write all code in python. Reply only 'TERMINATE' if the task is done.", + llm_config=llm_config, + is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"), +) + +user_proxy = UserProxyAgent( + name="user_proxy", + human_input_mode="NEVER", + max_consecutive_auto_reply=10, + code_execution_config={"code_executor": LocalCommandLineCodeExecutor(work_dir="coding")}, + llm_config=False, + is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"), +) + +chat_result = user_proxy.initiate_chat(assistant, message="Write a python script to print 'Hello, world!'") +# Intermediate messages are printed to the console directly. +print(chat_result) +``` + +To get the same behavior in `v0.4`, you can use the `AssistantAgent` +and `CodeExecutorAgent` together in a `RoundRobinGroupChat`. + +```python +import asyncio +from autogen_agentchat.agents import AssistantAgent, CodeExecutorAgent +from autogen_agentchat.teams import RoundRobinGroupChat +from autogen_agentchat.conditions import TextTermination, MaxMessageTermination +from autogen_agentchat.ui import Console +from autogen_ext.code_executors.local import LocalCommandLineCodeExecutor +from autogen_ext.models.openai import OpenAIChatCompletionClient + +async def main() -> None: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + + assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant. Write all code in python. Reply only 'TERMINATE' if the task is done.", + model_client=model_client, + ) + + code_executor = CodeExecutorAgent( + name="code_executor", + code_executor=LocalCommandLineCodeExecutor(work_dir="coding"), + ) + + # The termination condition is a combination of text termination and max message termination, either of which will cause the chat to terminate. + termination = TextTermination("TERMINATE") | MaxMessageTermination(10) + + # The group chat will alternate between the assistant and the code executor. + group_chat = RoundRobinGroupChat([assistant, code_executor], termination_condition=termination) + + # `run_stream` returns an async generator to stream the intermediate messages. + stream = group_chat.run_stream(task="Write a python script to print 'Hello, world!'") + # `Console` is a simple UI to display the stream. + await Console(stream) + +asyncio.run(main()) +``` + ## Tool Use ## Group Chat +## Group Chat with Resume + ## Group Chat with Custom Selector (Stateflow) ## Nested Chat From 5af894c0e9503e0961fcf23c8fb9f086a05e6c91 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Fri, 20 Dec 2024 17:32:52 -0800 Subject: [PATCH 07/38] WIP --- python/migration_guide/README.md | 67 ++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/python/migration_guide/README.md b/python/migration_guide/README.md index 741f8227c6bb..95e7daa5d8e4 100644 --- a/python/migration_guide/README.md +++ b/python/migration_guide/README.md @@ -387,6 +387,73 @@ asyncio.run(main()) ## Tool Use +In `v0.2`, to create a tool use chatbot, you must have two agents, one for calling the tool and one for executing the tool. +You need to initiate a two-agent chat for every user request. + +```python +from autogen.agentchat import AssistantAgent, UserProxyAgent, register_function + +llm_config = { + "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], + "seed": 42, + "temperature": 0, +} + +tool_caller = AssistantAgent( + name="tool_caller", + system_message="You are a helpful assistant. You can call tools to help user.", + llm_config=llm_config, +) + +tool_executor = UserProxyAgent( + name="tool_executor", + human_input_mode="NEVER", + code_execution_config=False, + max_consecutive_auto_reply=1, # Only one or a batch of tool calls at a time. + llm_config=False, +) + +def get_weather(city: str) -> str: + return f"The weather in {city} is 72 degree and sunny." + +# Register the tool function to the tool caller and executor. +register_function(get_weather, caller=tool_caller, executor=tool_executor) + +chat_result = tool_executor.initiate_chat( + tool_caller, + message="What's the weather in Seattle?", + summary_method="reflection_with_llm", # To let the model reflect on the tool use, set to "last_msg" to return the tool call result directly. +) +print(chat_result) +``` + +In `v0.4`, you really just need one agent -- the `AssistantAgent` -- to handle +both the tool calling and tool execution. + +```python +import asyncio +from autogen_core import CancellationToken +from autogen_ext.models.openai import OpenAIChatCompletionClient +from autogen_agentchat.agents import AssistantAgent + +def get_weather(city: str) -> str: # Async tool is possible too. + return f"The weather in {city} is 72 degree and sunny." + +async def main() -> None: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant. You can call tools to help user.", + model_client=model_client, + tools=[get_weather], + reflect_on_tool_use=False, # Set to True to have the model reflect on the tool use. + ) + response = await assistant.on_messages([TextMessage(content="What's the weather in Seattle?", source="user")], CancellationToken()) + print(response) + +asyncio.run(main()) +``` + ## Group Chat ## Group Chat with Resume From 9472701b03fa897d047684966ac872e4f96d8d71 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sat, 21 Dec 2024 12:30:12 -0800 Subject: [PATCH 08/38] Finished draft --- python/migration_guide/README.md | 519 ++++++++++++++++++++++++++++++- 1 file changed, 503 insertions(+), 16 deletions(-) diff --git a/python/migration_guide/README.md b/python/migration_guide/README.md index 95e7daa5d8e4..6c11737b7b24 100644 --- a/python/migration_guide/README.md +++ b/python/migration_guide/README.md @@ -7,6 +7,25 @@ to the `v0.4` version, which introduces a new set of APIs and features. We will still maintain the `v0.2` version in the `0.2` branch, however, we highly recommend you to upgrade to the `v0.4` version. +## What is `v0.4`? + +Since the release of AutoGen in 2023, we have intensively listened to our community and +users from small startups and large enterprise, and gathered many feedbacks. +Based on those feedbacks, we built AutoGen `v0.4`, a from-the-ground-up rewrite, +adopting an asynchronous, event-driven architecture to address issues +such as observability, flexibility, interactive control, and scale. + +The `v0.4` API is layered: +the [Core API](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/index.html) +is the foundation layer offering a scalable, event-driven +actor framework for creating agentic workflows; +the [AgentChat API](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/index.html) +is built on Core, offering a task-driven, high-level framework +for building interactive agentic applications. It is a replacement of AutoGen `v0.2`. + +Most of this guide focuses on `v0.4`'s AgentChat API, however, +you can also build your own high-level framework using just the Core API. + ## What's in this guide? We provide a detailed guide on how to migrate your existing codebase from `v0.2` to `v0.4`. @@ -38,6 +57,8 @@ will be providied in the future releases of `v0.4.*` versions: - Teacheable Agent - RAG Agent +We will update this guide when the missing features become available. + ## Model Client In `v0.2` you configure the model client as follows, and create the `OpenAIWrapper` object. @@ -186,7 +207,9 @@ The `CancellationToken` can be used to cancel the request asynchronously when you call `cancellation_token.cancel()`, which will cause the `await` on the `on_messages` call to raise a `CancelledError`. -Read more on [Assistant Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.AssistantAgent). +Read more on [Agent Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/agents.html) +and +[Assistant Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.AssistantAgent). ## Multi-Modal Agent @@ -293,22 +316,30 @@ conversable_agent.register_reply([ConversableAgent], reply_func, position=0) Rather than guessing what the `reply_func` does, all its parameters, and what the `position` should be, in `v0.4`, we can simply create a custom agent -and implement the `on_messages` method. +and implement the `on_messages`, `on_reset`, and `produced_message_types` methods. ```python from typing import Sequence from autogen_core import CancellationToken from autogen_agentchat.agents import BaseChatAgent -from autogen_agentchat.messages import TextMessage +from autogen_agentchat.messages import TextMessage, ChatMessage from autogen_agentchat.base import Response class CustomAgent(BaseChatAgent): async def on_messages(self, messages: Sequence[TextMessage], cancellation_token: CancellationToken) -> Response: - # Custom reply logic here return Response(chat_message=TextMessage(content="Custom reply", source=self.name)) + + async def on_reset(self, cancellation_token: CancellationToken) -> None: + pass + + @property + def produced_message_types(self) -> Sequence[type[ChatMessage]]: + return [TextMessage] ``` You can then use the custom agent in the same way as the `AssistantAgent`. +See [Custom Agent Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/custom-agents.html) +for more details. ## Two-Agent Chat @@ -403,13 +434,13 @@ tool_caller = AssistantAgent( name="tool_caller", system_message="You are a helpful assistant. You can call tools to help user.", llm_config=llm_config, + max_consecutive_auto_reply=1, # Set to 1 so that we return to the application after each assistant reply as we are building a chatbot. ) tool_executor = UserProxyAgent( name="tool_executor", human_input_mode="NEVER", code_execution_config=False, - max_consecutive_auto_reply=1, # Only one or a batch of tool calls at a time. llm_config=False, ) @@ -419,12 +450,16 @@ def get_weather(city: str) -> str: # Register the tool function to the tool caller and executor. register_function(get_weather, caller=tool_caller, executor=tool_executor) -chat_result = tool_executor.initiate_chat( - tool_caller, - message="What's the weather in Seattle?", - summary_method="reflection_with_llm", # To let the model reflect on the tool use, set to "last_msg" to return the tool call result directly. -) -print(chat_result) +while True: + user_input = input("User: ") + if user_input == "exit": + break + chat_result = tool_executor.initiate_chat( + tool_caller, + message=user_input, + summary_method="reflection_with_llm", # To let the model reflect on the tool use, set to "last_msg" to return the tool call result directly. + ) + print("Assistant:", chat_result.summary) ``` In `v0.4`, you really just need one agent -- the `AssistantAgent` -- to handle @@ -446,28 +481,480 @@ async def main() -> None: system_message="You are a helpful assistant. You can call tools to help user.", model_client=model_client, tools=[get_weather], - reflect_on_tool_use=False, # Set to True to have the model reflect on the tool use. + reflect_on_tool_use=True, # Set to True to have the model reflect on the tool use, set to False to return the tool call result directly. ) - response = await assistant.on_messages([TextMessage(content="What's the weather in Seattle?", source="user")], CancellationToken()) - print(response) + while True: + user_input = input("User: ") + if user_input == "exit": + break + response = await assistant.on_messages([TextMessage(content=user_input, source="user")], CancellationToken()) + print("Assistant:", response.chat_message.content) asyncio.run(main()) ``` ## Group Chat +In `v0.2`, you need to create a `GroupChat` dataclass and pass it into a +`GroupChatManager`, and have a participant that is a user proxy to initiate the chat. +For a simple scenario of a writer and a critic, you can do the following: + +```python +from autogen.agentchat import AssistantAgent, GroupChat, GroupChatManager + +llm_config = { + "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], + "seed": 42, + "temperature": 0, +} + +writer = AssistantAgent( + name="writer", + description="A writer.", + system_message="You are a writer.", + llm_config=llm_config, +) + +critic = AssistantAgent( + name="critic", + description="A critic.", + system_message="You are a critic, provide feedback on the writing. Reply only 'APPROVE' if the task is done.", + llm_config=llm_config, +) + +# Create a group chat with the writer and critic. +groupchat = GroupChat(agents=[writer, critic], messages=[], max_round=12) + +# Create a group chat manager to manage the group chat, use round-robin selection method. +manager = GroupChatManager(groupchat=groupchat, llm_config=llm_config, speaker_selection_method="round_robin") + +# Initiate the chat with the editor, intermediate messages are printed to the console directly. +result = editor.initiate_chat( + manager, + message="Write a short story about a robot that discovers it has feelings.", +) +print(result.summary) +``` + +In `v0.4`, you can use the `RoundRobinGroupChat` to achieve the same behavior. + +```python +from asyncio +from autogen_agentchat.agents import AssistantAgent +from autogen_agentchat.teams import RoundRobinGroupChat +from autogen_agentchat.conditions import TextTermination +from autogen_ext.models.openai import OpenAIChatCompletionClient + +async def main() -> None: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + + writer = AssistantAgent( + name="writer", + description="A writer.", + system_message="You are a writer.", + model_client=model_client, + ) + + critic = AssistantAgent( + name="critic", + description="A critic.", + system_message="You are a critic, provide feedback on the writing. Reply only 'APPROVE' if the task is done.", + model_client=model_client, + ) + + # The termination condition is a text termination, which will cause the chat to terminate when the text "APPROVE" is received. + termination = TextTermination("APPROVE") + + # The group chat will alternate between the writer and the critic. + group_chat = RoundRobinGroupChat([writer, critic], termination_condition=termination) + + # `run_stream` returns an async generator to stream the intermediate messages. + stream = group_chat.run_stream(task="Write a short story about a robot that discovers it has feelings.") + # `Console` is a simple UI to display the stream. + await Console(stream) + +asyncio.run(main()) +``` + +For LLM-based speaker selection, you can use the `SelectorGroupChat` instead. +See [Selector Group Chat Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/selector-group-chat.html) +and +[Selector Group Chat Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.teams.html#autogen_agentchat.teams.SelectorGroupChat) +for more details. + +> **Note**: In `v0.4`, you do not need to register functions on a user proxy to use tools +> in a group chat. You can simply pass the tool functions to the `AssistantAgent` as shown in the [Tool Use](#tool-use) section. +> The agent will automatically call the tools when needed. +> If your tool doesn't output well formed response, you can use the `reflect_on_tool_use` parameter to have the model reflect on the tool use. + ## Group Chat with Resume +In `v0.2`, group chat with resume is a bit complicated. You need to explicitly +save the group chat messages and load them back when you want to resume the chat. +See [Resuming Group Chat in v0.2](https://microsoft.github.io/autogen/0.2/docs/topics/groupchat/resuming_groupchat) for more details. + +In `v0.4`, you can simply call `run` or `run_stream` again with the same group chat object to resume the chat. To export and load the state, you can use +`save_state` and `load_state` methods. + +```python +from asyncio +import json +from autogen_agentchat.agents import AssistantAgent +from autogen_agentchat.teams import RoundRobinGroupChat +from autogen_agentchat.conditions import TextTermination +from autogen_ext.models.openai import OpenAIChatCompletionClient + +def create_team() -> RoundRobinGroupChat: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + + writer = AssistantAgent( + name="writer", + description="A writer.", + system_message="You are a writer.", + model_client=model_client, + ) + + critic = AssistantAgent( + name="critic", + description="A critic.", + system_message="You are a critic, provide feedback on the writing. Reply only 'APPROVE' if the task is done.", + model_client=model_client, + ) + + # The termination condition is a text termination, which will cause the chat to terminate when the text "APPROVE" is received. + termination = TextTermination("APPROVE") + + # The group chat will alternate between the writer and the critic. + group_chat = RoundRobinGroupChat([writer, critic], termination_condition=termination) + + return group_chat + + +async def main() -> None: + # Create team. + group_chat = create_team() + + # `run_stream` returns an async generator to stream the intermediate messages. + stream = group_chat.run_stream(task="Write a short story about a robot that discovers it has feelings.") + # `Console` is a simple UI to display the stream. + await Console(stream) + + # Save the state of the group chat and all participants. + state = await group_chat.save_state() + with open("group_chat_state.json", "w") as f: + json.dump(state, f) + + # Create a new team with the same participants configuration. + group_chat = create_team() + + # Load the state of the group chat and all participants. + with open("group_chat_state.json", "r") as f: + state = json.load(f) + await group_chat.load_state(state) + + # Resume the chat. + stream = group_chat.run_stream(task="Translate the story into Chinese.") + await Console(stream) + +asyncio.run(main()) +``` + ## Group Chat with Custom Selector (Stateflow) +In `v0.2` group chat, when the `speaker_selection_method` is set to a custom function, +it can override the default selection method. This is useful for implementing +a state-based selection method. +For more details, see [Custom Sepaker Selection in v0.2](https://microsoft.github.io/autogen/0.2/docs/topics/groupchat/customized_speaker_selection). + +In `v0.4`, you can use the `SelectorGroupChat` with `selector_func` to achieve the same behavior. +The `selector_func` is a function that takes the current message thread of the group chat +and returns the next speaker's name. If `None` is returned, the LLM-based +selection method will be used. + +Here is an example of using the state-based selection method to implement +a web search/analysis scenario. + +```python +from typing import Sequence + +from autogen_agentchat.agents import AssistantAgent +from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination +from autogen_agentchat.messages import AgentEvent, ChatMessage +from autogen_agentchat.teams import SelectorGroupChat +from autogen_agentchat.ui import Console +from autogen_ext.models.openai import OpenAIChatCompletionClient + +# Note: This example uses mock tools instead of real APIs for demonstration purposes +def search_web_tool(query: str) -> str: + if "2006-2007" in query: + return """Here are the total points scored by Miami Heat players in the 2006-2007 season: + Udonis Haslem: 844 points + Dwayne Wade: 1397 points + James Posey: 550 points + ... + """ + elif "2007-2008" in query: + return "The number of total rebounds for Dwayne Wade in the Miami Heat season 2007-2008 is 214." + elif "2008-2009" in query: + return "The number of total rebounds for Dwayne Wade in the Miami Heat season 2008-2009 is 398." + return "No data found." + + +def percentage_change_tool(start: float, end: float) -> float: + return ((end - start) / start) * 100 + +def create_team() -> SelectorGroupChat: + model_client = OpenAIChatCompletionClient(model="gpt-4o") + + planning_agent = AssistantAgent( + "PlanningAgent", + description="An agent for planning tasks, this agent should be the first to engage when given a new task.", + model_client=model_client, + system_message=""" + You are a planning agent. + Your job is to break down complex tasks into smaller, manageable subtasks. + Your team members are: + Web search agent: Searches for information + Data analyst: Performs calculations + + You only plan and delegate tasks - you do not execute them yourself. + + When assigning tasks, use this format: + 1. : + + After all tasks are complete, summarize the findings and end with "TERMINATE". + """, + ) + + web_search_agent = AssistantAgent( + "WebSearchAgent", + description="A web search agent.", + tools=[search_web_tool], + model_client=model_client, + system_message=""" + You are a web search agent. + Your only tool is search_tool - use it to find information. + You make only one search call at a time. + Once you have the results, you never do calculations based on them. + """, + ) + + data_analyst_agent = AssistantAgent( + "DataAnalystAgent", + description="A data analyst agent. Useful for performing calculations.", + model_client=model_client, + tools=[percentage_change_tool], + system_message=""" + You are a data analyst. + Given the tasks you have been assigned, you should analyze the data and provide results using the tools provided. + """, + ) + + # The termination condition is a combination of text mention termination and max message termination. + text_mention_termination = TextMentionTermination("TERMINATE") + max_messages_termination = MaxMessageTermination(max_messages=25) + termination = text_mention_termination | max_messages_termination + + # The selector function is a function that takes the current message thread of the group chat + # and returns the next speaker's name. If None is returned, the LLM-based selection method will be used. + def selector_func(messages: Sequence[AgentEvent | ChatMessage]) -> str | None: + if messages[-1].source != planning_agent.name: + return planning_agent.name # Always return to the planning agent after the other agents have spoken. + return None + + team = SelectorGroupChat( + [planning_agent, web_search_agent, data_analyst_agent], + model_client=OpenAIChatCompletionClient(model="gpt-4o-mini"), # Use a smaller model for the selector. + termination_condition=termination, + selector_func=selector_func, + ) + return team + +async def main() -> None: + team = create_team() + task = "Who was the Miami Heat player with the highest points in the 2006-2007 season, and what was the percentage change in his total rebounds between the 2007-2008 and 2008-2009 seasons?" + await Console(team.run_stream(task=task)) + +asyncio.run(main()) +``` + ## Nested Chat +Nested chat allows you to nest a whole team or another agent inside +an agent. This is useful for creating a hierarchical structure of agents +or "information silos", as the nested agents cannot communicate directly +with other agents outside of the same group. + +In `v0.2`, nested chat is supported by using the `register_nested_chats` method +on the `ConversableAgent` class. +You need to specify the nested sequence of agents using dictionaries, +See [Nested Chat in v0.2](https://microsoft.github.io/autogen/0.2/docs/tutorial/conversation-patterns#nested-chats) +for more details. + +In `v0.4`, nested chat is an implementation detail of a custom agent. +You can create a custom agent that takes a team or another agent as a parameter +and implements the `on_messages` method to trigger the nested team or agent. +It is up to the application to decide how to pass or transform the messages from +and to the nested team or agent. + +The following example shows a simple nested chat that counts numbers. + +```python +from typing import Sequence +from autogen_core import CancellationToken +from autogen_agentchat.agents import BaseChatAgent +from autogen_agentchat.teams import RoundRobinGroupChat +from autogen_agentchat.messages import TextMessage, ChatMessage +from autogen_agentchat.base import Response + +class CountingAgent(BaseChatAgent): + async def on_messages(self, messages: Sequence[TextMessage], cancellation_token: CancellationToken) -> Response: + if len(messages) == 0: + last_number = 0 # Start from 0 if no messages are given. + else: + last_number = int(messages[-1].content) # Otherwise, start from the last number. + return Response(chat_message=TextMessage(content=str(last_number + 1), source=self.name)) + + async def on_reset(self, cancellation_token: CancellationToken) -> None: + pass + + @property + def produced_message_types(self) -> Sequence[type[ChatMessage]]: + return [TextMessage] + +class NestedCountingAgent(BaseChatAgent): + def __init__(self, name: str, counting_team: RoundRobinGroupChat) -> None: + super().__init__(name, description="An agent that counts numbers.") + self._counting_team = counting_team + + async def on_messages(self, messages: Sequence[TextMessage], cancellation_token: CancellationToken) -> Response: + # Run the inner team with the given messages and returns the last message produced by the team. + result = await self._counting_team.run(task=messages, cancellation_token=cancellation_token) + # To stream the inner messages, implement `on_messages_stream` and use that to implement `on_messages`. + return Response(chat_message=result.messages[-1], inner_messages=result.messages[len(messages):-1]) + + async def on_reset(self, cancellation_token: CancellationToken) -> None: + # Reset the inner team. + await self._counting_team.reset(cancellation_token) + + @property + def produced_message_types(self) -> Sequence[type[ChatMessage]]: + return [TextMessage] + +async def main() -> None: + # Create a team of two counting agents as the inner team. + counting_team = RoundRobinGroupChat([CountingAgent("counting_agent_1"), CountingAgent("counting_agent_2")]) + # Create a nested counting agent that takes the inner team as a parameter. + nested_counting_agent = NestedCountingAgent("nested_counting_agent", counting_team) + response = await nested_counting_agent.on_messages([TextMessage(content="1", source="user")], CancellationToken()) + print(response.inner_messages) + print(response.chat_message) +``` + +You can take a look at [Society of Mind Agent (Experimental)](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.SocietyOfMindAgent) for a more complex implementation. + ## Sequential Chat +In `v0.2`, sequential chat is supported by using the `initiate_chats` function. +It takes input a list of dictionary configurations for each step of the sequence. +See [Sequential Chat in v0.2](https://microsoft.github.io/autogen/0.2/docs/tutorial/conversation-patterns#sequential-chats) +for more details. + +Base on the feedback from the community, the `initiate_chats` function +is too opinionated and not flexible enough to support the diverse set of scenarios that +users want to implement. We often find users struggling to get the `initiate_chats` function +to work when they can easily glue the steps together usign basic Python code. +Therefore, in `v0.4`, we do not provide a built-in function for sequential chat in the AgentChat API. + +Instead, you can create an event-driven sequential workflow using the Core API, +and use the other components provided the AgentChat API to implement each step of the workflow. +See an example of sequential workflow in the [Core API Tutorial](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/design-patterns/sequential-workflow.html). + +We recognize that the concept of workflow is at the heart of many applications, +and we will provide more built-in support for workflows in the future. + ## GPTAssistantAgent -## Long-Context Handling +In `v0.2`, `GPTAssistantAgent` is a special agent class that is backed by the OpenAI Assistant API. + +In `v0.4`, the equivalent is the `OpenAIAssistantAgent` class. +It supports the same set of features as the `GPTAssistantAgent` in `v0.2` with +more such as customizable threads and file uploads. +See [OpenAI Assistant Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_ext.agents.openai.html#autogen_ext.agents.openai.OpenAIAssistantAgent) for more details. + +## Long Context Handling + +In `v0.2`, long context that overflows the model's context window can be handled +by using the `transforms` capability that is added to an `ConversableAgent` +after which is contructed. + +The feedbacks from our community has led us to believe this feature is essential +and should be a built-in component of `AssistantAgent`, and can be used for +every custom agent. + +In `v0.4`, we introduce the `ChatCompletionContext` base class that manages +message history and provides a virtual view of the history. Applications can use +built-in implementations such as `BufferedChatCompletionContext` to +limit the message history sent to the model, or provide their own implementations +that creates different virtual views. + +To use `BufferedChatCompletionContext` in an `AssistantAgent` in a chatbot scenario. + +```python +import asyncio +from autogen_agentchat.messages import TextMessage +from autogen_agentchat.agents import AssistantAgent +from autogen_core import CancellationToken +from autogen_core.model_context import BufferedChatCompletionContext +from autogen_ext.models.openai import OpenAIChatCompletionClient + +async def main() -> None: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + + assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant.", + model_client=model_client, + model_context=BufferedChatCompletionContext(buffer_size=10), # Model can only view the last 10 messages. + ) + while True: + user_input = input("User: ") + if user_input == "exit": + break + response = await assistant.on_messages([TextMessage(content=user_input, source="user")], CancellationToken()) + print("Assistant:", response.chat_message.content) + +asyncio.run(main()) +``` + +In this example, the chatbot can only read the last 10 messages in the history. -## Observability +## Observability and Control + +In `v0.4` AgentChat, you can observe the agents by using the `on_messages_stream` method +which returns an async generator to stream the inner thoughts and actions of the agent. +For teams, you can use the `run_stream` method to stream the inner conversation among the agents in the team. +Your application can use these streams to observe the agents and teams in real-time. + +Both the `on_messages_stream` and `run_stream` methods takes a `CancellationToken` as a parameter +which can be used to cancel the output stream asynchronously and stop the agent or team. +For teams, you can also use termination conditions to stop the team when a certain condition is met. +See [Termination Condition Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/termination.html) +for more details. + +Unlike the `v0.2` which comes with a special logging module, the `v0.4` API +simply uses Python's `logging` module to log events such as model client calls. +See [Logging](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/logging.html) +in the Core API documentation for more details. ## Code Executors + +The code executors in `v0.2` and `v0.4` are nearly identical except +the `v0.4` executors support async API. You can also use +`CancellationToken` to cancel a code execution if it takes too long. +See [Command Line Code Executors Tutorial](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/command-line-code-executors.html) +in the Core API documentation. + +We also added `AzureContainerCodeExecutor` that can use Azure Container Apps (ACA) +dynamic sessions for code execution. +See [ACA Dynamic Sessions Code Executor Docs](https://microsoft.github.io/autogen/dev/user-guide/extensions-user-guide/azure-container-code-executor.html). \ No newline at end of file From c858738a908e9b74ec28903f8173f46ea76bb107 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sat, 21 Dec 2024 12:38:46 -0800 Subject: [PATCH 09/38] add statement --- python/migration_guide/README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/python/migration_guide/README.md b/python/migration_guide/README.md index 6c11737b7b24..3926b2201a60 100644 --- a/python/migration_guide/README.md +++ b/python/migration_guide/README.md @@ -2,11 +2,14 @@ This is a migration guide for users of the `v0.2.*` versions of `autogen-agentchat` to the `v0.4` version, which introduces a new set of APIs and features. - -> **Note**: The `v0.4` version contains breaking changes. Please read this guide carefully. -We will still maintain the `v0.2` version in the `0.2` branch, however, +The `v0.4` version contains breaking changes. Please read this guide carefully. +We still maintain the `v0.2` version in the `0.2` branch, however, we highly recommend you to upgrade to the `v0.4` version. +> **Note**: We no longer have admin access to the `pyautogen` PyPI package, and +> the releases from that package is no longer from Microsoft since version 0.2.34. +> Please read our [clarification statement] regarding forks. + ## What is `v0.4`? Since the release of AutoGen in 2023, we have intensively listened to our community and From a9ff1bb35967b5823727b6f6d5c185e7ddd24513 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sat, 21 Dec 2024 12:47:46 -0800 Subject: [PATCH 10/38] Update migration guide for v0.4: refine language and clarify PyPI package ownership --- python/migration_guide/README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/python/migration_guide/README.md b/python/migration_guide/README.md index 3926b2201a60..954a4d64587c 100644 --- a/python/migration_guide/README.md +++ b/python/migration_guide/README.md @@ -1,14 +1,13 @@ # Migration Guide for v0.2 to v0.4 - This is a migration guide for users of the `v0.2.*` versions of `autogen-agentchat` to the `v0.4` version, which introduces a new set of APIs and features. The `v0.4` version contains breaking changes. Please read this guide carefully. -We still maintain the `v0.2` version in the `0.2` branch, however, -we highly recommend you to upgrade to the `v0.4` version. +We still maintain the `v0.2` version in the `0.2` branch; however, +we highly recommend you upgrade to the `v0.4` version. > **Note**: We no longer have admin access to the `pyautogen` PyPI package, and -> the releases from that package is no longer from Microsoft since version 0.2.34. -> Please read our [clarification statement] regarding forks. +> the releases from that package are no longer from Microsoft since version 0.2.34. +> Please read our [clarification statement](https://github.com/microsoft/autogen/discussions/4217) regarding forks. ## What is `v0.4`? From f2fd73083c0e8943b21362e7e9ae70c2e7737b3c Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sat, 21 Dec 2024 13:58:57 -0800 Subject: [PATCH 11/38] wip --- python/migration_guide/README.md | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/python/migration_guide/README.md b/python/migration_guide/README.md index 954a4d64587c..bb52d6cb57fa 100644 --- a/python/migration_guide/README.md +++ b/python/migration_guide/README.md @@ -11,22 +11,13 @@ we highly recommend you upgrade to the `v0.4` version. ## What is `v0.4`? -Since the release of AutoGen in 2023, we have intensively listened to our community and -users from small startups and large enterprise, and gathered many feedbacks. -Based on those feedbacks, we built AutoGen `v0.4`, a from-the-ground-up rewrite, -adopting an asynchronous, event-driven architecture to address issues -such as observability, flexibility, interactive control, and scale. - -The `v0.4` API is layered: -the [Core API](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/index.html) -is the foundation layer offering a scalable, event-driven -actor framework for creating agentic workflows; -the [AgentChat API](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/index.html) -is built on Core, offering a task-driven, high-level framework -for building interactive agentic applications. It is a replacement of AutoGen `v0.2`. - -Most of this guide focuses on `v0.4`'s AgentChat API, however, -you can also build your own high-level framework using just the Core API. +Since the release of AutoGen in 2023, we have intensively listened to our community and users from small startups and large enterprises, gathering much feedback. Based on that feedback, we built AutoGen `v0.4`, a from-the-ground-up rewrite adopting an asynchronous, event-driven architecture to address issues such as observability, flexibility, interactive control, and scale. + +The `v0.4` API is layered: +the [Core API](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/index.html) is the foundation layer offering a scalable, event-driven actor framework for creating agentic workflows; +the [AgentChat API](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/index.html) is built on Core, offering a task-driven, high-level framework for building interactive agentic applications. It is a replacement for AutoGen `v0.2`. + +Most of this guide focuses on `v0.4`'s AgentChat API; however, you can also build your own high-level framework using just the Core API. ## What's in this guide? From a5f2e8ca23f562e6cac87bfbdb1f8ca5718e8534 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sat, 21 Dec 2024 14:02:03 -0800 Subject: [PATCH 12/38] mv --- python/README.md | 4 ++++ python/{migration_guide/README.md => migration_guide.md} | 0 2 files changed, 4 insertions(+) rename python/{migration_guide/README.md => migration_guide.md} (100%) diff --git a/python/README.md b/python/README.md index 24cb7e796334..9d2446172e0c 100644 --- a/python/README.md +++ b/python/README.md @@ -5,6 +5,10 @@ This directory works as a single `uv` workspace containing all project packages. See [`packages`](./packages/) to discover all project packages. +## Migrating from 0.2.x? + +Please refer to the [migration guide](./migration_guide.md) for how to migrate your code from 0.2.x to 0.4.x. + ## Development **TL;DR**, run all checks with: diff --git a/python/migration_guide/README.md b/python/migration_guide.md similarity index 100% rename from python/migration_guide/README.md rename to python/migration_guide.md From e2c01319b241bd205e56e7df6b4b5b9f39dc2a54 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sat, 21 Dec 2024 14:04:13 -0800 Subject: [PATCH 13/38] Update --- python/migration_guide.md | 1 + 1 file changed, 1 insertion(+) diff --git a/python/migration_guide.md b/python/migration_guide.md index bb52d6cb57fa..3d4f43e32db7 100644 --- a/python/migration_guide.md +++ b/python/migration_guide.md @@ -7,6 +7,7 @@ we highly recommend you upgrade to the `v0.4` version. > **Note**: We no longer have admin access to the `pyautogen` PyPI package, and > the releases from that package are no longer from Microsoft since version 0.2.34. +> To continue use the `v0.2` version of the API, install it using `autogen-agentchat~=0.2`. > Please read our [clarification statement](https://github.com/microsoft/autogen/discussions/4217) regarding forks. ## What is `v0.4`? From 21117e3b6a90998f6e70507f449b99c8f771dbed Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sat, 21 Dec 2024 14:04:39 -0800 Subject: [PATCH 14/38] wip --- python/migration_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/migration_guide.md b/python/migration_guide.md index 3d4f43e32db7..f3692f132b92 100644 --- a/python/migration_guide.md +++ b/python/migration_guide.md @@ -7,7 +7,7 @@ we highly recommend you upgrade to the `v0.4` version. > **Note**: We no longer have admin access to the `pyautogen` PyPI package, and > the releases from that package are no longer from Microsoft since version 0.2.34. -> To continue use the `v0.2` version of the API, install it using `autogen-agentchat~=0.2`. +> To continue use the `v0.2` version of AutoGen, install it using `autogen-agentchat~=0.2`. > Please read our [clarification statement](https://github.com/microsoft/autogen/discussions/4217) regarding forks. ## What is `v0.4`? From 1d1f315e162db7275857bf5873b8957177a209b3 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sat, 21 Dec 2024 15:31:34 -0800 Subject: [PATCH 15/38] Add lint for markdown files; fix lint errors --- .github/workflows/checks.yml | 18 ++++++++ python/check_md_code_blocks.py | 82 ++++++++++++++++++++++++++++++++++ python/migration_guide.md | 49 +++++++++++--------- python/pyproject.toml | 1 + 4 files changed, 129 insertions(+), 21 deletions(-) create mode 100644 python/check_md_code_blocks.py diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 703c07494251..87313b6e667a 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -173,6 +173,24 @@ jobs: source ${{ github.workspace }}/python/.venv/bin/activate poe --directory ${{ matrix.package }} docs-check-examples working-directory: ./python + +markdown-code-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + - run: uv sync --locked --all-extras + working-directory: ./python + - name: Run task + run: | + source ${{ github.workspace }}/python/.venv/bin/activate + poe markdown-code-lint + working-directory: ./python check-proto-changes-python: runs-on: ubuntu-latest diff --git a/python/check_md_code_blocks.py b/python/check_md_code_blocks.py new file mode 100644 index 000000000000..0a408a56615a --- /dev/null +++ b/python/check_md_code_blocks.py @@ -0,0 +1,82 @@ +"""Check code blocks in Markdown files for syntax errors.""" + +import argparse +import logging +import tempfile +from typing import List, Tuple + +from pygments import highlight # type: ignore +from pygments.formatters import TerminalFormatter +from pygments.lexers import PythonLexer +from sphinx.util.console import darkgreen, darkred, faint, red, teal # type: ignore[attr-defined] + +logger = logging.getLogger(__name__) +logger.addHandler(logging.StreamHandler()) +logger.setLevel(logging.INFO) + +def extract_python_code_blocks(markdown_file_path: str) -> List[Tuple[str, int]]: + """Extract Python code blocks from a Markdown file.""" + with open(markdown_file_path, "r", encoding="utf-8") as file: + lines = file.readlines() + + code_blocks: List[Tuple[str, int]] = [] + in_code_block = False + current_block : List[str] = [] + + for i, line in enumerate(lines): + if line.strip().startswith("```python"): + in_code_block = True + current_block = [] + elif line.strip().startswith("```"): + in_code_block = False + code_blocks.append(("\n".join(current_block), i - len(current_block) + 1)) + elif in_code_block: + current_block.append(line) + + return code_blocks + +def check_code_blocks(markdown_file_paths: List[str]) -> None: + """Check Python code blocks in a Markdown file for syntax errors.""" + files_with_errors = [] + + for markdown_file_path in markdown_file_paths: + code_blocks = extract_python_code_blocks(markdown_file_path) + had_errors = False + for code_block, line_no in code_blocks: + markdown_file_path_with_line_no = f"{markdown_file_path}:{line_no}" + logger.info("Checking a code block in %s...", markdown_file_path_with_line_no) + + # Skip blocks that don't import autogen_agentchat, autogen_core, or autogen_ext + if all(all(import_code not in code_block for import_code in [f"import {module}", f"from {module}"]) for module in ["autogen_agentchat", "autogen_core", "autogen_ext"]): + logger.info(" " + darkgreen("OK[ignored]")) + continue + + with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as temp_file: + temp_file.write(code_block.encode("utf-8")) + temp_file.flush() + + # Run pyright on the temporary file using subprocess.run + import subprocess + + result = subprocess.run(["pyright", temp_file.name], capture_output=True, text=True) + if result.returncode != 0: + logger.info(" " + darkred("FAIL")) + highlighted_code = highlight(code_block, PythonLexer(), TerminalFormatter()) # type: ignore + output = f"{faint('========================================================')}\n{red('Error')}: Pyright found issues in {teal(markdown_file_path_with_line_no)}:\n{faint('--------------------------------------------------------')}\n{highlighted_code}\n{faint('--------------------------------------------------------')}\n\n{teal('pyright output:')}\n{red(result.stdout)}{faint('========================================================')}\n" + logger.info(output) + had_errors = True + else: + logger.info(" " + darkgreen("OK")) + + if had_errors: + files_with_errors.append(markdown_file_path) + + if files_with_errors: + raise RuntimeError(f"Syntax errors found in the following files: {'\n'.join(files_with_errors)}\n") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Check code blocks in Markdown files for syntax errors.") + # Argument is a list of markdown files containing glob patterns + parser.add_argument("markdown_files", nargs="+", help="Markdown files to check.") + args = parser.parse_args() + check_code_blocks(args.markdown_files) diff --git a/python/migration_guide.md b/python/migration_guide.md index f3692f132b92..e552650f6cd5 100644 --- a/python/migration_guide.md +++ b/python/migration_guide.md @@ -313,21 +313,21 @@ and what the `position` should be, in `v0.4`, we can simply create a custom agen and implement the `on_messages`, `on_reset`, and `produced_message_types` methods. ```python -from typing import Sequence +from typing import Sequence, List from autogen_core import CancellationToken from autogen_agentchat.agents import BaseChatAgent from autogen_agentchat.messages import TextMessage, ChatMessage from autogen_agentchat.base import Response class CustomAgent(BaseChatAgent): - async def on_messages(self, messages: Sequence[TextMessage], cancellation_token: CancellationToken) -> Response: + async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> Response: return Response(chat_message=TextMessage(content="Custom reply", source=self.name)) async def on_reset(self, cancellation_token: CancellationToken) -> None: pass @property - def produced_message_types(self) -> Sequence[type[ChatMessage]]: + def produced_message_types(self) -> List[type[ChatMessage]]: return [TextMessage] ``` @@ -377,7 +377,7 @@ and `CodeExecutorAgent` together in a `RoundRobinGroupChat`. import asyncio from autogen_agentchat.agents import AssistantAgent, CodeExecutorAgent from autogen_agentchat.teams import RoundRobinGroupChat -from autogen_agentchat.conditions import TextTermination, MaxMessageTermination +from autogen_agentchat.conditions import TextMentionTermination, MaxMessageTermination from autogen_agentchat.ui import Console from autogen_ext.code_executors.local import LocalCommandLineCodeExecutor from autogen_ext.models.openai import OpenAIChatCompletionClient @@ -397,7 +397,7 @@ async def main() -> None: ) # The termination condition is a combination of text termination and max message termination, either of which will cause the chat to terminate. - termination = TextTermination("TERMINATE") | MaxMessageTermination(10) + termination = TextMentionTermination("TERMINATE") | MaxMessageTermination(10) # The group chat will alternate between the assistant and the code executor. group_chat = RoundRobinGroupChat([assistant, code_executor], termination_condition=termination) @@ -464,6 +464,7 @@ import asyncio from autogen_core import CancellationToken from autogen_ext.models.openai import OpenAIChatCompletionClient from autogen_agentchat.agents import AssistantAgent +from autogen_agentchat.messages import TextMessage def get_weather(city: str) -> str: # Async tool is possible too. return f"The weather in {city} is 72 degree and sunny." @@ -533,10 +534,11 @@ print(result.summary) In `v0.4`, you can use the `RoundRobinGroupChat` to achieve the same behavior. ```python -from asyncio +import asyncio from autogen_agentchat.agents import AssistantAgent from autogen_agentchat.teams import RoundRobinGroupChat -from autogen_agentchat.conditions import TextTermination +from autogen_agentchat.conditions import TextMentionTermination +from autogen_agentchat.ui import Console from autogen_ext.models.openai import OpenAIChatCompletionClient async def main() -> None: @@ -557,7 +559,7 @@ async def main() -> None: ) # The termination condition is a text termination, which will cause the chat to terminate when the text "APPROVE" is received. - termination = TextTermination("APPROVE") + termination = TextMentionTermination("APPROVE") # The group chat will alternate between the writer and the critic. group_chat = RoundRobinGroupChat([writer, critic], termination_condition=termination) @@ -591,11 +593,12 @@ In `v0.4`, you can simply call `run` or `run_stream` again with the same group c `save_state` and `load_state` methods. ```python -from asyncio +import asyncio import json from autogen_agentchat.agents import AssistantAgent from autogen_agentchat.teams import RoundRobinGroupChat -from autogen_agentchat.conditions import TextTermination +from autogen_agentchat.conditions import TextMentionTermination +from autogen_agentchat.ui import Console from autogen_ext.models.openai import OpenAIChatCompletionClient def create_team() -> RoundRobinGroupChat: @@ -616,7 +619,7 @@ def create_team() -> RoundRobinGroupChat: ) # The termination condition is a text termination, which will cause the chat to terminate when the text "APPROVE" is received. - termination = TextTermination("APPROVE") + termination = TextMentionTermination("APPROVE") # The group chat will alternate between the writer and the critic. group_chat = RoundRobinGroupChat([writer, critic], termination_condition=termination) @@ -669,8 +672,8 @@ Here is an example of using the state-based selection method to implement a web search/analysis scenario. ```python +import asyncio from typing import Sequence - from autogen_agentchat.agents import AssistantAgent from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination from autogen_agentchat.messages import AgentEvent, ChatMessage @@ -794,7 +797,7 @@ and to the nested team or agent. The following example shows a simple nested chat that counts numbers. ```python -from typing import Sequence +from typing import Sequence, List from autogen_core import CancellationToken from autogen_agentchat.agents import BaseChatAgent from autogen_agentchat.teams import RoundRobinGroupChat @@ -802,10 +805,11 @@ from autogen_agentchat.messages import TextMessage, ChatMessage from autogen_agentchat.base import Response class CountingAgent(BaseChatAgent): - async def on_messages(self, messages: Sequence[TextMessage], cancellation_token: CancellationToken) -> Response: + async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> Response: if len(messages) == 0: last_number = 0 # Start from 0 if no messages are given. else: + assert isinstance(messages[-1], TextMessage) last_number = int(messages[-1].content) # Otherwise, start from the last number. return Response(chat_message=TextMessage(content=str(last_number + 1), source=self.name)) @@ -813,7 +817,7 @@ class CountingAgent(BaseChatAgent): pass @property - def produced_message_types(self) -> Sequence[type[ChatMessage]]: + def produced_message_types(self) -> List[type[ChatMessage]]: return [TextMessage] class NestedCountingAgent(BaseChatAgent): @@ -821,23 +825,26 @@ class NestedCountingAgent(BaseChatAgent): super().__init__(name, description="An agent that counts numbers.") self._counting_team = counting_team - async def on_messages(self, messages: Sequence[TextMessage], cancellation_token: CancellationToken) -> Response: + async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> Response: # Run the inner team with the given messages and returns the last message produced by the team. - result = await self._counting_team.run(task=messages, cancellation_token=cancellation_token) + result = await self._counting_team.run(task=list(messages), cancellation_token=cancellation_token) # To stream the inner messages, implement `on_messages_stream` and use that to implement `on_messages`. - return Response(chat_message=result.messages[-1], inner_messages=result.messages[len(messages):-1]) + assert isinstance(result.messages[-1], TextMessage) + return Response(chat_message=result.messages[-1], inner_messages=list(result.messages[len(messages):-1])) async def on_reset(self, cancellation_token: CancellationToken) -> None: # Reset the inner team. - await self._counting_team.reset(cancellation_token) + await self._counting_team.reset() @property - def produced_message_types(self) -> Sequence[type[ChatMessage]]: + def produced_message_types(self) -> List[type[ChatMessage]]: return [TextMessage] async def main() -> None: # Create a team of two counting agents as the inner team. - counting_team = RoundRobinGroupChat([CountingAgent("counting_agent_1"), CountingAgent("counting_agent_2")]) + counting_agent_1 = CountingAgent("counting_agent_1", description="An agent that counts numbers.") + counting_agent_2 = CountingAgent("counting_agent_2", description="An agent that counts numbers.") + counting_team = RoundRobinGroupChat([counting_agent_1, counting_agent_2], max_turns=5) # Create a nested counting agent that takes the inner team as a parameter. nested_counting_agent = NestedCountingAgent("nested_counting_agent", counting_team) response = await nested_counting_agent.on_messages([TextMessage(content="1", source="user")], CancellationToken()) diff --git a/python/pyproject.toml b/python/pyproject.toml index 06f42ac69e54..ad535728c584 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -82,6 +82,7 @@ gen-proto = "python -m grpc_tools.protoc --python_out=./packages/autogen-ext/src gen-proto-samples = "python -m grpc_tools.protoc --python_out=./packages/autogen-core/samples/protos --grpc_python_out=./packages/autogen-core/samples/protos --mypy_out=./packages/autogen-core/samples/protos --mypy_grpc_out=./packages/autogen-core/samples/protos --proto_path ../protos/ agent_events.proto" +markdown-code-lint = "python check_md_code_blocks.py migration_guide.md ../README.md" teasdst = { cmd = "sphinx-build docs/src docs/build" } From d5d817fb401c2ea352fc58e6490aef209845fe7e Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sat, 21 Dec 2024 15:35:26 -0800 Subject: [PATCH 16/38] Fix indent --- .github/workflows/checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 87313b6e667a..eb6ae5319613 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -174,7 +174,7 @@ jobs: poe --directory ${{ matrix.package }} docs-check-examples working-directory: ./python -markdown-code-lint: + markdown-code-lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From d7dafd87a71576c263fa373590f3e87d5ec5e33a Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sat, 21 Dec 2024 15:39:45 -0800 Subject: [PATCH 17/38] Fix syntax --- python/check_md_code_blocks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/check_md_code_blocks.py b/python/check_md_code_blocks.py index 0a408a56615a..5f44e2c87940 100644 --- a/python/check_md_code_blocks.py +++ b/python/check_md_code_blocks.py @@ -72,7 +72,7 @@ def check_code_blocks(markdown_file_paths: List[str]) -> None: files_with_errors.append(markdown_file_path) if files_with_errors: - raise RuntimeError(f"Syntax errors found in the following files: {'\n'.join(files_with_errors)}\n") + raise RuntimeError(f"Syntax errors found in the following files: {'\n'.join(files_with_errors)}") if __name__ == "__main__": parser = argparse.ArgumentParser(description="Check code blocks in Markdown files for syntax errors.") From a860d754e97c7cb0f43d68faed8bc5bccf68594c Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sat, 21 Dec 2024 15:43:30 -0800 Subject: [PATCH 18/38] Fix --- python/check_md_code_blocks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/check_md_code_blocks.py b/python/check_md_code_blocks.py index 5f44e2c87940..2c8fda5d35fc 100644 --- a/python/check_md_code_blocks.py +++ b/python/check_md_code_blocks.py @@ -72,7 +72,7 @@ def check_code_blocks(markdown_file_paths: List[str]) -> None: files_with_errors.append(markdown_file_path) if files_with_errors: - raise RuntimeError(f"Syntax errors found in the following files: {'\n'.join(files_with_errors)}") + raise RuntimeError("Syntax errors found in the following files:\n" + "\n".join(files_with_errors)) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Check code blocks in Markdown files for syntax errors.") From e6e433ff8d47f31cb9ca3b911bfaaedfeff1a957 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sat, 21 Dec 2024 15:47:57 -0800 Subject: [PATCH 19/38] Update python/check_md_code_blocks.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- python/check_md_code_blocks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/check_md_code_blocks.py b/python/check_md_code_blocks.py index 2c8fda5d35fc..e4e7838b3c52 100644 --- a/python/check_md_code_blocks.py +++ b/python/check_md_code_blocks.py @@ -21,7 +21,7 @@ def extract_python_code_blocks(markdown_file_path: str) -> List[Tuple[str, int]] code_blocks: List[Tuple[str, int]] = [] in_code_block = False - current_block : List[str] = [] + current_block: List[str] = [] for i, line in enumerate(lines): if line.strip().startswith("```python"): From 04e475dcc808c9d97b37181aa2480cf88eef1443 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Mon, 23 Dec 2024 00:39:33 -0800 Subject: [PATCH 20/38] wip --- python/migration_guide.md | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/python/migration_guide.md b/python/migration_guide.md index e552650f6cd5..97d693df88a9 100644 --- a/python/migration_guide.md +++ b/python/migration_guide.md @@ -1,8 +1,9 @@ # Migration Guide for v0.2 to v0.4 + This is a migration guide for users of the `v0.2.*` versions of `autogen-agentchat` to the `v0.4` version, which introduces a new set of APIs and features. The `v0.4` version contains breaking changes. Please read this guide carefully. -We still maintain the `v0.2` version in the `0.2` branch; however, +We still maintain the `v0.2` version in the `0.2` branch; however, we highly recommend you upgrade to the `v0.4` version. > **Note**: We no longer have admin access to the `pyautogen` PyPI package, and @@ -20,6 +21,10 @@ the [AgentChat API](https://microsoft.github.io/autogen/dev/user-guide/agentchat Most of this guide focuses on `v0.4`'s AgentChat API; however, you can also build your own high-level framework using just the Core API. +## New to AutoGen? + +Jump straight to the [AgentChat Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/) to get started with `v0.4`. + ## What's in this guide? We provide a detailed guide on how to migrate your existing codebase from `v0.2` to `v0.4`. @@ -205,7 +210,6 @@ Read more on [Agent Tutorial](https://microsoft.github.io/autogen/dev/user-guide and [Assistant Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.AssistantAgent). - ## Multi-Modal Agent The `AssistantAgent` in `v0.4` supports multi-modal inputs if the model client supports it. @@ -308,7 +312,7 @@ conversable_agent.register_reply([ConversableAgent], reply_func, position=0) # NOTE: An async reply function will only be invoked with async send. ``` -Rather than guessing what the `reply_func` does, all its parameters, +Rather than guessing what the `reply_func` does, all its parameters, and what the `position` should be, in `v0.4`, we can simply create a custom agent and implement the `on_messages`, `on_reset`, and `produced_message_types` methods. @@ -370,7 +374,7 @@ chat_result = user_proxy.initiate_chat(assistant, message="Write a python script print(chat_result) ``` -To get the same behavior in `v0.4`, you can use the `AssistantAgent` +To get the same behavior in `v0.4`, you can use the `AssistantAgent` and `CodeExecutorAgent` together in a `RoundRobinGroupChat`. ```python @@ -456,7 +460,7 @@ while True: print("Assistant:", chat_result.summary) ``` -In `v0.4`, you really just need one agent -- the `AssistantAgent` -- to handle +In `v0.4`, you really just need one agent -- the `AssistantAgent` -- to handle both the tool calling and tool execution. ```python @@ -508,6 +512,7 @@ writer = AssistantAgent( description="A writer.", system_message="You are a writer.", llm_config=llm_config, + is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("APPROVE"), ) critic = AssistantAgent( @@ -562,7 +567,7 @@ async def main() -> None: termination = TextMentionTermination("APPROVE") # The group chat will alternate between the writer and the critic. - group_chat = RoundRobinGroupChat([writer, critic], termination_condition=termination) + group_chat = RoundRobinGroupChat([writer, critic], termination_condition=termination, max_turns=12) # `run_stream` returns an async generator to stream the intermediate messages. stream = group_chat.run_stream(task="Write a short story about a robot that discovers it has feelings.") @@ -574,7 +579,7 @@ asyncio.run(main()) For LLM-based speaker selection, you can use the `SelectorGroupChat` instead. See [Selector Group Chat Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/selector-group-chat.html) -and +and [Selector Group Chat Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.teams.html#autogen_agentchat.teams.SelectorGroupChat) for more details. @@ -589,7 +594,7 @@ In `v0.2`, group chat with resume is a bit complicated. You need to explicitly save the group chat messages and load them back when you want to resume the chat. See [Resuming Group Chat in v0.2](https://microsoft.github.io/autogen/0.2/docs/topics/groupchat/resuming_groupchat) for more details. -In `v0.4`, you can simply call `run` or `run_stream` again with the same group chat object to resume the chat. To export and load the state, you can use +In `v0.4`, you can simply call `run` or `run_stream` again with the same group chat object to resume the chat. To export and load the state, you can use `save_state` and `load_state` methods. ```python @@ -859,7 +864,7 @@ You can take a look at [Society of Mind Agent (Experimental)](https://microsoft. In `v0.2`, sequential chat is supported by using the `initiate_chats` function. It takes input a list of dictionary configurations for each step of the sequence. See [Sequential Chat in v0.2](https://microsoft.github.io/autogen/0.2/docs/tutorial/conversation-patterns#sequential-chats) -for more details. +for more details. Base on the feedback from the community, the `initiate_chats` function is too opinionated and not flexible enough to support the diverse set of scenarios that @@ -895,7 +900,7 @@ every custom agent. In `v0.4`, we introduce the `ChatCompletionContext` base class that manages message history and provides a virtual view of the history. Applications can use -built-in implementations such as `BufferedChatCompletionContext` to +built-in implementations such as `BufferedChatCompletionContext` to limit the message history sent to the model, or provide their own implementations that creates different virtual views. @@ -943,7 +948,7 @@ For teams, you can also use termination conditions to stop the team when a certa See [Termination Condition Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/termination.html) for more details. -Unlike the `v0.2` which comes with a special logging module, the `v0.4` API +Unlike the `v0.2` which comes with a special logging module, the `v0.4` API simply uses Python's `logging` module to log events such as model client calls. See [Logging](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/logging.html) in the Core API documentation for more details. @@ -958,4 +963,4 @@ in the Core API documentation. We also added `AzureContainerCodeExecutor` that can use Azure Container Apps (ACA) dynamic sessions for code execution. -See [ACA Dynamic Sessions Code Executor Docs](https://microsoft.github.io/autogen/dev/user-guide/extensions-user-guide/azure-container-code-executor.html). \ No newline at end of file +See [ACA Dynamic Sessions Code Executor Docs](https://microsoft.github.io/autogen/dev/user-guide/extensions-user-guide/azure-container-code-executor.html). From 47d0f429173dd57ccfa7488d04b59c76fa4582d3 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Mon, 23 Dec 2024 01:16:42 -0800 Subject: [PATCH 21/38] WIP --- python/migration_guide.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/python/migration_guide.md b/python/migration_guide.md index 97d693df88a9..29b8f1469ab6 100644 --- a/python/migration_guide.md +++ b/python/migration_guide.md @@ -802,6 +802,7 @@ and to the nested team or agent. The following example shows a simple nested chat that counts numbers. ```python +import asyncio from typing import Sequence, List from autogen_core import CancellationToken from autogen_agentchat.agents import BaseChatAgent @@ -810,6 +811,8 @@ from autogen_agentchat.messages import TextMessage, ChatMessage from autogen_agentchat.base import Response class CountingAgent(BaseChatAgent): + """An agent that returns a new number by adding 1 to the last number in the + input messages.""" async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> Response: if len(messages) == 0: last_number = 0 # Start from 0 if no messages are given. @@ -826,6 +829,8 @@ class CountingAgent(BaseChatAgent): return [TextMessage] class NestedCountingAgent(BaseChatAgent): + """An agent that increments the last number in the input messages + multiple times using a nested counting team.""" def __init__(self, name: str, counting_team: RoundRobinGroupChat) -> None: super().__init__(name, description="An agent that counts numbers.") self._counting_team = counting_team @@ -852,9 +857,24 @@ async def main() -> None: counting_team = RoundRobinGroupChat([counting_agent_1, counting_agent_2], max_turns=5) # Create a nested counting agent that takes the inner team as a parameter. nested_counting_agent = NestedCountingAgent("nested_counting_agent", counting_team) + # Run the nested counting agent with a message starting from 1. response = await nested_counting_agent.on_messages([TextMessage(content="1", source="user")], CancellationToken()) - print(response.inner_messages) + assert response.inner_messages is not None + for message in response.inner_messages: + print(message) print(response.chat_message) + +asyncio.run(main()) +``` + +You should see the following output: + +```bash +source='counting_agent_1' models_usage=None content='2' type='TextMessage' +source='counting_agent_2' models_usage=None content='3' type='TextMessage' +source='counting_agent_1' models_usage=None content='4' type='TextMessage' +source='counting_agent_2' models_usage=None content='5' type='TextMessage' +source='counting_agent_1' models_usage=None content='6' type='TextMessage' ``` You can take a look at [Society of Mind Agent (Experimental)](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.SocietyOfMindAgent) for a more complex implementation. From 3c6efb942e572b853d9deb273ee26852e2b9f74f Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Mon, 23 Dec 2024 01:22:13 -0800 Subject: [PATCH 22/38] wip --- python/migration_guide.md | 1 + 1 file changed, 1 insertion(+) diff --git a/python/migration_guide.md b/python/migration_guide.md index 29b8f1469ab6..5740ac95bde9 100644 --- a/python/migration_guide.md +++ b/python/migration_guide.md @@ -55,6 +55,7 @@ will be providied in the future releases of `v0.4.*` versions: - Model Client Cache - Teacheable Agent - RAG Agent +- Jupyter Code Executor We will update this guide when the missing features become available. From 47ab95b29ad689b3b400d2811e09cf844ee8538b Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Mon, 23 Dec 2024 11:06:15 -0800 Subject: [PATCH 23/38] fix links --- python/migration_guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/migration_guide.md b/python/migration_guide.md index 5740ac95bde9..22071029eb97 100644 --- a/python/migration_guide.md +++ b/python/migration_guide.md @@ -36,7 +36,7 @@ See each feature below for detailed information on how to migrate. - [Assistant Agent](#assistant-agent) - [Multi-Modal Agent](#multi-modal-agent) - [User Proxy](#user-proxy) -- [Custom Agent and Register Reply](#custom-agent-and-register-reply) +- [Conversable Agent and Register Reply](#conversable-agent-and-register-reply) - [Two-Agent Chat](#two-agent-chat) - [Tool Use](#tool-use) - [Group Chat](#group-chat) @@ -46,7 +46,7 @@ See each feature below for detailed information on how to migrate. - [Sequential Chat](#sequential-chat) - [GPTAssistantAgent](#gptassistantagent) - [Long-Context Handling](#long-context-handling) -- [Observability](#observability) +- [Observability and Control](#observability-and-control) - [Code Executors](#code-executors) The following features currently in `v0.2` From 4bf83f0578e1dc894a3ec662d52611f7e69df90b Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Mon, 23 Dec 2024 11:08:33 -0800 Subject: [PATCH 24/38] update --- python/migration_guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/migration_guide.md b/python/migration_guide.md index 22071029eb97..6a03b3bc5fe9 100644 --- a/python/migration_guide.md +++ b/python/migration_guide.md @@ -52,10 +52,10 @@ See each feature below for detailed information on how to migrate. The following features currently in `v0.2` will be providied in the future releases of `v0.4.*` versions: -- Model Client Cache +- Model Client Cache [#4752](https://github.com/microsoft/autogen/issues/4752) +- Jupyter Code Executor [#4795](https://github.com/microsoft/autogen/issues/4795) - Teacheable Agent - RAG Agent -- Jupyter Code Executor We will update this guide when the missing features become available. From 04c71f144ddf2b1be80af2ba684914c75db10f44 Mon Sep 17 00:00:00 2001 From: Jack Gerrits Date: Fri, 27 Dec 2024 13:35:49 -0500 Subject: [PATCH 25/38] Add 0.4 component config example --- python/migration_guide.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/python/migration_guide.md b/python/migration_guide.md index 6a03b3bc5fe9..540afc1cf412 100644 --- a/python/migration_guide.md +++ b/python/migration_guide.md @@ -74,13 +74,27 @@ config_list = [ model_client = OpenAIWrapper(config_list=config_list) ``` +> **Note**: In AutoGen 0.2, the OpenAI client would try configs in the list until one worked. 0.4 instead expects a specfic model configuration to be chosen. + In `v0.4`, we offers two ways to create a model client. ### Use component config -TODO: add example +AutoGen 0.4 has a [generic component configuration system](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/component-config.html). Model clients are a great use case for this. See below for how to create an OpenAI chat completion client. ```python + +from autogen_core.models import ChatCompletionClient + +config = { + "provider": "OpenAIChatCompletionClient", + "config": { + "model": "gpt-4o", + "api_key": "sk-xxx" # os.environ["...'] + } +} + +model_client = ChatCompletionClient.load_component(config) ``` ### Use model client class directly From a48bae340ce2475dcd4be451eed3c2d08a3ad124 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Fri, 27 Dec 2024 11:02:20 -0800 Subject: [PATCH 26/38] update --- python/migration_guide.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/python/migration_guide.md b/python/migration_guide.md index 540afc1cf412..c14e706a9e60 100644 --- a/python/migration_guide.md +++ b/python/migration_guide.md @@ -54,6 +54,7 @@ will be providied in the future releases of `v0.4.*` versions: - Model Client Cache [#4752](https://github.com/microsoft/autogen/issues/4752) - Jupyter Code Executor [#4795](https://github.com/microsoft/autogen/issues/4795) +- Model Client Cost [#4835](https://github.com/microsoft/autogen/issues/4835) - Teacheable Agent - RAG Agent @@ -332,7 +333,7 @@ and what the `position` should be, in `v0.4`, we can simply create a custom agen and implement the `on_messages`, `on_reset`, and `produced_message_types` methods. ```python -from typing import Sequence, List +from typing import Sequence, Tuple from autogen_core import CancellationToken from autogen_agentchat.agents import BaseChatAgent from autogen_agentchat.messages import TextMessage, ChatMessage @@ -346,8 +347,8 @@ class CustomAgent(BaseChatAgent): pass @property - def produced_message_types(self) -> List[type[ChatMessage]]: - return [TextMessage] + def produced_message_types(self) -> Tuple[type[ChatMessage], ...]: + return (TextMessage,) ``` You can then use the custom agent in the same way as the `AssistantAgent`. @@ -818,7 +819,7 @@ The following example shows a simple nested chat that counts numbers. ```python import asyncio -from typing import Sequence, List +from typing import Sequence, Tuple from autogen_core import CancellationToken from autogen_agentchat.agents import BaseChatAgent from autogen_agentchat.teams import RoundRobinGroupChat @@ -840,8 +841,8 @@ class CountingAgent(BaseChatAgent): pass @property - def produced_message_types(self) -> List[type[ChatMessage]]: - return [TextMessage] + def produced_message_types(self) -> Tuple[type[ChatMessage], ...]: + return (TextMessage,) class NestedCountingAgent(BaseChatAgent): """An agent that increments the last number in the input messages @@ -852,18 +853,18 @@ class NestedCountingAgent(BaseChatAgent): async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> Response: # Run the inner team with the given messages and returns the last message produced by the team. - result = await self._counting_team.run(task=list(messages), cancellation_token=cancellation_token) + result = await self._counting_team.run(task=messages, cancellation_token=cancellation_token) # To stream the inner messages, implement `on_messages_stream` and use that to implement `on_messages`. assert isinstance(result.messages[-1], TextMessage) - return Response(chat_message=result.messages[-1], inner_messages=list(result.messages[len(messages):-1])) + return Response(chat_message=result.messages[-1], inner_messages=result.messages[len(messages):-1]) async def on_reset(self, cancellation_token: CancellationToken) -> None: # Reset the inner team. await self._counting_team.reset() @property - def produced_message_types(self) -> List[type[ChatMessage]]: - return [TextMessage] + def produced_message_types(self) -> Tuple[type[ChatMessage], ...]: + return (TextMessage,) async def main() -> None: # Create a team of two counting agents as the inner team. From 1106f80ef0844f0d42eca7956715caf0ae62145f Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Fri, 27 Dec 2024 11:32:30 -0800 Subject: [PATCH 27/38] update --- python/migration_guide.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/python/migration_guide.md b/python/migration_guide.md index c14e706a9e60..fe7c704d9706 100644 --- a/python/migration_guide.md +++ b/python/migration_guide.md @@ -39,6 +39,7 @@ See each feature below for detailed information on how to migrate. - [Conversable Agent and Register Reply](#conversable-agent-and-register-reply) - [Two-Agent Chat](#two-agent-chat) - [Tool Use](#tool-use) +- [Chat Result](#chat-result) - [Group Chat](#group-chat) - [Group Chat with Resume](#group-chat-with-resume) - [Group Chat with Custom Selector (Stateflow)](#group-chat-with-custom-selector-stateflow) @@ -508,6 +509,44 @@ async def main() -> None: asyncio.run(main()) ``` +## Chat Result + +In `v0.2`, you get a `ChatResult` object from the `initiate_chat` method. +For example: + +```python +chat_result = tool_executor.initiate_chat( + tool_caller, + message=user_input, + summary_method="reflection_with_llm", +) +print(chat_result.summary) # Get LLM-reflected summary of the chat. +print(chat_result.chat_history) # Get the chat history. +print(chat_result.cost) # Get the cost of the chat. +print(chat_result.human_input) # Get the human input solicited by the chat. +``` + +See [ChatResult Docs](https://microsoft.github.io/autogen/0.2/docs/reference/agentchat/chat#chatresult) +for more details. + +In `v0.4`, you get a `TaskResult` object from a `run` or `run_stream` method. +The `TaskResult` object contains the `messages` which is the message history +of the chat, including both agents' private (tool calls, etc.) and public messages. + +TODO: Add token counting result after [#4719](https://github.com/microsoft/autogen/issues/4719) is resolved. + +There are some notable differences between `TaskResult` and `ChatResult`: + +- The `messages` list in `TaskResult` uses different message format than the `ChatResult.chat_history` list. +- There is no `summary` field. It is up to the application to decide how to summarize the chat using the `messages` list. +- `human_input` is not provided in the `TaskResult` object, as the user input can be extracted from the `messages` list by filtering with the `source` field. +- `cost` is not provided in the `TaskResult` object, however, you can calculate the cost based on token usage. It would be a great community extension to add cost calculation. See [community extensions](https://microsoft.github.io/autogen/dev/user-guide/extensions-user-guide/discover.html). + +We provide a conversion function to convert the `TaskResult.messages` to +a v0.2 compatible `ChatResult.chat_history` list. + +TODO: Add the conversion function here. + ## Group Chat In `v0.2`, you need to create a `GroupChat` dataclass and pass it into a From c59e0176f35c9325a3cbcfcc525dbecf0409038f Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Fri, 27 Dec 2024 11:33:18 -0800 Subject: [PATCH 28/38] update --- python/migration_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/migration_guide.md b/python/migration_guide.md index fe7c704d9706..b8fd17e22e9e 100644 --- a/python/migration_guide.md +++ b/python/migration_guide.md @@ -545,7 +545,7 @@ There are some notable differences between `TaskResult` and `ChatResult`: We provide a conversion function to convert the `TaskResult.messages` to a v0.2 compatible `ChatResult.chat_history` list. -TODO: Add the conversion function here. +TODO: Add the conversion function here, after [#4833](https://github.com/microsoft/autogen/issues/4833) is resolved. ## Group Chat From c502fa4783b0e3176ad4e82cd7d8c09faac35fa2 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Sat, 28 Dec 2024 00:29:22 -0800 Subject: [PATCH 29/38] add conversion function --- python/migration_guide.md | 119 +++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 3 deletions(-) diff --git a/python/migration_guide.md b/python/migration_guide.md index b8fd17e22e9e..6f8e0f766c17 100644 --- a/python/migration_guide.md +++ b/python/migration_guide.md @@ -40,6 +40,7 @@ See each feature below for detailed information on how to migrate. - [Two-Agent Chat](#two-agent-chat) - [Tool Use](#tool-use) - [Chat Result](#chat-result) +- [Conversion between v0.2 and v0.4 Messages](#conversion-between-v02-and-v04-messages) - [Group Chat](#group-chat) - [Group Chat with Resume](#group-chat-with-resume) - [Group Chat with Custom Selector (Stateflow)](#group-chat-with-custom-selector-stateflow) @@ -542,10 +543,122 @@ There are some notable differences between `TaskResult` and `ChatResult`: - `human_input` is not provided in the `TaskResult` object, as the user input can be extracted from the `messages` list by filtering with the `source` field. - `cost` is not provided in the `TaskResult` object, however, you can calculate the cost based on token usage. It would be a great community extension to add cost calculation. See [community extensions](https://microsoft.github.io/autogen/dev/user-guide/extensions-user-guide/discover.html). -We provide a conversion function to convert the `TaskResult.messages` to -a v0.2 compatible `ChatResult.chat_history` list. +## Conversion between v0.2 and v0.4 Messages -TODO: Add the conversion function here, after [#4833](https://github.com/microsoft/autogen/issues/4833) is resolved. +TODO: Resolves [#4833](https://github.com/microsoft/autogen/issues/4833), maybe include +the code in the `autogen-ext` package. + +You can use the following conversion functions to convert between a v0.4 message in +`TaskResult.messages` and a v0.2 message in `ChatResult.chat_history`. + +```python +from typing import Any, Dict, List, Literal + +from autogen_agentchat.messages import ( + AgentEvent, + ChatMessage, + HandoffMessage, + MultiModalMessage, + StopMessage, + TextMessage, + ToolCallExecutionEvent, + ToolCallRequestEvent, + ToolCallSummaryMessage, +) +from autogen_core import FunctionCall, Image +from autogen_core.models import FunctionExecutionResult + + +def convert_to_v02_message( + message: AgentEvent | ChatMessage, + role: Literal["assistant", "user", "tool"], + image_detail: Literal["auto", "high", "low"] = "auto", +) -> Dict[str, Any]: + """Convert a v0.4 AgentChat message to a v0.2 message. + + Args: + message (AgentEvent | ChatMessage): The message to convert. + role (Literal["assistant", "user", "tool"]): The role of the message. + image_detail (Literal["auto", "high", "low"], optional): The detail level of image content in multi-modal message. Defaults to "auto". + + Returns: + Dict[str, Any]: The converted AutoGen v0.2 message. + """ + v02_message: Dict[str, Any] = {} + if isinstance(message, TextMessage | StopMessage | HandoffMessage | ToolCallSummaryMessage): + v02_message = {"content": message.content, "role": role, "name": message.source} + elif isinstance(message, MultiModalMessage): + v02_message = {"content": [], "role": role, "name": message.source} + for modal in message.content: + if isinstance(modal, str): + v02_message["content"].append({"type": "text", "text": modal}) + elif isinstance(modal, Image): + v02_message["content"].append(modal.to_openai_format(detail=image_detail)) + else: + raise ValueError(f"Invalid multimodal message content: {modal}") + elif isinstance(message, ToolCallRequestEvent): + v02_message = {"tool_calls": [], "role": "assistant", "content": None, "name": message.source} + for tool_call in message.content: + v02_message["tool_calls"].append( + { + "id": tool_call.id, + "type": "function", + "function": {"name": tool_call.name, "args": tool_call.arguments}, + } + ) + elif isinstance(message, ToolCallExecutionEvent): + tool_responses: List[Dict[str, str]] = [] + for tool_result in message.content: + tool_responses.append( + { + "tool_call_id": tool_result.call_id, + "role": "tool", + "content": tool_result.content, + } + ) + content = "\n\n".join([response["content"] for response in tool_responses]) + v02_message = {"tool_responses": tool_responses, "role": "tool", "content": content} + else: + raise ValueError(f"Invalid message type: {type(message)}") + return v02_message + + +def convert_to_v04_message(message: Dict[str, Any]) -> AgentEvent | ChatMessage: + """Convert a v0.2 message to a v0.4 AgentChat message.""" + if "tool_calls" in message: + tool_calls: List[FunctionCall] = [] + for tool_call in message["tool_calls"]: + tool_calls.append( + FunctionCall( + id=tool_call["id"], + name=tool_call["function"]["name"], + arguments=tool_call["function"]["args"], + ) + ) + return ToolCallRequestEvent(source=message["name"], content=tool_calls) + elif "tool_responses" in message: + tool_results: List[FunctionExecutionResult] = [] + for tool_response in message["tool_responses"]: + tool_results.append( + FunctionExecutionResult( + call_id=tool_response["tool_call_id"], + content=tool_response["content"], + ) + ) + return ToolCallExecutionEvent(source="tools", content=tool_results) + elif isinstance(message["content"], list): + content: List[str | Image] = [] + for modal in message["content"]: # type: ignore + if modal["type"] == "text": # type: ignore + content.append(modal["text"]) # type: ignore + else: + content.append(Image.from_uri(modal["image_url"]["url"])) # type: ignore + return MultiModalMessage(content=content, source=message["name"]) + elif isinstance(message["content"], str): + return TextMessage(content=message["content"], source=message["name"]) + else: + raise ValueError(f"Unable to convert message: {message}") +``` ## Group Chat From ae65fe48938ad40512f2b94c725477eb20495ef9 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Mon, 30 Dec 2024 12:04:24 -0800 Subject: [PATCH 30/38] wip --- python/migration_guide.md | 1154 ------------------------------------- python/pyproject.toml | 2 +- 2 files changed, 1 insertion(+), 1155 deletions(-) delete mode 100644 python/migration_guide.md diff --git a/python/migration_guide.md b/python/migration_guide.md deleted file mode 100644 index 6f8e0f766c17..000000000000 --- a/python/migration_guide.md +++ /dev/null @@ -1,1154 +0,0 @@ -# Migration Guide for v0.2 to v0.4 - -This is a migration guide for users of the `v0.2.*` versions of `autogen-agentchat` -to the `v0.4` version, which introduces a new set of APIs and features. -The `v0.4` version contains breaking changes. Please read this guide carefully. -We still maintain the `v0.2` version in the `0.2` branch; however, -we highly recommend you upgrade to the `v0.4` version. - -> **Note**: We no longer have admin access to the `pyautogen` PyPI package, and -> the releases from that package are no longer from Microsoft since version 0.2.34. -> To continue use the `v0.2` version of AutoGen, install it using `autogen-agentchat~=0.2`. -> Please read our [clarification statement](https://github.com/microsoft/autogen/discussions/4217) regarding forks. - -## What is `v0.4`? - -Since the release of AutoGen in 2023, we have intensively listened to our community and users from small startups and large enterprises, gathering much feedback. Based on that feedback, we built AutoGen `v0.4`, a from-the-ground-up rewrite adopting an asynchronous, event-driven architecture to address issues such as observability, flexibility, interactive control, and scale. - -The `v0.4` API is layered: -the [Core API](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/index.html) is the foundation layer offering a scalable, event-driven actor framework for creating agentic workflows; -the [AgentChat API](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/index.html) is built on Core, offering a task-driven, high-level framework for building interactive agentic applications. It is a replacement for AutoGen `v0.2`. - -Most of this guide focuses on `v0.4`'s AgentChat API; however, you can also build your own high-level framework using just the Core API. - -## New to AutoGen? - -Jump straight to the [AgentChat Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/) to get started with `v0.4`. - -## What's in this guide? - -We provide a detailed guide on how to migrate your existing codebase from `v0.2` to `v0.4`. - -See each feature below for detailed information on how to migrate. - -- [Model Client](#model-client) -- [Model Client for OpenAI-Compatible APIs](#model-client-for-openai-compatible-apis) -- [Assistant Agent](#assistant-agent) -- [Multi-Modal Agent](#multi-modal-agent) -- [User Proxy](#user-proxy) -- [Conversable Agent and Register Reply](#conversable-agent-and-register-reply) -- [Two-Agent Chat](#two-agent-chat) -- [Tool Use](#tool-use) -- [Chat Result](#chat-result) -- [Conversion between v0.2 and v0.4 Messages](#conversion-between-v02-and-v04-messages) -- [Group Chat](#group-chat) -- [Group Chat with Resume](#group-chat-with-resume) -- [Group Chat with Custom Selector (Stateflow)](#group-chat-with-custom-selector-stateflow) -- [Nested Chat](#nested-chat) -- [Sequential Chat](#sequential-chat) -- [GPTAssistantAgent](#gptassistantagent) -- [Long-Context Handling](#long-context-handling) -- [Observability and Control](#observability-and-control) -- [Code Executors](#code-executors) - -The following features currently in `v0.2` -will be providied in the future releases of `v0.4.*` versions: - -- Model Client Cache [#4752](https://github.com/microsoft/autogen/issues/4752) -- Jupyter Code Executor [#4795](https://github.com/microsoft/autogen/issues/4795) -- Model Client Cost [#4835](https://github.com/microsoft/autogen/issues/4835) -- Teacheable Agent -- RAG Agent - -We will update this guide when the missing features become available. - -## Model Client - -In `v0.2` you configure the model client as follows, and create the `OpenAIWrapper` object. - -```python -from autogen.oai import OpenAIWrapper - -config_list = [ - {"model": "gpt-4o", "api_key": "sk-xxx"}, - {"model": "gpt-4o-mini", "api_key": "sk-xxx"}, -] - -model_client = OpenAIWrapper(config_list=config_list) -``` - -> **Note**: In AutoGen 0.2, the OpenAI client would try configs in the list until one worked. 0.4 instead expects a specfic model configuration to be chosen. - -In `v0.4`, we offers two ways to create a model client. - -### Use component config - -AutoGen 0.4 has a [generic component configuration system](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/component-config.html). Model clients are a great use case for this. See below for how to create an OpenAI chat completion client. - -```python - -from autogen_core.models import ChatCompletionClient - -config = { - "provider": "OpenAIChatCompletionClient", - "config": { - "model": "gpt-4o", - "api_key": "sk-xxx" # os.environ["...'] - } -} - -model_client = ChatCompletionClient.load_component(config) -``` - -### Use model client class directly - -Open AI: - -```python -from autogen_ext.models.openai import OpenAIChatCompletionClient - -model_client = OpenAIChatCompletionClient(model="gpt-4o", api_key="sk-xxx") -``` - -Azure OpenAI: - -```python -from autogen_ext.models.openai import AzureOpenAIChatCompletionClient - -model_client = AzureOpenAIChatCompletionClient( - azure_deployment="gpt-4o", - azure_endpoint="https://.openai.azure.com/", - model="gpt-4o", - api_version="2024-09-01-preview", - api_key="sk-xxx", -) -``` - -Read more on [OpenAI Chat Completion Client Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_ext.models.openai.html) - -## Model Client for OpenAI-Compatible APIs - -You can use a the `OpenAIChatCompletionClient` to connect to an OpenAI-Compatible API, -but you need to specify the `base_url` and `model_capabilities`. - -```python -from autogen_ext.models.openai import OpenAIChatCompletionClient - -custom_model_client = OpenAIChatCompletionClient( - model="custom-model-name", - base_url="https://custom-model.com/reset/of/the/path", - api_key="placeholder", - model_capabilities={ - "vision": True, - "function_calling": True, - "json_output": True, - }, -) -``` - -> **Note**: We don't test all the OpenAI-Compatible APIs, and many of them -> works differently from the OpenAI API even though they may claim to suppor it. -> Please test them before using them. - -Read about [Model Clients](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/models.html) -in AgentChat Tutorial and more detailed information on [Core API Docs](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/model-clients.html) - -Support for other hosted models will be added in the future. - -## Assistant Agent - -In `v0.2`, you create an assistant agent as follows: - -```python -from autogen.agentchat import AssistantAgent - -llm_config = { - "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], - "seed": 42, - "temperature": 0, -} - -assistant = AssistantAgent( - name="assistant", - system_message="You are a helpful assistant.", - llm_config=llm_config, -) -``` - -In `v0.4`, it is similar, but you need to specify `model_client` instead of `llm_config`. - -```python -from autogen_agentchat.agents import AssistantAgent -from autogen_ext.models.openai import OpenAIChatCompletionClient - -model_client = OpenAIChatCompletionClient(model="gpt-4o", api_key="sk-xxx", seed=42, temperature=0) - -assistant = AssistantAgent( - name="assistant", - system_message="You are a helpful assistant.", - model_client=model_client, -) -``` - -However, the usage is somewhat different. In `v0.4`, instead of calling `assistant.send`, -you call `assistant.on_messages` or `assistant.on_messages_stream` to handle incoming messages. -Furthermore, the `on_messages` and `on_messages_stream` methods are asynchronous, -and the latter returns an async generator to stream the inner thoughts of the agent. - -Here is how you can call the assistant agent in `v0.4` directly, continuing from the above example: - -```python -import asyncio -from autogen_agentchat.messages import TextMessage -from autogen_agentchat.agents import AssistantAgent -from autogen_core import CancellationToken -from autogen_ext.models.openai import OpenAIChatCompletionClient - -async def main() -> None: - model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) - - assistant = AssistantAgent( - name="assistant", - system_message="You are a helpful assistant.", - model_client=model_client, - ) - - cancellation_token = CancellationToken() - response = await assistant.on_messages([TextMessage(content="Hello!", source="user")], cancellation_token) - print(response) - -asyncio.run(main()) -``` - -The `CancellationToken` can be used to cancel the request asynchronously -when you call `cancellation_token.cancel()`, which will cause the `await` -on the `on_messages` call to raise a `CancelledError`. - -Read more on [Agent Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/agents.html) -and -[Assistant Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.AssistantAgent). - -## Multi-Modal Agent - -The `AssistantAgent` in `v0.4` supports multi-modal inputs if the model client supports it. -The `vision` capability of the model client is used to determine if the agent supports multi-modal inputs. - -```python -import asyncio -from pathlib import Path -from autogen_agentchat.messages import MultiModalMessage -from autogen_agentchat.agents import AssistantAgent -from autogen_core import CancellationToken, Image -from autogen_ext.models.openai import OpenAIChatCompletionClient - -async def main() -> None: - model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) - - assistant = AssistantAgent( - name="assistant", - system_message="You are a helpful assistant.", - model_client=model_client, - ) - - cancellation_token = CancellationToken() - message = MultiModalMessage( - content=["Here is an image:", Image.from_file(Path("test.png"))], - source="user", - ) - response = await assistant.on_messages([message], cancellation_token) - print(response) - -asyncio.run(main()) -``` - -## User Proxy - -In `v0.2`, you create a user proxy as follows: - -```python -from autogen.agentchat import UserProxyAgent - -user_proxy = UserProxyAgent( - name="user_proxy", - human_input_mode="NEVER", - max_consecutive_auto_reply=10, - code_execution_config=False, - llm_config=False, -) -``` - -This user proxy would take input from the user through console, and would terminate -if the incoming message ends with "TERMINATE". - -In `v0.4`, a user proxy is simply an agent that takes user input only, there is no -other special configuration needed. You can create a user proxy as follows: - -```python -from autogen_agentchat.agents import UserProxyAgent - -user_proxy = UserProxyAgent("user_proxy") -``` - -See [User Proxy Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.UserProxyAgent) -for more details and how to customize the input function with timeout. - -## Conversable Agent and Register Reply - -In `v0.2`, you can create a conversable agent and register a reply function as follows: - -```python -from typing import Any, Dict, List, Optional, Tuple, Union -from autogen.agentchat import ConversableAgent - -llm_config = { - "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], - "seed": 42, - "temperature": 0, -} - -conversable_agent = ConversableAgent( - name="conversable_agent", - system_message="You are a helpful assistant.", - llm_config=llm_config, - code_execution_config={"work_dir": "coding"}, - human_input_mode="NEVER", - max_consecutive_auto_reply=10, -) - -def reply_func( - recipient: ConversableAgent, - messages: Optional[List[Dict]] = None, - sender: Optional[Agent] = None, - config: Optional[Any] = None, -) -> Tuple[bool, Union[str, Dict, None]]: - # Custom reply logic here - return True, "Custom reply" - -# Register the reply function -conversable_agent.register_reply([ConversableAgent], reply_func, position=0) - -# NOTE: An async reply function will only be invoked with async send. -``` - -Rather than guessing what the `reply_func` does, all its parameters, -and what the `position` should be, in `v0.4`, we can simply create a custom agent -and implement the `on_messages`, `on_reset`, and `produced_message_types` methods. - -```python -from typing import Sequence, Tuple -from autogen_core import CancellationToken -from autogen_agentchat.agents import BaseChatAgent -from autogen_agentchat.messages import TextMessage, ChatMessage -from autogen_agentchat.base import Response - -class CustomAgent(BaseChatAgent): - async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> Response: - return Response(chat_message=TextMessage(content="Custom reply", source=self.name)) - - async def on_reset(self, cancellation_token: CancellationToken) -> None: - pass - - @property - def produced_message_types(self) -> Tuple[type[ChatMessage], ...]: - return (TextMessage,) -``` - -You can then use the custom agent in the same way as the `AssistantAgent`. -See [Custom Agent Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/custom-agents.html) -for more details. - -## Two-Agent Chat - -In `v0.2`, you can create a two-agent chat for code execution as follows: - -```python -from autogen.coding import LocalCommandLineCodeExecutor -from autogen.agentchat import AssistantAgent, UserProxyAgent - -llm_config = { - "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], - "seed": 42, - "temperature": 0, -} - -assistant = AssistantAgent( - name="assistant", - system_message="You are a helpful assistant. Write all code in python. Reply only 'TERMINATE' if the task is done.", - llm_config=llm_config, - is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"), -) - -user_proxy = UserProxyAgent( - name="user_proxy", - human_input_mode="NEVER", - max_consecutive_auto_reply=10, - code_execution_config={"code_executor": LocalCommandLineCodeExecutor(work_dir="coding")}, - llm_config=False, - is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"), -) - -chat_result = user_proxy.initiate_chat(assistant, message="Write a python script to print 'Hello, world!'") -# Intermediate messages are printed to the console directly. -print(chat_result) -``` - -To get the same behavior in `v0.4`, you can use the `AssistantAgent` -and `CodeExecutorAgent` together in a `RoundRobinGroupChat`. - -```python -import asyncio -from autogen_agentchat.agents import AssistantAgent, CodeExecutorAgent -from autogen_agentchat.teams import RoundRobinGroupChat -from autogen_agentchat.conditions import TextMentionTermination, MaxMessageTermination -from autogen_agentchat.ui import Console -from autogen_ext.code_executors.local import LocalCommandLineCodeExecutor -from autogen_ext.models.openai import OpenAIChatCompletionClient - -async def main() -> None: - model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) - - assistant = AssistantAgent( - name="assistant", - system_message="You are a helpful assistant. Write all code in python. Reply only 'TERMINATE' if the task is done.", - model_client=model_client, - ) - - code_executor = CodeExecutorAgent( - name="code_executor", - code_executor=LocalCommandLineCodeExecutor(work_dir="coding"), - ) - - # The termination condition is a combination of text termination and max message termination, either of which will cause the chat to terminate. - termination = TextMentionTermination("TERMINATE") | MaxMessageTermination(10) - - # The group chat will alternate between the assistant and the code executor. - group_chat = RoundRobinGroupChat([assistant, code_executor], termination_condition=termination) - - # `run_stream` returns an async generator to stream the intermediate messages. - stream = group_chat.run_stream(task="Write a python script to print 'Hello, world!'") - # `Console` is a simple UI to display the stream. - await Console(stream) - -asyncio.run(main()) -``` - -## Tool Use - -In `v0.2`, to create a tool use chatbot, you must have two agents, one for calling the tool and one for executing the tool. -You need to initiate a two-agent chat for every user request. - -```python -from autogen.agentchat import AssistantAgent, UserProxyAgent, register_function - -llm_config = { - "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], - "seed": 42, - "temperature": 0, -} - -tool_caller = AssistantAgent( - name="tool_caller", - system_message="You are a helpful assistant. You can call tools to help user.", - llm_config=llm_config, - max_consecutive_auto_reply=1, # Set to 1 so that we return to the application after each assistant reply as we are building a chatbot. -) - -tool_executor = UserProxyAgent( - name="tool_executor", - human_input_mode="NEVER", - code_execution_config=False, - llm_config=False, -) - -def get_weather(city: str) -> str: - return f"The weather in {city} is 72 degree and sunny." - -# Register the tool function to the tool caller and executor. -register_function(get_weather, caller=tool_caller, executor=tool_executor) - -while True: - user_input = input("User: ") - if user_input == "exit": - break - chat_result = tool_executor.initiate_chat( - tool_caller, - message=user_input, - summary_method="reflection_with_llm", # To let the model reflect on the tool use, set to "last_msg" to return the tool call result directly. - ) - print("Assistant:", chat_result.summary) -``` - -In `v0.4`, you really just need one agent -- the `AssistantAgent` -- to handle -both the tool calling and tool execution. - -```python -import asyncio -from autogen_core import CancellationToken -from autogen_ext.models.openai import OpenAIChatCompletionClient -from autogen_agentchat.agents import AssistantAgent -from autogen_agentchat.messages import TextMessage - -def get_weather(city: str) -> str: # Async tool is possible too. - return f"The weather in {city} is 72 degree and sunny." - -async def main() -> None: - model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) - assistant = AssistantAgent( - name="assistant", - system_message="You are a helpful assistant. You can call tools to help user.", - model_client=model_client, - tools=[get_weather], - reflect_on_tool_use=True, # Set to True to have the model reflect on the tool use, set to False to return the tool call result directly. - ) - while True: - user_input = input("User: ") - if user_input == "exit": - break - response = await assistant.on_messages([TextMessage(content=user_input, source="user")], CancellationToken()) - print("Assistant:", response.chat_message.content) - -asyncio.run(main()) -``` - -## Chat Result - -In `v0.2`, you get a `ChatResult` object from the `initiate_chat` method. -For example: - -```python -chat_result = tool_executor.initiate_chat( - tool_caller, - message=user_input, - summary_method="reflection_with_llm", -) -print(chat_result.summary) # Get LLM-reflected summary of the chat. -print(chat_result.chat_history) # Get the chat history. -print(chat_result.cost) # Get the cost of the chat. -print(chat_result.human_input) # Get the human input solicited by the chat. -``` - -See [ChatResult Docs](https://microsoft.github.io/autogen/0.2/docs/reference/agentchat/chat#chatresult) -for more details. - -In `v0.4`, you get a `TaskResult` object from a `run` or `run_stream` method. -The `TaskResult` object contains the `messages` which is the message history -of the chat, including both agents' private (tool calls, etc.) and public messages. - -TODO: Add token counting result after [#4719](https://github.com/microsoft/autogen/issues/4719) is resolved. - -There are some notable differences between `TaskResult` and `ChatResult`: - -- The `messages` list in `TaskResult` uses different message format than the `ChatResult.chat_history` list. -- There is no `summary` field. It is up to the application to decide how to summarize the chat using the `messages` list. -- `human_input` is not provided in the `TaskResult` object, as the user input can be extracted from the `messages` list by filtering with the `source` field. -- `cost` is not provided in the `TaskResult` object, however, you can calculate the cost based on token usage. It would be a great community extension to add cost calculation. See [community extensions](https://microsoft.github.io/autogen/dev/user-guide/extensions-user-guide/discover.html). - -## Conversion between v0.2 and v0.4 Messages - -TODO: Resolves [#4833](https://github.com/microsoft/autogen/issues/4833), maybe include -the code in the `autogen-ext` package. - -You can use the following conversion functions to convert between a v0.4 message in -`TaskResult.messages` and a v0.2 message in `ChatResult.chat_history`. - -```python -from typing import Any, Dict, List, Literal - -from autogen_agentchat.messages import ( - AgentEvent, - ChatMessage, - HandoffMessage, - MultiModalMessage, - StopMessage, - TextMessage, - ToolCallExecutionEvent, - ToolCallRequestEvent, - ToolCallSummaryMessage, -) -from autogen_core import FunctionCall, Image -from autogen_core.models import FunctionExecutionResult - - -def convert_to_v02_message( - message: AgentEvent | ChatMessage, - role: Literal["assistant", "user", "tool"], - image_detail: Literal["auto", "high", "low"] = "auto", -) -> Dict[str, Any]: - """Convert a v0.4 AgentChat message to a v0.2 message. - - Args: - message (AgentEvent | ChatMessage): The message to convert. - role (Literal["assistant", "user", "tool"]): The role of the message. - image_detail (Literal["auto", "high", "low"], optional): The detail level of image content in multi-modal message. Defaults to "auto". - - Returns: - Dict[str, Any]: The converted AutoGen v0.2 message. - """ - v02_message: Dict[str, Any] = {} - if isinstance(message, TextMessage | StopMessage | HandoffMessage | ToolCallSummaryMessage): - v02_message = {"content": message.content, "role": role, "name": message.source} - elif isinstance(message, MultiModalMessage): - v02_message = {"content": [], "role": role, "name": message.source} - for modal in message.content: - if isinstance(modal, str): - v02_message["content"].append({"type": "text", "text": modal}) - elif isinstance(modal, Image): - v02_message["content"].append(modal.to_openai_format(detail=image_detail)) - else: - raise ValueError(f"Invalid multimodal message content: {modal}") - elif isinstance(message, ToolCallRequestEvent): - v02_message = {"tool_calls": [], "role": "assistant", "content": None, "name": message.source} - for tool_call in message.content: - v02_message["tool_calls"].append( - { - "id": tool_call.id, - "type": "function", - "function": {"name": tool_call.name, "args": tool_call.arguments}, - } - ) - elif isinstance(message, ToolCallExecutionEvent): - tool_responses: List[Dict[str, str]] = [] - for tool_result in message.content: - tool_responses.append( - { - "tool_call_id": tool_result.call_id, - "role": "tool", - "content": tool_result.content, - } - ) - content = "\n\n".join([response["content"] for response in tool_responses]) - v02_message = {"tool_responses": tool_responses, "role": "tool", "content": content} - else: - raise ValueError(f"Invalid message type: {type(message)}") - return v02_message - - -def convert_to_v04_message(message: Dict[str, Any]) -> AgentEvent | ChatMessage: - """Convert a v0.2 message to a v0.4 AgentChat message.""" - if "tool_calls" in message: - tool_calls: List[FunctionCall] = [] - for tool_call in message["tool_calls"]: - tool_calls.append( - FunctionCall( - id=tool_call["id"], - name=tool_call["function"]["name"], - arguments=tool_call["function"]["args"], - ) - ) - return ToolCallRequestEvent(source=message["name"], content=tool_calls) - elif "tool_responses" in message: - tool_results: List[FunctionExecutionResult] = [] - for tool_response in message["tool_responses"]: - tool_results.append( - FunctionExecutionResult( - call_id=tool_response["tool_call_id"], - content=tool_response["content"], - ) - ) - return ToolCallExecutionEvent(source="tools", content=tool_results) - elif isinstance(message["content"], list): - content: List[str | Image] = [] - for modal in message["content"]: # type: ignore - if modal["type"] == "text": # type: ignore - content.append(modal["text"]) # type: ignore - else: - content.append(Image.from_uri(modal["image_url"]["url"])) # type: ignore - return MultiModalMessage(content=content, source=message["name"]) - elif isinstance(message["content"], str): - return TextMessage(content=message["content"], source=message["name"]) - else: - raise ValueError(f"Unable to convert message: {message}") -``` - -## Group Chat - -In `v0.2`, you need to create a `GroupChat` dataclass and pass it into a -`GroupChatManager`, and have a participant that is a user proxy to initiate the chat. -For a simple scenario of a writer and a critic, you can do the following: - -```python -from autogen.agentchat import AssistantAgent, GroupChat, GroupChatManager - -llm_config = { - "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], - "seed": 42, - "temperature": 0, -} - -writer = AssistantAgent( - name="writer", - description="A writer.", - system_message="You are a writer.", - llm_config=llm_config, - is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("APPROVE"), -) - -critic = AssistantAgent( - name="critic", - description="A critic.", - system_message="You are a critic, provide feedback on the writing. Reply only 'APPROVE' if the task is done.", - llm_config=llm_config, -) - -# Create a group chat with the writer and critic. -groupchat = GroupChat(agents=[writer, critic], messages=[], max_round=12) - -# Create a group chat manager to manage the group chat, use round-robin selection method. -manager = GroupChatManager(groupchat=groupchat, llm_config=llm_config, speaker_selection_method="round_robin") - -# Initiate the chat with the editor, intermediate messages are printed to the console directly. -result = editor.initiate_chat( - manager, - message="Write a short story about a robot that discovers it has feelings.", -) -print(result.summary) -``` - -In `v0.4`, you can use the `RoundRobinGroupChat` to achieve the same behavior. - -```python -import asyncio -from autogen_agentchat.agents import AssistantAgent -from autogen_agentchat.teams import RoundRobinGroupChat -from autogen_agentchat.conditions import TextMentionTermination -from autogen_agentchat.ui import Console -from autogen_ext.models.openai import OpenAIChatCompletionClient - -async def main() -> None: - model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) - - writer = AssistantAgent( - name="writer", - description="A writer.", - system_message="You are a writer.", - model_client=model_client, - ) - - critic = AssistantAgent( - name="critic", - description="A critic.", - system_message="You are a critic, provide feedback on the writing. Reply only 'APPROVE' if the task is done.", - model_client=model_client, - ) - - # The termination condition is a text termination, which will cause the chat to terminate when the text "APPROVE" is received. - termination = TextMentionTermination("APPROVE") - - # The group chat will alternate between the writer and the critic. - group_chat = RoundRobinGroupChat([writer, critic], termination_condition=termination, max_turns=12) - - # `run_stream` returns an async generator to stream the intermediate messages. - stream = group_chat.run_stream(task="Write a short story about a robot that discovers it has feelings.") - # `Console` is a simple UI to display the stream. - await Console(stream) - -asyncio.run(main()) -``` - -For LLM-based speaker selection, you can use the `SelectorGroupChat` instead. -See [Selector Group Chat Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/selector-group-chat.html) -and -[Selector Group Chat Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.teams.html#autogen_agentchat.teams.SelectorGroupChat) -for more details. - -> **Note**: In `v0.4`, you do not need to register functions on a user proxy to use tools -> in a group chat. You can simply pass the tool functions to the `AssistantAgent` as shown in the [Tool Use](#tool-use) section. -> The agent will automatically call the tools when needed. -> If your tool doesn't output well formed response, you can use the `reflect_on_tool_use` parameter to have the model reflect on the tool use. - -## Group Chat with Resume - -In `v0.2`, group chat with resume is a bit complicated. You need to explicitly -save the group chat messages and load them back when you want to resume the chat. -See [Resuming Group Chat in v0.2](https://microsoft.github.io/autogen/0.2/docs/topics/groupchat/resuming_groupchat) for more details. - -In `v0.4`, you can simply call `run` or `run_stream` again with the same group chat object to resume the chat. To export and load the state, you can use -`save_state` and `load_state` methods. - -```python -import asyncio -import json -from autogen_agentchat.agents import AssistantAgent -from autogen_agentchat.teams import RoundRobinGroupChat -from autogen_agentchat.conditions import TextMentionTermination -from autogen_agentchat.ui import Console -from autogen_ext.models.openai import OpenAIChatCompletionClient - -def create_team() -> RoundRobinGroupChat: - model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) - - writer = AssistantAgent( - name="writer", - description="A writer.", - system_message="You are a writer.", - model_client=model_client, - ) - - critic = AssistantAgent( - name="critic", - description="A critic.", - system_message="You are a critic, provide feedback on the writing. Reply only 'APPROVE' if the task is done.", - model_client=model_client, - ) - - # The termination condition is a text termination, which will cause the chat to terminate when the text "APPROVE" is received. - termination = TextMentionTermination("APPROVE") - - # The group chat will alternate between the writer and the critic. - group_chat = RoundRobinGroupChat([writer, critic], termination_condition=termination) - - return group_chat - - -async def main() -> None: - # Create team. - group_chat = create_team() - - # `run_stream` returns an async generator to stream the intermediate messages. - stream = group_chat.run_stream(task="Write a short story about a robot that discovers it has feelings.") - # `Console` is a simple UI to display the stream. - await Console(stream) - - # Save the state of the group chat and all participants. - state = await group_chat.save_state() - with open("group_chat_state.json", "w") as f: - json.dump(state, f) - - # Create a new team with the same participants configuration. - group_chat = create_team() - - # Load the state of the group chat and all participants. - with open("group_chat_state.json", "r") as f: - state = json.load(f) - await group_chat.load_state(state) - - # Resume the chat. - stream = group_chat.run_stream(task="Translate the story into Chinese.") - await Console(stream) - -asyncio.run(main()) -``` - -## Group Chat with Custom Selector (Stateflow) - -In `v0.2` group chat, when the `speaker_selection_method` is set to a custom function, -it can override the default selection method. This is useful for implementing -a state-based selection method. -For more details, see [Custom Sepaker Selection in v0.2](https://microsoft.github.io/autogen/0.2/docs/topics/groupchat/customized_speaker_selection). - -In `v0.4`, you can use the `SelectorGroupChat` with `selector_func` to achieve the same behavior. -The `selector_func` is a function that takes the current message thread of the group chat -and returns the next speaker's name. If `None` is returned, the LLM-based -selection method will be used. - -Here is an example of using the state-based selection method to implement -a web search/analysis scenario. - -```python -import asyncio -from typing import Sequence -from autogen_agentchat.agents import AssistantAgent -from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination -from autogen_agentchat.messages import AgentEvent, ChatMessage -from autogen_agentchat.teams import SelectorGroupChat -from autogen_agentchat.ui import Console -from autogen_ext.models.openai import OpenAIChatCompletionClient - -# Note: This example uses mock tools instead of real APIs for demonstration purposes -def search_web_tool(query: str) -> str: - if "2006-2007" in query: - return """Here are the total points scored by Miami Heat players in the 2006-2007 season: - Udonis Haslem: 844 points - Dwayne Wade: 1397 points - James Posey: 550 points - ... - """ - elif "2007-2008" in query: - return "The number of total rebounds for Dwayne Wade in the Miami Heat season 2007-2008 is 214." - elif "2008-2009" in query: - return "The number of total rebounds for Dwayne Wade in the Miami Heat season 2008-2009 is 398." - return "No data found." - - -def percentage_change_tool(start: float, end: float) -> float: - return ((end - start) / start) * 100 - -def create_team() -> SelectorGroupChat: - model_client = OpenAIChatCompletionClient(model="gpt-4o") - - planning_agent = AssistantAgent( - "PlanningAgent", - description="An agent for planning tasks, this agent should be the first to engage when given a new task.", - model_client=model_client, - system_message=""" - You are a planning agent. - Your job is to break down complex tasks into smaller, manageable subtasks. - Your team members are: - Web search agent: Searches for information - Data analyst: Performs calculations - - You only plan and delegate tasks - you do not execute them yourself. - - When assigning tasks, use this format: - 1. : - - After all tasks are complete, summarize the findings and end with "TERMINATE". - """, - ) - - web_search_agent = AssistantAgent( - "WebSearchAgent", - description="A web search agent.", - tools=[search_web_tool], - model_client=model_client, - system_message=""" - You are a web search agent. - Your only tool is search_tool - use it to find information. - You make only one search call at a time. - Once you have the results, you never do calculations based on them. - """, - ) - - data_analyst_agent = AssistantAgent( - "DataAnalystAgent", - description="A data analyst agent. Useful for performing calculations.", - model_client=model_client, - tools=[percentage_change_tool], - system_message=""" - You are a data analyst. - Given the tasks you have been assigned, you should analyze the data and provide results using the tools provided. - """, - ) - - # The termination condition is a combination of text mention termination and max message termination. - text_mention_termination = TextMentionTermination("TERMINATE") - max_messages_termination = MaxMessageTermination(max_messages=25) - termination = text_mention_termination | max_messages_termination - - # The selector function is a function that takes the current message thread of the group chat - # and returns the next speaker's name. If None is returned, the LLM-based selection method will be used. - def selector_func(messages: Sequence[AgentEvent | ChatMessage]) -> str | None: - if messages[-1].source != planning_agent.name: - return planning_agent.name # Always return to the planning agent after the other agents have spoken. - return None - - team = SelectorGroupChat( - [planning_agent, web_search_agent, data_analyst_agent], - model_client=OpenAIChatCompletionClient(model="gpt-4o-mini"), # Use a smaller model for the selector. - termination_condition=termination, - selector_func=selector_func, - ) - return team - -async def main() -> None: - team = create_team() - task = "Who was the Miami Heat player with the highest points in the 2006-2007 season, and what was the percentage change in his total rebounds between the 2007-2008 and 2008-2009 seasons?" - await Console(team.run_stream(task=task)) - -asyncio.run(main()) -``` - -## Nested Chat - -Nested chat allows you to nest a whole team or another agent inside -an agent. This is useful for creating a hierarchical structure of agents -or "information silos", as the nested agents cannot communicate directly -with other agents outside of the same group. - -In `v0.2`, nested chat is supported by using the `register_nested_chats` method -on the `ConversableAgent` class. -You need to specify the nested sequence of agents using dictionaries, -See [Nested Chat in v0.2](https://microsoft.github.io/autogen/0.2/docs/tutorial/conversation-patterns#nested-chats) -for more details. - -In `v0.4`, nested chat is an implementation detail of a custom agent. -You can create a custom agent that takes a team or another agent as a parameter -and implements the `on_messages` method to trigger the nested team or agent. -It is up to the application to decide how to pass or transform the messages from -and to the nested team or agent. - -The following example shows a simple nested chat that counts numbers. - -```python -import asyncio -from typing import Sequence, Tuple -from autogen_core import CancellationToken -from autogen_agentchat.agents import BaseChatAgent -from autogen_agentchat.teams import RoundRobinGroupChat -from autogen_agentchat.messages import TextMessage, ChatMessage -from autogen_agentchat.base import Response - -class CountingAgent(BaseChatAgent): - """An agent that returns a new number by adding 1 to the last number in the - input messages.""" - async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> Response: - if len(messages) == 0: - last_number = 0 # Start from 0 if no messages are given. - else: - assert isinstance(messages[-1], TextMessage) - last_number = int(messages[-1].content) # Otherwise, start from the last number. - return Response(chat_message=TextMessage(content=str(last_number + 1), source=self.name)) - - async def on_reset(self, cancellation_token: CancellationToken) -> None: - pass - - @property - def produced_message_types(self) -> Tuple[type[ChatMessage], ...]: - return (TextMessage,) - -class NestedCountingAgent(BaseChatAgent): - """An agent that increments the last number in the input messages - multiple times using a nested counting team.""" - def __init__(self, name: str, counting_team: RoundRobinGroupChat) -> None: - super().__init__(name, description="An agent that counts numbers.") - self._counting_team = counting_team - - async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> Response: - # Run the inner team with the given messages and returns the last message produced by the team. - result = await self._counting_team.run(task=messages, cancellation_token=cancellation_token) - # To stream the inner messages, implement `on_messages_stream` and use that to implement `on_messages`. - assert isinstance(result.messages[-1], TextMessage) - return Response(chat_message=result.messages[-1], inner_messages=result.messages[len(messages):-1]) - - async def on_reset(self, cancellation_token: CancellationToken) -> None: - # Reset the inner team. - await self._counting_team.reset() - - @property - def produced_message_types(self) -> Tuple[type[ChatMessage], ...]: - return (TextMessage,) - -async def main() -> None: - # Create a team of two counting agents as the inner team. - counting_agent_1 = CountingAgent("counting_agent_1", description="An agent that counts numbers.") - counting_agent_2 = CountingAgent("counting_agent_2", description="An agent that counts numbers.") - counting_team = RoundRobinGroupChat([counting_agent_1, counting_agent_2], max_turns=5) - # Create a nested counting agent that takes the inner team as a parameter. - nested_counting_agent = NestedCountingAgent("nested_counting_agent", counting_team) - # Run the nested counting agent with a message starting from 1. - response = await nested_counting_agent.on_messages([TextMessage(content="1", source="user")], CancellationToken()) - assert response.inner_messages is not None - for message in response.inner_messages: - print(message) - print(response.chat_message) - -asyncio.run(main()) -``` - -You should see the following output: - -```bash -source='counting_agent_1' models_usage=None content='2' type='TextMessage' -source='counting_agent_2' models_usage=None content='3' type='TextMessage' -source='counting_agent_1' models_usage=None content='4' type='TextMessage' -source='counting_agent_2' models_usage=None content='5' type='TextMessage' -source='counting_agent_1' models_usage=None content='6' type='TextMessage' -``` - -You can take a look at [Society of Mind Agent (Experimental)](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.SocietyOfMindAgent) for a more complex implementation. - -## Sequential Chat - -In `v0.2`, sequential chat is supported by using the `initiate_chats` function. -It takes input a list of dictionary configurations for each step of the sequence. -See [Sequential Chat in v0.2](https://microsoft.github.io/autogen/0.2/docs/tutorial/conversation-patterns#sequential-chats) -for more details. - -Base on the feedback from the community, the `initiate_chats` function -is too opinionated and not flexible enough to support the diverse set of scenarios that -users want to implement. We often find users struggling to get the `initiate_chats` function -to work when they can easily glue the steps together usign basic Python code. -Therefore, in `v0.4`, we do not provide a built-in function for sequential chat in the AgentChat API. - -Instead, you can create an event-driven sequential workflow using the Core API, -and use the other components provided the AgentChat API to implement each step of the workflow. -See an example of sequential workflow in the [Core API Tutorial](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/design-patterns/sequential-workflow.html). - -We recognize that the concept of workflow is at the heart of many applications, -and we will provide more built-in support for workflows in the future. - -## GPTAssistantAgent - -In `v0.2`, `GPTAssistantAgent` is a special agent class that is backed by the OpenAI Assistant API. - -In `v0.4`, the equivalent is the `OpenAIAssistantAgent` class. -It supports the same set of features as the `GPTAssistantAgent` in `v0.2` with -more such as customizable threads and file uploads. -See [OpenAI Assistant Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_ext.agents.openai.html#autogen_ext.agents.openai.OpenAIAssistantAgent) for more details. - -## Long Context Handling - -In `v0.2`, long context that overflows the model's context window can be handled -by using the `transforms` capability that is added to an `ConversableAgent` -after which is contructed. - -The feedbacks from our community has led us to believe this feature is essential -and should be a built-in component of `AssistantAgent`, and can be used for -every custom agent. - -In `v0.4`, we introduce the `ChatCompletionContext` base class that manages -message history and provides a virtual view of the history. Applications can use -built-in implementations such as `BufferedChatCompletionContext` to -limit the message history sent to the model, or provide their own implementations -that creates different virtual views. - -To use `BufferedChatCompletionContext` in an `AssistantAgent` in a chatbot scenario. - -```python -import asyncio -from autogen_agentchat.messages import TextMessage -from autogen_agentchat.agents import AssistantAgent -from autogen_core import CancellationToken -from autogen_core.model_context import BufferedChatCompletionContext -from autogen_ext.models.openai import OpenAIChatCompletionClient - -async def main() -> None: - model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) - - assistant = AssistantAgent( - name="assistant", - system_message="You are a helpful assistant.", - model_client=model_client, - model_context=BufferedChatCompletionContext(buffer_size=10), # Model can only view the last 10 messages. - ) - while True: - user_input = input("User: ") - if user_input == "exit": - break - response = await assistant.on_messages([TextMessage(content=user_input, source="user")], CancellationToken()) - print("Assistant:", response.chat_message.content) - -asyncio.run(main()) -``` - -In this example, the chatbot can only read the last 10 messages in the history. - -## Observability and Control - -In `v0.4` AgentChat, you can observe the agents by using the `on_messages_stream` method -which returns an async generator to stream the inner thoughts and actions of the agent. -For teams, you can use the `run_stream` method to stream the inner conversation among the agents in the team. -Your application can use these streams to observe the agents and teams in real-time. - -Both the `on_messages_stream` and `run_stream` methods takes a `CancellationToken` as a parameter -which can be used to cancel the output stream asynchronously and stop the agent or team. -For teams, you can also use termination conditions to stop the team when a certain condition is met. -See [Termination Condition Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/termination.html) -for more details. - -Unlike the `v0.2` which comes with a special logging module, the `v0.4` API -simply uses Python's `logging` module to log events such as model client calls. -See [Logging](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/logging.html) -in the Core API documentation for more details. - -## Code Executors - -The code executors in `v0.2` and `v0.4` are nearly identical except -the `v0.4` executors support async API. You can also use -`CancellationToken` to cancel a code execution if it takes too long. -See [Command Line Code Executors Tutorial](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/command-line-code-executors.html) -in the Core API documentation. - -We also added `AzureContainerCodeExecutor` that can use Azure Container Apps (ACA) -dynamic sessions for code execution. -See [ACA Dynamic Sessions Code Executor Docs](https://microsoft.github.io/autogen/dev/user-guide/extensions-user-guide/azure-container-code-executor.html). diff --git a/python/pyproject.toml b/python/pyproject.toml index 53e0c6a4f622..17c8833d3dad 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -87,7 +87,7 @@ gen-proto = "python -m grpc_tools.protoc --python_out=./packages/autogen-ext/src gen-proto-samples = "python -m grpc_tools.protoc --python_out=./packages/autogen-core/samples/protos --grpc_python_out=./packages/autogen-core/samples/protos --mypy_out=./packages/autogen-core/samples/protos --mypy_grpc_out=./packages/autogen-core/samples/protos --proto_path ../protos/ agent_events.proto" -markdown-code-lint = "python check_md_code_blocks.py migration_guide.md ../README.md" +markdown-code-lint = "python check_md_code_blocks.py ../README.md ./packages/autogen-core/docs/src/user-guide/agentchat-user-guide/*.md" [[tool.poe.tasks.gen-test-proto.sequence]] cmd = "python -m grpc_tools.protoc --python_out=./packages/autogen-core/tests/protos --grpc_python_out=./packages/autogen-core/tests/protos --mypy_out=./packages/autogen-core/tests/protos --mypy_grpc_out=./packages/autogen-core/tests/protos --proto_path ./packages/autogen-core/tests/protos serialization_test.proto" From 6511893c49d2c65fdb210ff6135abb61b7fd7687 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Mon, 30 Dec 2024 12:06:58 -0800 Subject: [PATCH 31/38] Move --- .../agentchat-user-guide/migration_guide.md | 1163 +++++++++++++++++ 1 file changed, 1163 insertions(+) create mode 100644 python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration_guide.md diff --git a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration_guide.md b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration_guide.md new file mode 100644 index 000000000000..b63e25948d19 --- /dev/null +++ b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration_guide.md @@ -0,0 +1,1163 @@ +# Migration Guide for v0.2 to v0.4 + +This is a migration guide for users of the `v0.2.*` versions of `autogen-agentchat` +to the `v0.4` version, which introduces a new set of APIs and features. +The `v0.4` version contains breaking changes. Please read this guide carefully. +We still maintain the `v0.2` version in the `0.2` branch; however, +we highly recommend you upgrade to the `v0.4` version. + +> **Note**: We no longer have admin access to the `pyautogen` PyPI package, and +> the releases from that package are no longer from Microsoft since version 0.2.34. +> To continue use the `v0.2` version of AutoGen, install it using `autogen-agentchat~=0.2`. +> Please read our [clarification statement](https://github.com/microsoft/autogen/discussions/4217) regarding forks. + +## What is `v0.4`? + +Since the release of AutoGen in 2023, we have intensively listened to our community and users from small startups and large enterprises, gathering much feedback. Based on that feedback, we built AutoGen `v0.4`, a from-the-ground-up rewrite adopting an asynchronous, event-driven architecture to address issues such as observability, flexibility, interactive control, and scale. + +The `v0.4` API is layered: +the [Core API](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/index.html) is the foundation layer offering a scalable, event-driven actor framework for creating agentic workflows; +the [AgentChat API](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/index.html) is built on Core, offering a task-driven, high-level framework for building interactive agentic applications. It is a replacement for AutoGen `v0.2`. + +Most of this guide focuses on `v0.4`'s AgentChat API; however, you can also build your own high-level framework using just the Core API. + +## New to AutoGen? + +Jump straight to the [AgentChat Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/) to get started with `v0.4`. + +## What's in this guide? + +We provide a detailed guide on how to migrate your existing codebase from `v0.2` to `v0.4`. + +See each feature below for detailed information on how to migrate. + +- [Model Client](#model-client) +- [Model Client for OpenAI-Compatible APIs](#model-client-for-openai-compatible-apis) +- [Assistant Agent](#assistant-agent) +- [Multi-Modal Agent](#multi-modal-agent) +- [User Proxy](#user-proxy) +- [Conversable Agent and Register Reply](#conversable-agent-and-register-reply) +- [Save and Load Agent State](#save-and-load-agent-state) +- [Two-Agent Chat](#two-agent-chat) +- [Tool Use](#tool-use) +- [Chat Result](#chat-result) +- [Conversion between v0.2 and v0.4 Messages](#conversion-between-v02-and-v04-messages) +- [Group Chat](#group-chat) +- [Group Chat with Resume](#group-chat-with-resume) +- [Group Chat with Custom Selector (Stateflow)](#group-chat-with-custom-selector-stateflow) +- [Nested Chat](#nested-chat) +- [Sequential Chat](#sequential-chat) +- [GPTAssistantAgent](#gptassistantagent) +- [Long-Context Handling](#long-context-handling) +- [Observability and Control](#observability-and-control) +- [Code Executors](#code-executors) + +The following features currently in `v0.2` +will be providied in the future releases of `v0.4.*` versions: + +- Model Client Cache [#4752](https://github.com/microsoft/autogen/issues/4752) +- Jupyter Code Executor [#4795](https://github.com/microsoft/autogen/issues/4795) +- Model Client Cost [#4835](https://github.com/microsoft/autogen/issues/4835) +- Teacheable Agent +- RAG Agent + +We will update this guide when the missing features become available. + +## Model Client + +In `v0.2` you configure the model client as follows, and create the `OpenAIWrapper` object. + +```python +from autogen.oai import OpenAIWrapper + +config_list = [ + {"model": "gpt-4o", "api_key": "sk-xxx"}, + {"model": "gpt-4o-mini", "api_key": "sk-xxx"}, +] + +model_client = OpenAIWrapper(config_list=config_list) +``` + +> **Note**: In AutoGen 0.2, the OpenAI client would try configs in the list until one worked. 0.4 instead expects a specfic model configuration to be chosen. + +In `v0.4`, we offers two ways to create a model client. + +### Use component config + +AutoGen 0.4 has a [generic component configuration system](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/component-config.html). Model clients are a great use case for this. See below for how to create an OpenAI chat completion client. + +```python + +from autogen_core.models import ChatCompletionClient + +config = { + "provider": "OpenAIChatCompletionClient", + "config": { + "model": "gpt-4o", + "api_key": "sk-xxx" # os.environ["...'] + } +} + +model_client = ChatCompletionClient.load_component(config) +``` + +### Use model client class directly + +Open AI: + +```python +from autogen_ext.models.openai import OpenAIChatCompletionClient + +model_client = OpenAIChatCompletionClient(model="gpt-4o", api_key="sk-xxx") +``` + +Azure OpenAI: + +```python +from autogen_ext.models.openai import AzureOpenAIChatCompletionClient + +model_client = AzureOpenAIChatCompletionClient( + azure_deployment="gpt-4o", + azure_endpoint="https://.openai.azure.com/", + model="gpt-4o", + api_version="2024-09-01-preview", + api_key="sk-xxx", +) +``` + +Read more on [OpenAI Chat Completion Client Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_ext.models.openai.html) + +## Model Client for OpenAI-Compatible APIs + +You can use a the `OpenAIChatCompletionClient` to connect to an OpenAI-Compatible API, +but you need to specify the `base_url` and `model_capabilities`. + +```python +from autogen_ext.models.openai import OpenAIChatCompletionClient + +custom_model_client = OpenAIChatCompletionClient( + model="custom-model-name", + base_url="https://custom-model.com/reset/of/the/path", + api_key="placeholder", + model_capabilities={ + "vision": True, + "function_calling": True, + "json_output": True, + }, +) +``` + +> **Note**: We don't test all the OpenAI-Compatible APIs, and many of them +> works differently from the OpenAI API even though they may claim to suppor it. +> Please test them before using them. + +Read about [Model Clients](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/models.html) +in AgentChat Tutorial and more detailed information on [Core API Docs](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/model-clients.html) + +Support for other hosted models will be added in the future. + +## Assistant Agent + +In `v0.2`, you create an assistant agent as follows: + +```python +from autogen.agentchat import AssistantAgent + +llm_config = { + "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], + "seed": 42, + "temperature": 0, +} + +assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant.", + llm_config=llm_config, +) +``` + +In `v0.4`, it is similar, but you need to specify `model_client` instead of `llm_config`. + +```python +from autogen_agentchat.agents import AssistantAgent +from autogen_ext.models.openai import OpenAIChatCompletionClient + +model_client = OpenAIChatCompletionClient(model="gpt-4o", api_key="sk-xxx", seed=42, temperature=0) + +assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant.", + model_client=model_client, +) +``` + +However, the usage is somewhat different. In `v0.4`, instead of calling `assistant.send`, +you call `assistant.on_messages` or `assistant.on_messages_stream` to handle incoming messages. +Furthermore, the `on_messages` and `on_messages_stream` methods are asynchronous, +and the latter returns an async generator to stream the inner thoughts of the agent. + +Here is how you can call the assistant agent in `v0.4` directly, continuing from the above example: + +```python +import asyncio +from autogen_agentchat.messages import TextMessage +from autogen_agentchat.agents import AssistantAgent +from autogen_core import CancellationToken +from autogen_ext.models.openai import OpenAIChatCompletionClient + +async def main() -> None: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + + assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant.", + model_client=model_client, + ) + + cancellation_token = CancellationToken() + response = await assistant.on_messages([TextMessage(content="Hello!", source="user")], cancellation_token) + print(response) + +asyncio.run(main()) +``` + +The `CancellationToken` can be used to cancel the request asynchronously +when you call `cancellation_token.cancel()`, which will cause the `await` +on the `on_messages` call to raise a `CancelledError`. + +Read more on [Agent Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/agents.html) +and +[Assistant Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.AssistantAgent). + +## Multi-Modal Agent + +The `AssistantAgent` in `v0.4` supports multi-modal inputs if the model client supports it. +The `vision` capability of the model client is used to determine if the agent supports multi-modal inputs. + +```python +import asyncio +from pathlib import Path +from autogen_agentchat.messages import MultiModalMessage +from autogen_agentchat.agents import AssistantAgent +from autogen_core import CancellationToken, Image +from autogen_ext.models.openai import OpenAIChatCompletionClient + +async def main() -> None: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + + assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant.", + model_client=model_client, + ) + + cancellation_token = CancellationToken() + message = MultiModalMessage( + content=["Here is an image:", Image.from_file(Path("test.png"))], + source="user", + ) + response = await assistant.on_messages([message], cancellation_token) + print(response) + +asyncio.run(main()) +``` + +## User Proxy + +In `v0.2`, you create a user proxy as follows: + +```python +from autogen.agentchat import UserProxyAgent + +user_proxy = UserProxyAgent( + name="user_proxy", + human_input_mode="NEVER", + max_consecutive_auto_reply=10, + code_execution_config=False, + llm_config=False, +) +``` + +This user proxy would take input from the user through console, and would terminate +if the incoming message ends with "TERMINATE". + +In `v0.4`, a user proxy is simply an agent that takes user input only, there is no +other special configuration needed. You can create a user proxy as follows: + +```python +from autogen_agentchat.agents import UserProxyAgent + +user_proxy = UserProxyAgent("user_proxy") +``` + +See [User Proxy Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.UserProxyAgent) +for more details and how to customize the input function with timeout. + +## Conversable Agent and Register Reply + +In `v0.2`, you can create a conversable agent and register a reply function as follows: + +```python +from typing import Any, Dict, List, Optional, Tuple, Union +from autogen.agentchat import ConversableAgent + +llm_config = { + "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], + "seed": 42, + "temperature": 0, +} + +conversable_agent = ConversableAgent( + name="conversable_agent", + system_message="You are a helpful assistant.", + llm_config=llm_config, + code_execution_config={"work_dir": "coding"}, + human_input_mode="NEVER", + max_consecutive_auto_reply=10, +) + +def reply_func( + recipient: ConversableAgent, + messages: Optional[List[Dict]] = None, + sender: Optional[Agent] = None, + config: Optional[Any] = None, +) -> Tuple[bool, Union[str, Dict, None]]: + # Custom reply logic here + return True, "Custom reply" + +# Register the reply function +conversable_agent.register_reply([ConversableAgent], reply_func, position=0) + +# NOTE: An async reply function will only be invoked with async send. +``` + +Rather than guessing what the `reply_func` does, all its parameters, +and what the `position` should be, in `v0.4`, we can simply create a custom agent +and implement the `on_messages`, `on_reset`, and `produced_message_types` methods. + +```python +from typing import Sequence +from autogen_core import CancellationToken +from autogen_agentchat.agents import BaseChatAgent +from autogen_agentchat.messages import TextMessage, ChatMessage +from autogen_agentchat.base import Response + +class CustomAgent(BaseChatAgent): + async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> Response: + return Response(chat_message=TextMessage(content="Custom reply", source=self.name)) + + async def on_reset(self, cancellation_token: CancellationToken) -> None: + pass + + @property + def produced_message_types(self) -> Sequence[type[ChatMessage]]: + return (TextMessage,) +``` + +You can then use the custom agent in the same way as the `AssistantAgent`. +See [Custom Agent Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/custom-agents.html) +for more details. + +## Save and Load Agent State + +In `v0.2` there is no built-in way to save and load an agent's state: you need +to implement it yourself by exporting the `chat_messages` attribute of `ConversableAgent` +and importing it back through the `chat_messages` parameter. + +In `v0.4` you can + +## Two-Agent Chat + +In `v0.2`, you can create a two-agent chat for code execution as follows: + +```python +from autogen.coding import LocalCommandLineCodeExecutor +from autogen.agentchat import AssistantAgent, UserProxyAgent + +llm_config = { + "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], + "seed": 42, + "temperature": 0, +} + +assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant. Write all code in python. Reply only 'TERMINATE' if the task is done.", + llm_config=llm_config, + is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"), +) + +user_proxy = UserProxyAgent( + name="user_proxy", + human_input_mode="NEVER", + max_consecutive_auto_reply=10, + code_execution_config={"code_executor": LocalCommandLineCodeExecutor(work_dir="coding")}, + llm_config=False, + is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"), +) + +chat_result = user_proxy.initiate_chat(assistant, message="Write a python script to print 'Hello, world!'") +# Intermediate messages are printed to the console directly. +print(chat_result) +``` + +To get the same behavior in `v0.4`, you can use the `AssistantAgent` +and `CodeExecutorAgent` together in a `RoundRobinGroupChat`. + +```python +import asyncio +from autogen_agentchat.agents import AssistantAgent, CodeExecutorAgent +from autogen_agentchat.teams import RoundRobinGroupChat +from autogen_agentchat.conditions import TextMentionTermination, MaxMessageTermination +from autogen_agentchat.ui import Console +from autogen_ext.code_executors.local import LocalCommandLineCodeExecutor +from autogen_ext.models.openai import OpenAIChatCompletionClient + +async def main() -> None: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + + assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant. Write all code in python. Reply only 'TERMINATE' if the task is done.", + model_client=model_client, + ) + + code_executor = CodeExecutorAgent( + name="code_executor", + code_executor=LocalCommandLineCodeExecutor(work_dir="coding"), + ) + + # The termination condition is a combination of text termination and max message termination, either of which will cause the chat to terminate. + termination = TextMentionTermination("TERMINATE") | MaxMessageTermination(10) + + # The group chat will alternate between the assistant and the code executor. + group_chat = RoundRobinGroupChat([assistant, code_executor], termination_condition=termination) + + # `run_stream` returns an async generator to stream the intermediate messages. + stream = group_chat.run_stream(task="Write a python script to print 'Hello, world!'") + # `Console` is a simple UI to display the stream. + await Console(stream) + +asyncio.run(main()) +``` + +## Tool Use + +In `v0.2`, to create a tool use chatbot, you must have two agents, one for calling the tool and one for executing the tool. +You need to initiate a two-agent chat for every user request. + +```python +from autogen.agentchat import AssistantAgent, UserProxyAgent, register_function + +llm_config = { + "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], + "seed": 42, + "temperature": 0, +} + +tool_caller = AssistantAgent( + name="tool_caller", + system_message="You are a helpful assistant. You can call tools to help user.", + llm_config=llm_config, + max_consecutive_auto_reply=1, # Set to 1 so that we return to the application after each assistant reply as we are building a chatbot. +) + +tool_executor = UserProxyAgent( + name="tool_executor", + human_input_mode="NEVER", + code_execution_config=False, + llm_config=False, +) + +def get_weather(city: str) -> str: + return f"The weather in {city} is 72 degree and sunny." + +# Register the tool function to the tool caller and executor. +register_function(get_weather, caller=tool_caller, executor=tool_executor) + +while True: + user_input = input("User: ") + if user_input == "exit": + break + chat_result = tool_executor.initiate_chat( + tool_caller, + message=user_input, + summary_method="reflection_with_llm", # To let the model reflect on the tool use, set to "last_msg" to return the tool call result directly. + ) + print("Assistant:", chat_result.summary) +``` + +In `v0.4`, you really just need one agent -- the `AssistantAgent` -- to handle +both the tool calling and tool execution. + +```python +import asyncio +from autogen_core import CancellationToken +from autogen_ext.models.openai import OpenAIChatCompletionClient +from autogen_agentchat.agents import AssistantAgent +from autogen_agentchat.messages import TextMessage + +def get_weather(city: str) -> str: # Async tool is possible too. + return f"The weather in {city} is 72 degree and sunny." + +async def main() -> None: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant. You can call tools to help user.", + model_client=model_client, + tools=[get_weather], + reflect_on_tool_use=True, # Set to True to have the model reflect on the tool use, set to False to return the tool call result directly. + ) + while True: + user_input = input("User: ") + if user_input == "exit": + break + response = await assistant.on_messages([TextMessage(content=user_input, source="user")], CancellationToken()) + print("Assistant:", response.chat_message.content) + +asyncio.run(main()) +``` + +## Chat Result + +In `v0.2`, you get a `ChatResult` object from the `initiate_chat` method. +For example: + +```python +chat_result = tool_executor.initiate_chat( + tool_caller, + message=user_input, + summary_method="reflection_with_llm", +) +print(chat_result.summary) # Get LLM-reflected summary of the chat. +print(chat_result.chat_history) # Get the chat history. +print(chat_result.cost) # Get the cost of the chat. +print(chat_result.human_input) # Get the human input solicited by the chat. +``` + +See [ChatResult Docs](https://microsoft.github.io/autogen/0.2/docs/reference/agentchat/chat#chatresult) +for more details. + +In `v0.4`, you get a `TaskResult` object from a `run` or `run_stream` method. +The `TaskResult` object contains the `messages` which is the message history +of the chat, including both agents' private (tool calls, etc.) and public messages. + +TODO: Add token counting result after [#4719](https://github.com/microsoft/autogen/issues/4719) is resolved. + +There are some notable differences between `TaskResult` and `ChatResult`: + +- The `messages` list in `TaskResult` uses different message format than the `ChatResult.chat_history` list. +- There is no `summary` field. It is up to the application to decide how to summarize the chat using the `messages` list. +- `human_input` is not provided in the `TaskResult` object, as the user input can be extracted from the `messages` list by filtering with the `source` field. +- `cost` is not provided in the `TaskResult` object, however, you can calculate the cost based on token usage. It would be a great community extension to add cost calculation. See [community extensions](https://microsoft.github.io/autogen/dev/user-guide/extensions-user-guide/discover.html). + +## Conversion between v0.2 and v0.4 Messages + +TODO: Resolves [#4833](https://github.com/microsoft/autogen/issues/4833), maybe include +the code in the `autogen-ext` package. + +You can use the following conversion functions to convert between a v0.4 message in +`TaskResult.messages` and a v0.2 message in `ChatResult.chat_history`. + +```python +from typing import Any, Dict, List, Literal + +from autogen_agentchat.messages import ( + AgentEvent, + ChatMessage, + HandoffMessage, + MultiModalMessage, + StopMessage, + TextMessage, + ToolCallExecutionEvent, + ToolCallRequestEvent, + ToolCallSummaryMessage, +) +from autogen_core import FunctionCall, Image +from autogen_core.models import FunctionExecutionResult + + +def convert_to_v02_message( + message: AgentEvent | ChatMessage, + role: Literal["assistant", "user", "tool"], + image_detail: Literal["auto", "high", "low"] = "auto", +) -> Dict[str, Any]: + """Convert a v0.4 AgentChat message to a v0.2 message. + + Args: + message (AgentEvent | ChatMessage): The message to convert. + role (Literal["assistant", "user", "tool"]): The role of the message. + image_detail (Literal["auto", "high", "low"], optional): The detail level of image content in multi-modal message. Defaults to "auto". + + Returns: + Dict[str, Any]: The converted AutoGen v0.2 message. + """ + v02_message: Dict[str, Any] = {} + if isinstance(message, TextMessage | StopMessage | HandoffMessage | ToolCallSummaryMessage): + v02_message = {"content": message.content, "role": role, "name": message.source} + elif isinstance(message, MultiModalMessage): + v02_message = {"content": [], "role": role, "name": message.source} + for modal in message.content: + if isinstance(modal, str): + v02_message["content"].append({"type": "text", "text": modal}) + elif isinstance(modal, Image): + v02_message["content"].append(modal.to_openai_format(detail=image_detail)) + else: + raise ValueError(f"Invalid multimodal message content: {modal}") + elif isinstance(message, ToolCallRequestEvent): + v02_message = {"tool_calls": [], "role": "assistant", "content": None, "name": message.source} + for tool_call in message.content: + v02_message["tool_calls"].append( + { + "id": tool_call.id, + "type": "function", + "function": {"name": tool_call.name, "args": tool_call.arguments}, + } + ) + elif isinstance(message, ToolCallExecutionEvent): + tool_responses: List[Dict[str, str]] = [] + for tool_result in message.content: + tool_responses.append( + { + "tool_call_id": tool_result.call_id, + "role": "tool", + "content": tool_result.content, + } + ) + content = "\n\n".join([response["content"] for response in tool_responses]) + v02_message = {"tool_responses": tool_responses, "role": "tool", "content": content} + else: + raise ValueError(f"Invalid message type: {type(message)}") + return v02_message + + +def convert_to_v04_message(message: Dict[str, Any]) -> AgentEvent | ChatMessage: + """Convert a v0.2 message to a v0.4 AgentChat message.""" + if "tool_calls" in message: + tool_calls: List[FunctionCall] = [] + for tool_call in message["tool_calls"]: + tool_calls.append( + FunctionCall( + id=tool_call["id"], + name=tool_call["function"]["name"], + arguments=tool_call["function"]["args"], + ) + ) + return ToolCallRequestEvent(source=message["name"], content=tool_calls) + elif "tool_responses" in message: + tool_results: List[FunctionExecutionResult] = [] + for tool_response in message["tool_responses"]: + tool_results.append( + FunctionExecutionResult( + call_id=tool_response["tool_call_id"], + content=tool_response["content"], + ) + ) + return ToolCallExecutionEvent(source="tools", content=tool_results) + elif isinstance(message["content"], list): + content: List[str | Image] = [] + for modal in message["content"]: # type: ignore + if modal["type"] == "text": # type: ignore + content.append(modal["text"]) # type: ignore + else: + content.append(Image.from_uri(modal["image_url"]["url"])) # type: ignore + return MultiModalMessage(content=content, source=message["name"]) + elif isinstance(message["content"], str): + return TextMessage(content=message["content"], source=message["name"]) + else: + raise ValueError(f"Unable to convert message: {message}") +``` + +## Group Chat + +In `v0.2`, you need to create a `GroupChat` dataclass and pass it into a +`GroupChatManager`, and have a participant that is a user proxy to initiate the chat. +For a simple scenario of a writer and a critic, you can do the following: + +```python +from autogen.agentchat import AssistantAgent, GroupChat, GroupChatManager + +llm_config = { + "config_list": [{"model": "gpt-4o", "api_key": "sk-xxx"}], + "seed": 42, + "temperature": 0, +} + +writer = AssistantAgent( + name="writer", + description="A writer.", + system_message="You are a writer.", + llm_config=llm_config, + is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("APPROVE"), +) + +critic = AssistantAgent( + name="critic", + description="A critic.", + system_message="You are a critic, provide feedback on the writing. Reply only 'APPROVE' if the task is done.", + llm_config=llm_config, +) + +# Create a group chat with the writer and critic. +groupchat = GroupChat(agents=[writer, critic], messages=[], max_round=12) + +# Create a group chat manager to manage the group chat, use round-robin selection method. +manager = GroupChatManager(groupchat=groupchat, llm_config=llm_config, speaker_selection_method="round_robin") + +# Initiate the chat with the editor, intermediate messages are printed to the console directly. +result = editor.initiate_chat( + manager, + message="Write a short story about a robot that discovers it has feelings.", +) +print(result.summary) +``` + +In `v0.4`, you can use the `RoundRobinGroupChat` to achieve the same behavior. + +```python +import asyncio +from autogen_agentchat.agents import AssistantAgent +from autogen_agentchat.teams import RoundRobinGroupChat +from autogen_agentchat.conditions import TextMentionTermination +from autogen_agentchat.ui import Console +from autogen_ext.models.openai import OpenAIChatCompletionClient + +async def main() -> None: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + + writer = AssistantAgent( + name="writer", + description="A writer.", + system_message="You are a writer.", + model_client=model_client, + ) + + critic = AssistantAgent( + name="critic", + description="A critic.", + system_message="You are a critic, provide feedback on the writing. Reply only 'APPROVE' if the task is done.", + model_client=model_client, + ) + + # The termination condition is a text termination, which will cause the chat to terminate when the text "APPROVE" is received. + termination = TextMentionTermination("APPROVE") + + # The group chat will alternate between the writer and the critic. + group_chat = RoundRobinGroupChat([writer, critic], termination_condition=termination, max_turns=12) + + # `run_stream` returns an async generator to stream the intermediate messages. + stream = group_chat.run_stream(task="Write a short story about a robot that discovers it has feelings.") + # `Console` is a simple UI to display the stream. + await Console(stream) + +asyncio.run(main()) +``` + +For LLM-based speaker selection, you can use the `SelectorGroupChat` instead. +See [Selector Group Chat Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/selector-group-chat.html) +and +[Selector Group Chat Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.teams.html#autogen_agentchat.teams.SelectorGroupChat) +for more details. + +> **Note**: In `v0.4`, you do not need to register functions on a user proxy to use tools +> in a group chat. You can simply pass the tool functions to the `AssistantAgent` as shown in the [Tool Use](#tool-use) section. +> The agent will automatically call the tools when needed. +> If your tool doesn't output well formed response, you can use the `reflect_on_tool_use` parameter to have the model reflect on the tool use. + +## Group Chat with Resume + +In `v0.2`, group chat with resume is a bit complicated. You need to explicitly +save the group chat messages and load them back when you want to resume the chat. +See [Resuming Group Chat in v0.2](https://microsoft.github.io/autogen/0.2/docs/topics/groupchat/resuming_groupchat) for more details. + +In `v0.4`, you can simply call `run` or `run_stream` again with the same group chat object to resume the chat. To export and load the state, you can use +`save_state` and `load_state` methods. + +```python +import asyncio +import json +from autogen_agentchat.agents import AssistantAgent +from autogen_agentchat.teams import RoundRobinGroupChat +from autogen_agentchat.conditions import TextMentionTermination +from autogen_agentchat.ui import Console +from autogen_ext.models.openai import OpenAIChatCompletionClient + +def create_team() -> RoundRobinGroupChat: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + + writer = AssistantAgent( + name="writer", + description="A writer.", + system_message="You are a writer.", + model_client=model_client, + ) + + critic = AssistantAgent( + name="critic", + description="A critic.", + system_message="You are a critic, provide feedback on the writing. Reply only 'APPROVE' if the task is done.", + model_client=model_client, + ) + + # The termination condition is a text termination, which will cause the chat to terminate when the text "APPROVE" is received. + termination = TextMentionTermination("APPROVE") + + # The group chat will alternate between the writer and the critic. + group_chat = RoundRobinGroupChat([writer, critic], termination_condition=termination) + + return group_chat + + +async def main() -> None: + # Create team. + group_chat = create_team() + + # `run_stream` returns an async generator to stream the intermediate messages. + stream = group_chat.run_stream(task="Write a short story about a robot that discovers it has feelings.") + # `Console` is a simple UI to display the stream. + await Console(stream) + + # Save the state of the group chat and all participants. + state = await group_chat.save_state() + with open("group_chat_state.json", "w") as f: + json.dump(state, f) + + # Create a new team with the same participants configuration. + group_chat = create_team() + + # Load the state of the group chat and all participants. + with open("group_chat_state.json", "r") as f: + state = json.load(f) + await group_chat.load_state(state) + + # Resume the chat. + stream = group_chat.run_stream(task="Translate the story into Chinese.") + await Console(stream) + +asyncio.run(main()) +``` + +## Group Chat with Custom Selector (Stateflow) + +In `v0.2` group chat, when the `speaker_selection_method` is set to a custom function, +it can override the default selection method. This is useful for implementing +a state-based selection method. +For more details, see [Custom Sepaker Selection in v0.2](https://microsoft.github.io/autogen/0.2/docs/topics/groupchat/customized_speaker_selection). + +In `v0.4`, you can use the `SelectorGroupChat` with `selector_func` to achieve the same behavior. +The `selector_func` is a function that takes the current message thread of the group chat +and returns the next speaker's name. If `None` is returned, the LLM-based +selection method will be used. + +Here is an example of using the state-based selection method to implement +a web search/analysis scenario. + +```python +import asyncio +from typing import Sequence +from autogen_agentchat.agents import AssistantAgent +from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination +from autogen_agentchat.messages import AgentEvent, ChatMessage +from autogen_agentchat.teams import SelectorGroupChat +from autogen_agentchat.ui import Console +from autogen_ext.models.openai import OpenAIChatCompletionClient + +# Note: This example uses mock tools instead of real APIs for demonstration purposes +def search_web_tool(query: str) -> str: + if "2006-2007" in query: + return """Here are the total points scored by Miami Heat players in the 2006-2007 season: + Udonis Haslem: 844 points + Dwayne Wade: 1397 points + James Posey: 550 points + ... + """ + elif "2007-2008" in query: + return "The number of total rebounds for Dwayne Wade in the Miami Heat season 2007-2008 is 214." + elif "2008-2009" in query: + return "The number of total rebounds for Dwayne Wade in the Miami Heat season 2008-2009 is 398." + return "No data found." + + +def percentage_change_tool(start: float, end: float) -> float: + return ((end - start) / start) * 100 + +def create_team() -> SelectorGroupChat: + model_client = OpenAIChatCompletionClient(model="gpt-4o") + + planning_agent = AssistantAgent( + "PlanningAgent", + description="An agent for planning tasks, this agent should be the first to engage when given a new task.", + model_client=model_client, + system_message=""" + You are a planning agent. + Your job is to break down complex tasks into smaller, manageable subtasks. + Your team members are: + Web search agent: Searches for information + Data analyst: Performs calculations + + You only plan and delegate tasks - you do not execute them yourself. + + When assigning tasks, use this format: + 1. : + + After all tasks are complete, summarize the findings and end with "TERMINATE". + """, + ) + + web_search_agent = AssistantAgent( + "WebSearchAgent", + description="A web search agent.", + tools=[search_web_tool], + model_client=model_client, + system_message=""" + You are a web search agent. + Your only tool is search_tool - use it to find information. + You make only one search call at a time. + Once you have the results, you never do calculations based on them. + """, + ) + + data_analyst_agent = AssistantAgent( + "DataAnalystAgent", + description="A data analyst agent. Useful for performing calculations.", + model_client=model_client, + tools=[percentage_change_tool], + system_message=""" + You are a data analyst. + Given the tasks you have been assigned, you should analyze the data and provide results using the tools provided. + """, + ) + + # The termination condition is a combination of text mention termination and max message termination. + text_mention_termination = TextMentionTermination("TERMINATE") + max_messages_termination = MaxMessageTermination(max_messages=25) + termination = text_mention_termination | max_messages_termination + + # The selector function is a function that takes the current message thread of the group chat + # and returns the next speaker's name. If None is returned, the LLM-based selection method will be used. + def selector_func(messages: Sequence[AgentEvent | ChatMessage]) -> str | None: + if messages[-1].source != planning_agent.name: + return planning_agent.name # Always return to the planning agent after the other agents have spoken. + return None + + team = SelectorGroupChat( + [planning_agent, web_search_agent, data_analyst_agent], + model_client=OpenAIChatCompletionClient(model="gpt-4o-mini"), # Use a smaller model for the selector. + termination_condition=termination, + selector_func=selector_func, + ) + return team + +async def main() -> None: + team = create_team() + task = "Who was the Miami Heat player with the highest points in the 2006-2007 season, and what was the percentage change in his total rebounds between the 2007-2008 and 2008-2009 seasons?" + await Console(team.run_stream(task=task)) + +asyncio.run(main()) +``` + +## Nested Chat + +Nested chat allows you to nest a whole team or another agent inside +an agent. This is useful for creating a hierarchical structure of agents +or "information silos", as the nested agents cannot communicate directly +with other agents outside of the same group. + +In `v0.2`, nested chat is supported by using the `register_nested_chats` method +on the `ConversableAgent` class. +You need to specify the nested sequence of agents using dictionaries, +See [Nested Chat in v0.2](https://microsoft.github.io/autogen/0.2/docs/tutorial/conversation-patterns#nested-chats) +for more details. + +In `v0.4`, nested chat is an implementation detail of a custom agent. +You can create a custom agent that takes a team or another agent as a parameter +and implements the `on_messages` method to trigger the nested team or agent. +It is up to the application to decide how to pass or transform the messages from +and to the nested team or agent. + +The following example shows a simple nested chat that counts numbers. + +```python +import asyncio +from typing import Sequence +from autogen_core import CancellationToken +from autogen_agentchat.agents import BaseChatAgent +from autogen_agentchat.teams import RoundRobinGroupChat +from autogen_agentchat.messages import TextMessage, ChatMessage +from autogen_agentchat.base import Response + +class CountingAgent(BaseChatAgent): + """An agent that returns a new number by adding 1 to the last number in the + input messages.""" + async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> Response: + if len(messages) == 0: + last_number = 0 # Start from 0 if no messages are given. + else: + assert isinstance(messages[-1], TextMessage) + last_number = int(messages[-1].content) # Otherwise, start from the last number. + return Response(chat_message=TextMessage(content=str(last_number + 1), source=self.name)) + + async def on_reset(self, cancellation_token: CancellationToken) -> None: + pass + + @property + def produced_message_types(self) -> Sequence[type[ChatMessage]]: + return (TextMessage,) + +class NestedCountingAgent(BaseChatAgent): + """An agent that increments the last number in the input messages + multiple times using a nested counting team.""" + def __init__(self, name: str, counting_team: RoundRobinGroupChat) -> None: + super().__init__(name, description="An agent that counts numbers.") + self._counting_team = counting_team + + async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> Response: + # Run the inner team with the given messages and returns the last message produced by the team. + result = await self._counting_team.run(task=messages, cancellation_token=cancellation_token) + # To stream the inner messages, implement `on_messages_stream` and use that to implement `on_messages`. + assert isinstance(result.messages[-1], TextMessage) + return Response(chat_message=result.messages[-1], inner_messages=result.messages[len(messages):-1]) + + async def on_reset(self, cancellation_token: CancellationToken) -> None: + # Reset the inner team. + await self._counting_team.reset() + + @property + def produced_message_types(self) -> Sequence[type[ChatMessage]]: + return (TextMessage,) + +async def main() -> None: + # Create a team of two counting agents as the inner team. + counting_agent_1 = CountingAgent("counting_agent_1", description="An agent that counts numbers.") + counting_agent_2 = CountingAgent("counting_agent_2", description="An agent that counts numbers.") + counting_team = RoundRobinGroupChat([counting_agent_1, counting_agent_2], max_turns=5) + # Create a nested counting agent that takes the inner team as a parameter. + nested_counting_agent = NestedCountingAgent("nested_counting_agent", counting_team) + # Run the nested counting agent with a message starting from 1. + response = await nested_counting_agent.on_messages([TextMessage(content="1", source="user")], CancellationToken()) + assert response.inner_messages is not None + for message in response.inner_messages: + print(message) + print(response.chat_message) + +asyncio.run(main()) +``` + +You should see the following output: + +```bash +source='counting_agent_1' models_usage=None content='2' type='TextMessage' +source='counting_agent_2' models_usage=None content='3' type='TextMessage' +source='counting_agent_1' models_usage=None content='4' type='TextMessage' +source='counting_agent_2' models_usage=None content='5' type='TextMessage' +source='counting_agent_1' models_usage=None content='6' type='TextMessage' +``` + +You can take a look at [Society of Mind Agent (Experimental)](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.SocietyOfMindAgent) for a more complex implementation. + +## Sequential Chat + +In `v0.2`, sequential chat is supported by using the `initiate_chats` function. +It takes input a list of dictionary configurations for each step of the sequence. +See [Sequential Chat in v0.2](https://microsoft.github.io/autogen/0.2/docs/tutorial/conversation-patterns#sequential-chats) +for more details. + +Base on the feedback from the community, the `initiate_chats` function +is too opinionated and not flexible enough to support the diverse set of scenarios that +users want to implement. We often find users struggling to get the `initiate_chats` function +to work when they can easily glue the steps together usign basic Python code. +Therefore, in `v0.4`, we do not provide a built-in function for sequential chat in the AgentChat API. + +Instead, you can create an event-driven sequential workflow using the Core API, +and use the other components provided the AgentChat API to implement each step of the workflow. +See an example of sequential workflow in the [Core API Tutorial](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/design-patterns/sequential-workflow.html). + +We recognize that the concept of workflow is at the heart of many applications, +and we will provide more built-in support for workflows in the future. + +## GPTAssistantAgent + +In `v0.2`, `GPTAssistantAgent` is a special agent class that is backed by the OpenAI Assistant API. + +In `v0.4`, the equivalent is the `OpenAIAssistantAgent` class. +It supports the same set of features as the `GPTAssistantAgent` in `v0.2` with +more such as customizable threads and file uploads. +See [OpenAI Assistant Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_ext.agents.openai.html#autogen_ext.agents.openai.OpenAIAssistantAgent) for more details. + +## Long Context Handling + +In `v0.2`, long context that overflows the model's context window can be handled +by using the `transforms` capability that is added to an `ConversableAgent` +after which is contructed. + +The feedbacks from our community has led us to believe this feature is essential +and should be a built-in component of `AssistantAgent`, and can be used for +every custom agent. + +In `v0.4`, we introduce the `ChatCompletionContext` base class that manages +message history and provides a virtual view of the history. Applications can use +built-in implementations such as `BufferedChatCompletionContext` to +limit the message history sent to the model, or provide their own implementations +that creates different virtual views. + +To use `BufferedChatCompletionContext` in an `AssistantAgent` in a chatbot scenario. + +```python +import asyncio +from autogen_agentchat.messages import TextMessage +from autogen_agentchat.agents import AssistantAgent +from autogen_core import CancellationToken +from autogen_core.model_context import BufferedChatCompletionContext +from autogen_ext.models.openai import OpenAIChatCompletionClient + +async def main() -> None: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + + assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant.", + model_client=model_client, + model_context=BufferedChatCompletionContext(buffer_size=10), # Model can only view the last 10 messages. + ) + while True: + user_input = input("User: ") + if user_input == "exit": + break + response = await assistant.on_messages([TextMessage(content=user_input, source="user")], CancellationToken()) + print("Assistant:", response.chat_message.content) + +asyncio.run(main()) +``` + +In this example, the chatbot can only read the last 10 messages in the history. + +## Observability and Control + +In `v0.4` AgentChat, you can observe the agents by using the `on_messages_stream` method +which returns an async generator to stream the inner thoughts and actions of the agent. +For teams, you can use the `run_stream` method to stream the inner conversation among the agents in the team. +Your application can use these streams to observe the agents and teams in real-time. + +Both the `on_messages_stream` and `run_stream` methods takes a `CancellationToken` as a parameter +which can be used to cancel the output stream asynchronously and stop the agent or team. +For teams, you can also use termination conditions to stop the team when a certain condition is met. +See [Termination Condition Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/termination.html) +for more details. + +Unlike the `v0.2` which comes with a special logging module, the `v0.4` API +simply uses Python's `logging` module to log events such as model client calls. +See [Logging](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/logging.html) +in the Core API documentation for more details. + +## Code Executors + +The code executors in `v0.2` and `v0.4` are nearly identical except +the `v0.4` executors support async API. You can also use +`CancellationToken` to cancel a code execution if it takes too long. +See [Command Line Code Executors Tutorial](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/command-line-code-executors.html) +in the Core API documentation. + +We also added `AzureContainerCodeExecutor` that can use Azure Container Apps (ACA) +dynamic sessions for code execution. +See [ACA Dynamic Sessions Code Executor Docs](https://microsoft.github.io/autogen/dev/user-guide/extensions-user-guide/azure-container-code-executor.html). From 7563107d5c4a5cb736e8850983a21bf520e29472 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Mon, 30 Dec 2024 12:14:43 -0800 Subject: [PATCH 32/38] Update --- .../agentchat-user-guide/migration_guide.md | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration_guide.md b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration_guide.md index b63e25948d19..1079c5ed7c27 100644 --- a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration_guide.md +++ b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration_guide.md @@ -44,6 +44,7 @@ See each feature below for detailed information on how to migrate. - [Conversion between v0.2 and v0.4 Messages](#conversion-between-v02-and-v04-messages) - [Group Chat](#group-chat) - [Group Chat with Resume](#group-chat-with-resume) +- [Save and Load Group Chat State](#save-and-load-group-chat-state) - [Group Chat with Custom Selector (Stateflow)](#group-chat-with-custom-selector-stateflow) - [Nested Chat](#nested-chat) - [Sequential Chat](#sequential-chat) @@ -364,7 +365,56 @@ In `v0.2` there is no built-in way to save and load an agent's state: you need to implement it yourself by exporting the `chat_messages` attribute of `ConversableAgent` and importing it back through the `chat_messages` parameter. -In `v0.4` you can +In `v0.4`, you can call `save_state` and `load_state` methods on agents to save and load their state. + +```python +import asyncio +import json +from autogen_agentchat.messages import TextMessage +from autogen_agentchat.agents import AssistantAgent +from autogen_core import CancellationToken +from autogen_ext.models.openai import OpenAIChatCompletionClient + +async def main() -> None: + model_client = OpenAIChatCompletionClient(model="gpt-4o", seed=42, temperature=0) + + assistant = AssistantAgent( + name="assistant", + system_message="You are a helpful assistant.", + model_client=model_client, + ) + + cancellation_token = CancellationToken() + response = await assistant.on_messages([TextMessage(content="Hello!", source="user")], cancellation_token) + print(response) + + # Save the state. + state = await assistant.save_state(cancellation_token) + + # (Optional) Write state to disk. + with open("assistant_state.json", "w") as f: + json.dump(state, f) + + # (Optional) Load it back from disk. + with open("assistant_state.json", "r") as f: + state = json.load(f) + print(state) # Inspect the state, which contains the chat history. + + # Carry on the chat. + response = await assistant.on_messages([TextMessage(content="Tell me a joke.", source="user")], cancellation_token) + print(response) + + # Load the state, resulting the agent to revert to the previous state before the last message. + await assistant.load_state(state) + + # Carry on the same chat again. + response = await assistant.on_messages([TextMessage(content="Tell me a joke.", source="user")], cancellation_token) + +asyncio.run(main()) +``` + +You can also call `save_state` and `load_state` on any teams, such as `RoundRobinGroupChat` +to save and load the state of the entire team. ## Two-Agent Chat @@ -838,6 +888,13 @@ async def main() -> None: asyncio.run(main()) ``` +## Save and Load Group Chat State + +In `v0.2`, you need to explicitly save the group chat messages and load them back when you want to resume the chat. + +In `v0.4`, you can simply call `save_state` and `load_state` methods on the group chat object. +See [Group Chat with Resume](#group-chat-with-resume) for an example. + ## Group Chat with Custom Selector (Stateflow) In `v0.2` group chat, when the `speaker_selection_method` is set to a custom function, From 2b6f3009c12f9e3712c3b9a167961c7d0984fae3 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Mon, 30 Dec 2024 12:38:54 -0800 Subject: [PATCH 33/38] Update --- .../packages/autogen-core/docs/src/index.md | 6 +++ .../user-guide/agentchat-user-guide/index.md | 7 +++ ...{migration_guide.md => migration-guide.md} | 46 +++++++++---------- 3 files changed, 36 insertions(+), 23 deletions(-) rename python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/{migration_guide.md => migration-guide.md} (93%) diff --git a/python/packages/autogen-core/docs/src/index.md b/python/packages/autogen-core/docs/src/index.md index 06fb97bb20b5..346a1b22b943 100644 --- a/python/packages/autogen-core/docs/src/index.md +++ b/python/packages/autogen-core/docs/src/index.md @@ -71,6 +71,12 @@ pip install "autogen-agentchat==0.4.0.dev12" Get Started ``` +```{button-ref} user-guide/agentchat-user-guide/migration-guide +:color: secondary + +Migration Guide (0.2.x to 0.4.x) +``` + ::: :::{grid-item-card} {fas}`palette;pst-color-primary` Studio :shadow: none diff --git a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/index.md b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/index.md index faadedc1611e..51867eda31b8 100644 --- a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/index.md +++ b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/index.md @@ -42,6 +42,12 @@ Step-by-step guide to using AgentChat Sample code and use cases ::: + +:::{grid-item-card} {fas}`truck-moving;pst-color-primary` Migration Guide +:link: ./migration-guide.html + +How to migrate from AutoGen 0.2.x to 0.4.x. +::: :::: ```{toctree} @@ -50,6 +56,7 @@ Sample code and use cases installation quickstart +migration-guide ``` ```{toctree} diff --git a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration_guide.md b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md similarity index 93% rename from python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration_guide.md rename to python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md index 1079c5ed7c27..cc02b6da5df0 100644 --- a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration_guide.md +++ b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md @@ -16,14 +16,14 @@ we highly recommend you upgrade to the `v0.4` version. Since the release of AutoGen in 2023, we have intensively listened to our community and users from small startups and large enterprises, gathering much feedback. Based on that feedback, we built AutoGen `v0.4`, a from-the-ground-up rewrite adopting an asynchronous, event-driven architecture to address issues such as observability, flexibility, interactive control, and scale. The `v0.4` API is layered: -the [Core API](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/index.html) is the foundation layer offering a scalable, event-driven actor framework for creating agentic workflows; -the [AgentChat API](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/index.html) is built on Core, offering a task-driven, high-level framework for building interactive agentic applications. It is a replacement for AutoGen `v0.2`. +the [Core API](../core-user-guide/index.md) is the foundation layer offering a scalable, event-driven actor framework for creating agentic workflows; +the [AgentChat API](./index.md) is built on Core, offering a task-driven, high-level framework for building interactive agentic applications. It is a replacement for AutoGen `v0.2`. Most of this guide focuses on `v0.4`'s AgentChat API; however, you can also build your own high-level framework using just the Core API. ## New to AutoGen? -Jump straight to the [AgentChat Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/) to get started with `v0.4`. +Jump straight to the [AgentChat Tutorial](./tutorial/models.ipynb) to get started with `v0.4`. ## What's in this guide? @@ -85,7 +85,7 @@ In `v0.4`, we offers two ways to create a model client. ### Use component config -AutoGen 0.4 has a [generic component configuration system](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/component-config.html). Model clients are a great use case for this. See below for how to create an OpenAI chat completion client. +AutoGen 0.4 has a [generic component configuration system](../core-user-guide/framework/component-config.ipynb). Model clients are a great use case for this. See below for how to create an OpenAI chat completion client. ```python @@ -126,12 +126,12 @@ model_client = AzureOpenAIChatCompletionClient( ) ``` -Read more on [OpenAI Chat Completion Client Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_ext.models.openai.html) +Read more on [OpenAI Chat Completion Client Docs](../../reference/python/autogen_ext.models.openai.rst) ## Model Client for OpenAI-Compatible APIs You can use a the `OpenAIChatCompletionClient` to connect to an OpenAI-Compatible API, -but you need to specify the `base_url` and `model_capabilities`. +but you need to specify the `base_url` and `model_info`. ```python from autogen_ext.models.openai import OpenAIChatCompletionClient @@ -140,7 +140,7 @@ custom_model_client = OpenAIChatCompletionClient( model="custom-model-name", base_url="https://custom-model.com/reset/of/the/path", api_key="placeholder", - model_capabilities={ + model_info={ "vision": True, "function_calling": True, "json_output": True, @@ -152,8 +152,8 @@ custom_model_client = OpenAIChatCompletionClient( > works differently from the OpenAI API even though they may claim to suppor it. > Please test them before using them. -Read about [Model Clients](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/models.html) -in AgentChat Tutorial and more detailed information on [Core API Docs](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/model-clients.html) +Read about [Model Clients](./tutorial/models.ipynb) +in AgentChat Tutorial and more detailed information on [Core API Docs](../core-user-guide/framework/model-clients.ipynb) Support for other hosted models will be added in the future. @@ -226,9 +226,9 @@ The `CancellationToken` can be used to cancel the request asynchronously when you call `cancellation_token.cancel()`, which will cause the `await` on the `on_messages` call to raise a `CancelledError`. -Read more on [Agent Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/agents.html) +Read more on [Agent Tutorial](./tutorial/agents.ipynb) and -[Assistant Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.AssistantAgent). +[Assistant Agent Docs](../../reference/python/autogen_agentchat.agents.rst#autogen_agentchat.agents.AssistantAgent). ## Multi-Modal Agent @@ -291,7 +291,7 @@ from autogen_agentchat.agents import UserProxyAgent user_proxy = UserProxyAgent("user_proxy") ``` -See [User Proxy Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.UserProxyAgent) +See [User Proxy Agent Docs](../../reference/python/autogen_agentchat.agents.rst#autogen_agentchat.agents.UserProxyAgent) for more details and how to customize the input function with timeout. ## Conversable Agent and Register Reply @@ -356,7 +356,7 @@ class CustomAgent(BaseChatAgent): ``` You can then use the custom agent in the same way as the `AssistantAgent`. -See [Custom Agent Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/custom-agents.html) +See [Custom Agent Tutorial](./tutorial/custom-agents.ipynb) for more details. ## Save and Load Agent State @@ -600,7 +600,7 @@ There are some notable differences between `TaskResult` and `ChatResult`: - The `messages` list in `TaskResult` uses different message format than the `ChatResult.chat_history` list. - There is no `summary` field. It is up to the application to decide how to summarize the chat using the `messages` list. - `human_input` is not provided in the `TaskResult` object, as the user input can be extracted from the `messages` list by filtering with the `source` field. -- `cost` is not provided in the `TaskResult` object, however, you can calculate the cost based on token usage. It would be a great community extension to add cost calculation. See [community extensions](https://microsoft.github.io/autogen/dev/user-guide/extensions-user-guide/discover.html). +- `cost` is not provided in the `TaskResult` object, however, you can calculate the cost based on token usage. It would be a great community extension to add cost calculation. See [community extensions](../extensions-user-guide/discover.md). ## Conversion between v0.2 and v0.4 Messages @@ -805,9 +805,9 @@ asyncio.run(main()) ``` For LLM-based speaker selection, you can use the `SelectorGroupChat` instead. -See [Selector Group Chat Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/selector-group-chat.html) +See [Selector Group Chat Tutorial](./tutorial/selector-group-chat.ipynb) and -[Selector Group Chat Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.teams.html#autogen_agentchat.teams.SelectorGroupChat) +[Selector Group Chat Docs](../../reference/python/autogen_agentchat.teams.rst#autogen_agentchat.teams.SelectorGroupChat) for more details. > **Note**: In `v0.4`, you do not need to register functions on a user proxy to use tools @@ -1111,7 +1111,7 @@ source='counting_agent_2' models_usage=None content='5' type='TextMessage' source='counting_agent_1' models_usage=None content='6' type='TextMessage' ``` -You can take a look at [Society of Mind Agent (Experimental)](https://microsoft.github.io/autogen/dev/reference/python/autogen_agentchat.agents.html#autogen_agentchat.agents.SocietyOfMindAgent) for a more complex implementation. +You can take a look at [Society of Mind Agent (Experimental)](../../reference/python/autogen_agentchat.agents.rst#autogen_agentchat.agents.SocietyOfMindAgent) for a more complex implementation. ## Sequential Chat @@ -1128,7 +1128,7 @@ Therefore, in `v0.4`, we do not provide a built-in function for sequential chat Instead, you can create an event-driven sequential workflow using the Core API, and use the other components provided the AgentChat API to implement each step of the workflow. -See an example of sequential workflow in the [Core API Tutorial](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/design-patterns/sequential-workflow.html). +See an example of sequential workflow in the [Core API Tutorial](../core-user-guide/design-patterns/sequential-workflow.ipynb). We recognize that the concept of workflow is at the heart of many applications, and we will provide more built-in support for workflows in the future. @@ -1140,7 +1140,7 @@ In `v0.2`, `GPTAssistantAgent` is a special agent class that is backed by the Op In `v0.4`, the equivalent is the `OpenAIAssistantAgent` class. It supports the same set of features as the `GPTAssistantAgent` in `v0.2` with more such as customizable threads and file uploads. -See [OpenAI Assistant Agent Docs](https://microsoft.github.io/autogen/dev/reference/python/autogen_ext.agents.openai.html#autogen_ext.agents.openai.OpenAIAssistantAgent) for more details. +See [OpenAI Assistant Agent Docs](../../reference/python/autogen_ext.agents.openai.rst#autogen_ext.agents.openai.OpenAIAssistantAgent) for more details. ## Long Context Handling @@ -1199,12 +1199,12 @@ Your application can use these streams to observe the agents and teams in real-t Both the `on_messages_stream` and `run_stream` methods takes a `CancellationToken` as a parameter which can be used to cancel the output stream asynchronously and stop the agent or team. For teams, you can also use termination conditions to stop the team when a certain condition is met. -See [Termination Condition Tutorial](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/termination.html) +See [Termination Condition Tutorial](./tutorial/termination.ipynb) for more details. Unlike the `v0.2` which comes with a special logging module, the `v0.4` API simply uses Python's `logging` module to log events such as model client calls. -See [Logging](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/logging.html) +See [Logging](../core-user-guide/framework/logging.md) in the Core API documentation for more details. ## Code Executors @@ -1212,9 +1212,9 @@ in the Core API documentation for more details. The code executors in `v0.2` and `v0.4` are nearly identical except the `v0.4` executors support async API. You can also use `CancellationToken` to cancel a code execution if it takes too long. -See [Command Line Code Executors Tutorial](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/framework/command-line-code-executors.html) +See [Command Line Code Executors Tutorial](../core-user-guide/framework/command-line-code-executors.ipynb) in the Core API documentation. We also added `AzureContainerCodeExecutor` that can use Azure Container Apps (ACA) dynamic sessions for code execution. -See [ACA Dynamic Sessions Code Executor Docs](https://microsoft.github.io/autogen/dev/user-guide/extensions-user-guide/azure-container-code-executor.html). +See [ACA Dynamic Sessions Code Executor Docs](../extensions-user-guide/azure-container-code-executor.ipynb). From 333780cf77a03285cb4f1e1ec901918c6d1a7cee Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Mon, 30 Dec 2024 12:53:02 -0800 Subject: [PATCH 34/38] update --- .../agentchat-user-guide/migration-guide.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md index cc02b6da5df0..606f197c9989 100644 --- a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md +++ b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md @@ -126,7 +126,7 @@ model_client = AzureOpenAIChatCompletionClient( ) ``` -Read more on [OpenAI Chat Completion Client Docs](../../reference/python/autogen_ext.models.openai.rst) +Read more on {py:class}`~autogen_ext.models.openai.OpenAIChatCompletionClient`. ## Model Client for OpenAI-Compatible APIs @@ -227,8 +227,7 @@ when you call `cancellation_token.cancel()`, which will cause the `await` on the `on_messages` call to raise a `CancelledError`. Read more on [Agent Tutorial](./tutorial/agents.ipynb) -and -[Assistant Agent Docs](../../reference/python/autogen_agentchat.agents.rst#autogen_agentchat.agents.AssistantAgent). +and {py:class}`~autogen_agentchat.agents.AssistantAgent`. ## Multi-Modal Agent @@ -291,7 +290,7 @@ from autogen_agentchat.agents import UserProxyAgent user_proxy = UserProxyAgent("user_proxy") ``` -See [User Proxy Agent Docs](../../reference/python/autogen_agentchat.agents.rst#autogen_agentchat.agents.UserProxyAgent) +See {py:class}`~autogen_agentchat.agents.UserProxyAgent` for more details and how to customize the input function with timeout. ## Conversable Agent and Register Reply @@ -389,7 +388,7 @@ async def main() -> None: print(response) # Save the state. - state = await assistant.save_state(cancellation_token) + state = await assistant.save_state() # (Optional) Write state to disk. with open("assistant_state.json", "w") as f: @@ -806,9 +805,7 @@ asyncio.run(main()) For LLM-based speaker selection, you can use the `SelectorGroupChat` instead. See [Selector Group Chat Tutorial](./tutorial/selector-group-chat.ipynb) -and -[Selector Group Chat Docs](../../reference/python/autogen_agentchat.teams.rst#autogen_agentchat.teams.SelectorGroupChat) -for more details. +and {py:class}`~autogen_agentchat.teams.SelectorGroupChat` for more details. > **Note**: In `v0.4`, you do not need to register functions on a user proxy to use tools > in a group chat. You can simply pass the tool functions to the `AssistantAgent` as shown in the [Tool Use](#tool-use) section. @@ -1111,7 +1108,8 @@ source='counting_agent_2' models_usage=None content='5' type='TextMessage' source='counting_agent_1' models_usage=None content='6' type='TextMessage' ``` -You can take a look at [Society of Mind Agent (Experimental)](../../reference/python/autogen_agentchat.agents.rst#autogen_agentchat.agents.SocietyOfMindAgent) for a more complex implementation. +You can take a look at {py:class}`~autogen_agentchat.agents.SocietyOfMindAgent` +for a more complex implementation. ## Sequential Chat @@ -1140,7 +1138,7 @@ In `v0.2`, `GPTAssistantAgent` is a special agent class that is backed by the Op In `v0.4`, the equivalent is the `OpenAIAssistantAgent` class. It supports the same set of features as the `GPTAssistantAgent` in `v0.2` with more such as customizable threads and file uploads. -See [OpenAI Assistant Agent Docs](../../reference/python/autogen_ext.agents.openai.rst#autogen_ext.agents.openai.OpenAIAssistantAgent) for more details. +See {py:class}`~autogen_ext.agents.openai.OpenAIAssistantAgent` for more details. ## Long Context Handling From 94bee4e3826b175a27196190dd171419e33ae6ac Mon Sep 17 00:00:00 2001 From: Jack Gerrits Date: Mon, 30 Dec 2024 16:18:33 -0500 Subject: [PATCH 35/38] Make buttons side by side --- python/packages/autogen-core/docs/src/index.md | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/python/packages/autogen-core/docs/src/index.md b/python/packages/autogen-core/docs/src/index.md index 346a1b22b943..a9334f5f4205 100644 --- a/python/packages/autogen-core/docs/src/index.md +++ b/python/packages/autogen-core/docs/src/index.md @@ -65,17 +65,8 @@ pip install "autogen-agentchat==0.4.0.dev12" +++ -```{button-ref} user-guide/agentchat-user-guide/quickstart -:color: secondary - -Get Started -``` - -```{button-ref} user-guide/agentchat-user-guide/migration-guide -:color: secondary - -Migration Guide (0.2.x to 0.4.x) -``` +Get Started +Migration Guide (0.2.x to 0.4.x) ::: :::{grid-item-card} {fas}`palette;pst-color-primary` Studio From 1c057cb653ae3a884ea28b2911c87167c6359b22 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Mon, 30 Dec 2024 13:23:25 -0800 Subject: [PATCH 36/38] Update --- .../agentchat-user-guide/migration-guide.md | 75 ++++++++++++------- .../tutorial/selector-group-chat.ipynb | 16 +++- 2 files changed, 65 insertions(+), 26 deletions(-) diff --git a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md index 606f197c9989..1199d1eb7d81 100644 --- a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md +++ b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md @@ -45,6 +45,7 @@ See each feature below for detailed information on how to migrate. - [Group Chat](#group-chat) - [Group Chat with Resume](#group-chat-with-resume) - [Save and Load Group Chat State](#save-and-load-group-chat-state) +- [Group Chat with Tool Use](#group-chat-with-tool-use) - [Group Chat with Custom Selector (Stateflow)](#group-chat-with-custom-selector-stateflow) - [Nested Chat](#nested-chat) - [Sequential Chat](#sequential-chat) @@ -144,6 +145,7 @@ custom_model_client = OpenAIChatCompletionClient( "vision": True, "function_calling": True, "json_output": True, + "model_family": "unknown", }, ) ``` @@ -222,7 +224,7 @@ async def main() -> None: asyncio.run(main()) ``` -The `CancellationToken` can be used to cancel the request asynchronously +The {py:class}`~autogen_core.CancellationToken` can be used to cancel the request asynchronously when you call `cancellation_token.cancel()`, which will cause the `await` on the `on_messages` call to raise a `CancelledError`. @@ -231,7 +233,7 @@ and {py:class}`~autogen_agentchat.agents.AssistantAgent`. ## Multi-Modal Agent -The `AssistantAgent` in `v0.4` supports multi-modal inputs if the model client supports it. +The {py:class}`~autogen_agentchat.agents.AssistantAgent` in `v0.4` supports multi-modal inputs if the model client supports it. The `vision` capability of the model client is used to determine if the agent supports multi-modal inputs. ```python @@ -354,7 +356,7 @@ class CustomAgent(BaseChatAgent): return (TextMessage,) ``` -You can then use the custom agent in the same way as the `AssistantAgent`. +You can then use the custom agent in the same way as the {py:class}`~autogen_agentchat.agents.AssistantAgent`. See [Custom Agent Tutorial](./tutorial/custom-agents.ipynb) for more details. @@ -412,7 +414,7 @@ async def main() -> None: asyncio.run(main()) ``` -You can also call `save_state` and `load_state` on any teams, such as `RoundRobinGroupChat` +You can also call `save_state` and `load_state` on any teams, such as {py:class}`~autogen_agentchat.teams.RoundRobinGroupChat` to save and load the state of the entire team. ## Two-Agent Chat @@ -450,8 +452,8 @@ chat_result = user_proxy.initiate_chat(assistant, message="Write a python script print(chat_result) ``` -To get the same behavior in `v0.4`, you can use the `AssistantAgent` -and `CodeExecutorAgent` together in a `RoundRobinGroupChat`. +To get the same behavior in `v0.4`, you can use the {py:class}`~autogen_agentchat.agents.AssistantAgent` +and {py:class}`~autogen_agentchat.agents.CodeExecutorAgent` together in a {py:class}`~autogen_agentchat.teams.RoundRobinGroupChat`. ```python import asyncio @@ -536,7 +538,7 @@ while True: print("Assistant:", chat_result.summary) ``` -In `v0.4`, you really just need one agent -- the `AssistantAgent` -- to handle +In `v0.4`, you really just need one agent -- the {py:class}`~autogen_agentchat.agents.AssistantAgent` -- to handle both the tool calling and tool execution. ```python @@ -568,6 +570,11 @@ async def main() -> None: asyncio.run(main()) ``` +When using tool-equipped agents inside a group chat such as +{py:class}`~autogen_agentchat.teams.RoundRobinGroupChat`, +you simply do the same as above to add tools to the agents, and create a +group chat with the agents. + ## Chat Result In `v0.2`, you get a `ChatResult` object from the `initiate_chat` method. @@ -588,18 +595,18 @@ print(chat_result.human_input) # Get the human input solicited by the chat. See [ChatResult Docs](https://microsoft.github.io/autogen/0.2/docs/reference/agentchat/chat#chatresult) for more details. -In `v0.4`, you get a `TaskResult` object from a `run` or `run_stream` method. -The `TaskResult` object contains the `messages` which is the message history +In `v0.4`, you get a {py:class}`~autogen_agentchat.base.TaskResult` object from a `run` or `run_stream` method. +The {py:class}`~autogen_agentchat.base.TaskResult` object contains the `messages` which is the message history of the chat, including both agents' private (tool calls, etc.) and public messages. TODO: Add token counting result after [#4719](https://github.com/microsoft/autogen/issues/4719) is resolved. -There are some notable differences between `TaskResult` and `ChatResult`: +There are some notable differences between {py:class}`~autogen_agentchat.base.TaskResult` and `ChatResult`: -- The `messages` list in `TaskResult` uses different message format than the `ChatResult.chat_history` list. +- The `messages` list in {py:class}`~autogen_agentchat.base.TaskResult` uses different message format than the `ChatResult.chat_history` list. - There is no `summary` field. It is up to the application to decide how to summarize the chat using the `messages` list. -- `human_input` is not provided in the `TaskResult` object, as the user input can be extracted from the `messages` list by filtering with the `source` field. -- `cost` is not provided in the `TaskResult` object, however, you can calculate the cost based on token usage. It would be a great community extension to add cost calculation. See [community extensions](../extensions-user-guide/discover.md). +- `human_input` is not provided in the {py:class}`~autogen_agentchat.base.TaskResult` object, as the user input can be extracted from the `messages` list by filtering with the `source` field. +- `cost` is not provided in the {py:class}`~autogen_agentchat.base.TaskResult` object, however, you can calculate the cost based on token usage. It would be a great community extension to add cost calculation. See [community extensions](../extensions-user-guide/discover.md). ## Conversion between v0.2 and v0.4 Messages @@ -607,7 +614,7 @@ TODO: Resolves [#4833](https://github.com/microsoft/autogen/issues/4833), maybe the code in the `autogen-ext` package. You can use the following conversion functions to convert between a v0.4 message in -`TaskResult.messages` and a v0.2 message in `ChatResult.chat_history`. +{py:attr}`autogen_agentchat.base.TaskResult.messages` and a v0.2 message in `ChatResult.chat_history`. ```python from typing import Any, Dict, List, Literal @@ -762,7 +769,7 @@ result = editor.initiate_chat( print(result.summary) ``` -In `v0.4`, you can use the `RoundRobinGroupChat` to achieve the same behavior. +In `v0.4`, you can use the {py:class}`~autogen_agentchat.teams.RoundRobinGroupChat` to achieve the same behavior. ```python import asyncio @@ -803,12 +810,12 @@ async def main() -> None: asyncio.run(main()) ``` -For LLM-based speaker selection, you can use the `SelectorGroupChat` instead. +For LLM-based speaker selection, you can use the {py:class}`~autogen_agentchat.teams.SelectorGroupChat` instead. See [Selector Group Chat Tutorial](./tutorial/selector-group-chat.ipynb) and {py:class}`~autogen_agentchat.teams.SelectorGroupChat` for more details. > **Note**: In `v0.4`, you do not need to register functions on a user proxy to use tools -> in a group chat. You can simply pass the tool functions to the `AssistantAgent` as shown in the [Tool Use](#tool-use) section. +> in a group chat. You can simply pass the tool functions to the {py:class}`~autogen_agentchat.agents.AssistantAgent` as shown in the [Tool Use](#tool-use) section. > The agent will automatically call the tools when needed. > If your tool doesn't output well formed response, you can use the `reflect_on_tool_use` parameter to have the model reflect on the tool use. @@ -892,6 +899,24 @@ In `v0.2`, you need to explicitly save the group chat messages and load them bac In `v0.4`, you can simply call `save_state` and `load_state` methods on the group chat object. See [Group Chat with Resume](#group-chat-with-resume) for an example. +## Group Chat with Tool Use + +In `v0.2` group chat, when tools are involved, you need to register the tool functions on a user proxy, +and include the user proxy in the group chat. The tool calls made by other agents +will be routed to the user proxy to execute. + +We have observed numerous issues with this approach, such as the the tool call +routing not working as expected, and the tool call request and result cannot be +accepted by models without support for function calling. + +In `v0.4`, there is no need to register the tool functions on a user proxy, +as the tools are directly executed within the {py:class}`~autogen_agentchat.agents.AssistantAgent`, +which publishes the response from the tool to the group chat. +So the group chat manager does not need to be involved in routing tool calls. + +See [Selector Group Chat Tutorial](./tutorial/selector-group-chat.ipynb) for an example +of using tools in a group chat. + ## Group Chat with Custom Selector (Stateflow) In `v0.2` group chat, when the `speaker_selection_method` is set to a custom function, @@ -899,7 +924,7 @@ it can override the default selection method. This is useful for implementing a state-based selection method. For more details, see [Custom Sepaker Selection in v0.2](https://microsoft.github.io/autogen/0.2/docs/topics/groupchat/customized_speaker_selection). -In `v0.4`, you can use the `SelectorGroupChat` with `selector_func` to achieve the same behavior. +In `v0.4`, you can use the {py:class}`~autogen_agentchat.teams.SelectorGroupChat` with `selector_func` to achieve the same behavior. The `selector_func` is a function that takes the current message thread of the group chat and returns the next speaker's name. If `None` is returned, the LLM-based selection method will be used. @@ -1135,7 +1160,7 @@ and we will provide more built-in support for workflows in the future. In `v0.2`, `GPTAssistantAgent` is a special agent class that is backed by the OpenAI Assistant API. -In `v0.4`, the equivalent is the `OpenAIAssistantAgent` class. +In `v0.4`, the equivalent is the {py:class}`~autogen_ext.agents.openai.OpenAIAssistantAgent` class. It supports the same set of features as the `GPTAssistantAgent` in `v0.2` with more such as customizable threads and file uploads. See {py:class}`~autogen_ext.agents.openai.OpenAIAssistantAgent` for more details. @@ -1147,16 +1172,16 @@ by using the `transforms` capability that is added to an `ConversableAgent` after which is contructed. The feedbacks from our community has led us to believe this feature is essential -and should be a built-in component of `AssistantAgent`, and can be used for +and should be a built-in component of {py:class}`~autogen_agentchat.agents.AssistantAgent`, and can be used for every custom agent. -In `v0.4`, we introduce the `ChatCompletionContext` base class that manages +In `v0.4`, we introduce the {py:class}`~autogen_core.model_context.ChatCompletionContext` base class that manages message history and provides a virtual view of the history. Applications can use -built-in implementations such as `BufferedChatCompletionContext` to +built-in implementations such as {py:class}`~autogen_core.model_context.BufferedChatCompletionContext` to limit the message history sent to the model, or provide their own implementations that creates different virtual views. -To use `BufferedChatCompletionContext` in an `AssistantAgent` in a chatbot scenario. +To use {py:class}`~autogen_core.model_context.BufferedChatCompletionContext` in an {py:class}`~autogen_agentchat.agents.AssistantAgent` in a chatbot scenario. ```python import asyncio @@ -1194,7 +1219,7 @@ which returns an async generator to stream the inner thoughts and actions of the For teams, you can use the `run_stream` method to stream the inner conversation among the agents in the team. Your application can use these streams to observe the agents and teams in real-time. -Both the `on_messages_stream` and `run_stream` methods takes a `CancellationToken` as a parameter +Both the `on_messages_stream` and `run_stream` methods takes a {py:class}`~autogen_core.CancellationToken` as a parameter which can be used to cancel the output stream asynchronously and stop the agent or team. For teams, you can also use termination conditions to stop the team when a certain condition is met. See [Termination Condition Tutorial](./tutorial/termination.ipynb) @@ -1209,7 +1234,7 @@ in the Core API documentation for more details. The code executors in `v0.2` and `v0.4` are nearly identical except the `v0.4` executors support async API. You can also use -`CancellationToken` to cancel a code execution if it takes too long. +{py:class}`~autogen_core.CancellationToken` to cancel a code execution if it takes too long. See [Command Line Code Executors Tutorial](../core-user-guide/framework/command-line-code-executors.ipynb) in the Core API documentation. diff --git a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/tutorial/selector-group-chat.ipynb b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/tutorial/selector-group-chat.ipynb index ae67661dc64d..7fbe5b13125b 100644 --- a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/tutorial/selector-group-chat.ipynb +++ b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/tutorial/selector-group-chat.ipynb @@ -176,6 +176,20 @@ ")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```{note}\n", + "By default, {py:class}`~autogen_agentchat.agents.AssistantAgent` returns the\n", + "tool output as the response. If your tool does not return a well-formed\n", + "string in natural language format, you may want to add a reflection step\n", + "within the agent by setting `reflect_on_tool_use=True` when creating the agent.\n", + "This will allow the agent to reflect on the tool output and provide a natural\n", + "language response.\n", + "```" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -496,7 +510,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.5" + "version": "3.12.7" } }, "nbformat": 4, From f35a94a567f93893a8232c5700ecf2bb94fce68b Mon Sep 17 00:00:00 2001 From: Jack Gerrits Date: Mon, 30 Dec 2024 16:27:50 -0500 Subject: [PATCH 37/38] small edits --- .../agentchat-user-guide/migration-guide.md | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md index 1199d1eb7d81..02b5d6db436c 100644 --- a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md +++ b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md @@ -55,12 +55,12 @@ See each feature below for detailed information on how to migrate. - [Code Executors](#code-executors) The following features currently in `v0.2` -will be providied in the future releases of `v0.4.*` versions: +will be provided in the future releases of `v0.4.*` versions: - Model Client Cache [#4752](https://github.com/microsoft/autogen/issues/4752) - Jupyter Code Executor [#4795](https://github.com/microsoft/autogen/issues/4795) - Model Client Cost [#4835](https://github.com/microsoft/autogen/issues/4835) -- Teacheable Agent +- Teachable Agent - RAG Agent We will update this guide when the missing features become available. @@ -82,7 +82,7 @@ model_client = OpenAIWrapper(config_list=config_list) > **Note**: In AutoGen 0.2, the OpenAI client would try configs in the list until one worked. 0.4 instead expects a specfic model configuration to be chosen. -In `v0.4`, we offers two ways to create a model client. +In `v0.4`, we offer two ways to create a model client. ### Use component config @@ -319,9 +319,9 @@ conversable_agent = ConversableAgent( ) def reply_func( - recipient: ConversableAgent, - messages: Optional[List[Dict]] = None, - sender: Optional[Agent] = None, + recipient: ConversableAgent, + messages: Optional[List[Dict]] = None, + sender: Optional[Agent] = None, config: Optional[Any] = None, ) -> Tuple[bool, Union[str, Dict, None]]: # Custom reply logic here @@ -395,13 +395,13 @@ async def main() -> None: # (Optional) Write state to disk. with open("assistant_state.json", "w") as f: json.dump(state, f) - + # (Optional) Load it back from disk. with open("assistant_state.json", "r") as f: state = json.load(f) print(state) # Inspect the state, which contains the chat history. - # Carry on the chat. + # Carry on the chat. response = await assistant.on_messages([TextMessage(content="Tell me a joke.", source="user")], cancellation_token) print(response) @@ -531,7 +531,7 @@ while True: if user_input == "exit": break chat_result = tool_executor.initiate_chat( - tool_caller, + tool_caller, message=user_input, summary_method="reflection_with_llm", # To let the model reflect on the tool use, set to "last_msg" to return the tool call result directly. ) @@ -582,7 +582,7 @@ For example: ```python chat_result = tool_executor.initiate_chat( - tool_caller, + tool_caller, message=user_input, summary_method="reflection_with_llm", ) @@ -876,7 +876,7 @@ async def main() -> None: state = await group_chat.save_state() with open("group_chat_state.json", "w") as f: json.dump(state, f) - + # Create a new team with the same participants configuration. group_chat = create_team() @@ -1067,8 +1067,7 @@ from autogen_agentchat.messages import TextMessage, ChatMessage from autogen_agentchat.base import Response class CountingAgent(BaseChatAgent): - """An agent that returns a new number by adding 1 to the last number in the - input messages.""" + """An agent that returns a new number by adding 1 to the last number in the input messages.""" async def on_messages(self, messages: Sequence[ChatMessage], cancellation_token: CancellationToken) -> Response: if len(messages) == 0: last_number = 0 # Start from 0 if no messages are given. From 365e5c887e65698d9d8404c984a44c553ded8ef0 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Mon, 30 Dec 2024 13:28:12 -0800 Subject: [PATCH 38/38] wip --- .../docs/src/user-guide/agentchat-user-guide/migration-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md index 02b5d6db436c..fb531e98824e 100644 --- a/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md +++ b/python/packages/autogen-core/docs/src/user-guide/agentchat-user-guide/migration-guide.md @@ -145,7 +145,7 @@ custom_model_client = OpenAIChatCompletionClient( "vision": True, "function_calling": True, "json_output": True, - "model_family": "unknown", + "family": "unknown", }, ) ```