跳到主要内容

Jupyter 代码执行器

Open In Colab Open on GitHub

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

要使用 JupyterCodeExecutor 你需要一个正在运行的Jupyter服务器。这可以在Docker中、本地,甚至是 远程服务器上。然后,在构造 JupyterCodeExecutor 时,你需要传入它应该连接的服务器。

依赖项

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

pip install 'autogen-agentchat[jupyter-executor]~=0.2'

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)
FROM 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}"

自定义Docker镜像

可以通过将custom_image_name参数传递给DockerJupyterServer构造函数来使用自定义镜像。为了使其正常工作,镜像需要满足一些要求:

  • 图像必须安装并运行在端口8888上的Jupyer Kernel Gateway,以便JupyterCodeExecutor能够连接到它。
  • 尊重TOKEN环境变量,该变量用于与JupyterCodeExecutor进行身份验证,以连接到Jupyter服务器。
  • Ensure the jupyter kernelgateway is started with:
    • --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

由于一个bug,LocalJupyterServer在Windows上无法正常工作。在这种情况下,您可以使用DockerJupyterServer或使用EmbeddedIPythonCodeExecutor。请注意,当bug修复后,EmbeddedIPythonCodeExecutor将会被移除。

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('Hello, World!')"),
]
)
)

远程

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输出一张图片时,该图片会被保存为文件到 output_dir 中,即 JupyterCodeExecutor 的输出目录,该目录由构造函数指定。默认情况下,这是当前工作目录。

分配给一个代理

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

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 您可能会设置一个编写代码的代理,如下所示:

# The code writer agent's system message is to instruct the LLM on how to
# use the Jupyter code executor with IPython kernel.
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)
```

Write code incrementally and leverage the statefulness of the kernel to avoid repeating code.
Import libraries in a separate code block.
Define a function or a class in a separate code block.
Run code that produces output in a separate code block.
Run code that involves expensive operations like download, upload, and call external APIs in a separate code block.

When your code produces an output, the output will be returned to you.
Because you have limited conversation memory, if your code creates an image,
the output will be a path to the image instead of the image itself."""

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, # Turn off code execution for this agent.
max_consecutive_auto_reply=2,
human_input_mode="NEVER",
)

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

import pprint

chat_result = code_executor_agent.initiate_chat(
code_writer_agent, message="Write Python code to calculate the 14th Fibonacci number."
)

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

Write Python code to calculate the 14th Fibonacci number.

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

Sure. The Fibonacci sequence is a series of numbers where the next number is found by adding up the two numbers before it. We know that the first two Fibonacci numbers are 0 and 1. After that, the series looks like:

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

So, let's define a Python function to calculate the nth Fibonacci number.

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



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

Here is the Python function to calculate the nth Fibonacci number:

```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
```

Now, let's use this function to calculate the 14th Fibonacci number.

```python
fibonacci(14)
```

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

exitcode: 0 (execution succeeded)
Code output:
377

--------------------------------------------------------------------------------
ChatResult(chat_id=None,
chat_history=[{'content': 'Write Python code to calculate the 14th '
'Fibonacci number.',
'role': 'assistant'},
{'content': 'Sure. The Fibonacci sequence is a series '
'of numbers where the next number is '
'found by adding up the two numbers '
'before it. We know that the first two '
'Fibonacci numbers are 0 and 1. After '
'that, the series looks like:\n'
'\n'
'0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, '
'...\n'
'\n'
"So, let's define a Python function to "
'calculate the nth Fibonacci number.',
'role': 'user'},
{'content': '', 'role': 'assistant'},
{'content': 'Here is the Python function to calculate '
'the nth Fibonacci number:\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'
"Now, let's use this function to "
'calculate the 14th Fibonacci number.\n'
'\n'
'```python\n'
'fibonacci(14)\n'
'```',
'role': 'user'},
{'content': 'exitcode: 0 (execution succeeded)\n'
'Code output: \n'
'377',
'role': 'assistant'}],
summary='exitcode: 0 (execution succeeded)\nCode output: \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=[])

最后,停止服务器。或者更好的是使用上下文管理器来自动停止它。

server.stop()