Prompt 基础

什么是 Prompt Engineering

Prompt Engineering(提示词工程)是指通过精心设计输入给大语言模型(LLM)的文本提示,来引导模型生成高质量、符合预期输出的技术。它不需要修改模型本身的参数,而是通过优化"问法"来获得更好的"答案"。

Prompt Engineering 是使用 AI 模型最重要的技能之一。同样的模型,不同的提示词可能产生截然不同的结果质量。

对于开发者而言,掌握 Prompt Engineering 意味着:

  • 降低开发成本:无需微调模型即可完成多数任务
  • 提升输出质量:结构化的提示词能显著改善生成结果
  • 增强可控性:精确控制模型的行为边界和输出格式
  • 加速迭代:快速调整提示词比重新训练模型高效得多

Prompt 的基本结构

一个高质量的 Prompt 通常包含以下四个核心组成部分:

组成部分说明是否必需
指令(Instruction)告诉模型要做什么必需
上下文(Context)提供背景信息和约束条件推荐
输入数据(Input Data)需要模型处理的具体内容视情况
输出格式(Output Format)指定期望的输出结构推荐

指令(Instruction)

指令是 Prompt 的核心,明确告诉模型需要执行的任务。好的指令应该清晰、具体、无歧义。

# 模糊的指令
帮我写点关于 Go 的东西。

# 清晰的指令
请为 Go 语言初学者编写一篇介绍 goroutine 并发编程的教程,包含 3 个由浅入深的代码示例。

上下文(Context)

上下文为模型提供执行任务所需的背景信息,帮助模型理解你的具体需求场景。

你正在为一个面向后端开发者的技术博客撰写内容。读者具备基本的编程经验,
但对 Go 语言了解有限。文章风格应当专业但通俗易懂。

请编写一篇关于 Go 接口(interface)的入门教程。

输入数据(Input Data)

输入数据是你需要模型处理的具体内容,比如一段代码、一篇文章、一组数据等。

请分析以下 Go 代码中的潜在问题并给出优化建议:

func fetchData(urls []string) []string {
    var results []string
    for _, url := range urls {
        resp, err := http.Get(url)
        if err != nil {
            continue
        }
        body, _ := io.ReadAll(resp.Body)
        results = append(results, string(body))
    }
    return results
}

输出格式(Output Format)

明确指定输出格式可以让模型生成结构化、可预测的结果,这对于程序化处理尤为重要。

请分析以下错误日志,以 JSON 格式返回结果,包含以下字段:
- error_type: 错误类型
- root_cause: 根本原因
- severity: 严重程度(high/medium/low)
- suggestion: 修复建议

角色设定(System Prompt)

角色设定是通过 System Prompt 为模型赋予特定身份和行为规范的技术。它是 Prompt Engineering 中最强大的工具之一。

基本角色设定

你是一位拥有 10 年经验的 Go 语言架构师,擅长设计高并发微服务系统。
你的回答风格简洁专业,善于用代码示例说明问题。
当遇到不确定的问题时,你会明确指出而不是猜测。

结构化 System Prompt

在实际应用开发中,System Prompt 通常需要更加结构化:

## 角色
你是 CoderMast 技术文档助手,负责帮助开发者解答技术问题。

## 能力范围
- Go 语言开发(核心语法、标准库、常用框架)
- 云原生技术(Docker、Kubernetes)
- 数据库(MySQL、Redis)

## 行为规范
1. 始终提供可运行的代码示例
2. 代码示例使用 Go 1.21+ 语法
3. 涉及安全相关问题时,优先推荐安全实践
4. 不了解的问题如实告知,不编造信息

## 输出格式
- 使用 Markdown 格式
- 代码块标注语言类型
- 关键概念用粗体标注
在 API 调用中,System Prompt 通过 `system` 参数或 `role: "system"` 的消息传递。不同的 API 有不同的设置方式,但核心思路一致。

Zero-shot vs Few-shot

这是 Prompt Engineering 中两种最基础的策略,区别在于是否提供示例。

Zero-shot(零样本)

直接描述任务,不提供任何示例。适用于简单、明确的任务。

将以下英文技术术语翻译为中文,保留原文在括号中:
goroutine, channel, interface, struct, slice

输出:

协程(goroutine)、通道(channel)、接口(interface)、结构体(struct)、切片(slice)

Few-shot(少样本)

提供几个输入-输出示例,让模型学习期望的模式。适用于复杂任务或需要特定格式的场景。

请按照以下格式为 Go 标准库函数生成中文文档:

示例 1:
函数:strings.Contains(s, substr string) bool
文档:判断字符串 s 是否包含子串 substr。若包含返回 true,否则返回 false。
场景:用于字符串搜索、内容过滤等场景。

示例 2:
函数:strings.Replace(s, old, new string, n int) string
文档:将字符串 s 中的 old 替换为 new,n 为替换次数(-1 表示全部替换)。返回替换后的新字符串。
场景:用于文本处理、模板渲染、数据清洗等场景。

请为以下函数生成文档:
函数:strings.Split(s, sep string) []string
Few-shot 的示例数量通常 2-5 个即可。示例太少可能不够模型学习模式,太多则浪费 Token 并可能引入噪音。

对比与选择

特性Zero-shotFew-shot
Token 消耗较高
适用场景简单明确的任务复杂格式、特定风格
准确度取决于任务复杂度通常更高
灵活性受示例约束

输出格式控制

控制输出格式是 Prompt Engineering 在开发中最实用的技能之一。

JSON 格式输出

分析以下 Go 代码的复杂度,以 JSON 格式返回结果:

代码:
func BubbleSort(arr []int) []int { ... }

请严格按照以下 JSON Schema 返回:
{
  "function_name": "函数名",
  "time_complexity": "时间复杂度(大 O 表示法)",
  "space_complexity": "空间复杂度(大 O 表示法)",
  "is_stable": true/false,
  "optimization_suggestions": ["建议1", "建议2"]
}
要求 JSON 输出时,建议明确指定"仅返回 JSON,不要包含其他文本",否则模型可能在 JSON 前后添加解释性文字,导致解析失败。许多 API 也提供了 `response_format` 参数来强制 JSON 输出。

Markdown 格式输出

请为 sync.WaitGroup 编写使用说明,格式要求:
- 使用二级标题 (##) 分隔各部分
- 包含"概述"、"核心方法"、"使用示例"、"注意事项"四个部分
- 核心方法用表格展示(列:方法名、参数、说明)
- 代码示例使用 Go 代码块

结构化列表输出

列出 Go 语言中常见的 5 种并发模式,按以下格式输出:

**模式名称**
- 简介:一句话描述
- 适用场景:列举 2-3 个场景
- 代码骨架:关键代码片段
- 注意事项:1-2 个常见陷阱
---

使用分隔符控制边界

使用明确的分隔符帮助模型区分不同部分的内容:

请翻译 <text> 标签中的内容为中文,并对 <review> 标签中的内容进行代码审查。

<text>
Goroutines are lightweight threads managed by the Go runtime.
</text>

<review>
func main() {
    go func() {
        fmt.Println("hello")
    }
}
</review>

常见误区与最佳实践

误区一:指令过于模糊

# 不好的写法
帮我优化这段代码。

# 好的写法
请从以下三个方面优化这段 Go 代码:
1. 性能:减少不必要的内存分配
2. 可读性:改善变量命名和代码结构
3. 错误处理:补充缺失的错误检查
请逐一给出优化后的代码和修改说明。

误区二:一次性要求过多

将复杂任务拆分为多个步骤,效果远好于一个巨大的 Prompt。

# 不好的写法
帮我设计一个完整的微服务架构,包含所有代码、部署配置和文档。

# 好的写法(分步骤)
第一步:请先帮我梳理这个电商系统需要拆分为哪些微服务,以及它们之间的依赖关系。
第二步:(基于第一步的结果)请为"订单服务"设计 API 接口。
第三步:(基于前两步)请实现订单服务的核心业务逻辑。

误区三:忽略否定指令的效果

"不要做 X"往往不如"做 Y"有效。模型可能会因为关注到 X 反而更倾向于生成 X。尽量用正向指令替代否定指令。
# 不好的写法
不要使用全局变量,不要忽略错误,不要写过长的函数。

# 好的写法
请遵循以下编码规范:
- 使用依赖注入传递依赖,避免全局状态
- 所有错误必须显式处理(检查或返回)
- 单个函数不超过 50 行,超出则拆分为子函数

误区四:缺少输出约束

没有约束的 Prompt 容易产生冗长、偏离主题的回答。

# 不好的写法
解释一下 Go 的 GC。

# 好的写法
用 3-5 句话概括 Go 垃圾回收(GC)的核心机制,重点说明三色标记法的工作原理。
面向有 Java GC 基础的开发者,可以对比说明。

实战综合示例

下面是一个结合以上所有技巧的完整 Prompt 示例:

## 角色
你是一位 Go 语言技术专家和技术写作者。

## 任务
为初学者编写 Go 错误处理的最佳实践指南。

## 上下文
目标读者是有其他语言(Java/Python)经验但刚接触 Go 的开发者。
他们习惯了 try-catch 模式,需要理解 Go 的错误处理哲学。

## 要求
1. 解释 Go 为什么选择返回值而非异常
2. 展示 3 个逐步进阶的错误处理模式:
   - 基础:简单的 if err != nil
   - 中级:自定义错误类型 + errors.Is/As
   - 高级:错误包装链 + sentinel errors
3. 每个模式提供可运行的完整代码示例

## 输出格式
- Markdown 格式
- 代码使用 go 代码块
- 每个模式以二级标题分隔
- 在每个模式末尾用 > 引用块总结要点

## 约束
- 总长度控制在 800-1200 字
- 代码示例使用 Go 1.21+ 语法
- 不要涉及 panic/recover(这是另一个主题)
养成编写结构化 Prompt 的习惯。即使是日常使用,花 30 秒组织一下提示词的结构,也能显著提升输出质量。随着经验积累,你会形成自己的 Prompt 模板库。

小结

Prompt Engineering 的核心原则可以归纳为:

  1. 明确性:清晰描述你想要什么,而不是让模型猜测
  2. 结构化:使用分隔符、标签、格式要求组织 Prompt
  3. 具体性:提供上下文、示例和约束条件
  4. 迭代性:Prompt 很少一次就完美,需要不断调优

掌握这些基础之后,你可以进一步学习更高级的提示词技巧,如 Chain-of-Thought 推理、ReAct 模式等,来处理更复杂的任务场景。