Skip to main content

Jupyter代码执行器

在Colab中打开 在GitHub上打开

AutoGen能够在有状态的Jupyter内核中执行代码,这与命令行代码执行器不同,后者会在每个代码块中启动一个新进程。这意味着您可以在一个代码块中定义变量,并在另一个代码块中使用它们。其中一个有趣的特性是,当遇到错误时,只需要重新执行失败的代码,而不是整个脚本。

要使用JupyterCodeExecutor,您需要运行一个Jupyter服务器。这可以是Docker、本地,甚至是远程服务器。然后,在构造JupyterCodeExecutor时,将其连接到应该连接的服务器。

依赖项

为了使用基于Jupyter的代码执行,需要安装一些额外的依赖项。可以使用额外的jupyter-executor安装它们:

pip install 'pyautogen[jupyter-executor]'

Jupyter服务器

Docker

要运行基于Docker的Jupyter服务器,可以使用DockerJupyterServer

from autogen.coding import CodeBlock
from autogen.coding.jupyter import DockerJupyterServer, JupyterCodeExecutor

with DockerJupyterServer() as server:
executor = JupyterCodeExecutor(server)
print(
executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code="print('Hello, World!')"),
]
)
)
exit_code=0 output='Hello, World!\n' output_files=[]

默认情况下,DockerJupyterServer将构建并使用一个捆绑的Dockerfile,如下所示:

print(DockerJupyterServer.DEFAULT_DOCKERFILE)
从 quay.io/jupyter/docker-stacks-foundation 镜像开始

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

USER ${NB_UID}
RUN mamba install --yes jupyter_kernel_gateway ipykernel && mamba clean --all -f -y && fix-permissions "${CONDA_DIR}" && fix-permissions "/home/${NB_USER}"

ENV TOKEN="UNSET"
CMD python -m jupyter kernelgateway --KernelGatewayApp.ip=0.0.0.0 --KernelGatewayApp.port=8888 --KernelGatewayApp.auth_token="${TOKEN}" --JupyterApp.answer_yes=true --JupyterWebsocketPersonality.list_kernels=true

EXPOSE 8888

WORKDIR "${HOME}"

这段代码是从 quay.io/jupyter/docker-stacks-foundation 镜像开始构建的。首先设置了 SHELL,然后切换到 ${NB_UID} 用户。接下来,使用 mamba 安装了 jupyter_kernel_gatewayipykernel,然后清理了所有的缓存文件。之后,修复了 ${CONDA_DIR}/home/${NB_USER} 的权限。设置了环境变量 TOKEN 的值为 "UNSET"。最后,通过 CMD 命令运行了 python -m jupyter kernelgateway,并设置了一些参数,如 KernelGatewayApp.ipKernelGatewayApp.portKernelGatewayApp.auth_tokenJupyterApp.answer_yesJupyterWebsocketPersonality.list_kernels。最后,通过 EXPOSE 命令暴露了端口 8888。工作目录被设置为 ${HOME}

自定义 Docker 镜像

可以通过将 custom_image_name 参数传递给 DockerJupyterServer 构造函数来使用自定义镜像。对于镜像能够正常工作,有一些要求:

  • 镜像必须安装并在端口 8888 上运行 Jupyer Kernel Gateway,以便 JupyterCodeExecutor 可以连接到它。
  • 遵守 TOKEN 环境变量,该变量用于对 JupyterCodeExecutor 进行身份验证。
  • 确保使用以下命令启动 jupyter kernelgateway
    • --JupyterApp.answer_yes=true - 这样可以确保内核网关在关闭时不会提示确认。
    • --JupyterWebsocketPersonality.list_kernels=true - 这样可以确保内核网关列出可用的内核。

如果您想要向此镜像添加额外的依赖项(例如 matplotlibnumpy),可以按照以下方式自定义 Dockerfile:

FROM quay.io/jupyter/docker-stacks-foundation

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

USER ${NB_UID}
RUN mamba install --yes jupyter_kernel_gateway ipykernel matplotlib numpy &&
mamba clean --all -f -y &&
fix-permissions "${CONDA_DIR}" &&
fix-permissions "/home/${NB_USER}"

ENV TOKEN="UNSET"
CMD python -m jupyter kernelgateway \
--KernelGatewayApp.ip=0.0.0.0 \
--KernelGatewayApp.port=8888 \
--KernelGatewayApp.auth_token="${TOKEN}" \
--JupyterApp.answer_yes=true \
--JupyterWebsocketPersonality.list_kernels=true

EXPOSE 8888

WORKDIR "${HOME}"
tip

要了解如何在 Docker 镜像中结合 AutoGen 并在单独的镜像中执行代码,请访问此处

本地

danger

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

要运行本地 Jupyter 服务器,可以使用 LocalJupyterServer

warning

由于一个错误,LocalJupyterServer 在 Windows 上无法正常工作。在这种情况下,您可以使用 DockerJupyterServerEmbeddedIPythonCodeExecutor。请注意,当错误修复后,意图是删除 EmbeddedIPythonCodeExecutor

```python
from autogen.coding import CodeBlock
from autogen.coding.jupyter import JupyterCodeExecutor, LocalJupyterServer

with LocalJupyterServer() as server:
executor = JupyterCodeExecutor(server)
print(
executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code="print('你好,世界!')"),
]
)
)

远程

JupyterCodeExecutor 也可以连接到远程 Jupyter 服务器。这是通过将连接信息传递给 JupyterCodeExecutor 构造函数而不是实际的服务器对象来完成的。

from autogen.coding.jupyter import JupyterCodeExecutor, JupyterConnectionInfo

executor = JupyterCodeExecutor(
jupyter_server=JupyterConnectionInfo(host='example.com', use_https=True, port=7893, token='mytoken')
)

图像输出

当 Jupyter 输出图像时,它会将其保存为文件到 JupyterCodeExecutoroutput_dir 中,如构造函数所指定的那样。默认情况下,这是当前工作目录。

分配给代理

一个服务器可以支持多个代理,因为每个执行器都会创建自己的内核。要将执行器分配给代理,可以这样做:

from pathlib import Path

from autogen import ConversableAgent
from autogen.coding.jupyter import DockerJupyterServer, JupyterCodeExecutor

server = DockerJupyterServer()

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

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

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

# 代码编写代理的系统消息是为了指导 LLM 如何使用带有 IPython 内核的 Jupyter 代码执行器。
code_writer_system_message = """
You have been given coding capability to solve tasks using Python code in a stateful IPython kernel.
You are responsible for writing the code, and the user is responsible for executing the code.

When you write Python code, put the code in a markdown code block with the language set to Python.
For example:
```python
x = 3
```
You can use the variable `x` in subsequent code blocks.
```python
print(x)
```

逐步编写代码,并利用内核的状态性来避免重复代码。
在单独的代码块中导入库。
在单独的代码块中定义函数或类。
在单独的代码块中运行产生输出的代码。
在单独的代码块中运行涉及下载、上传和调用外部API等耗时操作的代码。

当你的代码产生输出时,输出将返回给你。
由于你的对话记忆有限,如果你的代码创建了一张图片,
输出将是图片的路径而不是图片本身。"""

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",
)
```
然后我们可以使用这两个代理来解决一个问题:

```python
import pprint

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

pprint.pprint(chat_result)
```

```` text
code_executor_agent (to code_writer):

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

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

好的。斐波那契数列是一个由前两个数字相加得到下一个数字的数列。我们知道前两个斐波那契数是01。之后,数列如下所示:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...

所以,让我们定义一个 Python 函数来计算第n个斐波那契数。

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



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

这是计算第n个斐波那契数的 Python 函数:

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

现在,让我们使用这个函数来计算第14个斐波那契数。

```python
fibonacci(14)
```

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

退出码:0(执行成功)
代码输出:
377

--------------------------------------------------------------------------------
ChatResult(chat_id=None,
chat_history=[{'content': '编写 Python 代码来计算第14个斐波那契数。',
'role': 'assistant'},
{'content': '好的。斐波那契数列是一个由前两个数相加得到下一个数的数列。我们知道前两个斐波那契数是0和1。之后,数列如下所示:\n\n0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ... \n\n所以,让我们定义一个 Python 函数来计算第n个斐波那契数。',
'role': 'user'},
{'content': '', 'role': 'assistant'},
{'content': '这是计算第n个斐波那契数的 Python 函数:\n\n```python\n'
'def fibonacci(n):\n'
' if n <= 1:\n'
' return n\n'
' else:\n'
' a, b = 0, 1\n'
' for _ in range(2, n+1):\n'
' a, b = b, a+b\n'
' return b\n'
'```\n\n现在,让我们使用这个函数来计算第14个斐波那契数。\n\n```python\n'
'fibonacci(14)\n'
'```',
'role': 'user'},
{'content': '退出码:0(执行成功)\n'
'代码输出:\n'
'377',
'role': 'assistant'}],
summary='退出码:0(执行成功)\n代码输出:\n377',
cost=({'gpt-4-0613': {'completion_tokens': 193,
'cost': 0.028499999999999998,
'prompt_tokens': 564,
'total_tokens': 757},
'total_cost': 0.028499999999999998},
{'gpt-4-0613': {'completion_tokens': 193,
'cost': 0.028499999999999998,
'prompt_tokens': 564,
'total_tokens': 757},
'total_cost': 0.028499999999999998}),
human_input=[])
最后,停止服务器。或者更好的方法是使用上下文管理器来自动停止它。

```python
server.stop()
```