Skip to main content

用户定义函数

在 Colab 中打开 在 GitHub 上打开

note

这是一个实验性功能,目前并不是所有执行器都支持。目前只支持 LocalCommandLineCodeExecutor

目前,注册工具和使用此功能的方法是不同的。我们希望将它们统一起来。请参阅 Github 问题 here

用户定义函数允许您在 AutoGen 程序中定义 Python 函数,然后将其提供给执行器使用。这样可以为您的代理提供工具,而无需使用传统的工具调用 API。目前,此功能仅支持 Python。

涉及到几个步骤:

  1. 定义函数
  2. 将函数提供给执行器
  3. 向代码编写代理解释如何使用该函数

定义函数

warning

请注意,这些函数的整个源代码将对执行器可用。这意味着您不应在函数中包含任何敏感信息,因为 LLM 代理可能能够访问它。

如果函数不需要任何外部导入或依赖项,则可以直接使用该函数。例如:

def add_two_numbers(a: int, b: int) -> int:
"""将两个数字相加。"""
return a + b

这将是一个有效的独立函数。

tip

虽然不要求使用类型提示和文档字符串,但强烈建议使用。它们将帮助代码编写代理理解函数及其使用方法。

如果函数需要外部导入或依赖项,则可以使用 @with_requirements 装饰器来指定这些要求。例如:

import pandas

from autogen.coding.func_with_reqs import with_requirements


@with_requirements(python_packages=["pandas"], global_imports=["pandas"])
def load_data() -> pandas.DataFrame:
"""加载一些示例数据。

返回:
pandas.DataFrame: 一个包含以下列的DataFrame:name(str), location(str), age(int)
"""
data = {
"name": ["John", "Anna", "Peter", "Linda"],
"location": ["纽约", "巴黎", "柏林", "伦敦"],
"age": [24, 13, 53, 33],
}
return pandas.DataFrame(data)

如果你想将 pandas 重命名为 pd,或者直接导入 DataFrame,你可以按照以下方式操作:

import pandas as pd
from pandas import DataFrame
from pandas import DataFrame as df

from autogen.coding.func_with_reqs import Alias, ImportFromModule, with_requirements


@with_requirements(python_packages=["pandas"], global_imports=[Alias("pandas", "pd")])
def some_func1() -> pd.DataFrame: ...


@with_requirements(python_packages=["pandas"], global_imports=[ImportFromModule("pandas", "DataFrame")])
def some_func2() -> DataFrame: ...


@with_requirements(python_packages=["pandas"], global_imports=[ImportFromModule("pandas", Alias("DataFrame", "df"))])
def some_func3() -> df: ...

将函数提供给执行器

函数可以在执行器的构造函数中加载。例如:

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, functions=[add_two_numbers, load_data])

在涉及到代理之前,我们可以进行一些简单的检查,以确保当代理编写的代码如下时,执行器能够处理它。

code = f"""
from {LocalCommandLineCodeExecutor.FUNCTIONS_MODULE} import add_two_numbers

print(add_two_numbers(1, 2))
"""

print(
executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code=code),
]
)
)
exit_code=0 output='3\n' code_file='/Users/jackgerrits/w/autogen/website/docs/topics/code-execution/coding/tmp_code_1958fe3aea3e8e3c6e907fe951b5f6ab.py'

我们还可以尝试需要依赖和导入的函数。

code = f"""
from {LocalCommandLineCodeExecutor.FUNCTIONS_MODULE} import load_data

print(load_data())
"""

result = executor.execute_code_blocks(
code_blocks=[
CodeBlock(language="python", code=code),
]
)

print(result.output)
    name  location  age
0 John New York 24
1 Anna Paris 13
2 Peter Berlin 53
3 Linda London 33

限制

  • 目前仅支持 Python
  • 函数不能依赖于任何全局变量或外部状态,因为它们会被加载为源代码

向代码编写代理解释如何使用函数

现在函数可以被执行器调用了,你可以向代码编写代理解释如何使用函数。这一步非常重要,因为默认情况下代理不会知道函数的存在。

有一个实用函数可以用来生成一个默认的提示,描述可用的函数以及如何使用它们。这个函数的模板可以被覆盖以提供自定义消息,或者你可以使用完全不同的提示。

例如,你可以在关于本地执行的页面中扩展系统消息,添加一个新的部分来描述可用的函数。

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

# 添加新的函数
code_writer_system_message += executor.format_functions_for_prompt()

print(code_writer_system_message)

你已经获得了使用Python代码解决任务的编码能力。 在以下情况下,建议用户执行Python代码(在Python代码块中)或shell脚本(在sh代码块中)。

  1. 当您需要收集信息时,使用代码输出所需的信息,例如浏览或搜索网页,下载/读取文件,打印网页或文件的内容,获取当前日期/时间,检查操作系统。在打印足够的信息并准备好根据您的语言技能解决任务后,您可以自己解决任务。
  2. 当您需要使用代码执行某些任务时,请使用代码执行任务并输出结果。聪明地完成任务。 如果需要,逐步解决任务。如果没有提供计划,请先解释您的计划。清楚地指出哪个步骤使用代码,哪个步骤使用您的语言技能。 在使用代码时,必须在代码块中指示脚本类型。用户不能提供任何其他反馈或执行任何其他操作,超出执行您建议的代码。用户不能修改您的代码。因此,请不要建议需要用户修改的不完整代码。如果不打算由用户执行,请不要使用代码块。 如果您希望用户在执行代码之前将代码保存在文件中,请在代码块中的第一行中添加# filename: <filename>。不要在一个响应中包含多个代码块。不要要求用户复制和粘贴结果。相反,当相关时,请使用'print'函数进行输出。检查用户返回的执行结果。 您可以访问以下用户定义的函数。它们可以通过名为functions的模块按其函数名称访问。

例如,如果有一个名为foo的函数,您可以通过编写from functions import foo来导入它。

import os

from autogen import ConversableAgent

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",
)

现在,我们可以使用之前定义的本地命令行执行器设置代码执行代理。

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

然后,我们可以开始对话并让代理处理。 我们提供的数据框如下所示。

chat_result = code_executor_agent.initiate_chat(
code_writer_agent,
message="请使用 load_data 函数加载数据,并计算所有人的平均年龄。",
summary_method="reflection_with_llm",
)
code_executor_agent (to code_writer):

请使用 load_data 函数加载数据,并计算所有人的平均年龄。

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

以下是使用 `load_data()` 函数加载数据并计算所有人的平均年龄的 Python 代码。

```python
# python code
from functions import load_data
import numpy as np

# 加载数据
df = load_data()

# 计算平均年龄
avg_age = np.mean(df['age'])

print("平均年龄为", avg_age)
```

这段代码首先导入 `load_data()` 函数。然后使用该函数将数据加载到变量 `df` 中。然后,它计算了数据框中 'age' 列的平均值,并打印结果。

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

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

exitcode: 0 (执行成功)
代码输出: 平均年龄为 30.75


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

太棒了!代码运行正常。所以,数据集中所有人的平均年龄为 30.75 岁。

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



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

我们可以看到计算的摘要:

print(chat_result.summary)
数据集中所有人的平均年龄为 30.75 岁。