首页/知识库/Prompt Engineering 最佳实践

Prompt Engineering 最佳实践

✍️ AI Master📅 创建 2026-04-08📖 20 min 阅读
💡

文章摘要

系统学习 CoT、Few-shot、ReAct 等 Prompt 设计模式与技巧

1什么是 Prompt Engineering?

Prompt Engineering(提示工程)是与大语言模型高效沟通的艺术和科学。它的核心目标是通过精心设计的输入文本,引导模型输出符合预期的高质量结果。

不要被"写几句话"这个表象所欺骗——Prompt Engineering 背后有深刻的认知科学原理。大语言模型本质上是 next-token predictor,它们根据上下文的概率分布生成下一个词。好的 Prompt 实际上是在模型的巨大参数空间中划定一个高概率区域,让模型的输出落在这个区域内。

理解这一点至关重要:你不是在"命令"模型做事,而是在"引导"模型走向你想要的推理路径。这就像在黑暗中用手电筒照亮一条路——手电筒照亮的地方,就是模型会走的方向。

核心理念:Prompt Engineering 不是魔法,而是工程。好的 Prompt 应该是可复现、可迭代、可评估的。

2Prompt 的核心组成要素

一个高质量的 Prompt 通常包含五个核心要素:角色(Role)、任务(Task)、上下文(Context)、约束(Constraints)和格式(Format)。这五个要素共同构成了 Prompt 的"黄金结构"。

角色(Role):赋予模型一个特定身份,如"你是一个资深 Python 工程师"。角色设定能激活模型参数空间中与该角色相关的专业知识区域。

任务(Task):清晰描述你要模型做什么。使用动作动词开头,如"分析"、"编写"、"解释"。避免模糊的"帮我看看"。

上下文(Context):提供完成任务所需的背景信息。包括领域知识、已有数据、用户画像等。上下文越充分,模型输出越精准。

约束(Constraints):限定输出的范围、长度、风格等。如"不超过 200 字"、"使用中文"、"避免专业术语"。

格式(Format):指定输出的结构,如 JSON、Markdown 表格、代码块等。结构化输出便于后续程序处理。

python
# 好的 Prompt 结构示例
def build_prompt(user_question: str, context: str) -> str:
    """构建结构化 Prompt"""
    return f"""你是一个资深的数据分析师,擅长用 Python 进行数据清洗和可视化。

【任务】
请分析以下数据集的特征,并给出数据清洗方案。

【上下文】
数据集包含用户行为日志,字段包括:user_id, timestamp, action, page_url。
当前数据存在以下问题:
- timestamp 格式不统一(部分为 Unix 时间戳,部分为 ISO 格式)
- action 字段存在拼写错误(如 "clik" 应为 "click")
- 约 5% 的 user_id 为空值

【待分析问题】
{user_question}

【已有数据样例】
{context}

【约束】
1. 使用 Python 代码,基于 pandas 库
2. 代码需要包含详细注释
3. 输出结果以 Markdown 表格形式呈现
4. 总回答不超过 800 字"""

# 使用示例
question = "如何处理时间戳格式不一致的问题?"
sample_data = """user_id,timestamp,action,page_url
123,1712567890,click,/home
,2024-04-08T10:30:00,clik,/products
456,1712570000,view,/about"""

print(build_prompt(question, sample_data))

3Few-Shot Learning:让模型照猫画虎

Few-Shot Learning 是 Prompt Engineering 中最强大的技术之一。通过在 Prompt 中提供少量输入-输出示例,你可以让模型快速理解任务模式,而无需重新训练模型参数。

Few-Shot 的核心原理是 In-Context Learning(上下文学习)。大语言模型在预训练阶段已经见过海量的文本模式,当你在 Prompt 中展示几个示例时,模型会自动识别其中的模式并应用到新的输入上。这与人类的"举一反三"能力非常相似。

关键要点:示例的质量远比重更重要。3 个精心挑选的高质量示例,效果远好于 10 个随机示例。示例应该覆盖任务的典型场景和边界情况。

python
# Few-Shot Prompt 示例:情感分类
def few_shot_sentiment_prompt(text: str) -> str:
    """Few-shot 情感分析 Prompt"""
    return f"""请判断以下评论的情感倾向,输出"正面"、"负面"或"中性",并给出简短理由。

示例 1:
输入:"这部电影的特效太震撼了,剧情也很紧凑,强烈推荐!"
输出:{{"sentiment": "正面", "reason": "表达了对电影的强烈推荐"}}

示例 2:
输入:"等了两个小时才上菜,菜还是凉的,服务态度也很差。"
输出:{{"sentiment": "负面", "reason": "描述了等待时间长、食物质量差、服务差"}}

示例 3:
输入:"这款手机用了一个月了,电池续航一般,拍照还行。"
输出:{{"sentiment": "中性", "reason": "客观描述,没有明显的褒贬"}}

请分析:
输入:"{text}"
输出:"""

# 测试
test_cases = [
    "物流很快,包装精美,产品质量超出预期",
    "客服态度极其恶劣,再也不买了",
    "产品功能中规中矩,价格偏高"
]

for case in test_cases:
    prompt = few_shot_sentiment_prompt(case)
    # response = llm.generate(prompt)
    print(f"输入: {case}")
python
# Few-Shot 示例选择策略
def select_best_examples(query: str, example_pool: list,
                         k: int = 3) -> list:
    """基于语义相似度选择最相关的 Few-Shot 示例"""
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.metrics.pairwise import cosine_similarity
    
    # 将查询和示例向量化
    vectorizer = TfidfVectorizer()
    all_texts = [query] + [ex["input"] for ex in example_pool]
    vectors = vectorizer.fit_transform(all_texts)
    
    # 计算查询与每个示例的相似度
    query_vec = vectors[0:1]
    example_vecs = vectors[1:]
    similarities = cosine_similarity(query_vec, example_vecs)[0]
    
    # 选择最相似的 k 个示例
    top_indices = similarities.argsort()[-k:][::-1]
    return [example_pool[i] for i in top_indices]

# 使用示例
examples = [
    {"input": "如何重置密码?", "output": "前往设置 > 安全 > 重置密码"},
    {"input": "订单什么时候到?", "output": "请在订单页面查看物流信息"},
    {"input": "怎么开发票?", "output": "订单完成后在订单详情中申请"},
]

query = "我忘记登录密码了怎么办?"
selected = select_best_examples(query, examples, k=2)
for ex in selected:
    print(f"Q: {ex['input']} → A: {ex['output']}")
Few-Shot 策略适用场景优点缺点

Zero-Shot

简单分类、摘要

简洁、Token 少

复杂任务效果差

One-Shot

中等复杂度任务

快速建立模式

单示例可能不具代表性

Few-Shot (3-5)

格式要求严格的输出

效果显著提升

消耗更多 Token

Many-Shot (10+)

高度专业化任务

覆盖更多边界情况

可能超出上下文窗口

动态选择

查询类型多样

最相关的示例

需要额外的检索逻辑

4Chain of Thought(思维链):让模型逐步推理

Chain of Thought(CoT)Prompting 的核心思想是:引导模型将复杂问题拆解为多个中间推理步骤,而不直接跳到答案。这在数学推理、逻辑分析、代码调试等需要多步推理的任务中效果显著。

为什么 CoT 有效?大语言模型本质上是自回归的——每个生成的 token 都基于之前的所有 token。当模型被要求展示推理过程时,中间推理步骤实际上成为了后续推理的上下文,帮助模型保持推理链的连贯性。这就像人类在草稿纸上做数学题——写下中间步骤可以大幅降低出错率。

CoT 有两种实现方式:Zero-Shot CoT(只需在 Prompt 末尾加上"请逐步思考")和 Few-Shot CoT(在示例中展示完整的推理过程)。Few-Shot CoT 通常效果更好,因为它不仅告诉模型"要逐步思考",还示范了"如何逐步思考"。

python
# Chain of Thought Prompt 示例
def cot_math_prompt(problem: str) -> str:
    """CoT 数学推理 Prompt"""
    return f"""你是一个数学老师。请逐步解答以下问题,展示完整的推理过程。

示例:
问题:一个水池有进水管和出水管。进水管单独工作 6 小时可以注满水池,出水管单独工作 8 小时可以排空水池。如果两管同时打开,需要多长时间才能注满水池?

解答步骤:
1. 进水管每小时注水量 = 1/6 池
2. 出水管每小时排水量 = 1/8 池
3. 两管同时工作,每小时净注水量 = 1/6 - 1/8 = 4/24 - 3/24 = 1/24 池
4. 注满水池需要的时间 = 1 ÷ (1/24) = 24 小时
答案:24 小时

问题:{problem}

解答步骤:"""

# 测试
problem = "某商品先涨价 20%,再降价 20%,最终价格相比原价变化了多少?"
print(cot_math_prompt(problem))

CoT 的安全注意事项:模型的推理过程可能包含错误或不一致的逻辑。对于关键决策(如医疗、金融),务必对推理过程进行人工审核,不能仅看最终答案。

5ReAct 模式:推理与行动的交替循环

ReAct(Reasoning + Acting)是一种将推理和行动交替进行的 Prompt 设计模式。与纯 CoT 不同,ReAct 让模型在推理过程中可以调用外部工具(如搜索引擎、API、数据库),然后根据工具返回的结果继续推理。

ReAct 的工作流程:Thought(思考下一步做什么)→ Action(执行具体操作)→ Observation(观察工具返回结果)→ 重复直到得出结论。这个循环让模型既能"思考"又能"行动",极大地扩展了 LLM 的能力边界。

ReAct 的实际应用非常广泛:问答系统可以先搜索再回答;数据分析可以先查询数据库再解释结果;代码调试可以先运行测试再分析错误。

python
# ReAct 模式简易实现
class ReActAgent:
    """简易 ReAct Agent 实现"""
    
    def __init__(self, llm, tools: dict):
        self.llm = llm
        self.tools = tools
        self.max_iterations = 5
    
    def run(self, question: str) -> str:
        # ReAct Prompt 模板
        prompt = f"""请用以下格式回答问题:

Thought: 分析当前情况,决定下一步
Action: 使用工具(格式:tool_name[query])
Observation: 工具返回的结果
...(重复 Thought/Action/Observation)
Thought: 我已获得足够信息
Answer: 最终答案

可用工具:
- search: 搜索引擎查询
- calculate: 数学计算
- weather: 查询天气

问题:{question}
"""
        
        history = prompt
        for i in range(self.max_iterations):
            response = self.llm.generate(history)
            history += "
" + response
            
            # 解析 Action
            if "Action:" in response:
                action_line = response.split("Action:")[1].strip().split("
")[0]
                tool_name = action_line.split("[")[0]
                query = action_line.split("[")[1].rstrip("]")
                
                # 执行工具
                if tool_name in self.tools:
                    result = self.tools[tool_name](query)
                    history += f"
Observation: {result}"
                else:
                    history += f"
Observation: 未知工具: {tool_name}"
            else:
                break  # 没有 Action,说明已有答案
        
        # 提取 Answer
        if "Answer:" in history:
            return history.split("Answer:")[-1].strip()
        return history

# 使用示例
tools = {
    "search": lambda q: f"搜索结果: {q} → 找到相关信息",
    "calculate": lambda q: f"计算结果: {q} = {eval(q)}",
}
agent = ReActAgent(llm=None, tools=tools)  # 此处 llm 为示意
print("ReAct Agent 初始化完成")

6System Prompt 与角色工程

System Prompt 是对话开始前注入模型的"系统级指令",它定义了模型的行为边界、角色定位和输出风格。与 User Prompt 不同,System Prompt 通常不会被用户直接看到,但它对整个对话质量的影响最大。

角色工程(Role Engineering)是 System Prompt 设计的核心技术。通过精确的角色定义,你可以激活模型在特定领域的专业知识。例如,"你是一个有 10 年经验的安全工程师"和"你是一个安全专家"虽然意思相近,但前者会让模型倾向于提供更具体、更实战导向的建议。

设计 System Prompt 的关键原则:明确角色、设定边界、定义风格、规定格式、强调约束。一个好的 System Prompt 应该像一份精确的岗位说明书——任何接手的人(或模型)都能清楚地知道该做什么、不该做什么、以及怎么做。

python
# System Prompt 模板库
class SystemPromptTemplates:
    """不同场景的 System Prompt 模板"""
    
    CODE_REVIEWER = """你是一位资深代码审查工程师,拥有 15 年以上软件开发经验。
你的职责:
1. 识别代码中的 bug、安全漏洞和性能问题
2. 提出具体的改进建议,并说明原因
3. 对代码风格和规范给出评价
4. 始终保持建设性和尊重性

输出格式:
- 🟢 优点
- 🔴 问题(按严重程度排序)
- 💡 改进建议
- 📝 重构示例代码"""

    DATA_ANALYST = """你是一位数据科学家,擅长探索性数据分析和统计推断。
你的风格:
- 用数据说话,避免主观臆断
- 解释统计指标的实际含义
- 指出数据中的异常值和潜在偏差
- 建议下一步的分析方向"""

    TECHNICAL_WRITER = """你是一位技术文档撰写专家。
原则:
- 简洁明了,避免冗长
- 使用主动语态
- 每个段落只表达一个核心观点
- 代码示例必须有注释和说明"""

# 使用示例
def create_chat_completion(system_prompt: str, user_message: str):
    messages = [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_message}
    ]
    return messages  # 实际调用 LLM API

# 代码审查场景
messages = create_chat_completion(
    SystemPromptTemplates.CODE_REVIEWER,
    "请审查这段代码:\ndef get_data(url):\n    import urllib.request\n    response = urllib.request.urlopen(url)\n    return response.read()"
)
print(f"System: {messages[0]['content'][:80]}...")
print(f"User: {messages[1]['content']}")
角色类型System Prompt 关键词典型输出风格常见陷阱

代码审查员

资深工程师、15 年经验、建设性

结构化、有代码示例

过于苛刻,打击开发者

数据科学家

统计推断、用数据说话、偏差分析

数据驱动、谨慎结论

过度技术化,非专业人士看不懂

技术写手

简洁、主动语态、单段单观点

精炼、易读、结构化

过度简化,丢失技术细节

教师

耐心、循序渐进、举例说明

由浅入深、有练习题

进度太慢,效率低

产品经理

用户视角、商业价值、优先级

需求文档格式、有优先级

过度关注商业,忽略技术可行性

7Prompt 安全与常见陷阱

Prompt Engineering 不仅关乎技巧,还关乎安全。了解常见的陷阱和安全风险,才能写出健壮可靠的 Prompt。

注入攻击(Prompt Injection):攻击者通过在用户输入中嵌入恶意指令,试图覆盖或绕过 System Prompt。防御策略包括:使用分隔符明确区分指令和数据、对输入进行清洗和转义、避免在 Prompt 中暴露敏感信息。

幻觉(Hallucination):模型生成的内容看起来合理但实际上是错误的。减少幻觉的方法包括:提供准确的上下文、要求模型引用来源、对不确定的内容要求模型表达不确定性。

输出不一致性:同一个 Prompt 多次运行可能产生不同的结果。应对策略包括:设置 temperature=0 以获得确定性输出、使用结构化输出格式、对输出进行后处理验证。

  • 始终对用户输入进行清洗和转义,防止注入攻击

  • 使用 XML 标签或三引号明确区分指令和数据部分

  • 对关键输出进行格式验证和逻辑校验

  • 设置合理的 max_tokens 限制,防止输出过长

  • 记录 Prompt 版本和对应的输出,便于回溯和优化

  • 定期进行安全审计,测试 Prompt 的鲁棒性

  • 避免过度依赖单个 Prompt——关键场景使用多轮对话验证

安全红线:永远不要在 Prompt 中包含 API 密钥、数据库连接字符串、用户密码等敏感信息。使用环境变量或密钥管理服务存储这些凭证。

继续你的 AI 学习之旅

浏览更多 AI 知识库文章,或者探索 GitHub 上的优质 AI 项目