Skip to content

面向程序员的提示

Prompt Engineering - 程序员的高效提示

Section titled “Prompt Engineering - 程序员的高效提示”

在本章中,我们将探讨程序员如何通过有效的 Prompt Engineering(提示工程)来利用大型语言模型(LLMs),如 ChatGPT 的强大能力。LLM 凭借其自然语言处理和代码生成能力,可以成为程序员提升工作效率、解决编码难题、理解新概念以及生成各种代码相关成果的宝贵助手。

1. 代码生成与脚手架(Scaffolding)

Section titled “1. 代码生成与脚手架(Scaffolding)”

程序员可以提示 LLM 生成各种编程语言的代码片段、函数、类,甚至完整的应用程序脚手架(scaffolds)。

示例:

User: 编写一个 Python 函数,它接受一个整数列表并返回一个只包含偶数的新列表。
LLM:
```python
def get_even_numbers(numbers):
"""接收一个整数列表,并返回一个只包含偶数的新列表。"""
even_numbers = []
for num in numbers:
if num % 2 == 0:
even_numbers.append(num)
return even_numbers
# 示例用法:
# my_list = [1, 2, 3, 4, 5, 6]
# print(get_even_numbers(my_list)) # 输出:[2, 4, 6]
### 2. 代码解释与理解
LLM 可以用自然语言解释复杂的代码片段、算法或编程概念,帮助学习和理解。
示例:
```css
User: 像给初学者一样解释这段 JavaScript 代码:
`const sum = (a, b) => a + b;`
LLM: 当然!让我们来分解这段 JavaScript 代码:
* `const`: 这个关键字用来声明一个变量,它的值不会被重新赋值。你可以把它想象成一个带有固定标签的容器。
* `sum`: 这是我们给变量(我们的容器)起的名字。在这个例子中,这个变量将持有一个函数。
* `=`: 这是赋值运算符。它表示我们将某个东西放入 `sum` 这个容器中。
* `(a, b) => a + b;`: 这是一个箭头函数,它是 JavaScript 中一种简洁的函数写法。
* `(a, b)`: 这些是函数的参数。当你使用(或“调用”)这个函数时,你会提供两个值,在函数内部它们将被命名为 `a` 和 `b`。
* `=>`: 这个箭头分隔了参数和函数所做的事情。
* `a + b`: 这是函数的主体。它简单地将 `a` 的值和 `b` 的值相加。
* 因为它是一个没有花括号 `{}` 的简洁箭头函数,所以 `a + b` 的结果会自动被函数返回。
所以,简单来说,这段代码创建了一个名为 `sum` 的函数,它接受两个数字(`a` 和 `b`)作为输入,并返回它们的总和。

3. 调试(Debugging)和故障排除协助

Section titled “3. 调试(Debugging)和故障排除协助”

程序员可以描述错误或粘贴有问题的代码,并向 LLM 询问潜在的原因、调试技巧或解决方案。

示例:

User: 我的 Python 代码中遇到了 `TypeError: 'NoneType' object is not iterable` 错误。这可能是什么原因造成的?
这是我代码的相关部分:
```python
def get_user_data(user_id):
# 模拟一个可能返回 None 的数据库调用
if user_id == 0:
return None
return ["data1", "data2"]
user_info = get_user_data(0)
for item in user_info:
print(item)

LLM: TypeError: 'NoneType' object is not iterable 错误发生是因为当 user_id 为 0 时,你的 get_user_data 函数返回了 None。然后,在 for item in user_info: 这行代码中,你试图对 user_info 进行循环(迭代),而 user_info 当前的值是 None。

None 在 Python 中是一个特殊值,表示没有值,它不是一个你可以像列表或字符串那样进行循环的对象。

要解决这个问题,你需要在尝试迭代 user_info 之前检查它是否为 None:

def get_user_data(user_id):
if user_id == 0:
return None
return ["data1", "data2"]
user_info = get_user_data(0)
if user_info is not None:
for item in user_info:
print(item)
else:
print("未找到用户数据。")

这样,你只有在 user_info 确实包含数据(本例中是列表)时,才会尝试进行循环。

### 4. 代码重构(Refactoring)和优化(Optimization)
LLM 可以对现有代码提出改进建议,使其更高效、可读性更强或更符合最佳实践。
示例:
```python
User: 你能帮我重构这段 Python 代码,让它更符合 Python 习惯或更高效吗?
```python
my_string = "hello world"
upper_case_string = ""
for char in my_string:
upper_case_string += char.upper()

LLM: 可以,这段代码可以写得更符合 Python 风格且更高效。以下是几种方法:

  1. 直接使用 str.upper() 方法: 最简单的方法是直接调用整个字符串的 .upper() 方法:

    my_string = "hello world"
    upper_case_string = my_string.upper()
    # print(upper_case_string) # 输出:HELLO WORLD
  2. 使用列表推导式(list comprehension)和 str.join()(对于这种情况比较冗长,但适用于更复杂的转换): 对于简单的转换为大写字母,这种方法可能有点过度,但它展示了一种更复杂的逐字符转换的常见模式:

    my_string = "hello world"
    upper_case_string = "".join([char.upper() for char in my_string])
    # print(upper_case_string) # 输出:HELLO WORLD

仅仅为了将字符串转换为大写,第一种方法(my_string.upper())是最直接且最符合 Python 习惯的方式。

### 5. 生成文档(Documentation)和注释(Comments)
提供代码,并要求 LLM 生成 docstrings(文档字符串)、注释,甚至 README 文件中的部分内容。
### 6. 代码语言翻译
LLM 可以协助将代码从一种编程语言翻译成另一种,尽管始终需要仔细审查。
### 7. 编写单元测试(Unit Tests)
提供一个函数或类,并要求 LLM 使用特定的测试框架为其生成单元测试用例。
### 程序员使用 LLM 提示的最佳实践
- 具体并提供上下文:清晰地说明编程语言、涉及的库/框架以及期望的结果。对于代码生成,描述函数的功能、输入和输出。
- 提供代码片段:询问关于现有代码的问题时,包含相关的代码片段。
- 迭代和细化:如果首次回复不完美,细化您的提示或提出后续问题。
- 指定限制条件:如果您有特定的限制(例如,“使用 Python 3.8 的特性”、“避免使用外部库”),请提及。
- 验证和测试:在将 LLM 生成的任何代码用于生产环境之前,务必进行严格审查和彻底测试。LLM 可能会犯错。
- 分解复杂问题:对于大型任务,将其分解为更小、更易管理的部分,并分别对每个部分进行提示。
### 应用示例:Python 实现(使用 LLM 解决编码问题)
这个 Python 脚本演示了如何使用 OpenAI API 向 LLM 提问编程相关的问题,例如如何实现一个算法。
```python
import openai
import os
# 推荐:通过环境变量设置你的 API key。
# openai.api_key = os.getenv("OPENAI_API_KEY")
# 对于本教程,使用一个占位符。请替换为你实际的 key。
openai.api_key = 'YOUR_API_KEY'
def get_coding_assistance(question, language="Python"):
prompt_content = (
f"我有一个关于 {language} 的编程问题。\n"
f"问题:{question}\n"
f"请提供清晰的解释,并在适用的情况下提供代码示例。"
)
messages = [
{"role": "system", "content": "你是一位专业的编程助手,擅长解释概念并提供准确的代码示例。"},
{"role": "user", "content": prompt_content}
]
try:
response = openai.chat.completions.create(
model="gpt-3.5-turbo", # 或更新的模型,如 gpt-4o-mini
messages=messages,
max_tokens=800, # 为详细的解释和代码留出足够的 token
temperature=0.5, # 平衡创造性和事实性
n=1,
stop=None
)
return response.choices[0].message.content.strip()
except Exception as e:
return f"发生错误:{e}"
# 示例用法:
programming_question = "如何在 Python 中实现二分查找算法?请编写代码并解释其工作原理。"
assistance = get_coding_assistance(programming_question, language="Python")
print("LLM 协助结果:")
print(assistance)

该脚本将打印 LLM 的响应,通常会包含对二分查找的解释和一个 Python 代码实现:

LLM 协助结果:
当然!二分查找(Binary search)是一种从**已排序**的元素列表中查找特定元素的有效算法。它的工作原理是反复将可能包含目标元素的列表部分分成两半,直到将可能的位置缩小到只剩一个为止。
概念上它是这样工作的:
1. 将目标值与已排序列表的中间元素进行比较。
2. 如果目标值与中间元素匹配,则找到了其位置。
3. 如果目标值小于中间元素,则它只能位于列表的下半部分。因此,对下半部分重复此过程。
4. 如果目标值大于中间元素,则它只能位于列表的上半部分。因此,对上半部分重复此过程。
5. 持续此过程,直到找到元素或子列表变空(表示列表中不存在该元素)。
以下是二分查找的 Python 实现:
```python
def binary_search(sorted_list, target):
"""
在已排序列表中执行二分查找以查找目标元素。
如果找到目标,返回其索引;否则返回 -1。
"""
low = 0
high = len(sorted_list) - 1
while low <= high:
mid = (low + high) // 2 # 找到中间索引
if sorted_list[mid] == target:
return mid # 目标在索引 mid 处找到
elif sorted_list[mid] < target:
low = mid + 1 # 目标在右半部分
else:
high = mid - 1 # 目标在左半部分
return -1 # 列表中未找到目标
# 示例用法:
my_sorted_list = [2, 5, 7, 8, 11, 12, 15, 18]
target_element = 12
index = binary_search(my_sorted_list, target_element)
if index != -1:
print(f"元素 {target_element} 在索引 {index} 处找到")
else:
print(f"列表中未找到元素 {target_element}。")
# 另一个示例
target_element_not_present = 10
index_not_present = binary_search(my_sorted_list, target_element_not_present)
if index_not_present != -1:
print(f"元素 {target_element_not_present} 在索引 {index_not_present} 处找到")
else:
print(f"列表中未找到元素 {target_element_not_present}。")

关于二分查找需要记住的关键点:

  • 它必须用于已排序的列表。如果列表未排序,二分查找将无法正确工作。
  • 它非常高效,时间复杂度为 O(log n),适用于大型数据集。
### 结论
在本章中,我们探讨了程序员如何通过战略性的 Prompt Engineering 有效地使用 LLM,如 ChatGPT。从生成代码和解释,到协助调试、重构和编写文档,LLM 可以显著增强程序员的能力。
通过遵循最佳实践——具体说明、提供上下文、进行迭代以及始终验证 LLM 生成的输出——程序员可以利用这些 AI 工具来改进工作流程、更快地学习并提高整体生产力。