大規模モデル開発 (15): 高度に自動化された AI プロジェクト開発プロセスを 0 から 1 まで構築する (前編)

全文は合計5600文字以上、読了時間の目安は約13~20分|乾物満載(全コード付き)、ブックマーク推奨!

この記事の目的: 大規模言語モデル (LLM) を使用して、プロジェクト開発効率を高速化するソリューションを提案すること。この記事は、最初の部分として、チャット モデルをガイドして作成するプロセス全体を完全に実装する方法に主に焦点を当てています。外部関数コード、コード管理、およびテスト。
画像-20230801085146350

コードのダウンロードアドレス

1. 背景

この記事では、大規模モデル開発 (14): OpenAI Chat モデル + Google API を使用して、インテリジェントな電子メール送受信のための AI アプリケーションを実装することで、 Google Cloud Gmail API を中心とした AI アプリケーション開発プロセスを実現しました。ただし、2 つは で定義されています。プロセスこれは重要な自動化機能ですが、AI 開発プロセスでは、より多くの探索と要件が必要です。ソフトウェア開発の効率を向上させるために、大規模な言語モデルをソフトウェア開発プロセスに適用するには、AI の介入が必要です。大規模な言語モデル自体は現在非常に人気があり、応用の方向性が異なります大規模言語モデルの強力な人間の意図理解能力とコード記述能力により、開発エンジニアは大規模言語モデルのサポートにより開発効率を大幅に向上させることができます。

AI アプリケーション開発において、大規模な言語モデルを利用して開発効率を向上させるには、次の 2 つの段階に分かれますが、本記事での実装は第 2 段階の典型的な実装事例です。


前述の第 2 段階のプロセスに基づいて、この記事では、以前に実装されたメール AI アプリケーション開発プロジェクトを使用して、大規模言語モデルを使用してプロジェクトの開発効率を向上させる方法を確認します。

2. ChatGPT を使用して外部関数を作成する

チャット モデルを使用して AI アプリケーション開発の効率を向上させるための最も基本的な戦略の 1 つは、対応する関数の外部関数の記述をチャット モデルに完了させることです。比較的簡単な実装方法は、要件を整理した後、ChatGPT で直接質問し、外部関数のコードを生成させ、それを現在のコード環境にコピーしてテストと修正を行うことです。

たとえば、Gmail API の呼び出しに関する最近受信した 5 件の電子メールを表示する関数を作成した場合、次の方法で ChatGPT に質問できます。

プロンプト:

これで、Gmail API を取得し、OAuth 2.0 クライアントと認証を実行し、メールを表示するための資格情報を token.json ファイルとして保存しました。ここで、最新の n 件の電子メールを表示する関数を作成したいと思います。関数の要件は次のとおりです:
1. 関数のパラメーターは n と userId で、userId は文字列パラメーターで、デフォルト値は 'me' で、これは次のことを意味します。私の電子メールを表示します。n は、クエリされる電子メールの数を表す整数です。2.
関数によって返される結果は、json 形式で表現された複数の辞書を含むリストです。ここで、辞書は電子メール メッセージを表し、各辞書は電子メールを含める必要があります
3. すべての関数を 1 つの関数にカプセル化してください;
関数の作成プロセス中に、関数の関数、関数のパラメーター、関数の戻り値などの情報を記述するために使用される、関数の詳細な説明ドキュメントの作成を手伝ってください。結果;

画像-20230731150401003

ChatGPT によって生成された関数は次のとおりです。これを実行して、テストする前に Gmail API の認証ファイルを統合してください。

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)

出力は次のとおりです。

画像-20230731154157175

次に、そのような関数の結果が Chat モデルで解釈できるかどうかをテストします。

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']

出力は次のとおりです。

画像-20230731154215595

Chat モデルは、これら 5 件の電子メールの受信を正常に取得し、テストを続行できます。次に、ChatGPT によって作成された get_emails 関数をテストします。そのパラメーターは、auto_functions 関数によって認識できるかどうかを示し、関数パラメーターを作成します。コードは次のとおりです:

まず auto_functions 関数をインポートします。

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

結果を見てください:

画像-20230731154323061

次に、関数関数をテストして、Chat モデルで正しく認識できるかどうかを確認します。

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

効果を見てください:

画像-20230731154418748

一般に、ChatGPT を使用して外部関数を作成する上記のプロセスでは、合計 3 つのことが行われています。

  • 最初に API 関連の認証情報を取得し、API 認証プロセス全体を実行します。
  • 関数呼び出し関数と auto_functions の設定をよく理解した上で、ChatGPT に適切な注意事項を与えます。
  • ChatGPTで書かれた関数を取得したら、auto_functionsを使って外部関数の機能を確認します

3. チャットモデルを利用してローカルコード操作を実現する

ChatGPT を利用して外部関数を作成すると、AI アプリケーションの開発効率が大幅に向上しますが、毎回 ChatGPT に質問し、コードをローカルにコピーして貼り付けて検証するのは、あまり AI とは言えませんしたがって、自然言語プロンプトを通じてコード環境で外部関数コードを直接作成し、それらを自動的にテストしてカプセル化する必要があります。

これを行うには、まずチャット モデルによって作成された関数を実行し、プロセスを直接実行する必要があります。

3.1 チャットモデルの出力結果を直接コードに変換して実行

Chat モデルの入力と出力はどちらも文字列であるため、Chat モデルの出力を実行可能な外部関数に直接変換したい場合は、適切なプロンプトだけでなく、適切なプロンプトを抽出できるいくつかのメソッドも必要です。文字列内の Python コードを直接実行して、比較的単純なテスト プロセスを次に示します。

  • ステップ 1: 現在使用されている gpt-4-0613 モデルが適切なプロンプトの下で外部関数の要件を満たす関数を作成できるかどうかを確認します。
response = openai.ChatCompletion.create(
  model="gpt-4-0613",
  messages=[{
    
    "role": "system", "content": "你是一个python代码编辑器,你的功能是输出python代码,请勿输出任何和python代码无关的内容"},
            {
    
    "role": "user", "content": "请帮我定义一个python函数,输出Hello world字符串,请在函数编写过程中,在函数内部加入中文编写的详细的函数说明文档。"}
  ]
)

出力を見てください。

画像-20230731162046386

  • ステップ 2: Chat モデルによって出力された文字列はマークダウン形式のオブジェクトなので、md 形式で保存します

フォーマットされたコードがどのように見えるかを確認してください。コードは次のとおりです。

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

helloworld.md の内容を確認します。

画像-20230731162304550

  • ステップ 3: 関数をカプセル化する

Chat モデルで作成した関数自体には問題ありませんが、ローカルで直接呼び出したい場合は、何度も試した結果、上記の文字列から正規表現で Python だけを抽出する方が効率よく問題を解決できます。コードの文字列、コードは次のとおりです。

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_s = extract_code(response.choices[0].message['content'])
code_s

画像-20230731164420591

実際、ここでは s に含まれるコード部分を完全に抽出し、文字列として保存できることがわかります。文字列で表される Python プログラムの場合、それをローカルの py ファイルに書き込み、次の方法でコードを表示できます。

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

この時点で、print_hello_world 関数を保存する py ファイルがローカルに作成されます。

画像-20230731164622189

要約すると、完全な自動プロセスには次の機能が必要です。

  • 完全な extract_function_code 関数を定義します。この関数は、文字列内の Python コードを抽出し、コードの関数名を抽出し、同時に関数をローカルの py ファイルに保存できます。

  • 保存する際はtestedとuntestedの2つのファイルに分かれており、testedフォルダは手動で検証した関数を保存するフォルダ、untestedフォルダは検証していない関数を保存するフォルダで、それぞれの関数の名前はfunction_name_module.pyとなります。

  • この関数を実行すると、コード (関数を定義) が現在の環境で実行され、同時に関数のすべての情報を出力するか、関数の名前だけを出力するかを選択できます。

上記の機能要件に基づいて、関数の例は次のとおりです。

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)

この機能を使用すると、チャット モデルの出力結果をワンクリックで抽出、保存、実行することがより便利になります。

3.2 チャット機能を利用した外部関数の作成

このプロセスに基づいて、チャット機能に要件を満たす外部関数を直接記述することができます (ここでは、メールボックス内のすべてのメールの数をカウントする機能を例にします)。

以前に定義した get_latest_email 関数は次のとおりです。

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)
  • ステップ 1: 上記の関数のコードを、inspect.getsource を通じて直接抽出し、文字列形式で出力します。コードは次のとおりです。
code = inspect.getsource(get_latest_email)

出力を見てください。
画像-20230731165806897

  • ステップ 2: フューショットの方法でプロンプトを表示する

まず関数の説明情報を読みます。コードは次のとおりです。

# 写入本地
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()

読み出したデータは以下のとおりです。

画像-20230731171251020

したがって、最終的なプロンプトは次のようになります。

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}]

Chat モデルと ChatGPT のプロンプト方法は大きく異なり、同じ意味を伝える場合でも、両者の効果的なプロンプト方法は大きく異なる可能性があるため、具体的なプロンプト方法は繰り返しテストする必要があります。上記はあくまで考え方であり、まだまだ改善の余地はあります

  • ステップ 3: モデルを呼び出す
response = openai.ChatCompletion.create(
  model="gpt-4-0613",
  messages=messages
)

出力を見てみましょう。

画像-20230731172116440

  • ステップ 4: コードを抽出した後、ローカルに書き込む
extract_function_code(res, detail=0)

ローカルコードを見てください:

画像-20230731172458078

画像-20230731172557014

  • ステップ 5: 関数検証の実行

画像-20230731174018066

この機能は、現在のメールボックス内のメール数を正確にカウントできます。

  • ステップ 6: 関数が関数パラメーターに正常に変換できるかどうかをテストする
functions_list = [get_email_count]

functions = auto_functions(functions_list)
functions

出力を見てください。

画像-20230731174145163

  • ステップ 7: 関数の関数の説明がチャット モデルで正しく認識できるかどうかをテストする
response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[{
    
    "role": "user", "content": '请帮我查下Gmail邮箱里现在总共有多少封邮件'}],
        functions=functions,
        function_call="auto",  
    )
response

出力を見てください。

画像-20230731174400940

ここまでで、メールの総数をカウントする関数を記述しました。以前のプロセスと比較して、extract_function_code 関数の助けにより、外部関数の作成効率はすでに非常に高く、プロンプトを作成し、新しい関数をテストして外部関数の作成を完了するための少量の手動作業のみが必要です。

3.3 機能の保存

上記の開発プロセスでは、** 関数がテストされると、テストされたフォルダーに転送できます。これは、関数が大規模言語モデルによって正常に認識され、外部関数として呼び出されることを示します。**コードは以下のように表示されます:

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)

4. まとめ

この記事では、AI 開発プロセスの効率的な最適化の最初のステップとして、チャット モデルをガイドして外部関数コードを作成し、コード管理、テストするプロセス全体を完全に実行します。

しかし実際には、AI アプリケーションの開発プロセスは、たとえば、潜在的な未知のユーザー ニーズが多数存在する場合 (たとえば、メールボックスに誰かからの未読メールがあるかどうかを確認したいなど) に直面した場合、より効率的になる可能性があります。より良いアイデアは、大規模言語モデル (LLM) を使用し、ユーザーのニーズを外部関数によって作成されたプロンプトに即座に変換し、次に外部関数を即座に作成してチャット モデルに追加して、チャット モデルの機能をリアルタイムで更新することです。そして最終的には、より完全なソリューションを提供しますこれは自動化のより高度な形式であり、大規模モデルは機能を実現するためのコードを記述するだけでなく、要件から機能に至るまでのプロセスを大規模モデル自体で要約することもできます。これが実現できれば、本製品の機能は自己成長(ユーザーのニーズに合わせてリアルタイムに成長すること)を実現したことに相当し、そのような開発プロセスが想像するインテリジェントな開発プロセスに近づくことは間違いありません。

最後に、この記事を読んでいただきありがとうございます! 何か得をしたと感じたら、ぜひ「いいね!」「ブックマーク」「フォロー」をしてください。これが私が創作を続けるモチベーションです。ご質問やご提案がございましたら、コメント欄にメッセージを残してください。できる限りお答えし、フィードバックを受け付けます。知りたい特定のトピックがございましたら、お知らせください。喜んでそれに関する記事を書きます。ご支援に感謝し、あなたと一緒に成長することを楽しみにしています!

次の記事プロセスの最適化を継続します!

おすすめ

転載: blog.csdn.net/Lvbaby_/article/details/132034625