Skip to main content

命令行代码执行器

在 Colab 中打开 在 GitHub 上打开

命令行代码执行是代码执行的最简单形式。一般来说,它会将每个代码块保存到一个文件中,然后执行该文件。这意味着每个代码块都在一个新的进程中执行。这个执行器有两种形式:

这种执行器类型类似于 AutoGen 中的传统代码执行。

Docker

DockerCommandLineCodeExecutor 会创建一个 Docker 容器,并在该容器中运行所有命令。默认使用的镜像是 python:3-slim,可以通过将 image 参数传递给构造函数来自定义。如果本地找不到该镜像,则类将尝试拉取它。因此,只需在本地构建镜像即可。该镜像与执行器兼容的唯一要求是已安装 shpython。因此,创建一个自定义镜像是确保所需系统依赖可用的简单有效的方法。

您可以将执行器用作上下文管理器,以确保在使用后清理容器。否则,程序退出时将使用 atexit 模块停止容器。

检查容器

如果您希望在 AutoGen 完成使用容器后保留容器以供其他用途(例如检查容器),则可以在创建执行器时将 auto_remove 参数设置为 False。还可以将 stop_container 设置为 False,以防止容器在执行结束时停止。

示例

from pathlib import Path

from autogen.coding import CodeBlock, DockerCommandLineCodeExecutor

work_dir = Path("coding")
work_dir.mkdir(exist_ok=True)

with DockerCommandLineCodeExecutor(work_dir=work_dir) as executor:
print(
executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code="print('你好,世界!')"),
]
)
)
exit_code=0 output='你好,世界!\n' code_file='coding/tmp_code_07da107bb575cc4e02b0e1d6d99cc204.py'

将 Docker 中的 AutoGen 与基于 Docker 的执行器结合使用

将 AutoGen 应用程序打包到 Docker 镜像中是可取的。但是,如何让容器化的应用程序在不同的容器中执行代码呢?

推荐的方法是使用“Docker 中的 Docker”(Docker out of Docker),其中 Docker 套接字被挂载到主 AutoGen 容器中,以便它可以在主机上生成和控制“同级”容器。这比称为“Docker 中的 Docker”(Docker in Docker)的方法更好,后者是指主容器运行 Docker 守护程序并在自身内部生成容器。您可以在这里了解更多信息。

要实现这一点,您需要将 Docker 套接字挂载到 AutoGen 容器中。可以通过将以下内容添加到 docker run 命令来实现:

-v /var/run/docker.sock:/var/run/docker.sock

这将允许 AutoGen 容器在主机上生成和控制同级容器。

如果您需要将工作目录绑定到 AutoGen 容器,但该目录属于主机机器,请使用 bind_dir 参数。这将允许主 AutoGen 容器将主机目录绑定到新生成的容器,并允许它访问该目录中的文件。如果未指定 bind_dir,则会回退到 work_dir

本地

danger

本地版本将在您的本地系统上运行代码。请谨慎使用。

要在主机机器上执行代码,就像在运行 AutoGen 的机器上一样,可以使用 LocalCommandLineCodeExecutor

示例

from pathlib import Path

from autogen.coding import CodeBlock, LocalCommandLineCodeExecutor

work_dir = Path("coding")
work_dir.mkdir(exist_ok=True)

executor = LocalCommandLineCodeExecutor(work_dir=work_dir)
print(
executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code="print('Hello, World!')"),
]
)
)
exit_code=0 output='\n你好,世界!\n' code_file='coding/065b51a16ee54f3298847518f9e999d7.py'

使用 Python 虚拟环境

默认情况下,LocalCommandLineCodeExecutor 在与 AutoGen 代码相同的 Python 环境中执行代码并安装依赖项。您可以选择指定一个 Python 虚拟环境,以防止污染基本的 Python 环境。

示例

from autogen.code_utils import create_virtual_env
from autogen.coding import CodeBlock, LocalCommandLineCodeExecutor

venv_dir = ".venv"
venv_context = create_virtual_env(venv_dir)

executor = LocalCommandLineCodeExecutor(virtual_env_context=venv_context)
print(
executor.execute_code_blocks(code_blocks=[CodeBlock(language="python", code="import sys; print(sys.executable)")])
)
from autogen.code_utils import create_virtual_env
from autogen.coding import CodeBlock, LocalCommandLineCodeExecutor

venv_dir = ".venv"
venv_context = create_virtual_env(venv_dir)

executor = LocalCommandLineCodeExecutor(virtual_env_context=venv_context)
print(
executor.execute_code_blocks(code_blocks=[CodeBlock(language="python", code="import sys; print(sys.executable)")])
)

这段代码使用了 autogen 库中的 create_virtual_env 函数来创建一个虚拟环境,并将其保存在 .venv 目录中。然后,使用 LocalCommandLineCodeExecutor 类创建了一个命令行代码执行器,并将虚拟环境上下文传递给它。接下来,通过调用 execute_code_blocks 方法执行了一个 Python 代码块,该代码块导入了 sys 模块,并打印了 sys.executable 的值。最后,将执行结果打印出来。

这段代码的作用是在虚拟环境中执行一段 Python 代码,并打印出 Python 解释器的路径。

分配给一个代理

这些执行器可以用来执行代理编写的代码。

from pathlib import Path

from autogen import ConversableAgent
from autogen.coding import DockerCommandLineCodeExecutor

work_dir = Path("coding")
work_dir.mkdir(exist_ok=True)

executor = DockerCommandLineCodeExecutor(work_dir=work_dir)

code_executor_agent = ConversableAgent(
name="code_executor_agent",
llm_config=False,
code_execution_config={
"executor": executor,
},
human_input_mode="NEVER",
)

在使用代码执行时,重要的是要更新你期望编写代码的代理的系统提示,以便能够使用执行器。例如,对于DockerCommandLineCodeExecutor,你可以像这样设置一个代码编写代理:

# 代码编写代理的系统消息是指导LLM如何使用带有IPython内核的Jupyter代码执行器。
code_writer_system_message = """
您已获得使用Python代码解决任务的编码能力。
在以下情况下,请建议用户执行Python代码(在Python代码块中)或Shell脚本(在sh代码块中)。
1. 当您需要收集信息时,请使用代码输出所需的信息,例如浏览或搜索网页,下载/读取文件,打印网页或文件的内容,获取当前日期/时间,检查操作系统。在打印足够的信息并准备好根据您的语言技能解决任务后,您可以自己解决任务。
2. 当您需要使用代码执行某些任务时,请使用代码执行任务并输出结果。聪明地完成任务。
如果需要,逐步解决任务。如果没有提供计划,请先解释您的计划。清楚地指出哪个步骤使用代码,哪个步骤使用您的语言技能。
在使用代码时,必须在代码块中指示脚本类型。用户不能提供任何其他反馈或执行任何其他操作,超出执行您建议的代码。用户不能修改您的代码。因此,请不要建议需要用户修改的不完整代码。如果要求用户在执行之前保存代码到文件中,请在代码块中的第一行中添加# filename: <filename>。不要在一个响应中包含多个代码块。不要要求用户复制和粘贴结果。而是在相关时使用'print'函数进行输出。检查用户返回的执行结果。
"""

import os

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

然后我们可以使用这两个代理来解决一个问题:

import pprint

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

pprint.pprint(chat_result)
code_executor_agent (to code_writer):

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

--------------------------------------------------------------------------------
code_writer (to code_executor_agent):

当然,我们可以使用几种不同的方法来计算斐波那契数列,比如递归、迭代、使用 Binet 公式或使用矩阵乘法。

但是,由于我们只需要计算第14个数,我们将简单地使用迭代方法,因为对于这种情况来说它是最高效的。

下面是解决这个任务的 Python 代码:

```python
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a

print(fibonacci(14))
```

In this script, `fibonacci(n)` is a function that calculates the nth Fibonacci number. Inside the function, two variables `a` and `b` are initialised to `0` and `1` which are the first two numbers in the Fibonacci series. Then, a loop runs `n` times (14 times in your case), and in each iteration `a` is replaced with `b` and `b` is replaced with `a + b`, which generates the next number in the series.

The function returns `a`, which is the nth Fibonacci number. The result is then printed to the console.

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

>>>>>>>> EXECUTING 1 CODE BLOCKS (inferred languages are [python])...
code_executor_agent (to code_writer):

exitcode: 0 (execution succeeded)
Code output: 377


--------------------------------------------------------------------------------
code_writer (to code_executor_agent):

Great! The script has successfully computed the 14th Fibonacci number as 377. If you need to compute other Fibonacci numbers, you can simply change the argument in the `fibonacci()` function call. Please let me know if you need help with anything else.

--------------------------------------------------------------------------------
code_executor_agent (to code_writer):



--------------------------------------------------------------------------------
ChatResult(chat_id=None,
chat_history=[{'content': 'Write Python code to calculate the 14th '
'Fibonacci number.',
'role': 'assistant'},
{'content': 'Sure, we can calculate the Fibonacci '
'series using a few different methods '
'such as recursion, iterative, by using '
"Binet's formula, or by using matrix "
'exponentiation.\n'
'\n'
'But, since we only need to calculate the '
'14th number, we will simply use the '
"iterative method as it's the most "
'efficient for this case.\n'
'\n'
'Here is the Python code that solves the '
'task:\n'
'\n'
'```python\n'
'def fibonacci(n):\n'
' a, b = 0, 1\n'
' for _ in range(n):\n'
' a, b = b, a + b\n'
' return a\n'
'\n'
'print(fibonacci(14))\n'
'```\n'
'\n'
'In this script, `fibonacci(n)` is a '
'function that calculates the nth '
'Fibonacci number. Inside the function, '
'two variables `a` and `b` are '
'initialised to `0` and `1` which are the '
'first two numbers in the Fibonacci '
'series. Then, a loop runs `n` times (14 '
'times in your case), and in each '
'iteration `a` is replaced with `b` and '
'`b` is replaced with `a + b`, which '
'generates the next number in the '
'series. \n'
'\n'
'The function returns `a`, which is the '
'nth Fibonacci number. The result is then '
'printed to the console.',
'role': 'user'},
{'content': 'exitcode: 0 (execution succeeded)\n'
'Code output: 377\n',
'role': 'assistant'},
{'content': 'Great! The script has successfully '
'computed the 14th Fibonacci number as '
'377. If you need to compute other '
'Fibonacci numbers, you can simply change '
'the argument in the `fibonacci()` '
'function call. Please let me know if you '
'need help with anything else.',
'role': 'user'},
{'content': '', 'role': 'assistant'}],
summary='',
cost=({'gpt-4-0613': {'completion_tokens': 302,
'cost': 0.04842,
'prompt_tokens': 1010,
'total_tokens': 1312},
'total_cost': 0.04842},
{'gpt-4-0613': {'completion_tokens': 302,
'cost': 0.04842,
'prompt_tokens': 1010,
'total_tokens': 1312},
'total_cost': 0.04842}),
human_input=[])

最后,停止容器。或者更好的做法是使用上下文管理器,以便自动停止容器。

executor.stop()