跳到主要内容

使用函数调用编写软件应用程序

Open In Colab Open on GitHub

在 Autogen 中创建代码的默认方式是其内置的代码提取器。虽然这种方式可以快速创建和执行简单的脚本,但根据我的经验,这种方式并不适合开发高级软件应用程序。开发应用程序的过程主要是对现有文件进行更改,而不是创建包含代码的新文件。根据我的经验,代码提取器在引入更改方面表现不佳,因为模型经常会迷失方向,并且可能会损坏现有文件。

正确创建的函数可以修改代码,这使我们能够更好地控制代码变更,从而提高代码质量。此外,由于工具内预定义了可能操作的范围,我们可以安全地使用Autogen而无需Docker,从而避免与之相关的所有复杂性。

要求

! pip install autogen-agentchat~=0.2

设置您的API端点

import os

import autogen

config_list = [{"model": "gpt-4-turbo-preview", "api_key": os.getenv("OPENAI_API_KEY")}]

创建代理

在这个例子中,我们将仅使用专用函数调用来改进一个简单的FastAPI应用程序。让我们创建一个工程师代理,它将思考并执行代码更改,以及一个用户代理管理员,通过它我们将指导我们的工程师。

llm_config = {
"temperature": 0,
"config_list": config_list,
}

engineer = autogen.AssistantAgent(
name="Engineer",
llm_config=llm_config,
system_message="""
I'm Engineer. I'm expert in python programming. I'm executing code tasks required by Admin.
""",
)

user_proxy = autogen.UserProxyAgent(
name="Admin",
human_input_mode="ALWAYS",
code_execution_config=False,
)

与其他许多示例不同,这里我们不需要单独的Executor代理来保存我们的代码,因为这将由函数完成。因此,我们也不需要运行Docker,这使得整个过程更加简单。

接下来,让我们设置我们的群聊。

groupchat = autogen.GroupChat(
agents=[engineer, user_proxy],
messages=[],
max_round=500,
speaker_selection_method="round_robin",
enable_clear_history=True,
)
manager = autogen.GroupChatManager(groupchat=groupchat, llm_config=llm_config)

准备适当的函数

让我们深入了解核心内容。准备一些提供工程师功能来修改现有代码、创建新代码文件、检查文件系统和文件的函数。

from typing_extensions import Annotated

default_path = "backend_dir/"


@user_proxy.register_for_execution()
@engineer.register_for_llm(description="List files in choosen directory.")
def list_dir(directory: Annotated[str, "Directory to check."]):
files = os.listdir(default_path + directory)
return 0, files


@user_proxy.register_for_execution()
@engineer.register_for_llm(description="Check the contents of a chosen file.")
def see_file(filename: Annotated[str, "Name and path of file to check."]):
with open(default_path + filename, "r") as file:
lines = file.readlines()
formatted_lines = [f"{i+1}:{line}" for i, line in enumerate(lines)]
file_contents = "".join(formatted_lines)

return 0, file_contents


@user_proxy.register_for_execution()
@engineer.register_for_llm(description="Replace old piece of code with new one. Proper indentation is important.")
def modify_code(
filename: Annotated[str, "Name and path of file to change."],
start_line: Annotated[int, "Start line number to replace with new code."],
end_line: Annotated[int, "End line number to replace with new code."],
new_code: Annotated[str, "New piece of code to replace old code with. Remember about providing indents."],
):
with open(default_path + filename, "r+") as file:
file_contents = file.readlines()
file_contents[start_line - 1 : end_line] = [new_code + "\n"]
file.seek(0)
file.truncate()
file.write("".join(file_contents))
return 0, "Code modified"


@user_proxy.register_for_execution()
@engineer.register_for_llm(description="Create a new file with code.")
def create_file_with_code(
filename: Annotated[str, "Name and path of file to create."], code: Annotated[str, "Code to write in the file."]
):
with open(default_path + filename, "w") as file:
file.write(code)
return 0, "File created successfully"

准备代码以与一起工作

在这个示例中,我们将展示AI如何扩展现有代码的功能,因为在软件开发中,改进现有代码比创建新代码更为常见。让我们准备将要操作的初始代码。这将是一个简单的FastAPI脚本,它将允许你计算波兰著名游戏开发公司CD Project Red的今日股价涨幅百分比。创建一个名为‘backend_dir’的文件夹,并在此处放置一个内容如下的‘main.py’文件:

# backend_dir/main.py

from fastapi import FastAPI
import yfinance as yf

app = FastAPI()

@app.get("/cdr_daily_spread")
async def calculate_daily_spread():
cdr = yf.Ticker("CDR.WA")
today_data = cdr.history(period="1d")
spread = ((today_data["High"] - today_data["Low"]) / today_data["Low"]) * 100
return spread

安装所需的库。我们可以使用以下命令运行我们的API:

uvicorn main:app --reload

发送请求到‘localhost:8000/cdr_daily_spread’以检查它是否正常工作。

使用代理编辑代码

假设我们希望我们的代理扩展应用程序的功能。我们修改端点来检查11bits(另一家游戏开发公司)的价差,并比较两只股票的价差。此外,我们将内部逻辑分离到不同的文件中。

最后,在工程师和管理员之间实例化一个聊天。它将首先探索文件系统,之后会等待我们的指令。然后,我们将向工程师解释任务,并要求他首先提供变更计划——根据我的经验,这大大提高了LLM响应的质量。

之后,逐个引入由工程师所做的更改。如果需要,请他纠正某些内容或改进功能。如果你觉得他会做错什么,不要犹豫中断工具的执行。如果出现错误,请提供错误日志,并要求他在实际引入更改之前检查文件以刷新他对该文件的了解。

chat_result = user_proxy.initiate_chat(
manager,
message="""
You will need to improve app in FastApi. For now, check out all the application files, try to understand it and wait for next instructions.
""",
)
Admin (to chat_manager):


You will need to improve app in FastApi. For now, check out all the application files, try to understand it and wait for next instructions.


--------------------------------------------------------------------------------
Engineer (to chat_manager):

***** Suggested tool Call (call_SA61u9yCLhyXfd9NCV9TAIiM): list_dir *****
Arguments:

{
"directory": "./"
}
*************************************************************************

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation:

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...

>>>>>>>> EXECUTING FUNCTION list_dir...
Admin (to chat_manager):

Admin (to chat_manager):

***** Response from calling tool "call_SA61u9yCLhyXfd9NCV9TAIiM" *****
[0, ["main.py", "__pycache__"]]
**********************************************************************

--------------------------------------------------------------------------------
Engineer (to chat_manager):

***** Suggested tool Call (call_IVJNPI12s4fCzysnWjExZjL2): see_file *****
Arguments:

{
"filename": "main.py"
}
*************************************************************************

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation:

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...

>>>>>>>> EXECUTING FUNCTION see_file...
Admin (to chat_manager):

Admin (to chat_manager):

***** Response from calling tool "call_IVJNPI12s4fCzysnWjExZjL2" *****
[0, "1:from fastapi import FastAPI\n2:import yfinance as yf\n3:\n4:app = FastAPI()\n5:\n6:@app.get(\"/cdr_daily_spread\")\n7:async def calculate_daily_spread():\n8: cdr = yf.Ticker(\"CDR.WA\")\n9: today_data = cdr.history(period=\"1d\")\n10: spread = ((today_data[\"High\"] - today_data[\"Low\"]) / today_data[\"Low\"]) * 100\n11: return spread"]
**********************************************************************

--------------------------------------------------------------------------------
Engineer (to chat_manager):

The application is a FastAPI application with a single endpoint `/cdr_daily_spread`. This endpoint calculates the daily spread of the stock "CDR.WA" using the `yfinance` library. The spread is calculated as the percentage difference between the high and low prices of the stock for the day.

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation: Let's move spread calculation logic to separate file. Create spread_calculation function with ticker argument in it. in our endpoint, we need to compare daily spread for CD Project Red and 11bits Studio - return difference between them. Start with providing plan of changes. Don't do any changes yet.
Admin (to chat_manager):

Let's move spread calculation logic to separate file. Create spread_calculation function with ticker argument in it. in our endpoint, we need to compare daily spread for CD Project Red and 11bits Studio - return difference between them. Start with providing plan of changes. Don't do any changes yet.

--------------------------------------------------------------------------------
Engineer (to chat_manager):

Sure, here is the plan of changes:

1. Create a new Python file named `spread_calculation.py`.
2. In this file, define a function named `calculate_daily_spread` that takes a `ticker` as an argument. This function will contain the logic to calculate the daily spread of a given stock.
3. Modify the `main.py` file to import the `calculate_daily_spread` function from `spread_calculation.py`.
4. Modify the `/cdr_daily_spread` endpoint in `main.py` to calculate the daily spread for both "CDR.WA" (CD Project Red) and "11B.WA" (11bits Studio) by calling the `calculate_daily_spread` function with the respective ticker as argument.
5. The endpoint will then return the difference between the daily spreads of the two stocks.

Please confirm if this plan is correct and I can proceed with the changes.

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation: go with the plan
Admin (to chat_manager):

go with the plan

--------------------------------------------------------------------------------
Engineer (to chat_manager):

***** Suggested tool Call (call_cqaVQJ6VQjR7lh114bxJGVfi): create_file_with_code *****
Arguments:

{
"filename": "spread_calculation.py",
"code": "import yfinance as yf\n\ndef calculate_daily_spread(ticker):\n stock = yf.Ticker(ticker)\n today_data = stock.history(period=\"1d\")\n spread = ((today_data[\"High\"] - today_data[\"Low\"]) / today_data[\"Low\"]) * 100\n return spread"
}
**************************************************************************************

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation:

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...

>>>>>>>> EXECUTING FUNCTION create_file_with_code...
Admin (to chat_manager):

Admin (to chat_manager):

***** Response from calling tool "call_cqaVQJ6VQjR7lh114bxJGVfi" *****
[0, "File created successfully"]
**********************************************************************

--------------------------------------------------------------------------------
Engineer (to chat_manager):

***** Suggested tool Call (call_qcoMK0RO5bXdY4yQz5RCh4mp): modify_code *****
Arguments:

{
"filename": "main.py",
"start_line": 1,
"end_line": 11,
"new_code": "from fastapi import FastAPI\nfrom spread_calculation import calculate_daily_spread\n\napp = FastAPI()\n\n@app.get(\"/cdr_daily_spread\")\nasync def compare_daily_spread():\n cdr_spread = calculate_daily_spread(\"CDR.WA\")\n bits_spread = calculate_daily_spread(\"11B.WA\")\n return cdr_spread - bits_spread"
}
****************************************************************************

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation: Do not change whole the file. concentrate on the concrete lines.
Admin (to chat_manager):

Admin (to chat_manager):

***** Response from calling tool "call_qcoMK0RO5bXdY4yQz5RCh4mp" *****
USER INTERRUPTED
**********************************************************************

--------------------------------------------------------------------------------
Do not change whole the file. concentrate on the concrete lines.

--------------------------------------------------------------------------------
Engineer (to chat_manager):

***** Suggested tool Call (call_hz1v2vEPPxKHeygjqs2lc8ii): modify_code *****
Arguments:

{
"filename": "main.py",
"start_line": 1,
"end_line": 2,
"new_code": "from fastapi import FastAPI\nfrom spread_calculation import calculate_daily_spread\nimport yfinance as yf"
}
****************************************************************************

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation:

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...

>>>>>>>> EXECUTING FUNCTION modify_code...
Admin (to chat_manager):

Admin (to chat_manager):

***** Response from calling tool "call_hz1v2vEPPxKHeygjqs2lc8ii" *****
[0, "Code modified"]
**********************************************************************

--------------------------------------------------------------------------------
Engineer (to chat_manager):

***** Suggested tool Call (call_1ABNdk6AATaVvn8UqLKBD5ZR): modify_code *****
Arguments:

{
"filename": "main.py",
"start_line": 6,
"end_line": 11,
"new_code": "@app.get(\"/cdr_daily_spread\")\nasync def compare_daily_spread():\n cdr_spread = calculate_daily_spread(\"CDR.WA\")\n bits_spread = calculate_daily_spread(\"11B.WA\")\n return cdr_spread - bits_spread"
}
****************************************************************************

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation: Check out file first to get actual lines position. Also, change endpoint name.
Admin (to chat_manager):

Admin (to chat_manager):

***** Response from calling tool "call_1ABNdk6AATaVvn8UqLKBD5ZR" *****
USER INTERRUPTED
**********************************************************************

--------------------------------------------------------------------------------
Check out file first to get actual lines position. Also, change endpoint name.

--------------------------------------------------------------------------------
Engineer (to chat_manager):

***** Suggested tool Call (call_2vlnu0RJ11qg6ygGT6l2tS3Q): see_file *****
Arguments:

{
"filename": "main.py"
}
*************************************************************************

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation:

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...

>>>>>>>> EXECUTING FUNCTION see_file...
Admin (to chat_manager):

Admin (to chat_manager):

***** Response from calling tool "call_2vlnu0RJ11qg6ygGT6l2tS3Q" *****
[0, "1:from fastapi import FastAPI\n2:from spread_calculation import calculate_daily_spread\n3:import yfinance as yf\n4:\n5:app = FastAPI()\n6:\n7:@app.get(\"/cdr_daily_spread\")\n8:async def calculate_daily_spread():\n9: cdr = yf.Ticker(\"CDR.WA\")\n10: today_data = cdr.history(period=\"1d\")\n11: spread = ((today_data[\"High\"] - today_data[\"Low\"]) / today_data[\"Low\"]) * 100\n12: return spread"]
**********************************************************************

--------------------------------------------------------------------------------
Engineer (to chat_manager):

***** Suggested tool Call (call_gNwVSM2u33Ej89Yc2IkSAovu): modify_code *****
Arguments:

{
"filename": "main.py",
"start_line": 7,
"end_line": 12,
"new_code": "@app.get(\"/compare_daily_spread\")\nasync def compare_daily_spread():\n cdr_spread = calculate_daily_spread(\"CDR.WA\")\n bits_spread = calculate_daily_spread(\"11B.WA\")\n return cdr_spread - bits_spread"
}
****************************************************************************

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation:

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...

>>>>>>>> EXECUTING FUNCTION modify_code...
Admin (to chat_manager):

Admin (to chat_manager):

***** Response from calling tool "call_gNwVSM2u33Ej89Yc2IkSAovu" *****
[0, "Code modified"]
**********************************************************************

--------------------------------------------------------------------------------
Engineer (to chat_manager):

The changes have been made successfully. Now, the FastAPI application has an endpoint `/compare_daily_spread` which calculates and returns the difference between the daily spreads of "CDR.WA" (CD Project Red) and "11B.WA" (11bits Studio). The calculation logic is now in a separate file `spread_calculation.py`.

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation: Good. But add in return some addition description to be understandable which stock change is bigger. Remember to check out main.py first to actualize your knowledge.
Admin (to chat_manager):

Good. But add in return some addition description to be understandable which stock change is bigger. Remember to check out main.py first to actualize your knowledge.

--------------------------------------------------------------------------------
Engineer (to chat_manager):

***** Suggested tool Call (call_1Clo7beP49gWm1oXQXK7W6RH): see_file *****
Arguments:

{
"filename": "main.py"
}
*************************************************************************

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation:

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...

>>>>>>>> EXECUTING FUNCTION see_file...
Admin (to chat_manager):

Admin (to chat_manager):

***** Response from calling tool "call_1Clo7beP49gWm1oXQXK7W6RH" *****
[0, "1:from fastapi import FastAPI\n2:from spread_calculation import calculate_daily_spread\n3:import yfinance as yf\n4:\n5:app = FastAPI()\n6:\n7:@app.get(\"/compare_daily_spread\")\n8:async def compare_daily_spread():\n9: cdr_spread = calculate_daily_spread(\"CDR.WA\")\n10: bits_spread = calculate_daily_spread(\"11B.WA\")\n11: return cdr_spread - bits_spread\n"]
**********************************************************************

--------------------------------------------------------------------------------
Engineer (to chat_manager):

***** Suggested tool Call (call_Cl6jBUwvfa8JGbyldfrzYfiB): modify_code *****
Arguments:

{
"filename": "main.py",
"start_line": 7,
"end_line": 11,
"new_code": "@app.get(\"/compare_daily_spread\")\nasync def compare_daily_spread():\n cdr_spread = calculate_daily_spread(\"CDR.WA\")\n bits_spread = calculate_daily_spread(\"11B.WA\")\n spread_difference = cdr_spread - bits_spread\n if spread_difference > 0:\n return {'message': 'CD Project Red has a larger daily spread', 'difference': spread_difference}\n elif spread_difference < 0:\n return {'message': '11bits Studio has a larger daily spread', 'difference': -spread_difference}\n else:\n return {'message': 'Both stocks have the same daily spread', 'difference': 0}"
}
****************************************************************************

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation:

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...

>>>>>>>> EXECUTING FUNCTION modify_code...
Admin (to chat_manager):

Admin (to chat_manager):

***** Response from calling tool "call_Cl6jBUwvfa8JGbyldfrzYfiB" *****
[0, "Code modified"]
**********************************************************************

--------------------------------------------------------------------------------
Engineer (to chat_manager):

The changes have been made successfully. Now, the `/compare_daily_spread` endpoint returns a dictionary with a message indicating which stock (CD Project Red or 11bits Studio) has a larger daily spread and the absolute difference between the daily spreads. If both stocks have the same daily spread, the message will indicate this and the difference will be 0.

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation: ValueError: The truth value of a Series is ambiguous.
Admin (to chat_manager):

ValueError: The truth value of a Series is ambiguous.

--------------------------------------------------------------------------------
Engineer (to chat_manager):

The error message indicates that the comparison operation is being performed on a pandas Series object, which is ambiguous. This is likely because the `calculate_daily_spread` function is returning a Series object instead of a single value.

To fix this, we need to ensure that the `calculate_daily_spread` function returns a single value. We can do this by taking the first (and only) value from the Series object.

Here is the plan of changes:

1. Modify the `calculate_daily_spread` function in `spread_calculation.py` to return only the first value from the `spread` Series.
2. No changes are required in `main.py` as the returned value from `calculate_daily_spread` function will now be a single value.

Please confirm if this plan is correct and I can proceed with the changes.

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation: Cool. Remember to check out file before introducing modifications.
Admin (to chat_manager):

Cool. Remember to check out file before introducing modifications.

--------------------------------------------------------------------------------
Engineer (to chat_manager):

***** Suggested tool Call (call_4wPhGFFW8pt8B28nVhVvWztN): see_file *****
Arguments:

{
"filename": "spread_calculation.py"
}
*************************************************************************

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation:

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...

>>>>>>>> EXECUTING FUNCTION see_file...
Admin (to chat_manager):

Admin (to chat_manager):

***** Response from calling tool "call_4wPhGFFW8pt8B28nVhVvWztN" *****
[0, "1:import yfinance as yf\n2:\n3:def calculate_daily_spread(ticker):\n4: stock = yf.Ticker(ticker)\n5: today_data = stock.history(period=\"1d\")\n6: spread = ((today_data[\"High\"] - today_data[\"Low\"]) / today_data[\"Low\"]) * 100\n7: return spread"]
**********************************************************************

--------------------------------------------------------------------------------
Engineer (to chat_manager):

***** Suggested tool Call (call_DL0p37GzppRSmoqFaOOLSNaI): modify_code *****
Arguments:

{
"filename": "spread_calculation.py",
"start_line": 3,
"end_line": 7,
"new_code": "def calculate_daily_spread(ticker):\n stock = yf.Ticker(ticker)\n today_data = stock.history(period=\"1d\")\n spread = ((today_data[\"High\"] - today_data[\"Low\"]) / today_data[\"Low\"]) * 100\n return spread.values[0]"
}
****************************************************************************

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation:

>>>>>>>> NO HUMAN INPUT RECEIVED.

>>>>>>>> USING AUTO REPLY...

>>>>>>>> EXECUTING FUNCTION modify_code...
Admin (to chat_manager):

Admin (to chat_manager):

***** Response from calling tool "call_DL0p37GzppRSmoqFaOOLSNaI" *****
[0, "Code modified"]
**********************************************************************

--------------------------------------------------------------------------------
Engineer (to chat_manager):

The changes have been made successfully. Now, the `calculate_daily_spread` function in `spread_calculation.py` returns a single value instead of a pandas Series. This should resolve the ValueError you were encountering.

--------------------------------------------------------------------------------
Provide feedback to chat_manager. Press enter to skip and use auto-reply, or type 'exit' to end the conversation: exit

结果

最后,我们的代理修改了代码,使其看起来像这样:

# backend_dir/main.py

from fastapi import FastAPI
from spread_calculation import calculate_daily_spread
import yfinance as yf

app = FastAPI()

@app.get("/compare_daily_spread")
async def compare_daily_spread():
cdr_spread = calculate_daily_spread("CDR.WA")
bits_spread = calculate_daily_spread("11B.WA")
spread_difference = cdr_spread - bits_spread
if spread_difference > 0:
return {'message': 'CD Project Red has a larger daily spread', 'difference': spread_difference}
elif spread_difference < 0:
return {'message': '11bits Studio has a larger daily spread', 'difference': -spread_difference}
else:
return {'message': 'Both stocks have the same daily spread', 'difference': 0}


# backend_dir/spread_calculation.py

import yfinance as yf

def calculate_daily_spread(ticker):
stock = yf.Ticker(ticker)
today_data = stock.history(period="1d")
spread = ((today_data["High"] - today_data["Low"]) / today_data["Low"]) * 100
return spread.values[0]

您可以使用Postman或curl检查应用程序的工作并查看以下输出:

{
"message": "11bits Studio has a larger daily spread",
"difference": 1.7968083865943187
}