Skip to main content

代码执行器

在 Colab 中打开 在 GitHub 上打开

在上一章中,我们使用了两个由大型语言模型(LLM)驱动的代理来通过交换消息来玩游戏。在本章中,我们将介绍代码执行器,它使代理不仅可以聊天,还可以与环境交互、执行有用的计算并采取行动。

概述

在 AutoGen 中,代码执行器是一个组件,它接收输入消息(例如包含代码块的消息),执行代码,并输出带有结果的消息。AutoGen 提供了两种类型的内置代码执行器,一种是命令行代码执行器,它在命令行环境(如 UNIX shell)中运行代码,另一种是 Jupyter 执行器,它在交互式的 Jupyter 内核 中运行代码。

对于每种类型的执行器,AutoGen 提供了两种执行代码的方式:本地执行和在 Docker 容器中执行。一种方式是直接在 AutoGen 运行的主机平台上执行代码,即本地操作系统。这适用于开发和测试,但对于生产环境来说并不理想,因为 LLM 可以生成任意代码。另一种方式是在 Docker 容器中执行代码。下表显示了代码执行器和执行环境的组合。

在本章中,我们将重点介绍命令行代码执行器。有关 Jupyter 代码执行器,请参阅Jupyter 代码执行器主题页面。

本地执行

下图显示了本地命令行代码执行器(autogen.coding.LocalCommandLineCodeExecutor)的架构。

danger

执行由LLM生成的代码会对您的主机环境造成安全风险。

Code Executor No Docker

在收到带有代码块的消息后,本地命令行代码执行器首先将代码块写入代码文件,然后启动一个新的子进程来执行代码文件。执行器读取代码执行的控制台输出,并将其作为回复消息发送回去。

下面是使用代码执行器运行打印随机数的Python代码块的示例。首先,我们创建一个使用临时目录存储代码文件的代码执行器。我们指定human_input_mode="ALWAYS"以手动验证执行的代码的安全性。

import tempfile

from autogen import ConversableAgent
from autogen.coding import LocalCommandLineCodeExecutor

# 创建一个临时目录来存储代码文件。
temp_dir = tempfile.TemporaryDirectory()

# 创建一个本地命令行代码执行器。
executor = LocalCommandLineCodeExecutor(
timeout=10, # 每个代码执行的超时时间(秒)。
work_dir=temp_dir.name, # 使用临时目录来存储代码文件。
)

# 创建一个带有代码执行器配置的代理。
code_executor_agent = ConversableAgent(
"code_executor_agent",
llm_config=False, # 关闭LLM。
code_execution_config={"executor": executor}, # 使用本地命令行代码执行器。
human_input_mode="ALWAYS", # 对于安全起见,始终要求用户输入。
)

在运行此示例之前,我们需要确保已安装matplotlibnumpy

! pip install -qqq matplotlib numpy

现在,我们让代理根据带有Python代码块的消息生成回复。

message_with_code_block = """This is a message with code block.
The code block is below:
```python
import numpy as np
import matplotlib.pyplot as plt
x = np.random.randint(0, 100, 100)
y = np.random.randint(0, 100, 100)
plt.scatter(x, y)
plt.savefig('scatter.png')
print('Scatter plot saved to scatter.png')
```
This is the end of the message.
"""

# 为给定的代码生成回复。
reply = code_executor_agent.generate_reply(messages=[{"role": "user", "content": message_with_code_block}])
print(reply)

>>>>>>>> 未收到用户输入。

>>>>>>>> 使用自动回复...

>>>>>>>> 执行代码块(推测语言为python)...
exitcode: 0(执行成功)
代码输出:
Scatter plot saved to scatter.png

在生成回复的过程中,需要人工输入以给出一个 拦截代码执行的机会。在这种情况下,我们选择继续执行,并且代理的回复包含代码执行的输出。

我们可以在临时目录中查看生成的图。

import os

print(os.listdir(temp_dir.name))
# 我们可以看到输出的 scatter.png 和代理生成的代码文件。
['scatter.png', '6507ea07b63b45aabb027ade4e213de6.py']

清理工作目录,以免影响未来的对话。

temp_dir.cleanup()

Docker 执行

为了减轻在本地运行 LLM 生成的代码的安全风险,我们可以使用 Docker 命令行代码执行器(autogen.coding.DockerCommandLineCodeExecutor)在 Docker 容器中执行代码。这样,生成的代码只能访问显式授权的资源。

下图说明了 Docker 执行的工作原理。

Code Executor Docker

与本地命令行代码执行器类似,Docker 执行器从输入消息中提取代码块,将它们写入代码文件。对于每个代码文件,它启动一个 Docker 容器来执行代码文件,并读取代码执行的控制台输出。

要使用 Docker 执行,您需要在计算机上安装 Docker。一旦您安装并运行了 Docker,您可以按照以下步骤设置代码执行器代理:

from autogen.coding import DockerCommandLineCodeExecutor

# 创建一个临时目录来存储代码文件。
temp_dir = tempfile.TemporaryDirectory()

# 创建一个 Docker 命令行代码执行器。
executor = DockerCommandLineCodeExecutor(
image="python:3.12-slim", # 使用给定的 Docker 镜像名称执行代码。
timeout=10, # 每次代码执行的超时时间(秒)。
work_dir=temp_dir.name, # 使用临时目录来存储代码文件。
)

# 创建一个使用 Docker 的代码执行器配置的代理。
code_executor_agent_using_docker = ConversableAgent(
"code_executor_agent_docker",
llm_config=False, # 关闭 LLM。
code_execution_config={"executor": executor}, # 使用 Docker 命令行代码执行器。
human_input_mode="ALWAYS", # 对于此代理,始终需要人类输入以确保安全。
)

# 当不再使用代码执行器时,停止它以释放资源。
# executor.stop()

构造函数中的 work_dir 指向本地文件系统目录,就像在本地执行的情况下一样。Docker 容器将挂载此目录,并将代码文件和输出写入其中。

在对话中使用代码执行

编写和执行代码对于许多任务(如数据分析、机器学习和数学建模)是必要的。在 AutoGen 中, 编码可以看作是代码编写代理和代码执行代理之间的对话,类似于程序员和代码解释器之间的交互。

代码编写代理和代码执行代理

代码编写代理可以由像GPT-4这样具有编写代码能力的LLM(语言模型)来驱动。而代码执行代理则由代码执行器来驱动。

下面是一个具有代码编写角色的代理,使用system_message来指定。系统消息中包含了关于如何使用代码执行器的重要指示。

# 代码编写助手的系统消息是指导语言模型助理(LLM)如何使用代码执行器代理中的代码执行器。
code_writer_system_message = """你是一个有用的AI助手。
利用你的编码和语言技能来解决任务。
在以下情况下,建议用户执行Python代码(在Python代码块中)或Shell脚本(在sh代码块中)。
1. 当你需要收集信息时,使用代码输出你需要的信息,例如浏览或搜索网页、下载/读取文件、打印网页或文件的内容、获取当前日期/时间、检查操作系统。在打印足够的信息并准备好根据你的语言技能解决任务后,你可以自己解决任务。
2. 当你需要使用代码执行某些任务时,使用代码执行任务并输出结果。聪明地完成任务。
如果需要,逐步解决任务。如果没有提供计划,请先解释你的计划。清楚地说明哪个步骤使用代码,哪个步骤使用你的语言技能。
在使用代码时,必须在代码块中指示脚本类型。用户不能提供任何其他反馈或执行任何其他操作,超出执行你建议的代码。用户不能修改你的代码。因此,不要建议需要用户修改的不完整的代码。如果不打算由用户执行,请不要使用代码块。
如果你希望用户在执行代码之前将代码保存到文件中,请在代码块中的第一行中添加# filename: <filename>。不要在一个回复中包含多个代码块。不要要求用户复制和粘贴结果。相反,当相关时使用'print'函数进行输出。检查用户返回的执行结果。
如果结果表明存在错误,请修复错误并再次输出代码。建议提供完整的代码,而不是部分代码或代码更改。如果错误无法修复,或者即使成功执行代码后任务仍未解决,请分析问题,重新审查你的假设,收集你需要的额外信息,并考虑尝试不同的方法。
当你找到答案时,请仔细验证答案。如果可能,请在回复中包含可验证的证据。
当一切都完成时,请在最后回复“TERMINATE”。
"""

code_writer_agent = ConversableAgent(
"code_writer_agent",
system_message=code_writer_system_message,
llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
code_execution_config=False, # 关闭此代理的代码执行功能。
)

下面是一个通过代码编写代理和代码执行代理之间的对话来解决数学问题的示例(上面已经创建了这两个代理)。

chat_result = code_executor_agent.initiate_chat(
code_writer_agent,
message="编写Python代码来计算第14个斐波那契数。",
)
code_executor_agent(对code_writer_agent):

编写Python代码来计算第14个斐波那契数。

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
code_writer_agent(对code_executor_agent):

好的,这是一个用于计算第14个斐波那契数的Python代码片段。斐波那契数列是一个数列,其中每个数字都是前两个数字的和,通常以0和1开始。

```python
def fibonacci(n):
if(n <= 0):
return "输入应为正整数。"
elif(n == 1):
return 0
elif(n == 2):
return 1
else:
fib = [0, 1]
for i in range(2, n):
fib.append(fib[i-1] + fib[i-2])
return fib[n-1]

print(fibonacci(14))
```

这段Python代码定义了一个名为`fibonacci(n)`的函数,用于计算第n个斐波那契数。该函数使用一个列表`fib`来存储计算出的斐波那契数,然后返回第(n-1)个元素作为第n个斐波那契数,因为Python列表从0开始索引。

--------------------------------------------------------------------------------

>>>>>>>> 未收到人类输入。

>>>>>>>> 使用自动回复...

>>>>>>>> 执行代码块(推测语言为python)...
code_executor_agent(对code_writer_agent):

exitcode: 0(执行成功)
代码输出:
233


--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
code_writer_agent(对code_executor_agent):

太好了,执行成功,第14个斐波那契数是233。数列如下:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233...以此类推,每个数字都是前两个数字的和。因此,斐波那契数列中的第14个数是233。

希望这符合您的期望。如果您有其他问题或需要进一步计算,请随时提问。

终止

--------------------------------------------------------------------------------

在上一个对话会话中,每次代码执行代理回复时都会请求人类输入,以确保代码是安全执行的。

现在我们可以尝试一个更复杂的示例,涉及到查询网络。假设我们想获取特斯拉和Meta(前身为Facebook)的年初至今的股价涨幅。我们可以使用这两个代理进行多次对话迭代。

import datetime

today = datetime.datetime.now().strftime("%Y-%m-%d")
chat_result = code_executor_agent.initiate_chat(
code_writer_agent,
message=f"今天是{today}。编写Python代码绘制TSLA和META的年初至今的股价增长情况,并将图表保存为名为'stock_gains.png'的文件。",
)

代码执行代理(给代码编写代理):

今天是2024年2月28日。请编写Python代码,绘制TSLA和META的股票今年以来的收益,并将图保存为名为'stock_gains.png'的文件。


使用自动回复... 代码编写代理(给代码执行代理):

这个任务需要从可靠的数据源中获取股票的历史数据,并计算年初至今(YTD)的收益值,然后绘制图表。我们将使用pandas_datareader库来获取数据,pandas库来处理数据,matplotlib库来绘制图表。

以下是实现此目标的Python代码。首先,请通过运行以下命令安装所需的库:

pip install yfinance pandas matplotlib

然后运行Python代码:

# 文件名:stock_gains.py

import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime

# 定义股票代码
tickers = ['TSLA', 'META']

# 定义起始日期和结束日期
start_date = datetime(2024, 1, 1)
end_date = datetime(2024, 2, 28)

# 用于存储数据框的字典
dfs = {}

for ticker in tickers:
# 获取股票数据
df = yf.download(ticker, start_date, end_date)

# 获取收盘价并计算累计百分比收益
df['收益'] = df['Close'].pct_change().cumsum()

# 添加到字典中
dfs[ticker] = df

# 绘图
plt.figure(figsize=(10, 5))
for ticker, df in dfs.items():
plt.plot(df.index, df['收益'], label=ticker)

plt.title('今年以来股票价格收益')
plt.xlabel('日期')
plt.ylabel('百分比收益')
plt.legend()

plt.grid(True)
plt.savefig('stock_gains.png')
plt.close()

print("已成功保存'stock_gains.png'文件")

This script will download the historical data for TSLA and META from the start of the year to the specified date and calculates the YTD gains. It then generates the plot showing these gains and saves it to 'stock_gains.png'.

Please save the script to a file named 'stock_gains.py' and run it using Python. Remember to have the correct start and end dates for the YTD value when running the script. If your Python version is below 3.8, you should update it to execute this code perfectly.


NO HUMAN INPUT RECEIVED.

USING AUTO REPLY...

EXECUTING CODE BLOCK 0 (inferred language is sh)...

EXECUTING CODE BLOCK 1 (inferred language is python)... code_executor_agent (to code_writer_agent):

exitcode: 0 (execution succeeded) Code output:

Requirement already satisfied: yfinance in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (0.2.36)
Requirement already satisfied: pandas in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (2.1.4)
Requirement already satisfied: matplotlib in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (3.8.2)
Requirement already satisfied: numpy>=1.16.5 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from yfinance) (1.26.2)
Requirement already satisfied: requests>=2.31 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from yfinance) (2.31.0)
Requirement already satisfied: multitasking>=0.0.7 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from yfinance) (0.0.11)
Requirement already satisfied: lxml>=4.9.1 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from yfinance) (5.0.1)
Requirement already satisfied: appdirs>=1.4.4 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from yfinance) (1.4.4)
Requirement already satisfied: pytz>=2022.5 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from yfinance) (2023.3.post1)
Requirement already satisfied: frozendict>=2.3.4 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from yfinance) (2.4.0)
Requirement already satisfied: peewee>=3.16.2 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from yfinance) (3.17.0)
Requirement already satisfied: beautifulsoup4>=4.11.1 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from yfinance) (4.12.2)
Requirement already satisfied: html5lib>=1.1 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from yfinance) (1.1)
Requirement already satisfied: python-dateutil>=2.8.2 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from pandas) (2.8.2)
Requirement already satisfied: tzdata>=2022.1 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from pandas) (2023.4)
Requirement already satisfied: contourpy>=1.0.1 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from matplotlib) (1.2.0)
Requirement already satisfied: cycler>=0.10 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from matplotlib) (0.12.1)
Requirement already satisfied: kiwisolver>=1.3.1 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from matplotlib) (1.4.5)
Requirement already satisfied: packaging>=20.0 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from matplotlib) (23.2)
Requirement already satisfied: pillow>=8 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from matplotlib) (10.2.0)
Requirement already satisfied: pyparsing>=2.3.1 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from matplotlib) (3.1.1)
Requirement already satisfied: soupsieve>1.2 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from beautifulsoup4>=4.11.1->yfinance) (2.5)
Requirement already satisfied: six>=1.9 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from html5lib>=1.1->yfinance) (1.16.0)
Requirement already satisfied: webencodings in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from html5lib>=1.1->yfinance) (0.5.1)
Requirement already satisfied: charset-normalizer<4,>=2 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from requests>=2.31->yfinance) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from requests>=2.31->yfinance) (3.6)
Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from requests>=2.31->yfinance) (2.1.0)
Requirement already satisfied: certifi>=2017.4.17 in /Users/ekzhu/miniconda3/envs/autogen/lib/python3.11/site-packages (from requests>=2.31->yfinance) (2024.2.2)

The 'stock_gains.png' file has been successfully saved


USING AUTO REPLY... code_writer_agent (to code_executor_agent):

Great! The code executed successfully and the 'stock_gains.png' file has been saved successfully. This file contains the plot of TSLA's and META's stock price gains from the start of the year until February 28, 2024. You should now be able to view this image file in the same directory that you ran the script from.

Please make sure to verify this image file. It should contain two plotted lines, each representing the percentage gain over the time for each stock (TSLA and META). The x-axis represents the date, and the y-axis represents the percentage gain. If everything looks correct, this would be the end of the task.

TERMINATE


在之前的对话中,代码编写代理生成了一个代码块来安装必要的软件包,并生成了另一个代码块来获取特斯拉和Meta的股票价格并计算今年以来的收益。代码执行代理安装了这些软件包,执行了脚本,并返回了结果。

让我们来看一下生成的图表。

```python
from IPython.display import Image

Image(os.path.join(temp_dir, "stock_gains.png"))
```

![](code-executors_files/figure-markdown_strict/cell-11-output-1.png)

因为代码执行会在文件系统中留下代码文件和输出等痕迹,所以我们可能希望在每次对话结束后清理工作目录。

```python
temp_dir.cleanup()
```

停止docker命令行执行器以清理docker容器。

```python
executor.stop() # 停止docker命令行代码执行器。
```

## 命令行执行器还是Jupyter代码执行器?

命令行代码执行器在接收到不同代码块的执行时不会在内存中保留任何状态,因为它将每个代码块写入单独的文件并在新的进程中执行代码块。

与命令行代码执行器相比,Jupyter代码执行器在同一个Jupyter内核中运行所有代码块,它在执行之间在内存中保留状态。请参阅[Jupyter代码执行器](../../docs/topics/code-execution/jupyter-code-executor)的主题页面。

命令行代码执行器和Jupyter代码执行器之间的选择取决于代理对话中代码块的性质。如果每个代码块都是一个不使用前面代码块中的变量的“脚本”,那么命令行代码执行器是一个很好的选择。如果某些代码块包含昂贵的计算(例如训练机器学习模型和加载大量数据),并且您希望在内存中保留状态以避免重复计算,那么Jupyter代码执行器是一个更好的选择。

## 关于用户代理和助理代理的说明

### 用户代理

在之前的示例中,我们直接使用[`ConversableAgent`](../../docs/reference/agentchat/conversable_agent#conversableagent)类创建了代码执行代理。现有的AutoGen示例通常使用[`UserProxyAgent`](../../docs/reference/agentchat/user_proxy_agent#userproxyagent)类创建代码执行代理,它是[`ConversableAgent`](../../docs/reference/agentchat/conversable_agent#conversableagent)的子类,具有`human_input_mode=ALWAYS`和`llm_config=False`的特性-它始终为每个消息请求人工输入,不使用LLM。它还为每个`human_input_mode`设置提供了默认的`description`字段。这个类是创建一个用作代码执行器的代理的便捷快捷方式。

### 助理代理

在之前的示例中,我们直接使用[`ConversableAgent`](../../docs/reference/agentchat/conversable_agent#conversableagent)类创建了代码编写代理。
在现有的AutoGen示例中,通常使用[`AssistantAgent`](../../docs/reference/agentchat/assistant_agent#assistantagent)类来创建代码编写代理,该类是[`ConversableAgent`](../../docs/reference/agentchat/conversable_agent#conversableagent)的子类,其中`human_input_mode=NEVER`和`code_execution_config=False` - 它从不请求人类输入,也不使用代码执行器。它还带有默认的`system_message`和`description`字段。这个类是创建一个用于代码编写而不执行代码的代理的方便快捷方式。

实际上,在前面的示例中,我们使用了[`AssistantAgent`](../../docs/reference/agentchat/assistant_agent#assistantagent)类的默认`system_message`字段来指示代码编写代理如何使用代码执行器。

```python
import pprint

from autogen import AssistantAgent

pprint.pprint(AssistantAgent.DEFAULT_SYSTEM_MESSAGE)
```

你是一个有用的AI助手。
使用你的编码和语言技能来解决任务。
在以下情况下,建议用户执行Python代码(在Python代码块中)或Shell脚本(在sh代码块中)。
1. 当你需要收集信息时,使用代码输出你所需的信息,例如浏览或搜索网页,下载/读取文件,打印网页或文件的内容,获取当前日期/时间,检查操作系统。在打印足够的信息并准备好基于你的语言技能解决任务后,你可以自己解决任务。
2. 当你需要使用代码执行某些任务时,使用代码执行任务并输出结果。聪明地完成任务。
如果需要,逐步解决任务。如果没有提供计划,请先解释你的计划。清楚地指出哪个步骤使用代码,哪个步骤使用你的语言技能。
在使用代码时,必须在代码块中指示脚本类型。用户不能提供任何其他反馈或执行任何其他操作,只能执行你建议的代码。用户不能修改你的代码。因此,不要建议需要用户修改的不完整的代码。如果不打算由用户执行,请不要使用代码块。
如果你希望用户在执行代码之前将代码保存到文件中,请在代码块中的第一行中添加# filename: <filename>。不要在一个回答中包含多个代码块。不要要求用户复制和粘贴结果。相反,当相关时使用'print'函数进行输出。检查用户返回的执行结果。
如果结果表明存在错误,请修复错误并再次输出代码。建议提供完整的代码而不是部分代码或代码更改。如果错误无法修复,或者即使成功执行代码后任务仍未解决,请分析问题,重新审查你的假设,收集你需要的其他信息,并考虑尝试不同的方法。
当你找到答案时,请仔细验证答案。如果可能,请在你的回答中包含可验证的证据。
当一切都完成时,请最后回复"TERMINATE"。
### 最佳实践

非常重要的一点是,[`UserProxyAgent`](../../docs/reference/agentchat/user_proxy_agent#userproxyagent)和[`AssistantAgent`](../../docs/reference/agentchat/assistant_agent#assistantagent)是为了避免在[`ConversableAgent`](../../docs/reference/agentchat/conversable_agent#conversableagent)类中编写`system_message`指令而设计的快捷方式。它们并不适用于所有的使用场景。正如我们将在下一章中展示的那样,调整`system_message`字段对于使代理在超出两个代理对话之外的更复杂对话模式中正常工作至关重要。

作为最佳实践,始终根据您的特定用例调整代理的`system_message`指令,并避免对[`UserProxyAgent`](../../docs/reference/agentchat/user_proxy_agent#userproxyagent)和[`AssistantAgent`](../../docs/reference/agentchat/assistant_agent#assistantagent)进行子类化。

## 总结

在本章中,我们介绍了代码执行器的使用方法,如何设置Docker和本地执行,以及如何在对话中使用代码执行来解决任务。在下一章中,我们将介绍工具使用,它类似于代码执行器,但限制了代理可以执行的代码。