generative-ai-for-beginners 进阶提示工程:七大提示技巧、温度参数与 Self-Refine 代码改进实战
【免费下载链接】generative-ai-for-beginners21 Lessons, Get Started Building with Generative AI项目地址: https://gitcode.com/GitHub_Trending/ge/generative-ai-for-beginners
本文基于 generative-ai-for-beginners 课程第 05 课(进阶提示)整理成文,围绕提示工程(Prompt Engineering)的核心方法论展开:从解构一条简单提示词的上下文与输出限制,到零样本、少样本、思维链、生成式知识、由简入繁、自我精炼(Self-Refine)与产婆术(Maieutic)提示等七种可应用于任意 LLM 的技巧,并进一步讲解如何利用温度(temperature)参数在“多样化输出”与“确定性输出”之间做权衡。读完本文,你能够独立构造并优化结构化提示词,用温度参数控制输出的可预测性,并掌握用 Self-Refine 循环让 AI 助手迭代改进真实代码的完整流程。
前置回顾:提示工程在做什么
沿用课程第 04 课(提示工程基础,见 04-prompt-engineering-fundamentals/README.md)的结论:
提示_工程_是一个引导模型给出更相关回答的过程,方式是提供更有用的指令或上下文。
写提示词包含两个步骤:构造提示(提供相关上下文)与优化(逐步改进提示词)。本课(源文档为 translations/et/05-advanced-prompts/README.md,即第 05 课的翻译版本)的目标是:从“不断试各种提示词”进阶到“理解为什么某条提示词比另一条更好”,并学会构造可迁移到任意 LLM 的提示。
本课覆盖两大主题:
- 运用不同技巧扩展你的提示工程知识;
- 配置提示词以让输出产生变化(或不再变化)。
学完本节应能达到的学习目标:
- 运用能改善提示结果的提示工程技术;
- 执行“多样化”或“确定性”两种风格的提示。
提示工程:不只是“写一句话”
提示工程是创造“能产生期望结果”的提示的过程。它远不止写一段文本指令——提示工程并不是一门工程学科,而是一组为达成期望结果可以套用的技巧集合。
一条简单提示词的解剖
以一条基础提示为例:
Generate 10 questions on geography.(生成 10 个地理问题。)
这条看似简单的提示里,其实同时用到了两类技巧:
- 上下文(Context):你指定了主题必须是“地理”;
- 输出限制(Limiting the output):你要求问题数量不超过 10 个。
简单提示的局限
即便用了上述技巧,结果也不稳定,原因是:
- 主题过大:地理涵盖国家、首都、河流等,你不知道模型会聚焦到哪一块;
- 格式缺失:如果你想让问题以特定格式(如编号、JSON、表格)输出,这条提示完全没有约束。
可以看出,构造一条提示需要考虑的维度远不止“说要什么”。而生成式 AI 的能力也远不止回答简单问题,下面进入核心技术。
七种提示技巧
首先需要理解:提示(prompting)是 LLM 的一种涌现(emergent)属性——它不是模型内置的功能开关,而是我们在使用模型过程中发现并总结出来的能力。基于此,课程归纳了七种基础技巧:
| 技巧 | 一句话定义 |
|---|---|
| 零样本提示(Zero-shot) | 最基础形式:单条提示,模型仅凭训练数据作答 |
| 少样本提示(Few-shot) | 提供 1 个及以上示例,让模型“照着做” |
| 思维链(Chain-of-thought) | 教模型把问题拆成步骤、逐步推理 |
| 生成式知识(Generated knowledge) | 在提示中注入额外的事实或企业数据 |
| 由简入繁(Least-to-most) | 把大问题拆成子问题序列并按顺序执行 |
| 自我精炼(Self-refine) | 让模型批判自己的输出,再要求它改进 |
| 产婆术提示(Maieutic) | 要求模型逐一解释答案的各个部分,剔除不一致项,是一种自我精炼变体 |
零样本提示(Zero-shot prompting)
最简单的风格:只有一条提示,模型完全依赖自身训练数据回答。初学 LLM 时你几乎都在用这种形式。示例:
- 提示:“What is Algebra?”(什么是代数?)
- 回答:“Algebra 是研究数学符号及其运算规则的一门数学分支。”
没有任何示例、没有任何上下文注入,胜在简单直接;缺点是模型无法得知你偏好的格式、领域深度或风格。
少样本提示(Few-shot prompting)
在请求中附带若干任务相关的示例,帮助模型理解“输出应该长什么样”。示例:
- 提示:“Write a poem in the style of Shakespeare. Here are a few examples of Shakespearean sonnets: Sonnet 18: 'Shall I compare thee to a summer's day? Thou art more lovely and more temperate...' Sonnet 116: 'Let me not to the marriage of true minds Admit impediments. Love is not love Which alters when it alteration finds...' Sonnet 132: 'Thine eyes I love, and they, as pitying me, Knowing thy heart torment me with disdain,...' Now, write a sonnet about the beauty of the moon.”(用莎士比亚风格写一首关于月亮之美的十四行诗)
- 回答:“Upon the sky, the moon doth softly gleam, In silv'ry light that casts its gentle grace,...”
示例给 LLM 提供了期望输出的上下文、格式或文风。它帮助模型理解具体任务,从而生成更准确、更贴合的回答。注意示例数量不必多——1 到几个精心挑选的示例往往比一堆杂乱示例更有效。
思维链(Chain-of-thought)
思维链是本课最有代表性的技巧:通过多个步骤“带”着模型走,让它理解_如何_做一件事,而不只是_做什么_。先看一个反例——不用思维链:
- 提示:“Alice has 5 apples, throws 3 apples, gives 2 to Bob and Bob gives one back, how many apples does Alice have?”
- 回答:5
模型答 5,是错的。正确计算是 (5 - 3 - 2 + 1 = 1),正确答案为 1 个苹果。
那么如何教会 LLM 正确计算?应用思维链意味着三步:
- 给 LLM 一个相似的示例;
- 展示计算过程,演示如何一步步正确计算;
- 再给出原始提示。
组合后的提示如下:
- 提示:“Lisa has 7 apples, throws 1 apple, gives 4 apples to Bart and Bart gives one back: 7 - 1 = 6 6 - 4 = 2 2 + 1 = 3 Alice has 5 apples, throws 3 apples, gives 2 to Bob and Bob gives one back, how many apples does Alice have?”
- 回答:1
可以看到:我们写出了一条显著更长的提示——包含另一个示例、完整计算过程、然后才是原始问题——最终得到了正确答案 1。这就是思维链的威力:用一个“解题示范”把模型的推理轨道对齐。
生成式知识(Generated knowledge)
构造提示时,你常常想使用自己企业的数据:提示的一部分来自企业内容,另一部分才是你真正想问的问题。以保险业务为例,提示可以做成一个模板:
{{company}}: {{company_name}} {{products}}: {{products_list}} Please suggest an insurance given the following budget and requirements: Budget: {{budget}} Requirements: {{requirements}}提示通过模板构造,其中{{variable}}形式的变量会被企业 API 返回的真实值替换。变量替换后的提示如下:
Insurance company: ACME Insurance Insurance products (cost per month): - Car, cheap, 500 USD - Car, expensive, 1100 USD - Home, cheap, 600 USD - Home, expensive, 1200 USD - Life, cheap, 100 USD Please suggest an insurance given the following budget and requirements: Budget: $1000 Requirements: Car, Home, and Life insurance把这条提示交给 LLM,会得到类似回答:
Given the budget and requirements, we suggest the following insurance package from ACME Insurance: - Car, cheap, 500 USD - Home, cheap, 600 USD - Life, cheap, 100 USD Total cost: $1,200 USD问题出现了:它连 Life 保险也推荐了,而预算只有 1000 USD,总报价 1200 USD 已经超支。这说明需要优化提示,把“允许选什么”说得更清楚。经过若干次_试错_,得到改进后的提示:
Insurance company: ACME Insurance Insurance products (cost per month): - type: Car, cheap, cost: 500 USD - type: Car, expensive, cost: 1100 USD - type: Home, cheap, cost: 600 USD - type: Home, expensive, cost: 1200 USD - type: Life, cheap, cost: 100 USD Please suggest an insurance given the following budget and requirements: Budget: $1000 restrict choice to types: Car, Home关键改进点:为每个产品增加了type和cost字段标签,并在预算行后使用restrict(限制)这个关键词。此时回答变为:
Given the budget and requirements, we suggest the Car, Cheap insurance product which costs 500 USD per month.这个例子想说明的核心是:即便已经用上了“生成式知识”这样的基础技巧,绝大多数情况下仍需迭代优化提示才能得到期望结果——提示词是“调试对象”,不是一次写对的产品。
由简入繁(Least-to-most)
由简入繁提示的思路是:把一个大问题拆解为子问题,引导 LLM 逐步“攻克”它。适合数据科学这类多阶段任务。例如:
提示:How to perform data science in 5 steps?(如何用 5 个步骤完成数据科学工作?)
AI 助手回答:
- 收集数据(Collect data)
- 清洗数据(Clean data)
- 分析数据(Analyze data)
- 可视化数据(Plot data)
- 展示数据(Present data)
与思维链的区别在于:思维链通常靠“示范一个解题过程”来引导,由简入繁则是明确要求模型先分解、再按序执行子步骤,让模型自己规划拆解方案。
自我精炼(Self-refine):批判结果
对生成式 AI 和 LLM,你不能盲目信任输出,必须验证。毕竟 LLM 给出的只是“下一个最可能的词/句”,而不一定是正确答案。一个好办法就是让 LLM 自我批判,这正是 Self-refine 技巧。工作流程:
- 用初始提示要求 LLM 解决问题;
- LLM 作答;
- 你批判这个回答,并要求 AI 改进;
- LLM 再次作答,这次会考虑批判意见并提出自己的解决方案。
这个循环可以重复任意次。文档给出的完整示例:
提示:“Create a Python Web API with routes products and customers”(创建一个带 products 和 customers 路由的 Python Web API)
AI 回答:
import flask app = Flask(__name__) @app.route('/products') def products(): return 'Products' @app.route('/customers') def customers(): return 'Customers'提示:suggest 3 improvements of the above code(对上述代码提出 3 条改进建议)
AI 回答:
Sure, here are three suggestions to improve the code: Import the correct module: The first line should import Flask instead of flask. The correct import statement should be from flask import Flask. Add a main function: Add a if __name__ == '__main__': block to the end of the file to run the application when the file is executed. Use f-strings: Use f-strings to format the return statements in the route functions. For example, return 'Products' can be changed to return f'This is the {request.path} page'.改进后的代码:
from flask import Flask, request app = Flask(__name__) @app.route('/products') def products(): return f'This is the {request.path} page' @app.route('/customers') def customers(): return f'This is the {request.path} page' if __name__ == '__main__': app.run()可以看到,正是由于对第一版回答做了批判,第二版代码修正了导入错误、补齐了入口块并改进了返回语句。
产婆术提示(Maieutic prompting)
产婆术提示与 Self-refine 相似,但更强调让 LLM 解释自己:目标是通过追问减少输出的自相矛盾,从而确保答案正确。工作流:
- 让 LLM 回答问题;
- 针对答案的每个部分,要求 LLM 更深入地解释;
- 若发现不一致,丢弃不一致的部分;
- 重复第 2、3 步,直到所有部分都过一遍且你对答案满意。
示例:
提示:How can I create a crisis plan to mitigate a pandemic in 5 steps?(如何用 5 步制定缓解疫情的危机计划?) LLM 回答:
1. Identify the risks 2. Identify the stakeholders 3. Identify the resources 4. Identify the actions 5. Identify the communication channels它给出了 5 步,但如何确认这是对的?继续追问第一步:
提示:Explain the first step in more detail, what are the risks in detail with a pandemic?
The risks are the following: - Loss of life - Loss of business - Loss of reputation - Loss of customers - Loss of employees为进一步确认,追问第一个风险的细节,看回答是否与上文一致:
提示:In a Pandemic, which is the biggest risk and why?
The biggest risk is loss of life because it's the most important thing to protect.What are the two biggest risks in a pandemic?
The two biggest risks are loss of life and loss of business.此时 LLM 的回答保持了一致(“生命”与“商业”均为最高优先级风险),可以较有把握地进入下一步。但务必记住:不应盲目信任 LLM,始终要验证输出。
让输出产生变化:LLM 的非确定性
LLM 本质上是非确定性的:同一条提示每次运行都可能得到不同结果。例如提示:
“Generate code for a Python Web API”
第一次运行生成:
# Import necessary modules import flask from flask import request, jsonify # Create a Flask app app = flask.Flask(__name__) # Create a route for the API @app.route('/api', methods=['GET']) def api(): # Get the data from the request data = request.args # Process the data result = process_data(data) # Return the result as JSON return jsonify(result) # Function to process the data def process_data(data): # Do something with the data result = data + 1 # Return the result return result # Run the app if __name__ == '__main__': app.run()对同一条提示再次运行,得到一个明显不同的版本:
#import necessary packages import flask from flask import request, jsonify #create the Flask application app = flask.Flask(__name__) app.config["DEBUG"] = True #create a list of books books = [ {'id': 0, 'title': 'A Fire Upon the Deep', 'author': 'Vernor Vinge', 'first_sentence': 'The coldsleep itself was dreamless.', 'year_published': '1992'}, {'id': 1, 'title': 'The Ones Who Walk Away From Omelas', 'author': 'Ursula K. Le Guin', 'first_sentence': 'With a clamor of bells that set the swallows soaring, the Festival of Summer came to the city Omelas, bright-towered by the sea.', 'published': '1973'}, {'id': 2, 'title': 'Dhalgren', 'author': 'Samuel R. Delany', 'first_sentence': 'to wound the autumnal city.', 'published': '1975'} ] #create an endpoint for the API @app.route('/', methods=['GET']) def home(): return '''<h1>Books API</h1> <p>A prototype API for retrieving books.</p>''' #create an endpoint to return all books @app.route('/api/v1/resources/books/all', methods=['GET']) def api_all(): return jsonify(books) #create an endpoint to return a single book @app.route('/api/v1/resources/books', methods=['GET']) def api_id(): #check if an ID was provided as part of the URL #if ID is provided, assign it to a variable #if no ID is provided, display an error if 'id' in request.args: id = int(request.args['id']) else: return "Error: No id field provided. Please specify an id." #create an empty list for our results results = [] #loop through the data and match results that fit the requested ID #IDs are unique, but other fields might return many results for book in books: if book['id'] == id: results.append(book) #use the jsonify function from Flask to convert our list of #Python dictionaries to the JSON format return jsonify(results) app.run()多样化的输出算问题吗?
取决于你的目的:如果你需要特定的、可预期的回答,那就是问题;如果你能接受多样化输出(比如“生成任意 3 个地理问题”),则无所谓。
用温度(temperature)控制输出变化
如果希望输出更可控、更确定(deterministic),可以调节温度参数。温度是 0 到 1 之间的值:0 最确定,1 最多样,默认值 0.7。
把温度设为 0.1,对同一提示“Generate code for a Python Web API”运行两次:
第一次:
#import necessary libraries import flask from flask import request, jsonify #create an instance of the Flask class app = flask.Flask(__name__) #create an endpoint for the API @app.route('/api/v1/endpoint', methods=['GET']) def api_endpoint(): #get the request data data = request.get_json() #process the data result = process_data(data) #return the result return jsonify(result) #function to process the data def process_data(data): #process the data result = {'result': 'success'} #return the result return result #run the Flask app if __name__ == '__main__': app.run()第二次:
#import necessary libraries import flask from flask import request, jsonify #create a Flask app app = flask.Flask(__name__) #create a route for the API @app.route('/api', methods=['GET']) def api_call(): #get the data from the request data = request.get_json() #process the data result = process_data(data) #return the result return jsonify(result) #define the process_data function def process_data(data): #do something with the data result = data + 1 #return the result return result #run the app if __name__ == '__main__': app.run()两个输出之间只有极小差异——结构、路由、处理函数几乎一致,只有函数命名之类的细节不同。
反过来,把温度设为 0.9,同样运行两次:
第一次:
# Import necessary libraries import flask from flask import request, jsonify # Create a Flask app app = flask.Flask(__name__) # Create a route for the API @app.route('/api', methods=['GET']) def api_call(): # Get the data from the request data = request.args # Process the data result = process_data(data) # Return the result return jsonify(result) # Function to process the data def process_data(data): # Do something with the data result = data + 1 # Return the result return result # Run the app if __name__ == '__main__': app.run()第二次:
import flask from flask import request, jsonify # create the Flask app app = flask.Flask(__name__) app.config['DEBUG'] = True # create some test data books = [ {'id': 0, 'title': 'A Fire Upon The Deep', 'author': 'Vernor Vinge', 'first_sentence': 'The coldsleep itself was dreamless.', 'year_published': '1992'}, {'id': 1, 'title': 'The Ones Who Walk Away From Omelas', 'author': 'Ursula K. Le Guin', 'first_sentence': 'With a clamor of bells that set the swallows soaring, the Festival of Summer came to the city Omelas, bright-towered by the sea.', 'published': '1973'}, {'id': 2, 'title': 'Dhalgren', 'author': 'Samuel R. Delany', 'first_sentence': 'to wound the autumnal city.', 'published': '1975'} ] # create an endpoint @app.route('/', methods=['GET']) def home(): return '''<h1>Welcome to our book API!</h1>''' @app.route('/api/v1/resources/books(原课中该输出即在此处截断,恰好说明高温度下模型“跑题”到了一个图书 API 的实现。)
可以看到,0.9 温度下的两次结果差异极大:一次仍是简单的单端点 API,另一次直接演化成了带测试数据的 Books API。课程原表可以归纳为:
| 温度 | 输出特征 |
|---|---|
| 0.1 | 两次输出高度一致,仅命名级差异,适合需要稳定复现的任务 |
| 默认 0.7 | 平衡确定性与多样性 |
| 0.9 | 两次输出结构完全不同,适合头脑风暴、创意生成 |
注意:除了温度,还有 top-k、top-p、重复惩罚(repetition penalty)、长度惩罚(length penalty)、多样性惩罚(diversity penalty)等参数可以调节输出,但课程明确将其置于本课范围之外,此处只作了解即可。
提示的最佳实践
技巧之外,课程还总结了几条在提示 LLM 时值得遵循的最佳实践——随着使用增多,你会逐步形成自己的风格:
- 明确上下文(Specify context):上下文很重要,你能指定的领域、主题越精确越好;
- 限制输出:想要特定条数或特定长度,就明确写出来;
- 同时说明“要什么”和“要成什么样”:例如 “Create a Python Web API with routes products and customers, divide it into 3 files”;
- 使用模板(Use templates):经常需要用企业数据充实提示,模板中用变量占位、运行时替换为真实数据(与上文“生成式知识”的
{{variable}}模板一脉相承); - 拼写正确(Spell correctly):LLM 也许能容错,但拼写正确会拿到更好的回答。
实战任务:用 Self-Refine 改进一个 Flask API
课程给出的练习代码与仓库中 05-advanced-prompts/python/aoai-assignment.py 完全一致:
from flask import Flask, request app = Flask(__name__) @app.route('/') def hello(): name = request.args.get('name', 'World') return f'Hello, {name}!' if __name__ == '__main__': app.run()任务要求:使用 AI 助手(如 GitHub Copilot 或 ChatGPT),对这段代码应用 “self-refine” 技巧进行改进。
提示:把提示写成“要求改进代码”的形式,并限定改进条数(如“给出 3 条改进”)是个好主意;也可以指定改进方向,例如架构、性能、安全等方面。
参考答案:Self-Refine 循环的真实产物
仓库中的 05-advanced-prompts/python/aoai-solution.py 就是一次 Self-Refine 循环的落盘结果。文件开头保留了 AI 给出的三条改进建议(以注释形式):
- 增加输入校验,防止恶意输入被服务器处理(建议使用 flask-wtf 校验并清洗用户输入);
- 使用环境变量存放数据库凭据、API 密钥等敏感信息,避免硬编码;
- 实现错误处理,通过
@app.errorhandler()装饰器向用户返回有意义的错误信息。
随后的代码正是按建议改造后的版本,并且进一步落实了安全加固:
import os from flask import Flask, render_template_string from flask_wtf import FlaskForm from wtforms import StringField, SubmitField from wtforms.validators import DataRequired, Length, Email from markupsafe import escape app = Flask(__name__) # SECURITY: Load secret key from environment variable instead of hardcoding app.config['SECRET_KEY'] = os.environ.get('FLASK_SECRET_KEY', os.urandom(32)) class HelloForm(FlaskForm): name = StringField('Name', validators=[DataRequired(), Length(min=3)]) email = StringField('Email', validators=[DataRequired(), Email()]) submit = SubmitField('Submit')路由处理中使用escape()防 XSS,并返回 400 错误处理器:
@app.route('/', methods=['GET', 'POST']) def hello(): form = HelloForm() if form.validate_on_submit(): # SECURITY: Use escape() to prevent XSS attacks safe_name = escape(form.name.data) safe_email = escape(form.email.data) return f'Hello, {safe_name} ({safe_email})!' # SECURITY: Use Flask's render_template_string for proper escaping return render_template_string(FORM_TEMPLATE, form=form) @app.errorhandler(400) def bad_request(error): return 'Bad request', 400 if __name__ == '__main__': app.run()这个文件很好地印证了本课两个观点:一是 Self-Refine“批判—改进”循环能产出可落地的安全加固(输入校验、XSS 转义、环境变量管理密钥、错误处理);二是“限定改进方向/条数”的最佳实践让改进结果聚焦、可控。
仓库还提供了 JavaScript 版本的同一练习:05-advanced-prompts/javascript/assignment.js 是初始的 Express 代码,05-advanced-prompts/javascript/solution.js 则完整记录了两次提示及其产物——第一次提示“find 3 improvements to the code and apply”(找出 3 处改进并应用,结果包括 ES module 化、移除未使用参数、改进日志文案),第二次提示“improve the code and make it more secure”(提升安全性,结果引入环境变量、express-validator 输入校验、HTTPS 证书加载与路径校验)。两版答案互为印证:Self-Refine 的效果与你的提示措辞、限定条件直接相关。
知识检查
为什么我要使用思维链(chain-of-thought)提示?下面给出 1 个正确答案和 2 个错误答案:
- 为了教 LLM 如何解决问题。
- B:为了教 LLM 在代码中查找错误。
- C:为了指示 LLM 想出不止一种解决方案。
答案:1。因为思维链的核心是“向 LLM 展示如何解决问题”——给它一系列步骤,以及相似问题及其解法示范(如上文苹果问题中“7 - 1 = 6 / 6 - 4 = 2 / 2 + 1 = 3”的完整演算)。
挑战
你在练习中刚用过 Self-Refine 技巧。现在挑一个你自己写的程序,先想清楚希望对它做哪些改进,然后用 Self-Refine 技巧让 AI 助手落实这些变更——你认为结果变好了还是变坏了?把“改进前后”的差异记录下来,这正是评估提示技巧价值的最佳方式。
延伸阅读
- 本课源文档(爱沙尼亚语翻译):translations/et/05-advanced-prompts/README.md
- 英文原版第 05 课:05-advanced-prompts/README.md
- 第 04 课 提示工程基础:04-prompt-engineering-fundamentals/README.md
- 练习代码与答案:05-advanced-prompts/python/aoai-assignment.py、05-advanced-prompts/python/aoai-solution.py、05-advanced-prompts/javascript/solution.js
- 下一课:应用本课的提示工程技术构建文本生成应用,见 06-text-generation-apps/README.md
【免费下载链接】generative-ai-for-beginners21 Lessons, Get Started Building with Generative AI项目地址: https://gitcode.com/GitHub_Trending/ge/generative-ai-for-beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考