本文同步自个人博客怎么让GPT帮你生成模版代码,转载请注明出处。

Motivation

本文所述的GPT是指OpenAI的GPT-x模型,详见platform.openai.com/docs/models…

GPT是一个强壮的自然言语处理东西,它能够依据你的注释或许代码片段等帮你完结代码,但有时分期望机器能够辅助咱们完结一些重复性作业,比方编程中的模板代码生成。可是OpenAI的API有token数量约束,例如,gpt-3.5只支撑4096个tokens,不适合一次性生成整个文件的代码。虽然gpt-4能够支撑8k乃至32k tokens,但一个巨大的代码文件很容易就超越这个约束。

那么怎么让GPT帮咱们依据巨大的代码生成模板代码呢?中心思维是将代码拆分红小片段,然后将这些小片段输入给GPT。GPT会依据prompt处理代码片段,并输出成果。最后,咱们将生成后的代码片段拼接到模版中,输出最终的代码文件。为了防止GPT输出模棱两可的成果,咱们需求保证代码片段有满足的上下文信息。

依据以上思路,我开发了一个名为gpt_code_gen的东西,目前只支撑C++代码生成。下面我将共享一下实现这个东西的进程中所做的一些思考和经历。

拆分代码片段

大多数情况下,咱们能够运用正则表达式来拆分代码片段,假如需求愈加精确地获取代码信息,咱们能够运用对应言语的AST东西来进行拆分。详细实现细节不在本文评论范围之内。

拆分代码片段粒度

  1. 按代码界说来拆分

    咱们能够将classstructenum等界说作为一个代码块。

  2. 拆分子代码片段

    在一个大型、长期保护的项目中,class的界说通常会超越几百个办法,因而咱们需求将class拆分红子代码片段。

    • 将一个办法作为一个代码块。

    • 将宏界说作为一个代码块。这儿的宏界说指被宏界说包裹的代码,如下所示:

      #if defined(__ANDROID__)
          virtual void func3() = 0;
      #endif
      

以下是示例代码:

struct MyStruct {
    int field1;
    int field2;
};
enum MyEnum {
    ENUM1 = 1;
}
class MyClass {
    virtual void func1() = 0;
    virtual void func2(const MyStruct& my_struct) = 0;
#if defined(__ANDROID__)
    virtual void func3() = 0;
#endif
};

咱们能够将上述代码拆分红以下代码片段:

代码片段1:

struct MyStruct {
    int field1;
    int field2;
};

代码片段2:

enum MyEnum {
    ENUM1 = 1;
}

代码片段3:

class MyClass {
    virtual void func1() = 0;
    virtual void func2(const MyStruct& my_struct) = 0;
#if defined(__ANDROID__)
    virtual void func3() = 0;
#endif
};

咱们能够将class拆分红以下子代码片段:

子代码片段1:

virtual void func1() = 0;

子代码片段2:

virtual void func2(const MyStruct& my_struct) = 0;

子代码片段3:

#if defined(__ANDROID__)
    virtual void func3() = 0;
#endif

保证代码片段具有满足的上下文

为了保证准确性,咱们尽可能将代码片段中涉及到用户自界说的structenum的信息提供给GPT。例如,在子代码片段2中,咱们会将代码片段1的信息包括进去:

struct MyStruct {
    int field1;
    int field2;
};
virtual void func2(const MyStruct& my_struct) = 0;

自然言语生成模版代码

在前面咱们现已介绍了怎么拆分代码块,现在咱们来看看怎么运用拆分后的代码块来生成代码。在AI年代,自然言语编程成为可能,只需求编写Prompt就能够灵敏地控制代码生成(你能够参阅awesome-chatgpt-prompts或许凭借ChatGPT来编写比较准确的Prompt)。

在本项目中,主要运用Chat Completion API来完结代码生成。运用YAML格局能够省去符号转义的费事,同时还能更方便地将代码作为输入,更好地支撑Few-shot prompting。因而,本项目中选用了YAML作为Prompt输入文件格局,然后将其转换成JSON格局以发送给Chat Completion API。详细格局如下:

- role: system
  content: The system content
- role: user
  content: This is the user content
- role: assistant
  content: This is the assistant content

上述子代码片段1将会被转换成以下JSON格局并发送到Chat Completion API:

[
    {"role": "system", "content": "The system content"},
    {"role": "user", "content": "This is the user content"},
    {"role": "assistant", "content": "This is the assistant content"},
    {"role": "user", "content": "virtual void func1() = 0;"}
]

以下是项目中生成gMock MOCK_METHOD的Prompt例子:

- role: system
  content: |
    Write a gMock mock method definition in C++. The mock method should take C++ function code snippets as inputs and return the mock method definition. Use your knowledge of C++ and gMock to write the exact gMock mock function declaration. Your solution should be in the form of a C++ code snippet that defines the mock method.
    The mock method should should also handle any macro declarations in the input and include them in the output.
    I want you to only reply the mock method definition. Do not write explanations.

- role: user
  content: |
    virtual void release(bool sync = false) = 0;

- role: assistant
  content: |
    MOCK_METHOD(void, release, (bool sync), (override));

以上Prompt为每个办法代码块生成了MOCK_METHOD。最后一步是将生成的MOCK_METHOD拼接起来,如文章最初所说,咱们需求一个模版类来承载这些生成后的办法,模版类也能够通过Prompt来生成。以下是本项目中生成整个gMock Mock类模版的Prompt例子:

- role: system
  content: |
    Given the class name, replace the {{ CLASS_NAME }} in the following template:
    /// GENERATED BY gpt_code_gen, DO NOT MODIFY BY HAND.
    class Mock{{ CLASS_NAME }} : public {{ CLASS_NAME }} {
    public:
    {{ BODY }}
    };

    I want you to only reply the replaced template Do not write explanations.
- role: user
  content: IRtcEngine
- role: assistant
  content: |
    /// GENERATED BY gpt_code_gen, DO NOT MODIFY BY HAND.
    class MockIRtcEngine : public IRtcEngine {
    public:
    {{ BODY }}
    };

最终将生成的办法代码替换模版中的{{ BODY }}并输出即可:

/// GENERATED BY gpt_code_gen, DO NOT MODIFY BY HAND.
class MockIRtcEngine : public IRtcEngine {
public:
MOCK_METHOD(void, release, (bool sync), (override));
MOCK_METHOD(int, queryInterface, (INTERFACE_ID_TYPE iid, void** inter), (override));
...
};

TL;DR

这篇文章是ChatGPT帮我写的,写得不好请见谅。

以上,是我共享的关于gpt_code_gen项目的一些经历。期望对你有协助。

项目地址:github.com/littleGnAl/…
demo见:github.com/littleGnAl/…