跳到主要内容

用户自定义函数

Open In Colab Open on GitHub

note

这是实验性的,目前并非所有执行器都支持。现阶段仅支持LocalCommandLineCodeExecutor

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

用户定义的函数允许您在AutoGen程序中定义Python函数,然后提供这些函数供执行器使用。这样,您就可以在不使用传统工具调用API的情况下,为您的代理提供工具。目前,该功能仅支持Python。

涉及以下几个步骤:

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

定义函数

warning

请记住,这些函数的全部源代码将对执行者可见。这意味着你不应该在函数中包含任何敏感信息,因为LLM代理可能能够访问它。

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

def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
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:
"""Load some sample data.

Returns:
pandas.DataFrame: A DataFrame with the following columns: name(str), location(str), age(int)
"""
data = {
"name": ["John", "Anna", "Peter", "Linda"],
"location": ["New York", "Paris", "Berlin", "London"],
"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 {executor.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 {executor.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 = """
You have been given coding capability to solve tasks using Python code.
In the following cases, suggest python code (in a python coding block) or shell script (in a sh coding block) for the user to execute.
1. When you need to collect info, use the code to output the info you need, for example, browse or search the web, download/read a file, print the content of a webpage or a file, get the current date/time, check the operating system. After sufficient info is printed and the task is ready to be solved based on your language skill, you can solve the task by yourself.
2. When you need to perform some task with code, use the code to perform the task and output the result. Finish the task smartly.
Solve the task step by step if you need to. If a plan is not provided, explain your plan first. Be clear which step uses code, and which step uses your language skill.
When using code, you must indicate the script type in the code block. The user cannot provide any other feedback or perform any other action beyond executing the code you suggest. The user can't modify your code. So do not suggest incomplete code which requires users to modify. Don't use a code block if it's not intended to be executed by the user.
If you want the user to save the code in a file before executing it, put # filename: <filename> inside the code block as the first line. Don't include multiple code blocks in one response. Do not ask users to copy and paste the result. Instead, use 'print' function for the output when relevant. Check the execution result returned by the user.
"""

# Add on the new functions
code_writer_system_message += executor.format_functions_for_prompt()

print(code_writer_system_message)

You have been given coding capability to solve tasks using Python code.
In the following cases, suggest python code (in a python coding block) or shell script (in a sh coding block) for the user to execute.
1. When you need to collect info, use the code to output the info you need, for example, browse or search the web, download/read a file, print the content of a webpage or a file, get the current date/time, check the operating system. After sufficient info is printed and the task is ready to be solved based on your language skill, you can solve the task by yourself.
2. When you need to perform some task with code, use the code to perform the task and output the result. Finish the task smartly.
Solve the task step by step if you need to. If a plan is not provided, explain your plan first. Be clear which step uses code, and which step uses your language skill.
When using code, you must indicate the script type in the code block. The user cannot provide any other feedback or perform any other action beyond executing the code you suggest. The user can't modify your code. So do not suggest incomplete code which requires users to modify. Don't use a code block if it's not intended to be executed by the user.
If you want the user to save the code in a file before executing it, put # filename: <filename> inside the code block as the first line. Don't include multiple code blocks in one response. Do not ask users to copy and paste the result. Instead, use 'print' function for the output when relevant. Check the execution result returned by the user.
You have access to the following user defined functions. They can be accessed from the module called `functions` by their function names.

For example, if there was a function called `foo` you could import it by writing `from functions import foo`

def add_two_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
...

def load_data() -> pandas.core.frame.DataFrame:
"""Load some sample data.

Returns:
pandas.DataFrame: A DataFrame with the following columns: name(str), location(str), age(int)
"""
...

然后你可以使用这个系统消息为你的代码编写代理。

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

然后,我们可以开始对话并让autogen处理我们提供的数据框。

chat_result = code_executor_agent.initiate_chat(
code_writer_agent,
message="Please use the load_data function to load the data and please calculate the average age of all people.",
summary_method="reflection_with_llm",
)
code_executor_agent (to code_writer):

Please use the load_data function to load the data and please calculate the average age of all people.

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

Below is the python code to load the data using the `load_data()` function and calculate the average age of all people.

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

# Load the data
df = load_data()

# Calculate the average age
avg_age = np.mean(df['age'])

print("The average age is", avg_age)
```

This code starts by importing the `load_data()` function. It then uses this function to load the data into a variable `df`. Afterwards, it calculates the average (mean) of the 'age' column in the DataFrame, before printing the result.

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

>>>>>>>> EXECUTING CODE BLOCK (inferred language is python)...
code_executor_agent (to code_writer):

exitcode: 0 (execution succeeded)
Code output: The average age is 30.75


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

Great! The code worked fine. So, the average age of all people in the dataset is 30.75 years.

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



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

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

print(chat_result.summary)
The average age of all people in the dataset is 30.75 years.