Foreword
OpenAI has indeed sparked a boom in AIGC. As engineers, we have become the first beneficiaries, without worrying about being replaced. The efficiency improvement brought by AIGC is obvious. In the following series of articles, @Arno will no longer discuss how to achieve those results that are difficult for ordinary engineers to reach, but will focus on the current work and provide best practices for engineers to use AI to improve efficiency in daily scenarios.
Continuing from the previous article: AIGC Application Series - Unit Testing. After finishing unit tests, let's continue to discuss how to let AI help us write business components. The benefits in this area are also very high. Let's see how to write AntDesign components well.
Thanks to LLMs (Large Language Models) like OpenAI, which have learned the open-source AntDesign component library, we can enable models like OpenAI to quickly write AntDesign components with 0 fine-tuning and 0 context preparation.
π¦ Before We Start
Before we start preparing our Prompt magic, I want to refine a few principles:
- Considering the current token limits for OpenAI input and output, we should try to keep the tasks for AI to write not too long or too complex, controlling them within 500 lines, which is currently more appropriate for maintainability.
- Engineers should perform a macro-level decomposition of the target, breaking down a complex page into N sub-pages, so that the AntDesign component generation is controlled within 1 file and 1 component, ensuring the "cleanliness" of the generated code.
- It is best to write the PROMPT address into the file header for AI-generated tasks, to facilitate future updates and PROMPT reuse.
- ...
In short, keeping the AI-generated code to one PROMPT per file and a manageable length is the best paradigm under the current circumstances.
πͺ Writing the Prompt to Generate AntDesign
Let's first look at a classic scenario in backend systems: "Suppose we need to create a data table for managing the CRUD operations of a machine learning dataset." How do we achieve this?
Assume the server returns the following data ππ» for rendering the table:
export interface ResponseDatasetMeta {
code?: string;
createdAt?: string;
metaInfo?: string;
name?: string;
owner?: string;
tenantId?: string;
updatedAt?: string;
}
To write a good PROMPT for rendering the table: :::info We can optimize PROMPT design based on guidance from a series of Prompt tutorials in the community, including Prompt Engineering. :::
This Prompt structure includes:
- Language: We try to use English to describe the Prompt to cooperate with OpenAI.
- Firstly, it saves tokens.
- Secondly, OpenAI is better at English.
- Role: We define it as a code generator.
- Instructions: For the instruction area, we ask it to do the following, emphasizing the following instructions:
- Use the
AntDesign
component library to generate components. - Use
Typescript
as the coding language. - Write code in the
ReactHook
style. - Generate code blocks using
tsx
syntax for easy copy-pasting. - Export the component's property interface.
- Use
export const
to export the component. - Since the author is writing Next.js Client components, it's necessary to add the
use client
directive at the top of the file. Users can remove this if not needed. - The runtime environment is
Node.js
. - Only generate code, do not return explanations.
- Other supplements for running specific code: for example, only consider generating test code for XX classes or functions.
- Use the
- Output: Specify the output as
Typescript
code.
Let's organize this Prompt and see the effect:
Your current role is:A code generator.
##
Here are your instructions:
1. Use AntDesign Component Library to create a component
2. Use TypeScript
3. Use React Hook Style
4. Generate the text start with ```tsx and end with ```
5. Export defined interface
6. Do not use `export default` syntax, use `export const` instead
7. add `'use client';` on the first line of the code
8. Use tailwind for classname, and do not use classname lib
Based on the text below work:
"""
{{user_defined_task}}
"""
##
``` typescript ```
Let's try running a very simple task: **Write a HelloWorld button that displays "Hello World" text when clicked**
, and try it in the runtime. The code it outputs in the OpenAI Turbo 3.5
environment can be used directly, which is very impressiveοΌ
Your current role is:A code generator.
##
Here are your instructions:
1. Use AntDesign Component Library to create a component
2. Use TypeScript
3. Use React Hook Style
4. Generate the text start with ```tsx and end with ```
5. Export defined interface
6. Do not use `export default` syntax, use `export const` instead
7. add `'use client';` on the first line of the code
8. Use tailwind for classname, and do not use classname lib
Based on the text below work:
"""
Write a HelloWorld button that displays "Hello World" text when clicked
"""
##
``` typescript ```
'use client';
import { Button } from 'antd';
import React, { FC } from 'react';
export interface HelloWorldButtonProps {
onClick(): void;
}
const HelloWorldButton: FC<HelloWorldButtonProps> = ({ onClick }) => {
return (
<Button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" onClick={onClick}>
Hello World
</Button>
);
};
export const HelloWorldButton;
OK, this effect is not bad, let's make it more complicated and interesting ~
π¦ Table Scene: More Complex Context Passing
For some code with a lot of context, what if it's not easy to describe in natural language?
At this time, we can introduce the concept of context based on the existing Prompt structure and slightly rewrite and optimize the Prompt.
Returning to the previous idea of generating a table based on an interface, let's try to write the following Prompt:
Your current role is:A code generator.
##
Here are your instructions:
1. Use AntDesign Component Library to create a component
2. Use TypeScript
3. Use React Hook Style
4. Generate the text start with ```tsx and end with ```
5. Export defined interface
6. Do not use `export default` syntax, use `export const` instead
7. add `'use client';` on the first line of the code
8. Use tailwind for classname, and do not use classname lib
Based on the text below work:
"""
Write a DataSet management table with the following fields described by the interface below with the action column 'add' | 'remove' | 'detail'
"""
And here is your context:
"""
"""
export interface ResponseDatasetMeta {
code?: string;
createdAt?: string;
metaInfo?: string;
name?: string;
owner?: string;
tenantId?: string;
updatedAt?: string;
}
"""
"""
##
``` typescript ```
Let's see the effect it generates:
'use client';
import { Table, Button } from 'antd';
import { PlusOutlined, DeleteOutlined, EyeOutlined } from '@ant-design/icons';
import { useState, useMemo } from 'react';
import classNames from 'classnames';
import { ResponseDatasetMeta } from './types';
export interface DatasetTableProps {
datasets: ResponseDatasetMeta[];
onAdd: () => void;
onRemove: (id: string) => void;
onDetail: (id: string) => void;
}
const DatasetTable = ({ datasets, onAdd, onRemove, onDetail }: DatasetTableProps) => {
const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
const columns = useMemo(() => [
{
title: 'Code',
dataIndex: 'code',
key: 'code',
},
{
title: 'Name',
dataIndex: 'name',
key: 'name',
},
{
title: 'Owner',
dataIndex: 'owner',
key: 'owner',
},
{
title: 'Action',
dataIndex: '',
key: 'action',
render: (text: string, record: ResponseDatasetMeta) => (
<>
<Button
type="link"
icon={<EyeOutlined />}
className={classNames('text-blue-500', 'hover:text-blue-700')}
onClick={() => onDetail(record.code)}
/>
<Button
type="link"
icon={<DeleteOutlined />}
className={classNames('text-red-500', 'hover:text-red-700')}
onClick={() => onRemove(record.code)}
/>
</>
),
},
], [onDetail, onRemove]);
const rowSelection = {
selectedRowKeys,
onChange: (keys: string[]) => {
setSelectedRowKeys(keys);
},
};
return (
<>
<div className="flex mb-4">
<Button
icon={<PlusOutlined />}
onClick={onAdd}
className="ml-auto"
>
Add Dataset
</Button>
<Button
icon={<DeleteOutlined />}
onClick={() => onRemove(selectedRowKeys.join(','))}
disabled={selectedRowKeys.length === 0}
className="ml-4"
>
Remove Selected
</Button>
</div>
<Table
columns={columns}
dataSource={datasets}
rowSelection={rowSelection}
rowKey={(record) => record.code || ''}
/>
</>
);
};
export const DatasetTable;
Very good! But it seems that the two fields of update date and creation date are missing. We can continue to tell OpenAI to optimize on the basis of the session context and add a session: Based on the above code, add the two fields of creation and update date
. At this time, OpenAI will optimize the code to achieve a complex Table import.
π Continuous Iteration and Optimization of Prompt
As time goes by and technology changes, Prompt will continue to evolve like software engineering iteration. Imagine that in the future, component generation can have more context input:
- For example, PRD-type input can better let components understand the context
- For example, adding generation restrictions, limiting the number of tokens, etc. is also a strategy
- For example, using business data to fine-tune models, etc., using exclusive models to assist in generation optimization
- ...
In short, this article also needs to be updated rapidly with the continuous development of the AI field ~
As far as the eye can see, it is only the near future, yet even so, we can still see that there is much work worth doing waiting for us there. ββ Alan Turing, Computing Machinery and Intelligence, 1950
Let's encourage each other ~
Summary
It is difficult for AI to achieve success in one step, but it is natural for people to break down their goals into sub-goals and then use AI to empower themselves to quickly complete functions. In the future, Everyone will be a Prompt Engineer, a "magician π§π»", let's continue to explore natural language cast more spells ~
Series of articles: