【愚公系列】《AIGC辅助软件开发》008-面向软件开发的提示工程:如何提问才能让ChatGPT更懂你
【摘要】 🏆 作者简介,愚公搬代码🏆《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,亚马逊技领云博主,51CTO博客专家等。🏆《近期荣誉》:2022年度博客之星TOP2,2023年度博客之星TOP2,2022年华为云十佳博主,2023年华为云十佳博主...
🏆 作者简介,愚公搬代码
🏆《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,亚马逊技领云博主,51CTO博客专家等。
🏆《近期荣誉》:2022年度博客之星TOP2,2023年度博客之星TOP2,2022年华为云十佳博主,2023年华为云十佳博主等。
🏆《博客内容》:.NET、Java、Python、Go、Node、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、大数据、人工智能、U3D游戏、小程序等相关领域知识。
🏆🎉欢迎 👍点赞✍评论⭐收藏
🚀前言
随着人工智能技术的快速发展,越来越多的开发者开始利用像ChatGPT这样的智能助手来提升工作效率和解决问题。然而,如何有效地与这些智能工具进行沟通,尤其是在软件开发领域,往往决定了最终的结果和体验。这就引出了“提示工程”(Prompt Engineering)的概念——通过优化问题的表达方式,使AI更好地理解需求和上下文。
本文将深入探讨面向软件开发的提示工程,重点介绍如何构建高效的提问策略,以便让ChatGPT更准确地理解您的意图和需求。我们将分享一些实用的技巧和示例,帮助您明确问题、提供必要的上下文,并引导AI生成更符合期待的回答。
无论您是刚入门的开发者,还是希望提高与AI互动能力的资深工程师,这篇文章都将为您提供有价值的见解和实践建议。让我们一起探索如何通过有效的提问,让ChatGPT成为您软件开发过程中的得力助手,助力项目的顺利推进!
🚀一、如何提问才能让ChatGPT更懂你
OpenAI 提供了一些关于 Prompt 的指导性建议:
- 使你的 Prompt 更加明确。
- 指定你想要的答案格式。
- 让模型在确定答案之前逐步思考或辩论利弊。
此外,我们还可以提供案例,并尽可能提供足够的上下文,以帮助模型更好地理解问题的背景和意图。接下来将举一些例子,让读者有更直观的感受。
🔎1.提问清晰且明确
我们通过对比,再来感受下清晰、明确的 Prompt是怎样的。
先看一个粗略的 Prompt:
请用JavaScript 写一个贪吃蛇的游戏。
得到html文件和js文件如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>贪吃蛇游戏</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #333;
}
canvas {
background-color: #000;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<script src="snake.js"></script>
</body>
</html>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const gridSize = 20;
const canvasSize = canvas.width;
const tileCount = canvasSize / gridSize;
let snake = [{ x: 10, y: 10 }];
let direction = { x: 0, y: 0 };
let food = { x: 15, y: 15 };
let score = 0;
function getRandomFoodPosition() {
const x = Math.floor(Math.random() * tileCount);
const y = Math.floor(Math.random() * tileCount);
return { x, y };
}
function drawRect(x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x * gridSize, y * gridSize, gridSize, gridSize);
}
function update() {
const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y };
if (head.x === food.x && head.y === food.y) {
snake.unshift(head);
score += 10;
food = getRandomFoodPosition();
} else {
snake.pop();
snake.unshift(head);
}
if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount || snake.slice(1).some(segment => segment.x === head.x && segment.y === head.y)) {
resetGame();
}
}
function draw() {
ctx.clearRect(0, 0, canvasSize, canvasSize);
snake.forEach(segment => drawRect(segment.x, segment.y, 'lime'));
drawRect(food.x, food.y, 'red');
ctx.fillStyle = 'white';
ctx.font = '16px Arial';
ctx.fillText(`Score: ${score}`, 10, 20);
}
function resetGame() {
snake = [{ x: 10, y: 10 }];
direction = { x: 0, y: 0 };
score = 0;
food = getRandomFoodPosition();
}
window.addEventListener('keydown', (e) => {
switch (e.key) {
case 'ArrowUp':
if (direction.y === 0) direction = { x: 0, y: -1 };
break;
case 'ArrowDown':
if (direction.y === 0) direction = { x: 0, y: 1 };
break;
case 'ArrowLeft':
if (direction.x === 0) direction = { x: -1, y: 0 };
break;
case 'ArrowRight':
if (direction.x === 0) direction = { x: 1, y: 0 };
break;
}
});
function gameLoop() {
update();
draw();
}
resetGame();
setInterval(gameLoop, 100);
感兴趣的读者可以试试,ChatGPT立刻就能生成一个可运行的贪吃蛇游戏的代码。原因很简单,贪吃蛇是一个大众熟知的游戏,ChatGPT已经把相关源代码都存储在它的知识库了。
那么如何让 ChatGPT 输出一个定制化的贪吃蛇游戏的代码呢?我们举一个例子:
请用JavaScript 写一个贪吃蛇游戏,要求如下:
1.点击方向键时游戏开始,程序会选择一个随机的方向作为蛇的移动方向。可以用方向键控制贪吃蛇移动。
2.开始时,贪吃蛇用一个绿色的方格表示,食物用红色方格表示,食物与贪吃蛇间隔一段距离。
3.贪吃蛇每吃一点食物就增加一个坐标的长度,增加部分的色与蛇的颜色一样。
4.游戏每一局有一个积分、开始时积分为0。贪吃蛇每吃一个食物、增加2的幂次方积分。比如第一次增加1分,第二次增加2分,第五次增加16分。
5.游戏在贪吃蛇撞墙或撞到自己时结束,游戏结束时,在游戏区域显示最终的积分。
6.游戏区域是一个400x400像素的矩形区域,由20x20个方格组成。
7.所有CSS和JavaSenpt代码都写在同一个HTML文件中。
得到代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>贪吃蛇游戏</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
#gameArea {
width: 400px;
height: 400px;
background-color: #000;
display: grid;
grid-template-columns: repeat(20, 20px);
grid-template-rows: repeat(20, 20px);
position: relative;
}
.snake {
background-color: green;
grid-column-start: 1;
grid-row-start: 1;
}
.food {
background-color: red;
}
#score {
position: absolute;
top: 10px;
left: 10px;
color: white;
font-size: 16px;
}
#gameOver {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: 24px;
display: none;
}
</style>
</head>
<body>
<div id="gameArea">
<div id="score">积分: 0</div>
<div id="gameOver">游戏结束!<br>最终积分: <span id="finalScore"></span></div>
</div>
<script>
const gameArea = document.getElementById('gameArea');
const scoreDisplay = document.getElementById('score');
const gameOverDisplay = document.getElementById('gameOver');
const finalScoreDisplay = document.getElementById('finalScore');
const gridSize = 20;
const gridCount = 20;
let snake = [{ x: 10, y: 10 }];
let food = { x: 15, y: 15 };
let direction = { x: 0, y: 0 };
let nextDirection = { x: 0, y: 0 };
let score = 0;
let moveInterval;
let moveSpeed = 200;
let power = 1;
document.addEventListener('keydown', startGame);
function startGame(event) {
if (direction.x === 0 && direction.y === 0) {
switch (event.key) {
case 'ArrowUp':
nextDirection = { x: 0, y: -1 };
break;
case 'ArrowDown':
nextDirection = { x: 0, y: 1 };
break;
case 'ArrowLeft':
nextDirection = { x: -1, y: 0 };
break;
case 'ArrowRight':
nextDirection = { x: 1, y: 0 };
break;
}
direction = { ...nextDirection };
moveInterval = setInterval(moveSnake, moveSpeed);
document.removeEventListener('keydown', startGame);
document.addEventListener('keydown', changeDirection);
}
}
function changeDirection(event) {
switch (event.key) {
case 'ArrowUp':
if (direction.y === 0) nextDirection = { x: 0, y: -1 };
break;
case 'ArrowDown':
if (direction.y === 0) nextDirection = { x: 0, y: 1 };
break;
case 'ArrowLeft':
if (direction.x === 0) nextDirection = { x: -1, y: 0 };
break;
case 'ArrowRight':
if (direction.x === 0) nextDirection = { x: 1, y: 0 };
break;
}
}
function moveSnake() {
direction = { ...nextDirection };
const newHead = { x: snake[0].x + direction.x, y: snake[0].y + direction.y };
if (newHead.x < 0 || newHead.x >= gridCount || newHead.y < 0 || newHead.y >= gridCount || isCollision(newHead)) {
endGame();
return;
}
snake.unshift(newHead);
if (newHead.x === food.x && newHead.y === food.y) {
score += Math.pow(2, power);
power++;
scoreDisplay.textContent = '积分: ' + score;
generateFood();
} else {
snake.pop();
}
drawGame();
}
function isCollision(position) {
return snake.some(segment => segment.x === position.x && segment.y === position.y);
}
function generateFood() {
let newFoodPosition;
do {
newFoodPosition = {
x: Math.floor(Math.random() * gridCount),
y: Math.floor(Math.random() * gridCount)
};
} while (isCollision(newFoodPosition));
food = newFoodPosition;
}
function drawGame() {
gameArea.innerHTML = `<div id="score">积分: ${score}</div><div id="gameOver">游戏结束!<br>最终积分: <span id="finalScore"></span></div>`;
snake.forEach(segment => {
const snakeElement = document.createElement('div');
snakeElement.style.gridColumnStart = segment.x + 1;
snakeElement.style.gridRowStart = segment.y + 1;
snakeElement.classList.add('snake');
gameArea.appendChild(snakeElement);
});
const foodElement = document.createElement('div');
foodElement.style.gridColumnStart = food.x + 1;
foodElement.style.gridRowStart = food.y + 1;
foodElement.classList.add('food');
gameArea.appendChild(foodElement);
}
function endGame() {
clearInterval(moveInterval);
finalScoreDisplay.textContent = score;
gameOverDisplay.style.display = 'block';
}
drawGame();
</script>
</body>
</html>
我们罗列了一些具体的需求,包括游戏玩法规则、展示的样式、输出的形式等。ChatGPT生成代码时,会将这些需求都考虑进去。
一开始,我们对游戏的运行过程可能不是很清楚,对ChatGPT生成的效果也不好判断,那么我们该怎么把问题描述清楚呢?通常我们用递进的方式,一边与ChatGPT交互,一边根据结果不断地调整Prompt、直到符合我们的预期。需求描述越清晰,ChatGPT的表现就越好。
🔎2.提供可参考的格式和案例说明
有时候,你的目标可能在你心中有明确的图景,但不容易用命令式的语言描述出来,这时就可以提供一些案例让ChatGPT学习。
比如,你要让ChatGPT帮助做命名实体识别,从文本中提取出人名、公司名等作为文本标签,可以用案例的方式告诉ChatGPT你想要的结果是怎样的。
阅读下面的文本,提取2种实体类型:公司名、人名
期望格式:
公司名:<逗号分隔的公司名称列表>
人名:<逗号分隔的人名列表>
##
案例文本:华为宣布突破ERP系统封锁 任正非孟晚舟发声
公司名:华为
人名:任正非,孟晚舟
##
文本:特斯拉的创始人兼CEO埃隆·马斯克(Elon Musk)也因此成为世界上最富有人,他的个人财富达到了近3000亿美元,超过了亚马逊杰夫·贝索斯(Jeff Bezos)和微软的比尔·盖茨(Bill Gates)
ChatGPT学得很到位,它不仅理解了“公司名”和“人名”的含义,还学到了输出格式。
🔎3.提供上下文
ChatGPT的背后是大语言模型(LargeLanguage Model,LLM),它学习并吸收了全世界的知识,形成了自己的智慧。但它不了解你,包括你的想法、你的目的和你的要求。回想一下,我们在工作中给别人交代任务时,也需要考虑对方是否了解这个任务的背景如果对方不了解,你就需要把背景介绍清楚。ChatGPT也需要尽可能详细地描述你交给它的任务背景。
在写程序时,我们经常遇到错误或者运行结果不符合预期,这时候可以让ChatGPT帮忙进行分析。
🦋3.1 结果不符合预期
比如,有个简单的使用指针交换两个数的代码,运行结果不符合预期,想要ChatGPT帮忙看看。我们可以把代码复制上来,并把问题描述清楚。
我正在学习C语言,写了以下程序,想要实现a和b的交换:
但是在运行结果中,a和b并没有交换,请问为什么?
```c
#include<stdio.h>
void swap(int *a, int *b)
{
int *k;
k= a;a = b; b = k;
}
int main()
{
int a=3,b=6,*x=&a,*y= &b;
swap( x,y);
print£( "%d,%d ",a,b);
}
```c
得到正确的代码
#include<stdio.h>
void swap(int *a, int *b)
{
int k;
k = *a; // 将 a 指向的值存入 k
*a = *b; // 将 b 指向的值存入 a 指向的位置
*b = k; // 将 k 的值存入 b 指向的位置
}
int main()
{
int a = 3, b = 6;
swap(&a, &b); // 传递 a 和 b 的地址
printf("%d, %d\n", a, b); // 输出交换后的值
return 0;
}
🦋3.2 修改 bug
我故意将刚才的代码改错,让它编译不通过,我们可以这样问ChatGPT。
我正在学习C语言,我写了以下程序,想要实现a和b的交换。
但是编译报错了,错误信息是:
error: no matching function for call to 'swap'
程序代码是:
```c
#include<stdio.h>
void swap(int *a, int *b)
{
int *k;
k= a;a = b; b = k;
}
int main()
{
int a=3,b=6;
swap( a,b);
print£( "%d,%d ",a,b);
}
请帮我看看问题在哪,如何修改?
```c
注意看,我将上面的描述分成了4段:第一段描述问题背景;第二段给出编译报错信息;第三段给出源代码,用```c把代码包裹起来,表示代码是用C语言写的;第四段向ChatGPT发问。
得到代码
#include<stdio.h>
void swap(int *a, int *b)
{
int k;
k = *a; // 将 a 指向的值存入 k
*a = *b; // 将 b 指向的值存入 a 指向的位置
*b = k; // 将 k 的值存入 b 指向的位置
}
int main()
{
int a = 3, b = 6;
swap(&a, &b); // 传递 a 和 b 的地址
printf("%d, %d\n", a, b); // 输出交换后的值
return 0;
}
以上分析得很好,编译问题修改正确。更进一步地,ChatGPT认为我的期望是交换两个数,因此把不能正确交换的问题也一起修改了,表现超出预期。
🦋3.3 分析代码并加上注释
我们经常会看一些别人写的代码,比如开源代码,或者公司交接的历史项目。有的代码写得清晰明了,注释也丰富,让人如沐春风。但更多看到的是没有注释,甚至注释与代码对不上的情况。
在尝试让ChatGPT帮我分析代码之后,我发现代码阅读效率提高了,心情也愉悦了对于很长的代码文件,为了不超过 token 数量的限制,我们可以按函数接口来划分。
请分析下这段代码的功能,并加上注释:
```c
void InitDaemon()
{
pid_t pid;
if((pid=fork())!=0)
{
exit( 0);
}
setsid();
signal(SIGHUP,SIG_IGN);
signal(SIGPIPE,SIG_IGN);
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGCHLD,SIG_IGN);
struct sigaction sig;
sig.sa_handler =SIG_IGN;
sig,sa_flags =0;
sigemptyset(&sig.sa_mask);
sigaction( SGHUP,&sig,NULL);
if((pid=fork())!=0)
{
exit(0);
}
exit(0);
umask(0);
setpgrp();
}
```c
得到代码
void InitDaemon()
{
pid_t pid;
// 第一次fork,创建子进程,父进程退出
if((pid = fork()) != 0)
{
exit(0); // 父进程退出,子进程继续执行
}
// 创建新会话,使子进程成为会话首进程,脱离控制终端
setsid();
// 忽略一些信号
signal(SIGHUP, SIG_IGN); // 忽略挂起信号,防止进程被终止
signal(SIGPIPE, SIG_IGN); // 忽略管道破裂信号
signal(SIGTTOU, SIG_IGN); // 忽略后台写终端信号
signal(SIGTTIN, SIG_IGN); // 忽略后台读终端信号
signal(SIGCHLD, SIG_IGN); // 忽略子进程退出信号,防止僵尸进程
// 设置忽略信号的处理方式
struct sigaction sig;
sig.sa_handler = SIG_IGN; // 设置处理程序为忽略
sig.sa_flags = 0; // 无附加标志
sigemptyset(&sig.sa_mask); // 清空信号集
sigaction(SIGHUP, &sig, NULL); // 忽略挂起信号
// 第二次fork,创建孙子进程,子进程退出
if((pid = fork()) != 0)
{
exit(0); // 子进程退出,孙子进程继续执行
}
// 终止初始化进程,正式进入守护进程状态
exit(0);
// 将文件权限掩码设为0,以确保守护进程创建的文件具有最大的权限
umask(0);
// 将进程组ID设置为当前进程的PID,独立于任何终端
setpgrp();
}
🔎4.8个OpenAl推荐的最佳实践
在使用模型时,以下8个最佳实践已被反复验证,建议在使用时遵循。当然,也鼓励根据自己的任务需求进行探索,找到适合自己的 Prompt。
-
使用最新的模型。
-
在提示开始处放置说明,并使用###或"""分隔说明和上下文。
-
不够好:
将下面的文本总结为最重要的要点的项目符号列表。{你的文本} -
比较好:
将下面的文本总结为最重要的要点的项目符号列表。文本:"""{你的文本}"""
-
-
尽可能详细地描述所需上下文、结果、长度、格式、风格等。
-
不够好:
写一首关于 OpenAI的诗。 -
比较好:
用一位{著名诗人}的风格,写一首关于OpenAI的短诗,重点关注最近DALL-E产品的推出(DALL-E是一种文本到图像的机器学习模型),让人感到鼓舞。
-
-
给出示例以说明需求:展示所需内容的示例,这样模型更容易理解。
-
不够好:
提取以下文本中提到的实体。提取以下4种实体类型:公司名称、人名、具体主题和总体主题。文本:{text} -
比较好:
提取下面文本中提到的重要实体。首先提取所有公司名称,然后提取所有人名,接着提取适合内容的特定主题,最后提取一般的总体主题。期望格式:
公司名称:<逗号分隔的公司名称列表>
人名:-||-
具体主题:-||-
总体主题:-||-
文本:<text>当你提供特定的格式要求时,模型的响应更好。这也使得编程解析多个输出更加可靠。
-
-
先尝试让模型0样本生成,不行的话再给出少量样本试试,若还不行,再去做大量样本的训练。
-
Zero-shot (0样本):
从下面的文本中提取关键词。文本:{text} 关键词: -
Few-shot (少量样本):
提供少量样本
样例1文本:Stripe提供API,供Web开发人员使用,将付款处理集成到他们的网站和移动应用程序中。
样例1关键词:Stripe、付款处理、API、Web开发人员、网站、移动应用程序
##
样例2文本:OpenAI已经培训了先进的语言模型,非常擅长理解和生成文本。我们的API提供访问这些模型,并可用于解决几乎涉及处理语言的任何任务。
样例2关键词:OpenAI、语言模型、文本处理、API。
##
文本3:{text}
关键词3:微调: 微调就是给模型更多的样本进行学习训练,这里就不举例了。
-
-
在说明中使用清晰、精确的语言。
-
不够好:
此产品的描述应该相当简短,仅几句话,不要太多。 -
比较好:
使用3~5句话来描述此产品。
-
-
不要只说不要做什么,而要说该怎么做。
-
不够好:
你是一个客服,当用户向你咨询网站登录问题时,请不要询问用户名和密码。 -
比较好:
你是一个客服,当用户向你咨询网站登录问题时,不要询问用户名和密码,请将用户转到登录帮助页面:http://www.samplewebsite.com/help/faq。
-
-
使用具体的词汇来帮助模型生成正确的代码。
-
不够好:
#写一个简单的Python函数#1.询问我英里数#2.将英里转换为公里 -
比较好:
#写一个简单的 Python 函数#1.询问我英里数#2.将英里转换为公里
import在上面的代码示例中,添加“import”提示,告诉模型应该使用Python编写(类似用SELECT作为SQL语句的开头提示)。
-
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
作者其他文章
评论(0)