From a4bc2186fd3b1f2fc724a3713d7f4af6e855f3c4 Mon Sep 17 00:00:00 2001 From: wenhao Date: Thu, 22 Feb 2024 14:51:03 +0800 Subject: [PATCH 01/24] fix minor example bug --- docs/sphinx_doc/source/tutorial/103-example.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx_doc/source/tutorial/103-example.md b/docs/sphinx_doc/source/tutorial/103-example.md index 068b42ab7..08ead7cf5 100644 --- a/docs/sphinx_doc/source/tutorial/103-example.md +++ b/docs/sphinx_doc/source/tutorial/103-example.md @@ -52,7 +52,7 @@ from agentscope.agents import DialogAgent, UserAgent agentscope.init(model_configs="./openai_model_configs.json") # Create a dialog agent and a user agent -dialogAgent = DialogAgent(name="assistant", model_config_name="gpt-4") +dialogAgent = DialogAgent(name="assistant", model_config_name="gpt-4", sys_prompt="You are a helpful ai assistant") userAgent = UserAgent() ``` From b9f381d23a13e981fd0873b6adc4871a58ebd01e Mon Sep 17 00:00:00 2001 From: wenhao Date: Thu, 22 Feb 2024 16:52:19 +0800 Subject: [PATCH 02/24] fix single typo --- docs/sphinx_doc/source/tutorial/201-agent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx_doc/source/tutorial/201-agent.md b/docs/sphinx_doc/source/tutorial/201-agent.md index f4d513e53..76b6400b6 100644 --- a/docs/sphinx_doc/source/tutorial/201-agent.md +++ b/docs/sphinx_doc/source/tutorial/201-agent.md @@ -2,7 +2,7 @@ # Customizing Your Own Agent -This tutorial helps you to understand the `Agent` in mode depth and navigate through the process of crafting your own custom agent with AgentScope. We start by introducing the fundamental abstraction called `AgentBase`, which serves as the base class to maintain the general behaviors of all agents. Then, we will go through the *AgentPool*, an ensemble of pre-built, specialized agents, each designed with a specific purpose in mind. Finally, we will demonstrate how to customize your own agent, ensuring it fits the needs of your project. +This tutorial helps you to understand the `Agent` in more depth and navigate through the process of crafting your own custom agent with AgentScope. We start by introducing the fundamental abstraction called `AgentBase`, which serves as the base class to maintain the general behaviors of all agents. Then, we will go through the *AgentPool*, an ensemble of pre-built, specialized agents, each designed with a specific purpose in mind. Finally, we will demonstrate how to customize your own agent, ensuring it fits the needs of your project. ## Understanding `AgentBase` From e88ffad9155fa5e14ef7406cebd33ed980080709 Mon Sep 17 00:00:00 2001 From: wenhao Date: Thu, 22 Feb 2024 17:31:18 +0800 Subject: [PATCH 03/24] fix single typo in 105-logging.md --- docs/sphinx_doc/source/tutorial/105-logging.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/sphinx_doc/source/tutorial/105-logging.md b/docs/sphinx_doc/source/tutorial/105-logging.md index dd6cbbc56..913fc92cc 100644 --- a/docs/sphinx_doc/source/tutorial/105-logging.md +++ b/docs/sphinx_doc/source/tutorial/105-logging.md @@ -79,8 +79,7 @@ agentscope.web.init( ) ``` -By this way, you can see all the running instances and projects in `http://127. -0.0.1:5000` as follows: +By this way, you can see all the running instances and projects in `http://127.0.0.1:5000` as follows: ![webui](https://img.alicdn.com/imgextra/i3/O1CN01kpHFkn1HpeYEkn60I_!!6000000000807-0-tps-3104-1849.jpg) From b16a2c7d830482f9610538747fed06e0c01f2bce Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Fri, 17 May 2024 11:43:40 +0800 Subject: [PATCH 04/24] init notebook service function and codeact agent --- .../codeact_agent.py | 151 +++++++++++++ .../codeact_agent_example1.ipynb | 204 ++++++++++++++++++ .../codeact_agent_example2.ipynb | 196 +++++++++++++++++ .../linear_regression_plot.png | Bin 0 -> 32995 bytes src/agentscope/service/__init__.py | 2 + .../service/execute_code/exec_notebook.py | 157 ++++++++++++++ src/agentscope/service/service_toolkit.py | 6 +- 7 files changed, 714 insertions(+), 2 deletions(-) create mode 100644 examples/converstation_with_codeact_agent/codeact_agent.py create mode 100644 examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb create mode 100644 examples/converstation_with_codeact_agent/codeact_agent_example2.ipynb create mode 100644 examples/converstation_with_codeact_agent/linear_regression_plot.png create mode 100644 src/agentscope/service/execute_code/exec_notebook.py diff --git a/examples/converstation_with_codeact_agent/codeact_agent.py b/examples/converstation_with_codeact_agent/codeact_agent.py new file mode 100644 index 000000000..5ac0f1ca4 --- /dev/null +++ b/examples/converstation_with_codeact_agent/codeact_agent.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- +# pylint: disable=C0301 +"""An agent class that implements the CodeAct agent. +This agent can execute code interactively as actions. +More details can be found at the paper of codeact agent +https://arxiv.org/abs/2402.01030 +and the original repo of codeact https://github.com/xingyaoww/code-act +""" +import re +from typing import Union + + +from agentscope.agents import AgentBase +from agentscope.message import Msg +from agentscope.service import ( + ServiceResponse, + ServiceExecStatus, + NoteBookExecutor, +) + +SYSTEM_MESSAGE = """system +You are a helpful assistant that gives helpful, detailed, and polite answers to the user's questions. +You should interact with the interactive Python (Jupyter Notebook) environment and receive the corresponding output when needed. The code written by assistant should be enclosed using [execute] tag, for example: [execute] print('Hello World!') [/execute]. +You should attempt fewer things at a time instead of putting too much code in one [execute] block. You can install packages through PIP by [execute] !pip install [package needed] [/execute] and should always import packages and define variables before starting to use them. +You should stop [execute] and provide an answer when they have already obtained the answer from the execution result. Whenever possible, execute the code for the user using [execute] instead of providing it. +Your response should be concise, but do express their thoughts. Always write the code in [execute] block to execute them. +You should not ask for the user's input unless necessary. Solve the task on your own and leave no unanswered questions behind. +You should do every thing by your self. +""" # noqa + +EXAMPLE_MESSAGE = """ +Additionally, you are provided with the following code available: +{example_code} +The above code is already available in your interactive Python (Jupyter Notebook) environment, allowing you to directly use these variables and functions without needing to redeclare them. +""" # noqa + + +class CodeActAgent(AgentBase): + """ + The implementation of CodeAct-agent + """ + + def __init__( + self, + name: str, + model_config_name: str, + example_code: str = "", + ) -> None: + """ """ + super().__init__( + name=name, + model_config_name=model_config_name, + ) + self.n_max_executions = 5 + self.example_code = example_code + self.code_executor = NoteBookExecutor() + + sys_msg = Msg(name="system", role="system", content=SYSTEM_MESSAGE) + example_msg = Msg( + name="user", + role="user", + content=EXAMPLE_MESSAGE.format(example_code=self.example_code), + ) + + self.memory.add(sys_msg) + + if self.example_code != "": + code_execution_result = self.code_executor.run_code_on_notebook( + self.example_code, + ) + code_exec_msg = self.handle_code_result( + code_execution_result, + "Example Code excuted: ", + ) + self.memory.add(example_msg) + self.memory.add(code_exec_msg) + self.speak(code_exec_msg) + + def handle_code_execution( + self, + response_text: str, + ) -> Union[ServiceResponse, None]: + """check if there are code execution block in the response, + and execute them if exists""" + code = re.search( + r"\[execute\](.*)\[/execute\]", + response_text, + re.DOTALL, + ) + if code is not None: + code = code.group(1).strip() + result = self.code_executor.run_code_on_notebook(code) + return result + return None + + def handle_code_result( + self, + code_execution_result: ServiceResponse, + content_pre_sring: str = "", + ) -> Msg: + """return the message from code result""" + code_exec_content = content_pre_sring + if code_execution_result.status == ServiceExecStatus.SUCCESS: + code_exec_content += "Excution Successful:\n" + else: + code_exec_content += "Excution Failed:\n" + code_exec_content += "Execution Output:\n" + str( + code_execution_result.content, + ) + return Msg(name="user", role="user", content=code_exec_content) + + def reply(self, x: dict = None) -> None: + """The reply function that implements the codeact agent.""" + + self.memory.add(x) + + excution_count = 0 + while ( + self.memory.get_memory(1)[-1].role == "user" + and excution_count < self.n_max_executions + ): + prompt = self.model.format(self.memory.get_memory()) + model_res = self.model(prompt) + msg_res = Msg( + name=self.name, + content=model_res.text, + role="assistant", + ) + self.memory.add(msg_res) + self.speak(msg_res) + + code_execution_result = self.handle_code_execution(model_res.text) + if code_execution_result is not None: + excution_count += 1 + code_exec_msg = self.handle_code_result(code_execution_result) + self.memory.add(code_exec_msg) + self.speak(code_exec_msg) + + if excution_count == self.n_max_executions: + assert self.memory.get_memory(1)[-1].role == "user" + code_max_exec_msg = Msg( + name="assitant", + role="assistant", + content=( + "I have reached the maximum number " + f"of executions ({self.n_max_executions=}). " + "Can you assist me or ask me another question?" + ), + ) + self.memory.add(code_max_exec_msg) + self.speak(code_max_exec_msg) diff --git a/examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb b/examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb new file mode 100644 index 000000000..f2027c524 --- /dev/null +++ b/examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb @@ -0,0 +1,204 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Conversation with CodeAct Agent\n", + "\n", + "CodeAct agent is an agent that not only chats but also writes and executes Python code for you.\n", + "More details can be found in the project's related [github repo](https://github.com/xingyaoww/code-act). In the example here, we implement the CodeAct agent, and provide a simple example of how to use the CodeAct agent." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "YOUR_MODEL_CONFIGURATION_NAME = \"{YOUR_MODEL_CONFIGURATION_NAME}\"\n", + "\n", + "YOUR_MODEL_CONFIGURATION = {\n", + " \"model_type\": \"xxx\", \n", + " \"config_name\": YOUR_MODEL_CONFIGURATION_NAME\n", + " \n", + " # ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 1: Initalize the CodeAct-agent\n", + "\n", + "Here we load the CodeAct agent." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from codeact_agent import CodeActAgent\n", + "\n", + "import agentscope\n", + "\n", + "agentscope.init(model_configs=YOUR_MODEL_CONFIGURATION)\n", + "\n", + "agent = CodeActAgent(\n", + " name=\"assistant\",\n", + " model_config_name=YOUR_MODEL_CONFIGURATION_NAME,\n", + ")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "###Step 2: Ask the CodeAct-agent to execute tasks\n", + "\n", + "Here, we ask the CodeAct-agent to implement a statistical simulation and modeling procedure." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2024-05-16 19:48:01.620 | INFO | agentscope.models.model:__init__:201 - Initialize model by configuration [dashscope_chat-qwen-max]\n", + "2024-05-16 19:48:01.624 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.call_counter] to SqliteMonitor with unit [times] and quota [None]\n", + "2024-05-16 19:48:01.627 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.prompt_tokens] to SqliteMonitor with unit [token] and quota [None]\n", + "2024-05-16 19:48:01.630 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.completion_tokens] to SqliteMonitor with unit [token] and quota [None]\n", + "2024-05-16 19:48:01.632 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.total_tokens] to SqliteMonitor with unit [token] and quota [None]\n", + "user: Given y = 0.9x + 6.1, randomly sample data points as pairs of (x, y). Then fit a linear regression on the sampled data and plot the points, fitted line, and ground-truth line.\n", + "assistant: To accomplish this task, I will follow these steps:\n", + "\n", + "1. Generate random `x` values within a reasonable range.\n", + "2. Use the equation `y = 0.9x + 6.1` to compute the corresponding `y` values for each `x`.\n", + "3. Add some noise to the `y` values to simulate real-world data variability.\n", + "4. Split the data into training and testing sets (though for simplicity and since you didn't specify, we'll just use all the data for fitting).\n", + "5. Fit a linear regression model on the generated data.\n", + "6. Plot the original data points, the fitted line from the regression, and the ground-truth line.\n", + "\n", + "Let's start by generating the data points and fitting the regression model.\n", + "\n", + "[execute]\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from sklearn.linear_model import LinearRegression\n", + "\n", + "# Set a seed for reproducibility\n", + "np.random.seed(42)\n", + "\n", + "# Generate random x values between 0 and 100\n", + "x_values = np.random.rand(100) * 100\n", + "\n", + "# Compute the corresponding y values without noise\n", + "y_values_ground_truth = 0.9 * x_values + 6.1\n", + "\n", + "# Add some noise to y values\n", + "noise = np.random.normal(0, 10, size=x_values.shape)\n", + "y_values_noisy = y_values_ground_truth + noise\n", + "\n", + "# Combine into a single dataset\n", + "data = np.column_stack((x_values, y_values_noisy))\n", + "\n", + "# Fit a linear regression model\n", + "model = LinearRegression()\n", + "model.fit(data[:, 0].reshape(-1, 1), data[:, 1])\n", + "\n", + "# Prepare to plot\n", + "plt.figure(figsize=(10, 6))\n", + "[/execute]\n", + "user: Execution Output:\n", + "['
', 'Unsupported display type']\n", + "assistant: It seems there was an issue with displaying the plot directly here. No worries, I will still generate the plot and save it to a file instead, which you can view separately. Let me adjust the code to save the plot as an image file.\n", + "\n", + "[execute]\n", + "# Plotting the data points, fitted line, and ground-truth line\n", + "plt.scatter(data[:, 0], data[:, 1], label='Sampled Data', color='blue')\n", + "plt.plot(data[:, 0], model.predict(data[:, 0].reshape(-1, 1)), label='Fitted Line', color='red', linestyle='--')\n", + "plt.plot(data[:, 0], y_values_ground_truth, label='Ground-Truth Line', color='green')\n", + "\n", + "plt.title('Linear Regression on Sampled Data')\n", + "plt.xlabel('X')\n", + "plt.ylabel('Y')\n", + "plt.legend()\n", + "plt.grid(True)\n", + "\n", + "# Save the plot to a file\n", + "plt.savefig('linear_regression_plot.png')\n", + "plt.close()\n", + "[/execute]\n", + "\n", + "The plot has been saved as 'linear_regression_plot.png'. You can view this file to see the sampled data points, the fitted line from the linear regression, and the ground-truth line based on the equation `y = 0.9x + 6.1`. If you need further analysis or have any other requests, feel free to ask!\n", + "user: Execution Output:\n", + "[]\n", + "assistant: It appears the output confirmation was suppressed in this environment, but typically, when running the code locally or in a supported environment, you would see a message indicating the plot was successfully saved to 'linear_regression_plot.png'.\n", + "\n", + "Since we cannot directly view the saved file here, trust that the file has been created with the following components:\n", + "\n", + "- **Sampled Data Points**: Represented as blue dots, scattered according to the generated `x` values and the noisy `y` values.\n", + "- **Fitted Line**: Shown as a red dashed line, representing the linear regression model's prediction based on the sampled data.\n", + "- **Ground-Truth Line**: Displayed as a green line, illustrating the true relationship defined by `y = 0.9x + 6.1`.\n", + "\n", + "If you need further assistance or another operation, such as analyzing the quality of the fit or re-running the process with different parameters, please let me know!\n" + ] + } + ], + "source": [ + "from loguru import logger\n", + "from agentscope.message import Msg\n", + "\n", + "mss = Msg(\n", + " name=\"user\", \n", + " content=\"Given y = 0.9x + 6.1, randomly sample data points as pairs of (x, y). Then fit a linear regression on the sampled data and plot the points, fitted line, and ground-truth line.\", \n", + " role=\"user\"\n", + ")\n", + "logger.chat(mss)\n", + "answer_mss1 = agent(mss)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Conlusion" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "datajuicer", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/converstation_with_codeact_agent/codeact_agent_example2.ipynb b/examples/converstation_with_codeact_agent/codeact_agent_example2.ipynb new file mode 100644 index 000000000..1efbb2a5e --- /dev/null +++ b/examples/converstation_with_codeact_agent/codeact_agent_example2.ipynb @@ -0,0 +1,196 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Conversation with CodeAct Agent\n", + "\n", + "CodeAct agent is an agent that not only chats but also writes and executes Python code for you.\n", + "More details can be found in the project's related [github repo](https://github.com/xingyaoww/code-act). \n", + "\n", + "In the following CodeAct agent example, we demonstrate another method of empowering the Agent with the capability to invoke tools, specifically by directly providing the agent with the corresponding code for the tools and then allowing the Agent to utilize them independently.\n", + "Interm of tool usage, ReAct Agent also enables the agent to use tools to solve problems, but in a different way. You can refer to the [ReAct Agent](https://github.com/modelscope/agentscope/blob/main/examples/conversation_with_react_agent/main.ipynb) example for more details and compare the two.\n", + "\n", + "## Prerequisites\n", + "\n", + "- Follow [READMD.md](https://github.com/modelscope/agentscope) to install AgentScope. We require the lastest version, so you should build from source by running `pip install -e .` instead of intalling from pypi. \n", + "- Prepare a model configuration. AgentScope supports both local deployed model services (CPU or GPU) and third-party services. More details and example model configurations please refer to our [tutorial](https://modelscope.github.io/agentscope/en/tutorial/203-model.html).\n", + "\n", + "## Note\n", + "- The example is tested with the following models. For other models, you may need to adjust the prompt.\n", + " - qwen-max" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "YOUR_MODEL_CONFIGURATION_NAME = \"{YOUR_MODEL_CONFIGURATION_NAME}\"\n", + "\n", + "YOUR_MODEL_CONFIGURATION = {\n", + " \"model_type\": \"xxx\", \n", + " \"config_name\": YOUR_MODEL_CONFIGURATION_NAME\n", + " \n", + " # ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 1: Initalize the AgentScope environment\n", + "\n", + "Here we initialize the AgentScope environment by calling the `agentscope.init` function. The `model_configs` parameter specifies the path to the model configuration file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import agentscope\n", + "\n", + "agentscope.init(model_configs=YOUR_MODEL_CONFIGURATION)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 2: Set up the code for tool usage, and Init the CodeAct Agent.\n", + "\n", + "Here, we provide the CodeAct agent with the interactive code that the agent can use during the conversation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "from loguru import logger\n", + "from codeact_agent import CodeActAgent\n", + "\n", + "from agentscope.message import Msg\n", + "\n", + "YOUR_BING_API_KEY = \"xxx\" # fill in your bing api key here\n", + "\n", + "example_code = f\"\"\"\n", + "from agentscope.service import bing_search\n", + "import os\n", + "\n", + "api_key = \"{YOUR_BING_API_KEY}\"\n", + "\n", + "def search(question: str):\n", + " return bing_search(question, api_key=api_key, num_results=3).content\n", + "\"\"\"\n", + "\n", + "import nest_asyncio\n", + "nest_asyncio.apply()\n", + "agent = CodeActAgent(\n", + " name=\"assistant\",\n", + " model_config_name=YOUR_MODEL_CONFIGURATION_NAME,\n", + " example_code=example_code\n", + ")\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 3: Ask the CodeAct-agent to execute tasks\n", + "\n", + "Here, we ask the CodeAct-agent with the example question in the ReAct Paper: `\"Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to interact with?\"` as follows, same as in the [ReAct-agent]((https://github.com/modelscope/agentscope/blob/main/examples/conversation_with_react_agent/main.ipynb)) example." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2024-05-17 10:58:04.214 | INFO | agentscope.models.model:__init__:201 - Initialize model by configuration [dashscope_chat-qwen-max]\n", + "2024-05-17 10:58:04.215 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.call_counter] to SqliteMonitor with unit [times] and quota [None]\n", + "2024-05-17 10:58:04.218 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.prompt_tokens] to SqliteMonitor with unit [token] and quota [None]\n", + "2024-05-17 10:58:04.220 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.completion_tokens] to SqliteMonitor with unit [token] and quota [None]\n", + "2024-05-17 10:58:04.222 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.total_tokens] to SqliteMonitor with unit [token] and quota [None]\n", + "user: Example Code excuted: Excution Successful:\n", + " Execution Output:\n", + "[]\n", + "user: Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to interact with?\n", + "assistant: [execute]\n", + "search_result = search(\"Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to interact with?\")\n", + "search_result\n", + "[/execute]\n", + "user: Excution Successful:\n", + " Execution Output:\n", + "[\"[{'title': 'Multi-Agent实践第4期:智能体的“想”与“做”-ReAct Agent - 知乎',\\n 'link': 'https://zhuanlan.zhihu.com/p/689675968',\\n 'snippet': 'Other than the Apple Remote, you can use a supported TV or receiver remote, a network-based remote for home-control systems, an infrared remote (commonly known as a universal remote), or other Apple devices like an iPhone or iPad to control the Apple'},\\n {'title': 'REACT:在语言模型中协同推理与行动,使其能够解决各种 ...',\\n 'link': 'https://blog.csdn.net/fogdragon/article/details/132550968',\\n 'snippet': 'Q:Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to intect with? 除了苹果遥控器之外,还有哪些设备可以控制最初设计用于连接的苹果遥控器所配合的程序?'},\\n {'title': '《ReAct: SYNERGIZING REASONING AND ACTING IN ...',\\n 'link': 'https://www.cnblogs.com/LittleHann/p/17541295.html',\\n 'snippet': 'Aside from the Apple Remote, what other devices can control the program Apple Remote was originally designed to interact with?'}]\"]\n", + "assistant: According to the search results, aside from the Apple Remote, you can use the following devices to control the program it was designed to interact with:\n", + "\n", + "1. A supported TV or receiver remote.\n", + "2. A network-based remote for home-control systems.\n", + "3. An infrared remote (universal remote).\n", + "4. Other Apple devices such as an iPhone or iPad.\n", + "\n", + "These alternatives allow for similar control functionality as the Apple Remote.\n" + ] + } + ], + "source": [ + "mss = Msg(\n", + " name=\"user\", \n", + " content=\"Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to interact with?\", \n", + " role=\"user\"\n", + ")\n", + "logger.chat(mss)\n", + "answer_mss1 = agent(mss)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The CodeAct agent successfully use the given search tool functions and return the answer accordingly. This demonstrates the wide range of usage of agent with coding abilities.\n", + "Feel free to explore the Agent by yourself!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "datajuicer", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/converstation_with_codeact_agent/linear_regression_plot.png b/examples/converstation_with_codeact_agent/linear_regression_plot.png new file mode 100644 index 0000000000000000000000000000000000000000..a70267ed28c876643fa2e9e6ad065e745a52d290 GIT binary patch literal 32995 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%0DLn3^X>U?y^k0a8Z0|IyBkom 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 a+Voy`JD=gt!2qozfJ7*$%NJZR@%uNQ9veIW literal 0 HcmV?d00001 diff --git a/src/agentscope/service/__init__.py b/src/agentscope/service/__init__.py index 67cb7ce73..b761f305a 100644 --- a/src/agentscope/service/__init__.py +++ b/src/agentscope/service/__init__.py @@ -4,6 +4,7 @@ from .execute_code.exec_python import execute_python_code from .execute_code.exec_shell import execute_shell_command +from .execute_code.exec_notebook import NoteBookExecutor from .file.common import ( create_file, delete_file, @@ -78,6 +79,7 @@ def get_help() -> None: "dblp_search_publications", "dblp_search_authors", "dblp_search_venues", + "NoteBookExecutor", # to be deprecated "ServiceFactory", ] diff --git a/src/agentscope/service/execute_code/exec_notebook.py b/src/agentscope/service/execute_code/exec_notebook.py new file mode 100644 index 000000000..52d9aa0c5 --- /dev/null +++ b/src/agentscope/service/execute_code/exec_notebook.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- +# pylint: disable=C0301 +"""Service for executing jupyter notebooks interactively +Partially referenced the implementation of https://github.com/geekan/MetaGPT/blob/main/metagpt/actions/di/execute_nb_code.py +""" # noqa +import base64 +import uuid +import os +import asyncio +from nbclient import NotebookClient +from nbclient.exceptions import CellTimeoutError, DeadKernelError +import nbformat +from loguru import logger + +from agentscope.service.service_status import ServiceExecStatus +from agentscope.service.service_response import ServiceResponse + + +class NoteBookExecutor: + """Class for executing jupyter notebooks block interactively""" + + def __init__( + self, + notebook: nbformat.NotebookNode = nbformat.v4.new_notebook(), + timeout: int = 300, + output_dir: str = ".", + ): + self.nb = notebook + self.nb_client = NotebookClient(nb=self.nb) + self.timeout = timeout + self.output_dir = output_dir + + asyncio.run(self._start_client()) + + def output_parser(self, output: dict) -> str: + """Parse the output of the notebook cell and return str""" + if output["output_type"] == "stream": + return output["text"] + elif output["output_type"] == "execute_result": + return output["data"]["text/plain"] + elif output["output_type"] == "display_data": + if "image/png" in output["data"]: + file_path = self._save_image(output["data"]["image/png"]) + return f"Displayed image saved to {file_path}" + else: + return "Unsupported display type" + elif output["output_type"] == "error": + return output["traceback"] + else: + logger.info(f"Unsupported output encountered: {output}") + return "Unsupported output encountered" + + async def _start_client(self) -> None: + """start notebook client""" + if self.nb_client.kc is None or not await self.nb_client.kc.is_alive(): + self.nb_client.create_kernel_manager() + self.nb_client.start_new_kernel() + self.nb_client.start_new_kernel_client() + + async def _kill_client(self) -> None: + """kill notebook client""" + if ( + self.nb_client.km is not None + and await self.nb_client.km.is_alive() + ): + await self.nb_client.km.shutdown_kernel(now=True) + await self.nb_client.km.cleanup_resources() + + self.nb_client.kc.stop_channels() + self.nb_client.kc = None + self.nb_client.km = None + + async def _restart_client(self) -> None: + """Restart the notebook client""" + await self._kill_client() + self.nb_client = NotebookClient(self.nb, timeout=self.timeout) + await self._start_client() + + async def _run_cell(self, cell_index: int) -> ServiceResponse: + """Run a cell in the notebook by its index""" + try: + self.nb_client.execute_cell(self.nb.cells[cell_index], cell_index) + return ServiceResponse( + status=ServiceExecStatus.SUCCESS, + content=[ + self.output_parser(output) + for output in self.nb.cells[cell_index].outputs + ], + ) + except DeadKernelError: + await self.reset() + return ServiceResponse( + status=ServiceExecStatus.ERROR, + content="DeadKernelError when executing cell, reset kernel", + ) + except CellTimeoutError: + assert self.nb_client.km is not None + await self.nb_client.km.interrupt_kernel() + return ServiceResponse( + status=ServiceExecStatus.ERROR, + content=( + "CellTimeoutError when executing cell" + ", code execution timeout" + ), + ) + except Exception as e: + return ServiceResponse( + status=ServiceExecStatus.ERROR, + content=str(e), + ) + + @property + def cells_length(self) -> int: + """return cell length""" + return len(self.nb.cells) + + async def async_run_code_on_notebook(self, code: str) -> ServiceResponse: + """ + Run the code on interactive notebook + """ + self.nb.cells.append(nbformat.v4.new_code_cell(code)) + cell_index = self.cells_length - 1 + return await self._run_cell(cell_index) + + def run_code_on_notebook(self, code: str) -> ServiceResponse: + """ + Run the code on interactive jupyter notebook. + + Args: + code (`str`): + The Python code to be executed in the interactive notebook. + + Returns: + `ServiceResponse`: whether the code execution was successful, + and the output of the code execution. + """ + return asyncio.run(self.async_run_code_on_notebook(code)) + + def reset_notebook(self) -> ServiceResponse: + """ + Reset the notebook + """ + asyncio.run(self._restart_client()) + return ServiceResponse( + status=ServiceExecStatus.SUCCESS, + content="Reset notebook", + ) + + def _save_image(self, image_base64: str) -> str: + """Save image data to a file. + The image name is generated randomly here""" + image_data = base64.b64decode(image_base64) + filename = f"display_image_{uuid.uuid4().hex}.png" + path = os.path.join(self.output_dir, filename) + with open(path, "wb") as f: + f.write(image_data) + return os.path.abspath(path) diff --git a/src/agentscope/service/service_toolkit.py b/src/agentscope/service/service_toolkit.py index f7172a450..820f8d948 100644 --- a/src/agentscope/service/service_toolkit.py +++ b/src/agentscope/service/service_toolkit.py @@ -492,7 +492,8 @@ def bing_search(query: str, api_key: str, num_results: int=10): ) # The arguments that requires the agent to specify - args_agent = set(argsspec.args) - set(kwargs.keys()) + # to support class method, the self args are deprecated + args_agent = set(argsspec.args) - set(kwargs.keys()) - {"self"} # Check if the arguments from agent have descriptions in docstring args_description = { @@ -649,7 +650,8 @@ def bing_search(query: str, api_key: str, num_results: int=10): ) # The arguments that requires the agent to specify - args_agent = set(argsspec.args) - set(kwargs.keys()) + # we remove the self argument, for class methods + args_agent = set(argsspec.args) - set(kwargs.keys()) - {"self"} # Check if the arguments from agent have descriptions in docstring args_description = { From 0e8d6faa6e002bfe869616ec6eeef2679a46a84f Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Fri, 17 May 2024 11:59:03 +0800 Subject: [PATCH 05/24] add notebook dependency in setup.py --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index 2f2a75c34..1042bf523 100644 --- a/setup.py +++ b/setup.py @@ -72,6 +72,8 @@ "google-generativeai>=0.4.0", "zhipuai", "litellm", + "nbclient", + "nbformat", ] distribute_requires = minimal_requires + rpc_requires From a734b17b53863e1ee5ce141e3c355baf83aa2d0e Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Fri, 17 May 2024 14:22:41 +0800 Subject: [PATCH 06/24] update example in README --- README.md | 1 + README_ZH.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index ec9e6cbe6..f55d5ade9 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,7 @@ the following libraries. - [Conversation with RAG Agent](./examples/conversation_with_RAG_agents) - new[Conversation with gpt-4o](./examples/conversation_with_gpt-4o) - new[Conversation with Software Engineering Agent](./examples/swe_agent/) + - new[Conversation with CodeAct Agent](./examples/conversation_with_codeact_agent/) - Game diff --git a/README_ZH.md b/README_ZH.md index 40ebc42c7..546e7e50a 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -121,6 +121,7 @@ AgentScope支持使用以下库快速部署本地模型服务。 - [与RAG智能体对话](./examples/conversation_with_RAG_agents) - new[与gpt-4o模型对话](./examples/conversation_with_gpt-4o) - new[与SoftWare Engineering智能体对话](./examples/swe_agent/) + - new[与CodeAct智能体对话](./examples/conversation_with_codeact_agent/) - 游戏 From 4793b86c0161000918b1146567c1d2d63fa46aae Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Wed, 22 May 2024 13:55:57 +0800 Subject: [PATCH 07/24] add nested asyncio --- .../codeact_agent_example1.ipynb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb b/examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb index f2027c524..17b6e7234 100644 --- a/examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb +++ b/examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb @@ -47,6 +47,8 @@ "\n", "agentscope.init(model_configs=YOUR_MODEL_CONFIGURATION)\n", "\n", + "import nest_asyncio\n", + "nest_asyncio.apply()\n", "agent = CodeActAgent(\n", " name=\"assistant\",\n", " model_config_name=YOUR_MODEL_CONFIGURATION_NAME,\n", From 4147541d9f85969e2913bb760914ba7b6fcd2f05 Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Mon, 27 May 2024 18:52:08 +0800 Subject: [PATCH 08/24] remove empty blocks --- .../codeact_agent_example1.ipynb | 14 -------------- .../codeact_agent_example2.ipynb | 7 ------- 2 files changed, 21 deletions(-) diff --git a/examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb b/examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb index 17b6e7234..6062a6583 100644 --- a/examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb +++ b/examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb @@ -166,20 +166,6 @@ "logger.chat(mss)\n", "answer_mss1 = agent(mss)" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Conlusion" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/examples/converstation_with_codeact_agent/codeact_agent_example2.ipynb b/examples/converstation_with_codeact_agent/codeact_agent_example2.ipynb index 1efbb2a5e..fdaa1c544 100644 --- a/examples/converstation_with_codeact_agent/codeact_agent_example2.ipynb +++ b/examples/converstation_with_codeact_agent/codeact_agent_example2.ipynb @@ -163,13 +163,6 @@ "The CodeAct agent successfully use the given search tool functions and return the answer accordingly. This demonstrates the wide range of usage of agent with coding abilities.\n", "Feel free to explore the Agent by yourself!" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { From 132147d9a52c680992a37eeca209685f9f0beb9d Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Mon, 27 May 2024 19:15:59 +0800 Subject: [PATCH 09/24] resolve threads --- .../codeact_agent.py | 6 +++++- .../service/execute_code/exec_notebook.py | 21 +++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/examples/converstation_with_codeact_agent/codeact_agent.py b/examples/converstation_with_codeact_agent/codeact_agent.py index 5ac0f1ca4..d3487bcd1 100644 --- a/examples/converstation_with_codeact_agent/codeact_agent.py +++ b/examples/converstation_with_codeact_agent/codeact_agent.py @@ -37,7 +37,11 @@ class CodeActAgent(AgentBase): """ - The implementation of CodeAct-agent + The implementation of CodeAct-agent. + The agent can execute code interactively as actions. + More details can be found at the paper of codeact agent + https://arxiv.org/abs/2402.01030 + and the original repo of codeact https://github.com/xingyaoww/code-act """ def __init__( diff --git a/src/agentscope/service/execute_code/exec_notebook.py b/src/agentscope/service/execute_code/exec_notebook.py index 52d9aa0c5..314b28272 100644 --- a/src/agentscope/service/execute_code/exec_notebook.py +++ b/src/agentscope/service/execute_code/exec_notebook.py @@ -7,13 +7,24 @@ import uuid import os import asyncio -from nbclient import NotebookClient -from nbclient.exceptions import CellTimeoutError, DeadKernelError -import nbformat +from typing import Union from loguru import logger + +try: + from nbclient import NotebookClient + from nbclient.exceptions import CellTimeoutError, DeadKernelError + import nbformat +except ImportError as import_error: + from agentscope.utils.tools import ImportErrorReporter + + nbclient = ImportErrorReporter(import_error) + nbformat = ImportErrorReporter(import_error) + NotebookClient = ImportErrorReporter(import_error) + from agentscope.service.service_status import ServiceExecStatus from agentscope.service.service_response import ServiceResponse +from agentscope.file_manager import file_manager class NoteBookExecutor: @@ -23,11 +34,13 @@ def __init__( self, notebook: nbformat.NotebookNode = nbformat.v4.new_notebook(), timeout: int = 300, - output_dir: str = ".", + output_dir: Union[str, None] = None, ): self.nb = notebook self.nb_client = NotebookClient(nb=self.nb) self.timeout = timeout + if output_dir is None: + self.output_dir = file_manager.dir_file self.output_dir = output_dir asyncio.run(self._start_client()) From 633115cb91ae261fa311cedbc022adcb30163513 Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Tue, 28 May 2024 14:52:41 +0800 Subject: [PATCH 10/24] add example --- .../service/execute_code/exec_notebook.py | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/agentscope/service/execute_code/exec_notebook.py b/src/agentscope/service/execute_code/exec_notebook.py index 314b28272..0113fecf2 100644 --- a/src/agentscope/service/execute_code/exec_notebook.py +++ b/src/agentscope/service/execute_code/exec_notebook.py @@ -28,7 +28,31 @@ class NoteBookExecutor: - """Class for executing jupyter notebooks block interactively""" + """ + Class for executing jupyter notebooks block interactively + + Example Use Case: + + ```ipython + from agentscope.service.service_toolkit import * + from agentscope.service.execute_code.exec_notebook import * + nbe = NoteBookExecutor() + code = "print('helloworld')" + nbe.run_code_on_notebook(code) + + >>> Executing function run_code_on_notebook with arguments: + >>> code: print('helloworld') + >>> END + + service_toolkit = ServiceToolkit() + service_toolkit.add(nbe.run_code_on_notebook) + input_obs = [{"name": "run_code_on_notebook", "arguments":{"code": code}}] + res_of_string_input = service_toolkit.parse_and_call_func(input_obs) + + "1. Execute function run_code_on_notebook\n [ARGUMENTS]:\n code: print('helloworld')\n [STATUS]: SUCCESS\n [RESULT]: ['helloworld\\n']\n" + + ``` + """ # noqa def __init__( self, From 4ee5516559ed5d123667c7a3eec920e7738c99e6 Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Tue, 28 May 2024 14:58:32 +0800 Subject: [PATCH 11/24] add more detail in comments --- .../service/execute_code/exec_notebook.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/agentscope/service/execute_code/exec_notebook.py b/src/agentscope/service/execute_code/exec_notebook.py index 0113fecf2..9cf64f3a8 100644 --- a/src/agentscope/service/execute_code/exec_notebook.py +++ b/src/agentscope/service/execute_code/exec_notebook.py @@ -29,21 +29,25 @@ class NoteBookExecutor: """ - Class for executing jupyter notebooks block interactively - - Example Use Case: + Class for executing jupyter notebooks block interactively. + To use the service function, you should first init the class, then call the + run_code_on_notebook function. + + Example: ```ipython from agentscope.service.service_toolkit import * from agentscope.service.execute_code.exec_notebook import * nbe = NoteBookExecutor() code = "print('helloworld')" + # calling directly nbe.run_code_on_notebook(code) >>> Executing function run_code_on_notebook with arguments: >>> code: print('helloworld') - >>> END + >>> END + # calling with service toolkit service_toolkit = ServiceToolkit() service_toolkit.add(nbe.run_code_on_notebook) input_obs = [{"name": "run_code_on_notebook", "arguments":{"code": code}}] @@ -52,7 +56,7 @@ class NoteBookExecutor: "1. Execute function run_code_on_notebook\n [ARGUMENTS]:\n code: print('helloworld')\n [STATUS]: SUCCESS\n [RESULT]: ['helloworld\\n']\n" ``` - """ # noqa + """ # noqa def __init__( self, From ef6744364e8d99d0e40bf55f8f23f412fe0ed7d8 Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Tue, 4 Jun 2024 17:05:27 +0800 Subject: [PATCH 12/24] resolve threads --- ...le1.ipynb => codeact_agent_example_modeling.ipynb} | 11 ++++++++++- ...ample2.ipynb => codeact_agent_example_tools.ipynb} | 6 +++--- 2 files changed, 13 insertions(+), 4 deletions(-) rename examples/converstation_with_codeact_agent/{codeact_agent_example1.ipynb => codeact_agent_example_modeling.ipynb} (91%) rename examples/converstation_with_codeact_agent/{codeact_agent_example2.ipynb => codeact_agent_example_tools.ipynb} (96%) diff --git a/examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb b/examples/converstation_with_codeact_agent/codeact_agent_example_modeling.ipynb similarity index 91% rename from examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb rename to examples/converstation_with_codeact_agent/codeact_agent_example_modeling.ipynb index 6062a6583..f7b80eba2 100644 --- a/examples/converstation_with_codeact_agent/codeact_agent_example1.ipynb +++ b/examples/converstation_with_codeact_agent/codeact_agent_example_modeling.ipynb @@ -7,7 +7,16 @@ "# Conversation with CodeAct Agent\n", "\n", "CodeAct agent is an agent that not only chats but also writes and executes Python code for you.\n", - "More details can be found in the project's related [github repo](https://github.com/xingyaoww/code-act). In the example here, we implement the CodeAct agent, and provide a simple example of how to use the CodeAct agent." + "More details can be found in the project's related [github repo](https://github.com/xingyaoww/code-act). In the example here, we implement the CodeAct agent, and provide a simple example of how to use the CodeAct agent.\n", + "\n", + "## Prerequisites\n", + "\n", + "- Follow [READMD.md](https://github.com/modelscope/agentscope) to install AgentScope. We require the lastest version, so you should build from source by running `pip install -e .` instead of intalling from pypi. \n", + "- Prepare a model configuration. AgentScope supports both local deployed model services (CPU or GPU) and third-party services. More details and example model configurations please refer to our [tutorial](https://modelscope.github.io/agentscope/en/tutorial/203-model.html).\n", + "\n", + "## Note\n", + "- The example is tested with the following models. For other models, you may need to adjust the prompt.\n", + " - qwen-max" ] }, { diff --git a/examples/converstation_with_codeact_agent/codeact_agent_example2.ipynb b/examples/converstation_with_codeact_agent/codeact_agent_example_tools.ipynb similarity index 96% rename from examples/converstation_with_codeact_agent/codeact_agent_example2.ipynb rename to examples/converstation_with_codeact_agent/codeact_agent_example_tools.ipynb index fdaa1c544..787e1a15f 100644 --- a/examples/converstation_with_codeact_agent/codeact_agent_example2.ipynb +++ b/examples/converstation_with_codeact_agent/codeact_agent_example_tools.ipynb @@ -9,8 +9,8 @@ "CodeAct agent is an agent that not only chats but also writes and executes Python code for you.\n", "More details can be found in the project's related [github repo](https://github.com/xingyaoww/code-act). \n", "\n", - "In the following CodeAct agent example, we demonstrate another method of empowering the Agent with the capability to invoke tools, specifically by directly providing the agent with the corresponding code for the tools and then allowing the Agent to utilize them independently.\n", - "Interm of tool usage, ReAct Agent also enables the agent to use tools to solve problems, but in a different way. You can refer to the [ReAct Agent](https://github.com/modelscope/agentscope/blob/main/examples/conversation_with_react_agent/main.ipynb) example for more details and compare the two.\n", + "In the following CodeAct agent example, we demonstrate another method of empowering the agent with the capability to invoke tools, specifically by directly providing the agent with the corresponding code for the tools and then allowing the agent to utilize them independently.\n", + "Interm of tool usage, ReAct agent also enables the agent to use tools to solve problems, but in a different way. You can refer to the [ReAct agent](https://github.com/modelscope/agentscope/blob/main/examples/conversation_with_react_agent/main.ipynb) example for more details and compare the two.\n", "\n", "## Prerequisites\n", "\n", @@ -62,7 +62,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Step 2: Set up the code for tool usage, and Init the CodeAct Agent.\n", + "### Step 2: Set up the code for tool usage, and init the CodeAct Agent.\n", "\n", "Here, we provide the CodeAct agent with the interactive code that the agent can use during the conversation." ] From 85416dc19c7fc0f64c72dc9155bdec97a2c9bb66 Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Fri, 12 Jul 2024 18:24:03 +0800 Subject: [PATCH 13/24] fix dir name typo --- .../codeact_agent.py | 155 ++++++++++++++ .../codeact_agent_example_modeling.ipynb | 201 ++++++++++++++++++ .../codeact_agent_example_tools.ipynb | 189 ++++++++++++++++ .../linear_regression_plot.png | Bin 0 -> 32995 bytes 4 files changed, 545 insertions(+) create mode 100644 examples/conversation_with_codeact_agent/codeact_agent.py create mode 100644 examples/conversation_with_codeact_agent/codeact_agent_example_modeling.ipynb create mode 100644 examples/conversation_with_codeact_agent/codeact_agent_example_tools.ipynb create mode 100644 examples/conversation_with_codeact_agent/linear_regression_plot.png diff --git a/examples/conversation_with_codeact_agent/codeact_agent.py b/examples/conversation_with_codeact_agent/codeact_agent.py new file mode 100644 index 000000000..d3487bcd1 --- /dev/null +++ b/examples/conversation_with_codeact_agent/codeact_agent.py @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +# pylint: disable=C0301 +"""An agent class that implements the CodeAct agent. +This agent can execute code interactively as actions. +More details can be found at the paper of codeact agent +https://arxiv.org/abs/2402.01030 +and the original repo of codeact https://github.com/xingyaoww/code-act +""" +import re +from typing import Union + + +from agentscope.agents import AgentBase +from agentscope.message import Msg +from agentscope.service import ( + ServiceResponse, + ServiceExecStatus, + NoteBookExecutor, +) + +SYSTEM_MESSAGE = """system +You are a helpful assistant that gives helpful, detailed, and polite answers to the user's questions. +You should interact with the interactive Python (Jupyter Notebook) environment and receive the corresponding output when needed. The code written by assistant should be enclosed using [execute] tag, for example: [execute] print('Hello World!') [/execute]. +You should attempt fewer things at a time instead of putting too much code in one [execute] block. You can install packages through PIP by [execute] !pip install [package needed] [/execute] and should always import packages and define variables before starting to use them. +You should stop [execute] and provide an answer when they have already obtained the answer from the execution result. Whenever possible, execute the code for the user using [execute] instead of providing it. +Your response should be concise, but do express their thoughts. Always write the code in [execute] block to execute them. +You should not ask for the user's input unless necessary. Solve the task on your own and leave no unanswered questions behind. +You should do every thing by your self. +""" # noqa + +EXAMPLE_MESSAGE = """ +Additionally, you are provided with the following code available: +{example_code} +The above code is already available in your interactive Python (Jupyter Notebook) environment, allowing you to directly use these variables and functions without needing to redeclare them. +""" # noqa + + +class CodeActAgent(AgentBase): + """ + The implementation of CodeAct-agent. + The agent can execute code interactively as actions. + More details can be found at the paper of codeact agent + https://arxiv.org/abs/2402.01030 + and the original repo of codeact https://github.com/xingyaoww/code-act + """ + + def __init__( + self, + name: str, + model_config_name: str, + example_code: str = "", + ) -> None: + """ """ + super().__init__( + name=name, + model_config_name=model_config_name, + ) + self.n_max_executions = 5 + self.example_code = example_code + self.code_executor = NoteBookExecutor() + + sys_msg = Msg(name="system", role="system", content=SYSTEM_MESSAGE) + example_msg = Msg( + name="user", + role="user", + content=EXAMPLE_MESSAGE.format(example_code=self.example_code), + ) + + self.memory.add(sys_msg) + + if self.example_code != "": + code_execution_result = self.code_executor.run_code_on_notebook( + self.example_code, + ) + code_exec_msg = self.handle_code_result( + code_execution_result, + "Example Code excuted: ", + ) + self.memory.add(example_msg) + self.memory.add(code_exec_msg) + self.speak(code_exec_msg) + + def handle_code_execution( + self, + response_text: str, + ) -> Union[ServiceResponse, None]: + """check if there are code execution block in the response, + and execute them if exists""" + code = re.search( + r"\[execute\](.*)\[/execute\]", + response_text, + re.DOTALL, + ) + if code is not None: + code = code.group(1).strip() + result = self.code_executor.run_code_on_notebook(code) + return result + return None + + def handle_code_result( + self, + code_execution_result: ServiceResponse, + content_pre_sring: str = "", + ) -> Msg: + """return the message from code result""" + code_exec_content = content_pre_sring + if code_execution_result.status == ServiceExecStatus.SUCCESS: + code_exec_content += "Excution Successful:\n" + else: + code_exec_content += "Excution Failed:\n" + code_exec_content += "Execution Output:\n" + str( + code_execution_result.content, + ) + return Msg(name="user", role="user", content=code_exec_content) + + def reply(self, x: dict = None) -> None: + """The reply function that implements the codeact agent.""" + + self.memory.add(x) + + excution_count = 0 + while ( + self.memory.get_memory(1)[-1].role == "user" + and excution_count < self.n_max_executions + ): + prompt = self.model.format(self.memory.get_memory()) + model_res = self.model(prompt) + msg_res = Msg( + name=self.name, + content=model_res.text, + role="assistant", + ) + self.memory.add(msg_res) + self.speak(msg_res) + + code_execution_result = self.handle_code_execution(model_res.text) + if code_execution_result is not None: + excution_count += 1 + code_exec_msg = self.handle_code_result(code_execution_result) + self.memory.add(code_exec_msg) + self.speak(code_exec_msg) + + if excution_count == self.n_max_executions: + assert self.memory.get_memory(1)[-1].role == "user" + code_max_exec_msg = Msg( + name="assitant", + role="assistant", + content=( + "I have reached the maximum number " + f"of executions ({self.n_max_executions=}). " + "Can you assist me or ask me another question?" + ), + ) + self.memory.add(code_max_exec_msg) + self.speak(code_max_exec_msg) diff --git a/examples/conversation_with_codeact_agent/codeact_agent_example_modeling.ipynb b/examples/conversation_with_codeact_agent/codeact_agent_example_modeling.ipynb new file mode 100644 index 000000000..f7b80eba2 --- /dev/null +++ b/examples/conversation_with_codeact_agent/codeact_agent_example_modeling.ipynb @@ -0,0 +1,201 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Conversation with CodeAct Agent\n", + "\n", + "CodeAct agent is an agent that not only chats but also writes and executes Python code for you.\n", + "More details can be found in the project's related [github repo](https://github.com/xingyaoww/code-act). In the example here, we implement the CodeAct agent, and provide a simple example of how to use the CodeAct agent.\n", + "\n", + "## Prerequisites\n", + "\n", + "- Follow [READMD.md](https://github.com/modelscope/agentscope) to install AgentScope. We require the lastest version, so you should build from source by running `pip install -e .` instead of intalling from pypi. \n", + "- Prepare a model configuration. AgentScope supports both local deployed model services (CPU or GPU) and third-party services. More details and example model configurations please refer to our [tutorial](https://modelscope.github.io/agentscope/en/tutorial/203-model.html).\n", + "\n", + "## Note\n", + "- The example is tested with the following models. For other models, you may need to adjust the prompt.\n", + " - qwen-max" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "YOUR_MODEL_CONFIGURATION_NAME = \"{YOUR_MODEL_CONFIGURATION_NAME}\"\n", + "\n", + "YOUR_MODEL_CONFIGURATION = {\n", + " \"model_type\": \"xxx\", \n", + " \"config_name\": YOUR_MODEL_CONFIGURATION_NAME\n", + " \n", + " # ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 1: Initalize the CodeAct-agent\n", + "\n", + "Here we load the CodeAct agent." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from codeact_agent import CodeActAgent\n", + "\n", + "import agentscope\n", + "\n", + "agentscope.init(model_configs=YOUR_MODEL_CONFIGURATION)\n", + "\n", + "import nest_asyncio\n", + "nest_asyncio.apply()\n", + "agent = CodeActAgent(\n", + " name=\"assistant\",\n", + " model_config_name=YOUR_MODEL_CONFIGURATION_NAME,\n", + ")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "###Step 2: Ask the CodeAct-agent to execute tasks\n", + "\n", + "Here, we ask the CodeAct-agent to implement a statistical simulation and modeling procedure." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2024-05-16 19:48:01.620 | INFO | agentscope.models.model:__init__:201 - Initialize model by configuration [dashscope_chat-qwen-max]\n", + "2024-05-16 19:48:01.624 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.call_counter] to SqliteMonitor with unit [times] and quota [None]\n", + "2024-05-16 19:48:01.627 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.prompt_tokens] to SqliteMonitor with unit [token] and quota [None]\n", + "2024-05-16 19:48:01.630 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.completion_tokens] to SqliteMonitor with unit [token] and quota [None]\n", + "2024-05-16 19:48:01.632 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.total_tokens] to SqliteMonitor with unit [token] and quota [None]\n", + "user: Given y = 0.9x + 6.1, randomly sample data points as pairs of (x, y). Then fit a linear regression on the sampled data and plot the points, fitted line, and ground-truth line.\n", + "assistant: To accomplish this task, I will follow these steps:\n", + "\n", + "1. Generate random `x` values within a reasonable range.\n", + "2. Use the equation `y = 0.9x + 6.1` to compute the corresponding `y` values for each `x`.\n", + "3. Add some noise to the `y` values to simulate real-world data variability.\n", + "4. Split the data into training and testing sets (though for simplicity and since you didn't specify, we'll just use all the data for fitting).\n", + "5. Fit a linear regression model on the generated data.\n", + "6. Plot the original data points, the fitted line from the regression, and the ground-truth line.\n", + "\n", + "Let's start by generating the data points and fitting the regression model.\n", + "\n", + "[execute]\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from sklearn.linear_model import LinearRegression\n", + "\n", + "# Set a seed for reproducibility\n", + "np.random.seed(42)\n", + "\n", + "# Generate random x values between 0 and 100\n", + "x_values = np.random.rand(100) * 100\n", + "\n", + "# Compute the corresponding y values without noise\n", + "y_values_ground_truth = 0.9 * x_values + 6.1\n", + "\n", + "# Add some noise to y values\n", + "noise = np.random.normal(0, 10, size=x_values.shape)\n", + "y_values_noisy = y_values_ground_truth + noise\n", + "\n", + "# Combine into a single dataset\n", + "data = np.column_stack((x_values, y_values_noisy))\n", + "\n", + "# Fit a linear regression model\n", + "model = LinearRegression()\n", + "model.fit(data[:, 0].reshape(-1, 1), data[:, 1])\n", + "\n", + "# Prepare to plot\n", + "plt.figure(figsize=(10, 6))\n", + "[/execute]\n", + "user: Execution Output:\n", + "['
', 'Unsupported display type']\n", + "assistant: It seems there was an issue with displaying the plot directly here. No worries, I will still generate the plot and save it to a file instead, which you can view separately. Let me adjust the code to save the plot as an image file.\n", + "\n", + "[execute]\n", + "# Plotting the data points, fitted line, and ground-truth line\n", + "plt.scatter(data[:, 0], data[:, 1], label='Sampled Data', color='blue')\n", + "plt.plot(data[:, 0], model.predict(data[:, 0].reshape(-1, 1)), label='Fitted Line', color='red', linestyle='--')\n", + "plt.plot(data[:, 0], y_values_ground_truth, label='Ground-Truth Line', color='green')\n", + "\n", + "plt.title('Linear Regression on Sampled Data')\n", + "plt.xlabel('X')\n", + "plt.ylabel('Y')\n", + "plt.legend()\n", + "plt.grid(True)\n", + "\n", + "# Save the plot to a file\n", + "plt.savefig('linear_regression_plot.png')\n", + "plt.close()\n", + "[/execute]\n", + "\n", + "The plot has been saved as 'linear_regression_plot.png'. You can view this file to see the sampled data points, the fitted line from the linear regression, and the ground-truth line based on the equation `y = 0.9x + 6.1`. If you need further analysis or have any other requests, feel free to ask!\n", + "user: Execution Output:\n", + "[]\n", + "assistant: It appears the output confirmation was suppressed in this environment, but typically, when running the code locally or in a supported environment, you would see a message indicating the plot was successfully saved to 'linear_regression_plot.png'.\n", + "\n", + "Since we cannot directly view the saved file here, trust that the file has been created with the following components:\n", + "\n", + "- **Sampled Data Points**: Represented as blue dots, scattered according to the generated `x` values and the noisy `y` values.\n", + "- **Fitted Line**: Shown as a red dashed line, representing the linear regression model's prediction based on the sampled data.\n", + "- **Ground-Truth Line**: Displayed as a green line, illustrating the true relationship defined by `y = 0.9x + 6.1`.\n", + "\n", + "If you need further assistance or another operation, such as analyzing the quality of the fit or re-running the process with different parameters, please let me know!\n" + ] + } + ], + "source": [ + "from loguru import logger\n", + "from agentscope.message import Msg\n", + "\n", + "mss = Msg(\n", + " name=\"user\", \n", + " content=\"Given y = 0.9x + 6.1, randomly sample data points as pairs of (x, y). Then fit a linear regression on the sampled data and plot the points, fitted line, and ground-truth line.\", \n", + " role=\"user\"\n", + ")\n", + "logger.chat(mss)\n", + "answer_mss1 = agent(mss)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "datajuicer", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/conversation_with_codeact_agent/codeact_agent_example_tools.ipynb b/examples/conversation_with_codeact_agent/codeact_agent_example_tools.ipynb new file mode 100644 index 000000000..787e1a15f --- /dev/null +++ b/examples/conversation_with_codeact_agent/codeact_agent_example_tools.ipynb @@ -0,0 +1,189 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Conversation with CodeAct Agent\n", + "\n", + "CodeAct agent is an agent that not only chats but also writes and executes Python code for you.\n", + "More details can be found in the project's related [github repo](https://github.com/xingyaoww/code-act). \n", + "\n", + "In the following CodeAct agent example, we demonstrate another method of empowering the agent with the capability to invoke tools, specifically by directly providing the agent with the corresponding code for the tools and then allowing the agent to utilize them independently.\n", + "Interm of tool usage, ReAct agent also enables the agent to use tools to solve problems, but in a different way. You can refer to the [ReAct agent](https://github.com/modelscope/agentscope/blob/main/examples/conversation_with_react_agent/main.ipynb) example for more details and compare the two.\n", + "\n", + "## Prerequisites\n", + "\n", + "- Follow [READMD.md](https://github.com/modelscope/agentscope) to install AgentScope. We require the lastest version, so you should build from source by running `pip install -e .` instead of intalling from pypi. \n", + "- Prepare a model configuration. AgentScope supports both local deployed model services (CPU or GPU) and third-party services. More details and example model configurations please refer to our [tutorial](https://modelscope.github.io/agentscope/en/tutorial/203-model.html).\n", + "\n", + "## Note\n", + "- The example is tested with the following models. For other models, you may need to adjust the prompt.\n", + " - qwen-max" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "YOUR_MODEL_CONFIGURATION_NAME = \"{YOUR_MODEL_CONFIGURATION_NAME}\"\n", + "\n", + "YOUR_MODEL_CONFIGURATION = {\n", + " \"model_type\": \"xxx\", \n", + " \"config_name\": YOUR_MODEL_CONFIGURATION_NAME\n", + " \n", + " # ...\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 1: Initalize the AgentScope environment\n", + "\n", + "Here we initialize the AgentScope environment by calling the `agentscope.init` function. The `model_configs` parameter specifies the path to the model configuration file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import agentscope\n", + "\n", + "agentscope.init(model_configs=YOUR_MODEL_CONFIGURATION)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 2: Set up the code for tool usage, and init the CodeAct Agent.\n", + "\n", + "Here, we provide the CodeAct agent with the interactive code that the agent can use during the conversation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "from loguru import logger\n", + "from codeact_agent import CodeActAgent\n", + "\n", + "from agentscope.message import Msg\n", + "\n", + "YOUR_BING_API_KEY = \"xxx\" # fill in your bing api key here\n", + "\n", + "example_code = f\"\"\"\n", + "from agentscope.service import bing_search\n", + "import os\n", + "\n", + "api_key = \"{YOUR_BING_API_KEY}\"\n", + "\n", + "def search(question: str):\n", + " return bing_search(question, api_key=api_key, num_results=3).content\n", + "\"\"\"\n", + "\n", + "import nest_asyncio\n", + "nest_asyncio.apply()\n", + "agent = CodeActAgent(\n", + " name=\"assistant\",\n", + " model_config_name=YOUR_MODEL_CONFIGURATION_NAME,\n", + " example_code=example_code\n", + ")\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step 3: Ask the CodeAct-agent to execute tasks\n", + "\n", + "Here, we ask the CodeAct-agent with the example question in the ReAct Paper: `\"Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to interact with?\"` as follows, same as in the [ReAct-agent]((https://github.com/modelscope/agentscope/blob/main/examples/conversation_with_react_agent/main.ipynb)) example." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2024-05-17 10:58:04.214 | INFO | agentscope.models.model:__init__:201 - Initialize model by configuration [dashscope_chat-qwen-max]\n", + "2024-05-17 10:58:04.215 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.call_counter] to SqliteMonitor with unit [times] and quota [None]\n", + "2024-05-17 10:58:04.218 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.prompt_tokens] to SqliteMonitor with unit [token] and quota [None]\n", + "2024-05-17 10:58:04.220 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.completion_tokens] to SqliteMonitor with unit [token] and quota [None]\n", + "2024-05-17 10:58:04.222 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.total_tokens] to SqliteMonitor with unit [token] and quota [None]\n", + "user: Example Code excuted: Excution Successful:\n", + " Execution Output:\n", + "[]\n", + "user: Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to interact with?\n", + "assistant: [execute]\n", + "search_result = search(\"Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to interact with?\")\n", + "search_result\n", + "[/execute]\n", + "user: Excution Successful:\n", + " Execution Output:\n", + "[\"[{'title': 'Multi-Agent实践第4期:智能体的“想”与“做”-ReAct Agent - 知乎',\\n 'link': 'https://zhuanlan.zhihu.com/p/689675968',\\n 'snippet': 'Other than the Apple Remote, you can use a supported TV or receiver remote, a network-based remote for home-control systems, an infrared remote (commonly known as a universal remote), or other Apple devices like an iPhone or iPad to control the Apple'},\\n {'title': 'REACT:在语言模型中协同推理与行动,使其能够解决各种 ...',\\n 'link': 'https://blog.csdn.net/fogdragon/article/details/132550968',\\n 'snippet': 'Q:Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to intect with? 除了苹果遥控器之外,还有哪些设备可以控制最初设计用于连接的苹果遥控器所配合的程序?'},\\n {'title': '《ReAct: SYNERGIZING REASONING AND ACTING IN ...',\\n 'link': 'https://www.cnblogs.com/LittleHann/p/17541295.html',\\n 'snippet': 'Aside from the Apple Remote, what other devices can control the program Apple Remote was originally designed to interact with?'}]\"]\n", + "assistant: According to the search results, aside from the Apple Remote, you can use the following devices to control the program it was designed to interact with:\n", + "\n", + "1. A supported TV or receiver remote.\n", + "2. A network-based remote for home-control systems.\n", + "3. An infrared remote (universal remote).\n", + "4. Other Apple devices such as an iPhone or iPad.\n", + "\n", + "These alternatives allow for similar control functionality as the Apple Remote.\n" + ] + } + ], + "source": [ + "mss = Msg(\n", + " name=\"user\", \n", + " content=\"Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to interact with?\", \n", + " role=\"user\"\n", + ")\n", + "logger.chat(mss)\n", + "answer_mss1 = agent(mss)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The CodeAct agent successfully use the given search tool functions and return the answer accordingly. This demonstrates the wide range of usage of agent with coding abilities.\n", + "Feel free to explore the Agent by yourself!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "datajuicer", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/conversation_with_codeact_agent/linear_regression_plot.png b/examples/conversation_with_codeact_agent/linear_regression_plot.png new file mode 100644 index 0000000000000000000000000000000000000000..a70267ed28c876643fa2e9e6ad065e745a52d290 GIT binary patch literal 32995 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%0DLn3^X>U?y^k0a8Z0|IyBkom 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 a+Voy`JD=gt!2qozfJ7*$%NJZR@%uNQ9veIW literal 0 HcmV?d00001 From dcd8da1a99df041cbbb4e6409f32cea6e6053fb8 Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Thu, 25 Jul 2024 16:09:56 +0800 Subject: [PATCH 14/24] using RegexTaggedContentParser --- .../codeact_agent.py | 40 ++++++------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/examples/conversation_with_codeact_agent/codeact_agent.py b/examples/conversation_with_codeact_agent/codeact_agent.py index d3487bcd1..0dbb4d110 100644 --- a/examples/conversation_with_codeact_agent/codeact_agent.py +++ b/examples/conversation_with_codeact_agent/codeact_agent.py @@ -6,10 +6,6 @@ https://arxiv.org/abs/2402.01030 and the original repo of codeact https://github.com/xingyaoww/code-act """ -import re -from typing import Union - - from agentscope.agents import AgentBase from agentscope.message import Msg from agentscope.service import ( @@ -17,13 +13,14 @@ ServiceExecStatus, NoteBookExecutor, ) +from agentscope.parsers import RegexTaggedContentParser SYSTEM_MESSAGE = """system You are a helpful assistant that gives helpful, detailed, and polite answers to the user's questions. -You should interact with the interactive Python (Jupyter Notebook) environment and receive the corresponding output when needed. The code written by assistant should be enclosed using [execute] tag, for example: [execute] print('Hello World!') [/execute]. -You should attempt fewer things at a time instead of putting too much code in one [execute] block. You can install packages through PIP by [execute] !pip install [package needed] [/execute] and should always import packages and define variables before starting to use them. -You should stop [execute] and provide an answer when they have already obtained the answer from the execution result. Whenever possible, execute the code for the user using [execute] instead of providing it. -Your response should be concise, but do express their thoughts. Always write the code in [execute] block to execute them. +You should interact with the interactive Python (Jupyter Notebook) environment and receive the corresponding output when needed. The code written by assistant should be enclosed using tag, for example: print('Hello World!') . +You should attempt fewer things at a time instead of putting too much code in one block. You can install packages through PIP by !pip install [package needed] and should always import packages and define variables before starting to use them. +You should stop and provide an answer when they have already obtained the answer from the execution result. Whenever possible, execute the code for the user using instead of providing it. +Your response should be concise, but do express their thoughts. Always write the code in block to execute them. You should not ask for the user's input unless necessary. Solve the task on your own and leave no unanswered questions behind. You should do every thing by your self. """ # noqa @@ -80,22 +77,7 @@ def __init__( self.memory.add(code_exec_msg) self.speak(code_exec_msg) - def handle_code_execution( - self, - response_text: str, - ) -> Union[ServiceResponse, None]: - """check if there are code execution block in the response, - and execute them if exists""" - code = re.search( - r"\[execute\](.*)\[/execute\]", - response_text, - re.DOTALL, - ) - if code is not None: - code = code.group(1).strip() - result = self.code_executor.run_code_on_notebook(code) - return result - return None + self.parser = RegexTaggedContentParser(try_parse_json=False) def handle_code_result( self, @@ -132,9 +114,13 @@ def reply(self, x: dict = None) -> None: ) self.memory.add(msg_res) self.speak(msg_res) - - code_execution_result = self.handle_code_execution(model_res.text) - if code_execution_result is not None: + res = self.parser.parse(model_res) + code = res.parsed.get("execute") + if code is not None: + code = code.strip() + code_execution_result = ( + self.code_executor.run_code_on_notebook(code) + ) excution_count += 1 code_exec_msg = self.handle_code_result(code_execution_result) self.memory.add(code_exec_msg) From d2644ac16ef8e58b39166771e8e9517016f7fb57 Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Thu, 25 Jul 2024 16:15:12 +0800 Subject: [PATCH 15/24] fix typo repo --- .../codeact_agent.py | 155 -------------- .../codeact_agent_example_modeling.ipynb | 201 ------------------ .../codeact_agent_example_tools.ipynb | 189 ---------------- .../linear_regression_plot.png | Bin 32995 -> 0 bytes 4 files changed, 545 deletions(-) delete mode 100644 examples/converstation_with_codeact_agent/codeact_agent.py delete mode 100644 examples/converstation_with_codeact_agent/codeact_agent_example_modeling.ipynb delete mode 100644 examples/converstation_with_codeact_agent/codeact_agent_example_tools.ipynb delete mode 100644 examples/converstation_with_codeact_agent/linear_regression_plot.png diff --git a/examples/converstation_with_codeact_agent/codeact_agent.py b/examples/converstation_with_codeact_agent/codeact_agent.py deleted file mode 100644 index d3487bcd1..000000000 --- a/examples/converstation_with_codeact_agent/codeact_agent.py +++ /dev/null @@ -1,155 +0,0 @@ -# -*- coding: utf-8 -*- -# pylint: disable=C0301 -"""An agent class that implements the CodeAct agent. -This agent can execute code interactively as actions. -More details can be found at the paper of codeact agent -https://arxiv.org/abs/2402.01030 -and the original repo of codeact https://github.com/xingyaoww/code-act -""" -import re -from typing import Union - - -from agentscope.agents import AgentBase -from agentscope.message import Msg -from agentscope.service import ( - ServiceResponse, - ServiceExecStatus, - NoteBookExecutor, -) - -SYSTEM_MESSAGE = """system -You are a helpful assistant that gives helpful, detailed, and polite answers to the user's questions. -You should interact with the interactive Python (Jupyter Notebook) environment and receive the corresponding output when needed. The code written by assistant should be enclosed using [execute] tag, for example: [execute] print('Hello World!') [/execute]. -You should attempt fewer things at a time instead of putting too much code in one [execute] block. You can install packages through PIP by [execute] !pip install [package needed] [/execute] and should always import packages and define variables before starting to use them. -You should stop [execute] and provide an answer when they have already obtained the answer from the execution result. Whenever possible, execute the code for the user using [execute] instead of providing it. -Your response should be concise, but do express their thoughts. Always write the code in [execute] block to execute them. -You should not ask for the user's input unless necessary. Solve the task on your own and leave no unanswered questions behind. -You should do every thing by your self. -""" # noqa - -EXAMPLE_MESSAGE = """ -Additionally, you are provided with the following code available: -{example_code} -The above code is already available in your interactive Python (Jupyter Notebook) environment, allowing you to directly use these variables and functions without needing to redeclare them. -""" # noqa - - -class CodeActAgent(AgentBase): - """ - The implementation of CodeAct-agent. - The agent can execute code interactively as actions. - More details can be found at the paper of codeact agent - https://arxiv.org/abs/2402.01030 - and the original repo of codeact https://github.com/xingyaoww/code-act - """ - - def __init__( - self, - name: str, - model_config_name: str, - example_code: str = "", - ) -> None: - """ """ - super().__init__( - name=name, - model_config_name=model_config_name, - ) - self.n_max_executions = 5 - self.example_code = example_code - self.code_executor = NoteBookExecutor() - - sys_msg = Msg(name="system", role="system", content=SYSTEM_MESSAGE) - example_msg = Msg( - name="user", - role="user", - content=EXAMPLE_MESSAGE.format(example_code=self.example_code), - ) - - self.memory.add(sys_msg) - - if self.example_code != "": - code_execution_result = self.code_executor.run_code_on_notebook( - self.example_code, - ) - code_exec_msg = self.handle_code_result( - code_execution_result, - "Example Code excuted: ", - ) - self.memory.add(example_msg) - self.memory.add(code_exec_msg) - self.speak(code_exec_msg) - - def handle_code_execution( - self, - response_text: str, - ) -> Union[ServiceResponse, None]: - """check if there are code execution block in the response, - and execute them if exists""" - code = re.search( - r"\[execute\](.*)\[/execute\]", - response_text, - re.DOTALL, - ) - if code is not None: - code = code.group(1).strip() - result = self.code_executor.run_code_on_notebook(code) - return result - return None - - def handle_code_result( - self, - code_execution_result: ServiceResponse, - content_pre_sring: str = "", - ) -> Msg: - """return the message from code result""" - code_exec_content = content_pre_sring - if code_execution_result.status == ServiceExecStatus.SUCCESS: - code_exec_content += "Excution Successful:\n" - else: - code_exec_content += "Excution Failed:\n" - code_exec_content += "Execution Output:\n" + str( - code_execution_result.content, - ) - return Msg(name="user", role="user", content=code_exec_content) - - def reply(self, x: dict = None) -> None: - """The reply function that implements the codeact agent.""" - - self.memory.add(x) - - excution_count = 0 - while ( - self.memory.get_memory(1)[-1].role == "user" - and excution_count < self.n_max_executions - ): - prompt = self.model.format(self.memory.get_memory()) - model_res = self.model(prompt) - msg_res = Msg( - name=self.name, - content=model_res.text, - role="assistant", - ) - self.memory.add(msg_res) - self.speak(msg_res) - - code_execution_result = self.handle_code_execution(model_res.text) - if code_execution_result is not None: - excution_count += 1 - code_exec_msg = self.handle_code_result(code_execution_result) - self.memory.add(code_exec_msg) - self.speak(code_exec_msg) - - if excution_count == self.n_max_executions: - assert self.memory.get_memory(1)[-1].role == "user" - code_max_exec_msg = Msg( - name="assitant", - role="assistant", - content=( - "I have reached the maximum number " - f"of executions ({self.n_max_executions=}). " - "Can you assist me or ask me another question?" - ), - ) - self.memory.add(code_max_exec_msg) - self.speak(code_max_exec_msg) diff --git a/examples/converstation_with_codeact_agent/codeact_agent_example_modeling.ipynb b/examples/converstation_with_codeact_agent/codeact_agent_example_modeling.ipynb deleted file mode 100644 index f7b80eba2..000000000 --- a/examples/converstation_with_codeact_agent/codeact_agent_example_modeling.ipynb +++ /dev/null @@ -1,201 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Conversation with CodeAct Agent\n", - "\n", - "CodeAct agent is an agent that not only chats but also writes and executes Python code for you.\n", - "More details can be found in the project's related [github repo](https://github.com/xingyaoww/code-act). In the example here, we implement the CodeAct agent, and provide a simple example of how to use the CodeAct agent.\n", - "\n", - "## Prerequisites\n", - "\n", - "- Follow [READMD.md](https://github.com/modelscope/agentscope) to install AgentScope. We require the lastest version, so you should build from source by running `pip install -e .` instead of intalling from pypi. \n", - "- Prepare a model configuration. AgentScope supports both local deployed model services (CPU or GPU) and third-party services. More details and example model configurations please refer to our [tutorial](https://modelscope.github.io/agentscope/en/tutorial/203-model.html).\n", - "\n", - "## Note\n", - "- The example is tested with the following models. For other models, you may need to adjust the prompt.\n", - " - qwen-max" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "YOUR_MODEL_CONFIGURATION_NAME = \"{YOUR_MODEL_CONFIGURATION_NAME}\"\n", - "\n", - "YOUR_MODEL_CONFIGURATION = {\n", - " \"model_type\": \"xxx\", \n", - " \"config_name\": YOUR_MODEL_CONFIGURATION_NAME\n", - " \n", - " # ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Step 1: Initalize the CodeAct-agent\n", - "\n", - "Here we load the CodeAct agent." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from codeact_agent import CodeActAgent\n", - "\n", - "import agentscope\n", - "\n", - "agentscope.init(model_configs=YOUR_MODEL_CONFIGURATION)\n", - "\n", - "import nest_asyncio\n", - "nest_asyncio.apply()\n", - "agent = CodeActAgent(\n", - " name=\"assistant\",\n", - " model_config_name=YOUR_MODEL_CONFIGURATION_NAME,\n", - ")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "###Step 2: Ask the CodeAct-agent to execute tasks\n", - "\n", - "Here, we ask the CodeAct-agent to implement a statistical simulation and modeling procedure." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2024-05-16 19:48:01.620 | INFO | agentscope.models.model:__init__:201 - Initialize model by configuration [dashscope_chat-qwen-max]\n", - "2024-05-16 19:48:01.624 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.call_counter] to SqliteMonitor with unit [times] and quota [None]\n", - "2024-05-16 19:48:01.627 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.prompt_tokens] to SqliteMonitor with unit [token] and quota [None]\n", - "2024-05-16 19:48:01.630 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.completion_tokens] to SqliteMonitor with unit [token] and quota [None]\n", - "2024-05-16 19:48:01.632 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.total_tokens] to SqliteMonitor with unit [token] and quota [None]\n", - "user: Given y = 0.9x + 6.1, randomly sample data points as pairs of (x, y). Then fit a linear regression on the sampled data and plot the points, fitted line, and ground-truth line.\n", - "assistant: To accomplish this task, I will follow these steps:\n", - "\n", - "1. Generate random `x` values within a reasonable range.\n", - "2. Use the equation `y = 0.9x + 6.1` to compute the corresponding `y` values for each `x`.\n", - "3. Add some noise to the `y` values to simulate real-world data variability.\n", - "4. Split the data into training and testing sets (though for simplicity and since you didn't specify, we'll just use all the data for fitting).\n", - "5. Fit a linear regression model on the generated data.\n", - "6. Plot the original data points, the fitted line from the regression, and the ground-truth line.\n", - "\n", - "Let's start by generating the data points and fitting the regression model.\n", - "\n", - "[execute]\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "from sklearn.linear_model import LinearRegression\n", - "\n", - "# Set a seed for reproducibility\n", - "np.random.seed(42)\n", - "\n", - "# Generate random x values between 0 and 100\n", - "x_values = np.random.rand(100) * 100\n", - "\n", - "# Compute the corresponding y values without noise\n", - "y_values_ground_truth = 0.9 * x_values + 6.1\n", - "\n", - "# Add some noise to y values\n", - "noise = np.random.normal(0, 10, size=x_values.shape)\n", - "y_values_noisy = y_values_ground_truth + noise\n", - "\n", - "# Combine into a single dataset\n", - "data = np.column_stack((x_values, y_values_noisy))\n", - "\n", - "# Fit a linear regression model\n", - "model = LinearRegression()\n", - "model.fit(data[:, 0].reshape(-1, 1), data[:, 1])\n", - "\n", - "# Prepare to plot\n", - "plt.figure(figsize=(10, 6))\n", - "[/execute]\n", - "user: Execution Output:\n", - "['
', 'Unsupported display type']\n", - "assistant: It seems there was an issue with displaying the plot directly here. No worries, I will still generate the plot and save it to a file instead, which you can view separately. Let me adjust the code to save the plot as an image file.\n", - "\n", - "[execute]\n", - "# Plotting the data points, fitted line, and ground-truth line\n", - "plt.scatter(data[:, 0], data[:, 1], label='Sampled Data', color='blue')\n", - "plt.plot(data[:, 0], model.predict(data[:, 0].reshape(-1, 1)), label='Fitted Line', color='red', linestyle='--')\n", - "plt.plot(data[:, 0], y_values_ground_truth, label='Ground-Truth Line', color='green')\n", - "\n", - "plt.title('Linear Regression on Sampled Data')\n", - "plt.xlabel('X')\n", - "plt.ylabel('Y')\n", - "plt.legend()\n", - "plt.grid(True)\n", - "\n", - "# Save the plot to a file\n", - "plt.savefig('linear_regression_plot.png')\n", - "plt.close()\n", - "[/execute]\n", - "\n", - "The plot has been saved as 'linear_regression_plot.png'. You can view this file to see the sampled data points, the fitted line from the linear regression, and the ground-truth line based on the equation `y = 0.9x + 6.1`. If you need further analysis or have any other requests, feel free to ask!\n", - "user: Execution Output:\n", - "[]\n", - "assistant: It appears the output confirmation was suppressed in this environment, but typically, when running the code locally or in a supported environment, you would see a message indicating the plot was successfully saved to 'linear_regression_plot.png'.\n", - "\n", - "Since we cannot directly view the saved file here, trust that the file has been created with the following components:\n", - "\n", - "- **Sampled Data Points**: Represented as blue dots, scattered according to the generated `x` values and the noisy `y` values.\n", - "- **Fitted Line**: Shown as a red dashed line, representing the linear regression model's prediction based on the sampled data.\n", - "- **Ground-Truth Line**: Displayed as a green line, illustrating the true relationship defined by `y = 0.9x + 6.1`.\n", - "\n", - "If you need further assistance or another operation, such as analyzing the quality of the fit or re-running the process with different parameters, please let me know!\n" - ] - } - ], - "source": [ - "from loguru import logger\n", - "from agentscope.message import Msg\n", - "\n", - "mss = Msg(\n", - " name=\"user\", \n", - " content=\"Given y = 0.9x + 6.1, randomly sample data points as pairs of (x, y). Then fit a linear regression on the sampled data and plot the points, fitted line, and ground-truth line.\", \n", - " role=\"user\"\n", - ")\n", - "logger.chat(mss)\n", - "answer_mss1 = agent(mss)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "datajuicer", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.18" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/converstation_with_codeact_agent/codeact_agent_example_tools.ipynb b/examples/converstation_with_codeact_agent/codeact_agent_example_tools.ipynb deleted file mode 100644 index 787e1a15f..000000000 --- a/examples/converstation_with_codeact_agent/codeact_agent_example_tools.ipynb +++ /dev/null @@ -1,189 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Conversation with CodeAct Agent\n", - "\n", - "CodeAct agent is an agent that not only chats but also writes and executes Python code for you.\n", - "More details can be found in the project's related [github repo](https://github.com/xingyaoww/code-act). \n", - "\n", - "In the following CodeAct agent example, we demonstrate another method of empowering the agent with the capability to invoke tools, specifically by directly providing the agent with the corresponding code for the tools and then allowing the agent to utilize them independently.\n", - "Interm of tool usage, ReAct agent also enables the agent to use tools to solve problems, but in a different way. You can refer to the [ReAct agent](https://github.com/modelscope/agentscope/blob/main/examples/conversation_with_react_agent/main.ipynb) example for more details and compare the two.\n", - "\n", - "## Prerequisites\n", - "\n", - "- Follow [READMD.md](https://github.com/modelscope/agentscope) to install AgentScope. We require the lastest version, so you should build from source by running `pip install -e .` instead of intalling from pypi. \n", - "- Prepare a model configuration. AgentScope supports both local deployed model services (CPU or GPU) and third-party services. More details and example model configurations please refer to our [tutorial](https://modelscope.github.io/agentscope/en/tutorial/203-model.html).\n", - "\n", - "## Note\n", - "- The example is tested with the following models. For other models, you may need to adjust the prompt.\n", - " - qwen-max" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "YOUR_MODEL_CONFIGURATION_NAME = \"{YOUR_MODEL_CONFIGURATION_NAME}\"\n", - "\n", - "YOUR_MODEL_CONFIGURATION = {\n", - " \"model_type\": \"xxx\", \n", - " \"config_name\": YOUR_MODEL_CONFIGURATION_NAME\n", - " \n", - " # ...\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Step 1: Initalize the AgentScope environment\n", - "\n", - "Here we initialize the AgentScope environment by calling the `agentscope.init` function. The `model_configs` parameter specifies the path to the model configuration file." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import agentscope\n", - "\n", - "agentscope.init(model_configs=YOUR_MODEL_CONFIGURATION)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Step 2: Set up the code for tool usage, and init the CodeAct Agent.\n", - "\n", - "Here, we provide the CodeAct agent with the interactive code that the agent can use during the conversation." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "from loguru import logger\n", - "from codeact_agent import CodeActAgent\n", - "\n", - "from agentscope.message import Msg\n", - "\n", - "YOUR_BING_API_KEY = \"xxx\" # fill in your bing api key here\n", - "\n", - "example_code = f\"\"\"\n", - "from agentscope.service import bing_search\n", - "import os\n", - "\n", - "api_key = \"{YOUR_BING_API_KEY}\"\n", - "\n", - "def search(question: str):\n", - " return bing_search(question, api_key=api_key, num_results=3).content\n", - "\"\"\"\n", - "\n", - "import nest_asyncio\n", - "nest_asyncio.apply()\n", - "agent = CodeActAgent(\n", - " name=\"assistant\",\n", - " model_config_name=YOUR_MODEL_CONFIGURATION_NAME,\n", - " example_code=example_code\n", - ")\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Step 3: Ask the CodeAct-agent to execute tasks\n", - "\n", - "Here, we ask the CodeAct-agent with the example question in the ReAct Paper: `\"Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to interact with?\"` as follows, same as in the [ReAct-agent]((https://github.com/modelscope/agentscope/blob/main/examples/conversation_with_react_agent/main.ipynb)) example." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2024-05-17 10:58:04.214 | INFO | agentscope.models.model:__init__:201 - Initialize model by configuration [dashscope_chat-qwen-max]\n", - "2024-05-17 10:58:04.215 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.call_counter] to SqliteMonitor with unit [times] and quota [None]\n", - "2024-05-17 10:58:04.218 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.prompt_tokens] to SqliteMonitor with unit [token] and quota [None]\n", - "2024-05-17 10:58:04.220 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.completion_tokens] to SqliteMonitor with unit [token] and quota [None]\n", - "2024-05-17 10:58:04.222 | INFO | agentscope.utils.monitor:register:417 - Register metric [qwen-max.total_tokens] to SqliteMonitor with unit [token] and quota [None]\n", - "user: Example Code excuted: Excution Successful:\n", - " Execution Output:\n", - "[]\n", - "user: Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to interact with?\n", - "assistant: [execute]\n", - "search_result = search(\"Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to interact with?\")\n", - "search_result\n", - "[/execute]\n", - "user: Excution Successful:\n", - " Execution Output:\n", - "[\"[{'title': 'Multi-Agent实践第4期:智能体的“想”与“做”-ReAct Agent - 知乎',\\n 'link': 'https://zhuanlan.zhihu.com/p/689675968',\\n 'snippet': 'Other than the Apple Remote, you can use a supported TV or receiver remote, a network-based remote for home-control systems, an infrared remote (commonly known as a universal remote), or other Apple devices like an iPhone or iPad to control the Apple'},\\n {'title': 'REACT:在语言模型中协同推理与行动,使其能够解决各种 ...',\\n 'link': 'https://blog.csdn.net/fogdragon/article/details/132550968',\\n 'snippet': 'Q:Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to intect with? 除了苹果遥控器之外,还有哪些设备可以控制最初设计用于连接的苹果遥控器所配合的程序?'},\\n {'title': '《ReAct: SYNERGIZING REASONING AND ACTING IN ...',\\n 'link': 'https://www.cnblogs.com/LittleHann/p/17541295.html',\\n 'snippet': 'Aside from the Apple Remote, what other devices can control the program Apple Remote was originally designed to interact with?'}]\"]\n", - "assistant: According to the search results, aside from the Apple Remote, you can use the following devices to control the program it was designed to interact with:\n", - "\n", - "1. A supported TV or receiver remote.\n", - "2. A network-based remote for home-control systems.\n", - "3. An infrared remote (universal remote).\n", - "4. Other Apple devices such as an iPhone or iPad.\n", - "\n", - "These alternatives allow for similar control functionality as the Apple Remote.\n" - ] - } - ], - "source": [ - "mss = Msg(\n", - " name=\"user\", \n", - " content=\"Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to interact with?\", \n", - " role=\"user\"\n", - ")\n", - "logger.chat(mss)\n", - "answer_mss1 = agent(mss)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The CodeAct agent successfully use the given search tool functions and return the answer accordingly. This demonstrates the wide range of usage of agent with coding abilities.\n", - "Feel free to explore the Agent by yourself!" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "datajuicer", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.18" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/converstation_with_codeact_agent/linear_regression_plot.png b/examples/converstation_with_codeact_agent/linear_regression_plot.png deleted file mode 100644 index a70267ed28c876643fa2e9e6ad065e745a52d290..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32995 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%0DLn3^X>U?y^k0a8Z0|IyBkom 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 a+Voy`JD=gt!2qozfJ7*$%NJZR@%uNQ9veIW From 7c06d2ceae3298442c0c21d55990d037ef965b23 Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Tue, 30 Jul 2024 17:22:25 +0800 Subject: [PATCH 16/24] fix threads --- .../codeact_agent.py | 29 +++++++++++++++++-- .../service/execute_code/exec_notebook.py | 2 +- src/agentscope/service/service_toolkit.py | 4 +-- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/examples/conversation_with_codeact_agent/codeact_agent.py b/examples/conversation_with_codeact_agent/codeact_agent.py index 0dbb4d110..e4c71cb3e 100644 --- a/examples/conversation_with_codeact_agent/codeact_agent.py +++ b/examples/conversation_with_codeact_agent/codeact_agent.py @@ -47,7 +47,29 @@ def __init__( model_config_name: str, example_code: str = "", ) -> None: - """ """ + """ + Initialize the CodeActAgent. + Args: + name(`str`): + The name of the agent. + model_config_name(`str`): + The name of the model configuration. + example_code(Optional`str`): + The example code to be executed bewfore the interaction. + You can import reference libs, define variables + and functions to be called. For example: + + ```python + from agentscope.service import bing_search + import os + + api_key = "{YOUR_BING_API_KEY}" + + def search(question: str): + return bing_search(question, api_key=api_key, num_results=3).content + ``` + + """ # noqa super().__init__( name=name, model_config_name=model_config_name, @@ -95,7 +117,7 @@ def handle_code_result( ) return Msg(name="user", role="user", content=code_exec_content) - def reply(self, x: dict = None) -> None: + def reply(self, x: Msg = None) -> Msg: """The reply function that implements the codeact agent.""" self.memory.add(x) @@ -139,3 +161,6 @@ def reply(self, x: dict = None) -> None: ) self.memory.add(code_max_exec_msg) self.speak(code_max_exec_msg) + return code_max_exec_msg + + return msg_res diff --git a/src/agentscope/service/execute_code/exec_notebook.py b/src/agentscope/service/execute_code/exec_notebook.py index 9cf64f3a8..d36b43e11 100644 --- a/src/agentscope/service/execute_code/exec_notebook.py +++ b/src/agentscope/service/execute_code/exec_notebook.py @@ -63,7 +63,7 @@ def __init__( notebook: nbformat.NotebookNode = nbformat.v4.new_notebook(), timeout: int = 300, output_dir: Union[str, None] = None, - ): + ) -> None: self.nb = notebook self.nb_client = NotebookClient(nb=self.nb) self.timeout = timeout diff --git a/src/agentscope/service/service_toolkit.py b/src/agentscope/service/service_toolkit.py index 4a3d21244..afa112bd9 100644 --- a/src/agentscope/service/service_toolkit.py +++ b/src/agentscope/service/service_toolkit.py @@ -497,7 +497,7 @@ def bing_search(query: str, api_key: str, num_results: int=10): # The arguments that requires the agent to specify # to support class method, the self args are deprecated - args_agent = set(argsspec.args) - set(kwargs.keys()) - {"self"} + args_agent = set(argsspec.args) - set(kwargs.keys()) - {"self", "cls"} # Check if the arguments from agent have descriptions in docstring args_description = { @@ -655,7 +655,7 @@ def bing_search(query: str, api_key: str, num_results: int=10): # The arguments that requires the agent to specify # we remove the self argument, for class methods - args_agent = set(argsspec.args) - set(kwargs.keys()) - {"self"} + args_agent = set(argsspec.args) - set(kwargs.keys()) - {"self", "cls"} # Check if the arguments from agent have descriptions in docstring args_description = { From 534f30a0ef167f710f4362cb9216cf2085444156 Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Tue, 30 Jul 2024 17:35:04 +0800 Subject: [PATCH 17/24] add file_manager --- src/agentscope/file_manager.py | 15 +++++++++++-- .../service/execute_code/exec_notebook.py | 21 ++++++++++--------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/agentscope/file_manager.py b/src/agentscope/file_manager.py index ab042f5b5..4adde73a4 100644 --- a/src/agentscope/file_manager.py +++ b/src/agentscope/file_manager.py @@ -2,6 +2,7 @@ """Manage the file system for saving files, code and logs.""" import json import os +import io from typing import Any, Union, Optional, List, Literal from pathlib import Path import numpy as np @@ -169,7 +170,7 @@ def save_api_invocation( def save_image( self, - image: Union[str, np.ndarray], + image: Union[str, np.ndarray, bytes], filename: Optional[str] = None, ) -> str: """Save image file locally, and return the local image path. @@ -195,11 +196,21 @@ def save_image( if isinstance(image, str): # download the image from url _download_file(image, path_file) - else: + elif isinstance(image, np.ndarray): from PIL import Image # save image via PIL Image.fromarray(image).save(path_file) + elif isinstance(image, bytes): + from PIL import Image + + # save image via bytes + Image.open(io.BytesIO(image)).save(path_file) + else: + raise ValueError( + f"Unsupported image type: {type(image)}" + "Must be str, np.ndarray, or bytes.", + ) return path_file diff --git a/src/agentscope/service/execute_code/exec_notebook.py b/src/agentscope/service/execute_code/exec_notebook.py index d36b43e11..706c24639 100644 --- a/src/agentscope/service/execute_code/exec_notebook.py +++ b/src/agentscope/service/execute_code/exec_notebook.py @@ -5,9 +5,7 @@ """ # noqa import base64 import uuid -import os import asyncio -from typing import Union from loguru import logger @@ -62,14 +60,20 @@ def __init__( self, notebook: nbformat.NotebookNode = nbformat.v4.new_notebook(), timeout: int = 300, - output_dir: Union[str, None] = None, ) -> None: + """ + The construct function of the NoteBookExecutor. + Args: + notebook (Optional`nbformat.NotebookNode`): + The notebook to execute. + Will init a new notebook instanace by default. + timeout (Optional`int`): + The timeout for each cell execution. + Default to 300. + """ self.nb = notebook self.nb_client = NotebookClient(nb=self.nb) self.timeout = timeout - if output_dir is None: - self.output_dir = file_manager.dir_file - self.output_dir = output_dir asyncio.run(self._start_client()) @@ -192,7 +196,4 @@ def _save_image(self, image_base64: str) -> str: The image name is generated randomly here""" image_data = base64.b64decode(image_base64) filename = f"display_image_{uuid.uuid4().hex}.png" - path = os.path.join(self.output_dir, filename) - with open(path, "wb") as f: - f.write(image_data) - return os.path.abspath(path) + return file_manager.save_image(image_data, filename) From c6718e94448eb9941467352bac2b9b62b092b16e Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Tue, 30 Jul 2024 17:41:19 +0800 Subject: [PATCH 18/24] fix typing and threads --- src/agentscope/service/execute_code/exec_notebook.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/agentscope/service/execute_code/exec_notebook.py b/src/agentscope/service/execute_code/exec_notebook.py index 706c24639..586e1d426 100644 --- a/src/agentscope/service/execute_code/exec_notebook.py +++ b/src/agentscope/service/execute_code/exec_notebook.py @@ -58,20 +58,16 @@ class NoteBookExecutor: def __init__( self, - notebook: nbformat.NotebookNode = nbformat.v4.new_notebook(), timeout: int = 300, ) -> None: """ The construct function of the NoteBookExecutor. Args: - notebook (Optional`nbformat.NotebookNode`): - The notebook to execute. - Will init a new notebook instanace by default. timeout (Optional`int`): The timeout for each cell execution. Default to 300. """ - self.nb = notebook + self.nb = nbformat.v4.new_notebook() self.nb_client = NotebookClient(nb=self.nb) self.timeout = timeout From d6d83b591a7e4b66bc19e72268b58d3efc026c7f Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Tue, 30 Jul 2024 18:30:59 +0800 Subject: [PATCH 19/24] fix threads --- examples/conversation_with_codeact_agent/codeact_agent.py | 2 +- src/agentscope/service/execute_code/exec_notebook.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/conversation_with_codeact_agent/codeact_agent.py b/examples/conversation_with_codeact_agent/codeact_agent.py index e4c71cb3e..d09b7fda4 100644 --- a/examples/conversation_with_codeact_agent/codeact_agent.py +++ b/examples/conversation_with_codeact_agent/codeact_agent.py @@ -15,7 +15,7 @@ ) from agentscope.parsers import RegexTaggedContentParser -SYSTEM_MESSAGE = """system +SYSTEM_MESSAGE = """ You are a helpful assistant that gives helpful, detailed, and polite answers to the user's questions. You should interact with the interactive Python (Jupyter Notebook) environment and receive the corresponding output when needed. The code written by assistant should be enclosed using tag, for example: print('Hello World!') . You should attempt fewer things at a time instead of putting too much code in one block. You can install packages through PIP by !pip install [package needed] and should always import packages and define variables before starting to use them. diff --git a/src/agentscope/service/execute_code/exec_notebook.py b/src/agentscope/service/execute_code/exec_notebook.py index 586e1d426..784cf41f9 100644 --- a/src/agentscope/service/execute_code/exec_notebook.py +++ b/src/agentscope/service/execute_code/exec_notebook.py @@ -73,7 +73,7 @@ def __init__( asyncio.run(self._start_client()) - def output_parser(self, output: dict) -> str: + def _output_parser(self, output: dict) -> str: """Parse the output of the notebook cell and return str""" if output["output_type"] == "stream": return output["text"] @@ -124,7 +124,7 @@ async def _run_cell(self, cell_index: int) -> ServiceResponse: return ServiceResponse( status=ServiceExecStatus.SUCCESS, content=[ - self.output_parser(output) + self._output_parser(output) for output in self.nb.cells[cell_index].outputs ], ) From 93f8afb23e14e13b561b7c21b19e9692319c1ee0 Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Thu, 1 Aug 2024 16:57:10 +0800 Subject: [PATCH 20/24] add test for ipython and update tutorial --- .../en/source/tutorial/204-service.md | 1 + .../zh_CN/source/tutorial/204-service.md | 1 + tests/execute_notebook_code_test.py | 44 +++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 tests/execute_notebook_code_test.py diff --git a/docs/sphinx_doc/en/source/tutorial/204-service.md b/docs/sphinx_doc/en/source/tutorial/204-service.md index 38b4047b0..532cb68b3 100644 --- a/docs/sphinx_doc/en/source/tutorial/204-service.md +++ b/docs/sphinx_doc/en/source/tutorial/204-service.md @@ -15,6 +15,7 @@ The following table outlines the various Service functions by type. These functi | Service Scene | Service Function Name | Description | |-----------------------------|----------------------------|----------------------------------------------------------------------------------------------------------------| | Code | `execute_python_code` | Execute a piece of Python code, optionally inside a Docker container. | +| | `NoteBookExecutor.run_code_on_notebook` | Compute Execute a piece of Python code, in `ipython` fashion, within the ipython environment of the NoteBookExecutor. | | Retrieval | `retrieve_from_list` | Retrieve a specific item from a list based on given criteria. | | | `cos_sim` | Compute the cosine similarity between two different embeddings. | | SQL Query | `query_mysql` | Execute SQL queries on a MySQL database and return results. | diff --git a/docs/sphinx_doc/zh_CN/source/tutorial/204-service.md b/docs/sphinx_doc/zh_CN/source/tutorial/204-service.md index 23c145a05..4416f85c4 100644 --- a/docs/sphinx_doc/zh_CN/source/tutorial/204-service.md +++ b/docs/sphinx_doc/zh_CN/source/tutorial/204-service.md @@ -12,6 +12,7 @@ | Service场景 | Service函数名称 | 描述 | |------------|-----------------------|-----------------------------------------| | 代码 | `execute_python_code` | 执行一段 Python 代码,可选择在 Docker
容器内部执行。 | +| | `NoteBookExecutor.run_code_on_notebook` | 在由NoteBookExecutor创建的`ipython`环境中,执行一段 Python 代码。 | | 检索 | `retrieve_from_list` | 根据给定的标准从列表中检索特定项目。 | | | `cos_sim` | 计算2个embedding的余弦相似度。 | | SQL查询 | `query_mysql` | 在 MySQL 数据库上执行 SQL 查询并返回结果。 | diff --git a/tests/execute_notebook_code_test.py b/tests/execute_notebook_code_test.py new file mode 100644 index 000000000..1fc2a2341 --- /dev/null +++ b/tests/execute_notebook_code_test.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +""" iPython code execution test.""" +import unittest +from agentscope.service.execute_code.exec_notebook import NoteBookExecutor +from agentscope.service.service_status import ServiceExecStatus + + +class ExecuteNotebookCodeTest(unittest.TestCase): + """ + Notebook code execution test. + """ + + def setUp(self) -> None: + """Init for ExecuteNotebookCodeTest.""" + self.executor = NoteBookExecutor() + + # Basic expression + self.arg0 = {"code": "print('Hello World')"} + # Using External Libraries + self.arg1 = {"code": "import math\nprint(math.sqrt(16))"} + # No input code + self.arg2 = {"code": ""} + + def test_basic_expression(self) -> None: + """Execute basic expression test.""" + response = self.executor.run_code_on_notebook(self.arg0["code"]) + self.assertEqual(response.status, ServiceExecStatus.SUCCESS) + self.assertIn("Hello World\n", response.content[0]) + + def test_using_external_libs(self) -> None: + """Execute using external libs test.""" + response = self.executor.run_code_on_notebook(self.arg1["code"]) + self.assertEqual(response.status, ServiceExecStatus.SUCCESS) + self.assertIn("4.0\n", response.content[0]) + + def test_no_input_code(self) -> None: + """Execute no input code test.""" + response = self.executor.run_code_on_notebook(self.arg2["code"]) + self.assertEqual(response.status, ServiceExecStatus.SUCCESS) + self.assertEqual(response.content, []) + + +if __name__ == "__main__": + unittest.main() From 86c316a2c7644de5c7cefbd3343c02b215c025ea Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Thu, 1 Aug 2024 17:48:05 +0800 Subject: [PATCH 21/24] update doc language --- docs/sphinx_doc/en/source/tutorial/204-service.md | 2 +- docs/sphinx_doc/zh_CN/source/tutorial/204-service.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sphinx_doc/en/source/tutorial/204-service.md b/docs/sphinx_doc/en/source/tutorial/204-service.md index 532cb68b3..0cfaec6a3 100644 --- a/docs/sphinx_doc/en/source/tutorial/204-service.md +++ b/docs/sphinx_doc/en/source/tutorial/204-service.md @@ -15,7 +15,7 @@ The following table outlines the various Service functions by type. These functi | Service Scene | Service Function Name | Description | |-----------------------------|----------------------------|----------------------------------------------------------------------------------------------------------------| | Code | `execute_python_code` | Execute a piece of Python code, optionally inside a Docker container. | -| | `NoteBookExecutor.run_code_on_notebook` | Compute Execute a piece of Python code, in `ipython` fashion, within the ipython environment of the NoteBookExecutor. | +| | `NoteBookExecutor.run_code_on_notebook` | Compute Execute a segment of Python code in the IPython environment of the NoteBookExecutor, adhering to the IPython interactive computing style. | | Retrieval | `retrieve_from_list` | Retrieve a specific item from a list based on given criteria. | | | `cos_sim` | Compute the cosine similarity between two different embeddings. | | SQL Query | `query_mysql` | Execute SQL queries on a MySQL database and return results. | diff --git a/docs/sphinx_doc/zh_CN/source/tutorial/204-service.md b/docs/sphinx_doc/zh_CN/source/tutorial/204-service.md index 4416f85c4..00de68001 100644 --- a/docs/sphinx_doc/zh_CN/source/tutorial/204-service.md +++ b/docs/sphinx_doc/zh_CN/source/tutorial/204-service.md @@ -12,7 +12,7 @@ | Service场景 | Service函数名称 | 描述 | |------------|-----------------------|-----------------------------------------| | 代码 | `execute_python_code` | 执行一段 Python 代码,可选择在 Docker
容器内部执行。 | -| | `NoteBookExecutor.run_code_on_notebook` | 在由NoteBookExecutor创建的`ipython`环境中,执行一段 Python 代码。 | +| | `NoteBookExecutor.run_code_on_notebook` | 在 NoteBookExecutor 的 IPython 环境中执行一段 Python 代码,遵循 IPython 交互式计算风格。 | | 检索 | `retrieve_from_list` | 根据给定的标准从列表中检索特定项目。 | | | `cos_sim` | 计算2个embedding的余弦相似度。 | | SQL查询 | `query_mysql` | 在 MySQL 数据库上执行 SQL 查询并返回结果。 | From eaed4b8e6f5ad08091e88d8965c63f043a421eb2 Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Thu, 1 Aug 2024 21:23:41 +0800 Subject: [PATCH 22/24] add notebook dependency --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 2a3215f99..6813ada6f 100644 --- a/setup.py +++ b/setup.py @@ -86,6 +86,7 @@ "nbformat", "psutil", "scipy", + "notebook" ] distribute_requires = minimal_requires + rpc_requires From 8df0f73ad335e0fe01fc3ccb353c9f9efd2adb1f Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Fri, 2 Aug 2024 10:39:07 +0800 Subject: [PATCH 23/24] fix precommit --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6813ada6f..812b9436d 100644 --- a/setup.py +++ b/setup.py @@ -82,11 +82,11 @@ "google-generativeai>=0.4.0", "zhipuai", "litellm", + "notebook", "nbclient", "nbformat", "psutil", "scipy", - "notebook" ] distribute_requires = minimal_requires + rpc_requires From 460a60d8fa4bccfc2163294821ef4466aafd3d75 Mon Sep 17 00:00:00 2001 From: garyzhang99 Date: Fri, 2 Aug 2024 11:05:38 +0800 Subject: [PATCH 24/24] update test with no print --- tests/execute_notebook_code_test.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/execute_notebook_code_test.py b/tests/execute_notebook_code_test.py index 1fc2a2341..0259d8f76 100644 --- a/tests/execute_notebook_code_test.py +++ b/tests/execute_notebook_code_test.py @@ -20,6 +20,8 @@ def setUp(self) -> None: self.arg1 = {"code": "import math\nprint(math.sqrt(16))"} # No input code self.arg2 = {"code": ""} + # test without print + self.arg3 = {"code": "1+1"} def test_basic_expression(self) -> None: """Execute basic expression test.""" @@ -39,6 +41,12 @@ def test_no_input_code(self) -> None: self.assertEqual(response.status, ServiceExecStatus.SUCCESS) self.assertEqual(response.content, []) + def test_no_print(self) -> None: + """Execute no print test.""" + response = self.executor.run_code_on_notebook(self.arg3["code"]) + self.assertEqual(response.status, ServiceExecStatus.SUCCESS) + self.assertIn("2", response.content[0]) + if __name__ == "__main__": unittest.main()