Large Model Development (15): Build a highly automated AI project development process from 0 to 1 (Part 1)

The full text has a total of more than 5600 words, and the expected reading time is about 13~20 minutes | Full of dry goods (with all codes), it is recommended to bookmark!

The goal of this article: to propose a solution to speed up project development efficiency by using large language models (LLMs). This article, as the first part, mainly focuses on how to completely implement the entire process of guiding the Chat model to create external function codes, code management, and testing.
image-20230801085146350

Code download address

1. Background

In this article, large model development (fourteen): Using the OpenAI Chat model + Google API to implement an AI application for intelligent sending and receiving emails has realized the process of AI application development around the Google Cloud Gmail API, although two are defined in the process It is a key automation function, but for the AI ​​development process, more exploration and requirements are needed. AI intervention is needed to apply the large language model to the software development process to improve the efficiency of software development. The large language model itself is very popular at present. direction of application . The powerful human intent understanding ability and code writing ability of the large language model will enable development engineers to greatly improve development efficiency with the support of the large language model.

For AI application development, using large language models to improve development efficiency can be divided into the following two stages. The implementation in this article is a typical implementation case of the second stage.


Based on the above-mentioned second-stage process, this article uses the previously implemented email AI application development project to see how to use the large language model to speed up the development efficiency of the project.

2. Write external functions with the help of ChatGPT

Using the Chat model to help improve the efficiency of AI application development, one of the most basic strategies is to try to let the Chat model complete the writing of external functions for corresponding functions. A relatively simple implementation method is to directly ask questions in ChatGPT after sorting out the requirements, let it generate the code of the external function, and then copy it to the current code environment for testing and modification.

For example, if you write a function to view the last 5 received emails around the call of Gmail API, you can ask ChatGPT questions in the following way:

Prompt:

I've now got the Gmail API and done the OAuth 2.0 client and authorization, and saved the credentials to view mail as a token.json file. Now I want to write a function to view the latest n emails. The function requirements are as follows:
1. The function parameters are n and userId, where userId is a string parameter, and the default value is 'me', which means to view my emails. And n is an integer, representing the number of emails to be queried;
2. The result returned by the function is a list containing multiple dictionaries, expressed in json format, where a dictionary represents an email message, and each dictionary needs to contain emails
3. Please encapsulate all the functions in one function; 4.
Please help me write a detailed function description document during the function writing process, using It is used to describe information such as function functions, function parameters, and function return results;

image-20230731150401003

The function generated by ChatGPT is as follows, you can try to run it, and unify the authorization file of Gmail API before testing:

SCOPES = ['https://www.googleapis.com/auth/gmail.send','https://www.googleapis.com/auth/gmail.readonly']
flow = InstalledAppFlow.from_client_secrets_file(
                'credentials-web.json', SCOPES)
creds = flow.run_local_server(port=8000, access_type='offline', prompt='consent')

with open('token.json', 'w') as token:
    token.write(creds.to_json())
import base64
import re
import json
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build

def get_recent_emails(n, userId='me'):
    """
    获取最近的n封邮件
    
    功能:
        这个函数用于获取最近的n封邮件信息。返回的邮件信息包括邮件的发件人、发件时间、邮件主题和邮件内容。
    
    参数:
        n (int):需要获取的邮件数量。
        userId (str):用户ID。默认值为'me',代表当前授权用户。
    
    返回:
        返回一个包含多个字典的列表,以json格式表示。每个字典对应一封邮件信息,包括邮件的发件人、发件时间、邮件主题和邮件内容。
    """
    
    # If modifying these SCOPES, delete the file token.json.
    SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']

    creds = None
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json')
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.json', 'w') as token:
            token.write(creds.to_json())

    service = build('gmail', 'v1', credentials=creds)
    
    # Get the recent n emails
    results = service.users().messages().list(userId=userId, maxResults=n).execute()
    messages = results.get('messages', [])
    
    email_list = []
    for message in messages:
        msg = service.users().messages().get(userId=userId, id=message['id']).execute()
        
        email_data = msg['payload']['headers']
        
        for values in email_data:
            name = values['name']
            if name == 'From':
                from_name = values['value']
            if name == 'Date':
                date = values['value']
            if name == 'Subject':
                subject = values['value']
                
        try:
            payload = msg['payload']
            body = payload['body']
            data = body['data']
            data = data.replace("-","+").replace("_","/")
            decoded_data = base64.b64decode(data)
            soup = BeautifulSoup(decoded_data , "lxml")
            body_text = soup.body()
            body_text = body_text[0].replace('\r\n', ' ').replace('\n', ' ')
        except:
            body_text = "Not available"
        
        email_dict = {
    
    'Date': date, 'From': from_name, 'Subject': subject, 'Body': body_text}
        email_list.append(email_dict)
    
    return json.dumps(email_list, indent=4, ensure_ascii=False)

The output is this:

image-20230731154157175

Then test whether the result of such a function can be interpreted by the Chat model:

response = openai.ChatCompletion.create(
  model="gpt-4-0613",
  messages=[
    {
    
    "role": "system", "content": "这是我的Gmail邮箱最近五封邮件的内容:%s" % msg},
    {
    
    "role": "system", "content": "邮件内容是由Gmail API获取"},
    {
    
    "role": "user", "content": "请问我的Gmail最近五封邮件是谁发送的,都有什么内容?"}
  ]
)
response.choices[0].message['content']

The output is as follows:

image-20230731154215595

The Chat model can successfully obtain the letters of these 5 emails and continue testing. Next, test the get_emails function written by ChatGPT. Its parameters indicate whether it can be recognized by the auto_functions function and create functions parameters. The code is as follows:

First import the auto_functions function:

def auto_functions(functions_list):
    """
    Chat模型的functions参数编写函数
    :param functions_list: 包含一个或者多个函数对象的列表;
    :return:满足Chat模型functions参数要求的functions对象
    """
    def functions_generate(functions_list):
        # 创建空列表,用于保存每个函数的描述字典
        functions = []
        
        def chen_ming_algorithm(data):
            """
            陈明算法函数,该函数定义了一种特殊的数据集计算过程
            :param data: 必要参数,表示带入计算的数据表,用字符串进行表示
            :return:陈明函数计算后的结果,返回结果为表示为JSON格式的Dataframe类型对象
            """
            df_new = pd.read_json(data)
            res = np.sum(df_new, axis=1) - 1
            return res.to_json(orient='records')
        
        chen_ming_function_description = inspect.getdoc(chen_ming_algorithm)
        
        chen_ming_function_name = chen_ming_algorithm.__name__
        
        chen_ming_function = {
    
    "name": "chen_ming_algorithm",
                              "description": "用于执行陈明算法的函数,定义了一种特殊的数据集计算过程",
                              "parameters": {
    
    "type": "object",
                                             "properties": {
    
    "data": {
    
    "type": "string",
                                                                     "description": "执行陈明算法的数据集"},
                                                           },
                                             "required": ["data"],
                                            },
                             }

        
        # 对每个外部函数进行循环
        for function in functions_list:
            # 读取函数对象的函数说明
            function_description = inspect.getdoc(function)
            # 读取函数的函数名字符串
            function_name = function.__name__

            user_message1 = '以下是某函数说明:%s。' % chen_ming_function_description +\
                            '根据这个函数的函数说明,请帮我创建一个function对象,用于描述这个函数的基本情况。这个function对象是一个JSON格式的字典,\
                            这个字典有如下5点要求:\
                            1.字典总共有三个键值对;\
                            2.第一个键值对的Key是字符串name,value是该函数的名字:%s,也是字符串;\
                            3.第二个键值对的Key是字符串description,value是该函数的函数的功能说明,也是字符串;\
                            4.第三个键值对的Key是字符串parameters,value是一个JSON Schema对象,用于说明该函数的参数输入规范。\
                            5.输出结果必须是一个JSON格式的字典,只输出这个字典即可,前后不需要任何前后修饰或说明的语句' % chen_ming_function_name
            
            
            assistant_message1 = json.dumps(chen_ming_function)
            
            user_prompt = '现在有另一个函数,函数名为:%s;函数说明为:%s;\
                          请帮我仿造类似的格式为当前函数创建一个function对象。' % (function_name, function_description)

            response = openai.ChatCompletion.create(
                              model="gpt-4-0613",
                              messages=[
                                {
    
    "role": "user", "name":"example_user", "content": user_message1},
                                {
    
    "role": "assistant", "name":"example_assistant", "content": assistant_message1},
                                {
    
    "role": "user", "name":"example_user", "content": user_prompt}]
                            )
            functions.append(json.loads(response.choices[0].message['content']))
        return functions
    
    max_attempts = 3
    attempts = 0

    while attempts < max_attempts:
        try:
            functions = functions_generate(functions_list)
            break  # 如果代码成功执行,跳出循环
        except Exception as e:
            attempts += 1  # 增加尝试次数
            print("发生错误:", e)
            if attempts == max_attempts:
                print("已达到最大尝试次数,程序终止。")
                raise  # 重新引发最后一个异常
            else:
                print("正在重新运行...")
    return functions
functions_list = [get_emails]
functions = auto_functions(functions_list)
functions

Look at the results:

image-20230731154323061

Next, test the functions function to show whether it can be correctly recognized by the Chat model:

response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[{
    
    "role": "user", "content": '请帮我查下最近3封邮件的邮件内容'}],
        functions=functions,
        function_call="auto",  
    )
response

Look at the effect:

image-20230731154418748

In general, in the above process of writing external functions with the help of ChatGPT, a total of three things have been done:

  • Obtain API-related credentials first, and run through the entire API authorization process
  • Based on a good understanding of the Function calling function and the set auto_functions, give a reasonable reminder to ChatGPT
  • After obtaining the function written by ChatGPT, use auto_functions to verify the function of the external function

3. Use the Chat model to realize local code operation

Writing external functions with the help of ChatGPT can greatly speed up the development efficiency of AI applications, but it is not very AI to ask questions to ChatGPT every time and then copy and paste the code locally for verification . Therefore, it is necessary to create external function codes directly in the code environment through natural language prompts, and automatically test and encapsulate them.

To do this, you first need to run through the function created by the Chat model and run the process directly.

3.1 Chat model output results are directly converted into code and run

The input and output of the Chat model are both strings, so if you want the output of the Chat model to be directly converted into an external function that can be run, you need not only a reasonable prompt, but also some methods that can extract the python code in the string and run it directly. A relatively simple test process is:

  • Step 1: Check whether the currently used gpt-4-0613 model can create a function that meets the requirements of external functions under reasonable prompts
response = openai.ChatCompletion.create(
  model="gpt-4-0613",
  messages=[{
    
    "role": "system", "content": "你是一个python代码编辑器,你的功能是输出python代码,请勿输出任何和python代码无关的内容"},
            {
    
    "role": "user", "content": "请帮我定义一个python函数,输出Hello world字符串,请在函数编写过程中,在函数内部加入中文编写的详细的函数说明文档。"}
  ]
)

Look at the output:

image-20230731162046386

  • Step 2: The string output by the Chat model is a markdown format object, save it in md format

See what the formatted code looks like, the code is as follows:

with open('helloworld.md', 'a', encoding='utf-8') as f:
    f.write(response.choices[0].message['content'])

Look at the contents of helloworld.md:

image-20230731162304550

  • Step 3: Encapsulate the function

There is no problem with the function created by the Chat model itself, but if you want to call it directly locally, after many attempts, a more efficient way to solve the problem is to extract only Python from the above string through regular expressions. The string of the code , the code is as follows:

def extract_code(s):
    """
    如果输入的字符串s是一个包含Python代码的Markdown格式字符串,提取出代码部分。
    否则,返回原字符串。

    参数:
    s: 输入的字符串。

    返回:
    提取出的代码部分,或原字符串。
    """
    # 判断字符串是否是Markdown格式
    if '```python' in s or 'Python' in s or'PYTHON' in s:
        # 找到代码块的开始和结束位置
        code_start = s.find('def')
        code_end = s.find('```\n', code_start)
        # 提取代码部分
        code = s[code_start:code_end]
    else:
        # 如果字符串不是Markdown格式,返回原字符串
        code = s

    return code

Test the execution result:

code_s = extract_code(response.choices[0].message['content'])
code_s

image-20230731164420591

In fact, it can be seen here that the code part contained in s can be completely extracted and saved as a string. For a python program represented by a string, you can write it into a local py file and view the code in the following way:

with open('helloworld.py', 'w', encoding='utf-8') as f:
    f.write(code_s)

At this point, a py file that saves the print_hello_world function will be created locally:

image-20230731164622189

So in summary, a complete automatic process should have the following capabilities:

  • Define a complete extract_function_code function. This function can extract the python code in the string, and extract the function name of the code, and save the function locally in the py file at the same time

  • When saving, it is divided into two files, tested and untested. The tested folder is used to save manually verified functions, and untested is used to save functions that have not been verified, and each function is named function_name_module.py.

  • When running this function, the code (defining the function) will be executed in the current environment, and at the same time, you can choose to print all the information of the function or just print the name of the function

Based on the above functional requirements, an example function is as follows:

def extract_function_code(s, detail=0, tested=False):
    """
    函数提取函数,同时执行函数内容,可以选择打印函数信息,并选择代码保存的地址
    """
    def extract_code(s):
        """
        如果输入的字符串s是一个包含Python代码的Markdown格式字符串,提取出代码部分。
        否则,返回原字符串。

        参数:
        s: 输入的字符串。

        返回:
        提取出的代码部分,或原字符串。
        """
        # 判断字符串是否是Markdown格式
        if '```python' in s or 'Python' in s or'PYTHON' in s:
            # 找到代码块的开始和结束位置
            code_start = s.find('def')
            code_end = s.find('```\n', code_start)
            # 提取代码部分
            code = s[code_start:code_end]
        else:
            # 如果字符串不是Markdown格式,返回原字符串
            code = s

        return code
    
    # 提取代码字符串
    code = extract_code(s)
    
    # 提取函数名称
    match = re.search(r'def (\w+)', code)
    function_name = match.group(1)
    
    # 将函数写入本地
    if tested == False:
        with open('./functions/untested functions/%s_module.py' % function_name, 'w', encoding='utf-8') as f:
            f.write(code)
    else:
        with open('./functions/tested functions/%s_module.py' % function_name, 'w', encoding='utf-8') as f:
            f.write(code)
    
    # 执行该函数
    try:
        exec(code, globals())
    except Exception as e:
        print("An error occurred while executing the code:")
        print(e)
    
    # 打印函数名称
    if detail == 0:
        print("The function name is:%s" % function_name)
    
    if detail == 1:
        with open('%s.py' % function_name, encoding='utf-8') as f:
            content = f.read()
        print(content)

With this function, it is more convenient to extract, save and run the output results of the Chat model with one click.

3.2 Write external functions with the help of Chat function

On the basis of this process, you can try to guide the Chat function to directly write external functions that meet the requirements. Here, the function of counting the number of all emails in the mailbox is taken as an example.

The previously defined get_latest_email function is as follows:

def get_latest_email(userId):
    """
    查询Gmail邮箱中最后一封邮件信息
    :param userId: 必要参数,字符串类型,用于表示需要查询的邮箱ID,\
    注意,当查询我的邮箱时,userId需要输入'me';
    :return:包含最后一封邮件全部信息的对象,该对象由Gmail API创建得到,且保存为JSON格式
    """
    # 从本地文件中加载凭据
    creds = Credentials.from_authorized_user_file('token.json')
    
    # 创建 Gmail API 客户端
    service = build('gmail', 'v1', credentials=creds)
    
    # 列出用户的一封最新邮件
    results = service.users().messages().list(userId=userId, maxResults=1).execute()
    messages = results.get('messages', [])

    # 遍历邮件
    for message in messages:
        # 获取邮件的详细信息
        msg = service.users().messages().get(userId='me', id=message['id']).execute()
        
    return json.dumps(msg)
  • Step 1: Directly extract the code of the above function through inspect.getsource and output it in string format . The code is as follows:
code = inspect.getsource(get_latest_email)

Look at the output:
image-20230731165806897

  • Step 2: Prompt it in the way of Few-shot

First read the description information of the function, the code is as follows:

# 写入本地
with open('./functions/tested functions/%s_module.py' % 'get_latest_email', 'w', encoding='utf-8') as f:
    f.write(code)

# 从本地读取
with open('./functions/tested functions/%s_module.py' % 'get_latest_email', encoding='utf-8') as f:
    content = f.read()

The read data is as follows:

image-20230731171251020

So the final Prompt is as follows:

assistant_example_content = content

system_content = "我现在已完成Gmail API授权,授权文件为本地文件token.json。"

user_example_content = "请帮我编写一个python函数,用于查看我的Gmail邮箱中最后一封邮件信息,函数要求如下:\
                        1.函数参数userId,userId是字符串参数,默认情况下取值为'me',表示查看我的邮件;\
                        2.函数返回结果是一个包含最后一封邮件信息的对象,返回结果本身必须是一个json格式对象;\
                        3.请将全部功能封装在一个函数内;\
                        4.请在函数编写过程中,在函数内部加入中文编写的详细的函数说明文档,用于说明函数功能、函数参数情况以及函数返回结果等信息;"

user_content = "请帮我编写一个python函数,用于查看我的Gmail邮箱中总共有多少封邮件,函数要求如下:\
                1.函数参数userId,userId是字符串参数,默认情况下取值为'me',表示查看我的邮件;\
                2.函数返回结果是当前邮件总数,返回结果本身必须是一个json格式对象;\
                3.请将全部功能封装在一个函数内;\
                4.请在函数编写过程中,在函数内部加入中文编写的详细的函数说明文档,用于说明函数功能、函数参数情况以及函数返回结果等信息;"

messages=[{
    
    "role": "system", "content": system_content},
          {
    
    "role": "user", "name":"example_user", "content": user_example_content},
          {
    
    "role": "assistant", "name":"example_assistant", "content": assistant_example_content},
          {
    
    "role": "user", "name":"example_user", "content": user_content}]

The prompting method of the Chat model is very different from that of ChatGPT. When the same meaning is conveyed, the effective prompting methods of the two may also be very different. Therefore, the specific prompting method needs to be tested repeatedly. The above is just a way of thinking, there is still a lot of room for improvement

  • Step 3: Call the model
response = openai.ChatCompletion.create(
  model="gpt-4-0613",
  messages=messages
)

Take a look at the output:

image-20230731172116440

  • Step 4: After extracting the code, write it locally
extract_function_code(res, detail=0)

Look at the local code:

image-20230731172458078

image-20230731172557014

  • Step 5: Run Function Validation

image-20230731174018066

This function can correctly count the number of mails in the current mailbox.

  • Step 6: Test whether the function can be successfully converted into functions parameter
functions_list = [get_email_count]

functions = auto_functions(functions_list)
functions

Look at the output:

image-20230731174145163

  • Step 7: Test whether the functions function description can be correctly recognized by the Chat model
response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[{
    
    "role": "user", "content": '请帮我查下Gmail邮箱里现在总共有多少封邮件'}],
        functions=functions,
        function_call="auto",  
    )
response

Look at the output:

image-20230731174400940

So far, the function of counting the total number of emails has been written. Compared with the previous process, the efficiency of writing external functions is already very high with the help of the extract_function_code function. It only requires a small amount of manual labor to write prompts and test around new functions to complete the writing of external functions.

3.3 Function saving

For the above-mentioned development process, when the ** function has been tested, it can be transferred to the tested folder, indicating that the function can be successfully recognized by the large language model and called as an external function. **code show as below:

import shutil
import os
import glob

def functions_move(module_name):
    """
    将通过测试的函数转移到tested functions文件夹内
    """
    current_directory = os.getcwd()
    src_dir = current_directory + "\\functions\\untested functions"
    dst_dir = current_directory + "\\functions\\tested functions"

    src_file = os.path.join(src_dir, module_name)
    dst_file = os.path.join(dst_dir, module_name)

    shutil.move(src_file, dst_file)

Four. Summary

This article, as the first step in the efficient optimization of the AI ​​development process, completely executes the entire process of guiding the Chat model to create external function codes, code management, and testing.

But in fact, the development process of AI applications can be more efficient. For example, in the face of a large number of potential unknown user needs (for example, now I want to check whether there are unread emails from someone in the mailbox), a better idea is to use Large Language Models (LLMs), instantly translate user needs into prompts created by external functions, and then instantly create external functions and add them to the Chat model to update the capabilities of the Chat model in real time, and finally provide a more complete solution . This is a more advanced form of automation, so that the large model can not only write the code for function realization, but also the process from requirement to function can be summarized by the large model itself. If this can be achieved, the function of this product is equivalent to realizing self-growth (growth in real time according to the needs of users). There is no doubt that such a development process is closer to the imagined intelligent development process.

Finally, thank you for reading this article! If you feel that you have gained something, don't forget to like, bookmark and follow me, this is the motivation for my continuous creation. If you have any questions or suggestions, you can leave a message in the comment area, I will try my best to answer and accept your feedback. If there's a particular topic you'd like to know about, please let me know and I'd be happy to write an article about it. Thank you for your support and look forward to growing up with you!

Next articleContinue to process optimization!

Guess you like

Origin blog.csdn.net/Lvbaby_/article/details/132034625