如何让GPT帮你生成模版代码
Motivation
本文所述的GPT是指OpenAI的GPT-x模型,详见https://platform.openai.com/docs/models/overview
GPT是一个强大的自然语言处理工具,它可以基于你的注释或者代码片段等帮你完成代码,但有时候希望机器能够辅助我们完成一些重复性工作,比如编程中的模板代码生成。但是OpenAI的API有token数量限制,例如,gpt-3.5只支持4096个tokens,不适合一次性生成整个文件的代码。虽然gpt-4可以支持8k甚至32k tokens,但一个庞大的代码文件很容易就超过这个限制。
那么如何让GPT帮我们基于庞大的代码生成模板代码呢?核心思想是将代码拆分成小片段,然后将这些小片段输入给GPT。GPT会根据prompt处理代码片段,并输出结果。最后,我们将生成后的代码片段拼接到模版中,输出最终的代码文件。为了避免GPT输出模棱两可的结果,我们需要确保代码片段有足够的上下文信息。
基于以上思路,我开发了一个名为gpt_code_gen的工具,目前只支持C++代码生成。下面我将分享一下实现这个工具的过程中所做的一些思考和经验。
拆分代码片段
大多数情况下,我们可以使用正则表达式来拆分代码片段,如果需要更加精确地获取代码信息,我们可以使用对应语言的AST工具来进行拆分。具体实现细节不在本文讨论范围之内。
拆分代码片段粒度
-
按代码定义来拆分
我们可以将
class
,struct
,enum
等定义作为一个代码块。 -
拆分子代码片段
在一个大型、长期维护的项目中,
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
确保代码片段具有足够的上下文
为了保证准确性,我们尽可能将代码片段中涉及到用户自定义的struct
和 enum
的信息提供给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 in the following template:
/// GENERATED BY gpt_code_gen, DO NOT MODIFY BY HAND.
class Mock : public {
public:
};
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:
};
最终将生成的方法代码替换模版中的``并输出即可:
/// 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项目的一些经验。希望对你有帮助。
项目地址:https://github.com/littleGnAl/gpt_code_gen
demo见:https://github.com/littleGnAl/gpt_code_gen/tree/main/examples