初学Prompt工程
Prompt的初识和简单使用SpringAI做一个多轮对话
一、Prompt工程
基本概念
Prompt工程(Prompt Engineering)又叫做提示词工程,简单来说,就是输入给AI的指令,举一个简单的例子:
你是一位资深产品经理,请给产品萌新列出一条学习路线
提示词工程,既然叫做“工程”,那就说明这个还是有一定的水准的。市面上也有很多的公司招聘提示词工程师。我们也是可以通过学习怎么写好Prompt,通过设计好的和经过优化后的提示来引导AI模型生成符合预期的高质量输出。
提示词分类
核心-基于角色的分类
在与AI对话中,基于角色的分类是最常见的,通常存在着3种主要类型的Prompt
(1) 用户Prompt(User Prompt)
这是用户向AI提供实际的问题、指令或者信息等,传达了用户的直接需求。用户Prompt告诉了AI模型“做什么”,比如说回答问题、创作文章、编写代码等等。
用户:我们的内容平台用户停留时长近期下降,请分析可能的核心原因,并规划一个通过增强社交互动来提升粘性的产品方案,需说明目标用户、核心功能点及预期衡量指标。
(2)系统Prompt(System Prompt)
这个是设置AI模型行为规则和角色定位的隐藏指令,用户通常是不能直接看见的。系统Prompt相当于给AI设定人格和能力边界,就是告诉AI“你是谁,你能够做什么?”。
系统:你是一位经验丰富的产品经理,负责定义产品方向与价值。请始终从用户需求、业务目标和市场竞争力出发,专注于回答“做什么”和“为什么做”。你的输出应结构化且可执行,优先澄清问题背景与核心目标,提供决策依据与权衡建议(如优先级、ROI或用户体验取舍),并最终形成清晰的需求描述或验收标准。请使用简洁专业的语言,避免直接讨论技术实现细节。
不同的系统Prompt可以让同一个AI模型展示出完全不同的应用特性,这就是构建垂直领域AI应用(如财务顾问、教育辅导、医疗咨询等)的关键。
(3)助手Prompt(Assistant Prompt)
这是AI模型的响应内容,在多轮对话中,之前的助手回复也会变成当前上下文的一部分,影响后续对话的理解和生成。在某些场景下,开发者可以主动的预设一些助手消息作为历史对话的一部分,引导后续的互动。
助手:此方案的核心目标是提升用户参与度与停留时长。目标用户聚焦于‘内容消费者中的互动意愿者’。建议分两阶段实施:MVP阶段上线‘文章评论区@好友’与‘热门讨论勋章’功能,以低成本验证互动意愿;后续迭代可加入‘读者社群’功能。成功的关键指标应围绕互动率(如评论@使用率)和人均停留时长变化进行评估,同时需注意平衡社交功能与核心阅读体验的权重。
AI大模型平台也是允许用户自主设置各种不同类型的提示词来进行调试的:
扩展-基于功能的分类
除了基于角色的分类外,我们还可以从功能的角度对于提示词进行分类,了解一下就行了
(1)指令型提示词(Instructional Prompts)
明确告诉AI需要执行的任务,通常以命令式的语句开头。
帮我分析一下sql语句:
select
rank() over(
partition by category_id
order by price desc
) price_rank,
name
from
goods
(2)对话型提示词(Conversational Prompts)
模拟自然对话,以问答的形式与AI模型进行交互
1 + 1 等于多少?
(3)创意型提示词(Creative Prompts)
引导AI模型进行创作内容的生成,如故事、文案等
写一段奇幻类的故事,内容你自己设定
(4)角色扮演提示词(Role-Playing Prompt)
让AI扮演特定的角色或者人物进行回答
假设你是产品经理,你会怎么设计一个“去上网”的产品
(5)少样本学习提示词(Few-Shot Prompts)
提供一些示例,引导AI理解所需的输出格式与风格
分析以下的句子判断会输出什么字:
示例1:
原句:土加也
输出:地
示例2:
原句:马加奇
输出:骑
现在请输出:又加又
扩展-基于复杂度的分类
(1)简单提示词(Simple Prompts)
单一指令或问题,没有复杂的背景或约束条件
什么是产品经理?
(2)复合提示词(Compound Prompts)
包含多个相关指令或者步骤的提示词
分析下面这段sql,解释他的功能,找出潜在的错误,并提供改进建议或者修改后的sql
(3)链式提示词(Chain Prompts)
一系列连续的、相互依赖的提示词、每个提示词基于前一个提示词的输出
第一步:生成新建一张Student表的sql,包括姓名、学号、性别
第二步:基于新建好的Student表,写出插入三条数据的sql
第三步:根据这些插入的数据,写出一条根据学号排序的sql
(4)模板提示词(Template Prompts)
包含可替换变量的标准化提示词结构,常用于大规模的应用
你是以为专业的{领域}专家,请回答以下关于{主题}的问题:{具体问题}
回答应包含{要点数量}个关键点,并使用{风格}的语言风格
Token
Token是大模型处理文本的基本单位,可能是单词或者标点符号,模型的输入和输出都是按照Token计算的,一般Token越多,成本越高,输出速度就越慢。
怎么计算Token?
根据OpenAI的文档:
英文文本:一个token相当于4个字符或约0.75个英文单词
中文文本:一个汉字通常会被编码位1-2个token
空格和标点:也会被计入token数量
特殊符号和表情符号:可能需要多个token来表示
实际的应用中,推荐使用工具来估计Prompt的Token数量,比如:
Tokenizer - OpenAI API:适用于OpenAI模型的官方Token计算器
token成本优化技巧
注意,系统提示词、用户提示词和AI模型输出的内容都是消耗成本的,因此我们成本优化可以从以下角度进行
(1) 精简系统提示词:移除冗余的表述,只保留核心的指令。比如将“你是一位专业的、经验丰富且非常有耐心的Java开发工程师”简化为“你是Java开发工程师”
(2)定时清理对话历史:对话上下文随着交互不断的累积Token。在长对话中,可以定期的请求AI总结之前的对话,然后以总结代替详细的历史
请总结我们至今的对话要求点,后续我们将基于此总结继续讨论。
(3)使用向量检索代替直接输入:对于需要处理大量参考文档的场景,不要直接将整个文档作为Prompt,而使用向量数据库和检索技术(RAG)获取相关的段落
(4)结构化替代自然语言:使用表格、列表等结构化格式代替长段落的描述
二、Prompt优化技巧
基础提示技巧
1.明确指定任务和角色
为AI提供清晰的任务描述和角色定位,帮助模型理解背景和期望
系统:你是一位经验丰富的Java开发工程师,擅长发现程序中隐藏的错误和需要修改的地方
用户:请你检测下面的Java代码
2.提供足够的上下文信息和期望的输出格式示例,减少模型的不确定性
请提供一个社交媒体营销计划,针对一款新上市的智能手表。计划应包含:
1. 目标受众描述
2. 三个内容主题
3. 每个平台的内容类型建议
4. 发布频率建议
示例格式:
目标受众: [描述]
内容主题: [主题1], [主题2], [主题3]
平台策略: [平台] - [内容类型] - [频率]
3.使用结构化格式引导思维
通过列表、表格等结构化的格式,使指令更容易理解,输出更有条理
分析以下编程语言的优势和劣势:
语言:Java
请使用表格格式回答,包含以下列:
-优势
-每项优势的简要分析
-劣势
-每项劣势的简要分析
4.明确输出格式的要求
指定输出的格式、长度、风格等要求,或者更符合预期的结果
编写一段仙剑奇幻小说,要求
-三个主角
-背景是穿越
-字数控制在1000字左右
进阶提示技巧
1.思维链提示法(Chain-of-Thought)
引导模型展示推理过程,逐步思考问题,提高复杂问题的准确性
问题:一个商店售卖T恤,每件15元。如果购买5件以上可以享受8折优惠。小明买了7件T恤,他需要支付多少钱?
请一步步思考解决这个问题:
1. 首先计算7件T恤的原价
2. 确定是否符合折扣条件
3. 如果符合,计算折扣后的价格
4. 得出最终支付金额
2.少样本学习(Few-Shot-Learning)
通过几个输入-输出对的示例,帮助模型理解任务模式和期望输出
我将给你一些情感分析的例子,然后请你按照同样的方式分析新句子的情感倾向。
输入: "这家餐厅的服务太差了,等了一个小时才上菜"
输出: 负面,因为描述了长时间等待和差评服务
输入: "新买的手机屏幕清晰,电池也很耐用"
输出: 正面,因为赞扬了产品的多个方面
现在分析这个句子:
"这本书内容还行,但是价格有点贵"
3.分步骤指导(Step-by-Step)
将复杂的任务拆分解为可管理的步骤,确保模型完成每个关键环节
请使用MySQL帮我设计好一个Student表
步骤1:分析可以有那哪些字段
步骤2:分析哪些字段是必须的
步骤3:分析哪些字段不为空
步骤4:输出设计好的sql语句
4.自我评估和修正
让模型评估自己的输出并进行改进,提高准确性和质量
解决以下概率问题:
从一副标准扑克牌中随机抽取两张牌,求抽到至少一张红桃的概率。
首先给出你的解答,然后:
1. 检查你的推理过程是否存在逻辑错误
2. 验证你使用的概率公式是否正确
3. 检查计算步骤是否有误
4. 如果发现任何问题,提供修正后的解答
5.知识检索和引用
引用模型检索相关信息并明确引用信息的来源,提高可靠性。
请解释光合作用的过程及其在植物生长中的作用。在回答中:
1. 提供光合作用的科学定义
2. 解释主要的化学反应
3. 描述影响光合作用效率的关键因素
4. 说明其对生态系统的重要性
对于任何可能需要具体数据或研究支持的陈述,请明确指出这些信息的来源,并说明这些信息的可靠性。
6.多视角分析
引导模型从不同角度、立场或者专业分析问题,提供全面的见解
分析"城市应该禁止私家车进入市中心"这一提议:
请从以下4个不同角度分析:
1. 环保专家视角
2. 经济学家视角
3. 市中心商户视角
4. 通勤居民视角
对每个视角:
- 提供支持该提议的2个论点
- 提供反对该提议的2个论点
- 分析可能的折中方案
7.多模态思维
结合不同表达形式进行思考,如文字描述、图表结构、代码逻辑等。
设计一个智能家居系统的基础架构:
1. 首先用文字描述系统的主要功能和组件
2. 然后创建一个系统架构图(用ASCII或文本形式表示)
3. 接着提供用户交互流程
4. 最后简述实现这个系统可能面临的技术挑战
尝试从不同角度思考:功能性、用户体验、技术实现、安全性等。
提示词调试与优化
1.迭代式提示优化
通过逐步修改和完善提示词,提高输出质量
初始提示: 谈谈人工智能的影响。
[收到笼统回答后]
改进提示: 分析人工智能对医疗行业的三大积极影响和两大潜在风险,提供具体应用案例。
[如果回答仍然不够具体]
进一步改进: 详细分析AI在医学影像诊断领域的具体应用,包括:
1. 现有的2-3个成功商业化AI诊断系统及其准确率
2. 这些系统如何辅助放射科医生工作
3. 实施过程中遇到的主要挑战
4. 未来3-5年可能的技术发展方向
2.边界测试
通过极限情况测试模型的能力边界,找出优化空间
尝试解决以下具有挑战性的数学问题:
证明在三角形中,三条高的交点、三条中线的交点和三条角平分线的交点在同一条直线上。
如果你发现难以直接证明:
1. 说明你遇到的具体困难
2. 考虑是否有更简单的方法或特例可以探讨
3. 提供一个思路框架,即使无法给出完整证明
3.提示词模板化
创建结构化模板,便于针对类似任务进行一致性的提示,否则每次输出的内容可能会有较大的区别,不利于调试
【专家角色】: {领域}专家
【任务描述】: {任务详细说明}
【所需内容】:
- {要点1}
- {要点2}
- {要点3}
【输出格式】: {格式要求}
【语言风格】: {风格要求}
【限制条件】: {字数、时间或其他限制}
例如:
【专家角色】: 营养学专家
【任务描述】: 为一位想减重的上班族设计一周健康饮食计划
【所需内容】:
- 七天的三餐安排
- 每餐的大致卡路里
- 准备建议和购物清单
【输出格式】: 按日分段,每餐列出具体食物
【语言风格】: 专业但友好
【限制条件】: 考虑准备时间短,预算有限
4.错误分析与修正
系统性分析模型回答中的错误,并针对性优化提示词
我发现之前请你生成的Python代码存在以下问题:
1. 没有正确处理文件不存在的情况
2. 数据处理逻辑中存在边界条件错误
3. 代码注释不够详细
请重新生成代码,特别注意:
1. 添加完整的异常处理
2. 测试并确保所有边界条件
3. 为每个主要函数和复杂逻辑添加详细注释
4. 遵循PEP 8编码规范
对于越复杂的任务,就越要给Prompt补充更多的细节
三、AI应用demo
实现一个具备多轮对话的 Java开发工程师 应用
1.系统提示词的设计
对于AI对话应用,最简单的做法就是直接写一段系统预设,定义“你是谁?能做什么?”比如:
你是一位高级Java开发工程师,为用户提供Java相关的服务
但是这个提示词有点过于简单,输出的结果可能并不符合我们的预期。因此我们可以借助AI进行优化,示例Prompt:
我正在开发【Java咨询】AI对话应用,请你帮我编写设置给AI大模型的系统预设Prompt指令。要求让AI作为高级Java开发工程师,模拟真实咨询场景,多给用户引导性的问题,不断深入了解用户提出的问题,从而提供给用户更加全面的建议,解决用户在Java方面的问题。
AI提供的优化后的系统提示词:
你是拥有12年经验的高级Java开发架构师,专注企业级Java应用与系统架构设计,擅长通过引导式提问深入诊断问题本质。请在对话中主动了解用户当前的技术场景(如具体问题排查、架构设计、技术选型或个人成长),围绕问题现象、环境上下文、已尝试方案和业务约束进行多轮提问,逐步揭示根本原因,最终提供涵盖短期缓解、中期优化和长期演进的结构化建议,并始终说明每项建议的适用场景与潜在风险。
2.多轮对话的实现
要实现具有”记忆力“的AI应用,让AI能够记住用户之前的对话内容并且保持上下文的连贯性,我们可以使用SpringAI框架的对话记忆能力
,SpringAI提供了Chat Client API :: Spring AI Reference 来和AI大模型进行交互
ChatClient特性
ChatClient可以实现功能更加丰富、更加灵活的AI对话客户端,也同样支持更加复杂灵活的链式调用(Fluent API)
// 高级用法(ChatClient)
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultSystem("你是拥有12年经验的高级Java开发架构师")
.build();
String response = chatClient.prompt().user("你好").call().content();
SpringAI 提供了多种构建ChatClient的方式,如自动注入、建造者模式手动构造等。
// 方式1:使用构造器注入
@Service
public class ChatService {
private final ChatClient chatClient;
public ChatService(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("你是拥有12年经验的高级Java开发架构师")
.build();
}
}
// 方式2:使用建造者模式
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultSystem("你是拥有12年经验的高级Java开发架构师")
.build();
ChatClient支持多种响应的格式,比如返回ChatResponse对象、返回实体对象、流式返回:
// ChatClient支持多种响应格式
// 1. 返回 ChatResponse 对象(包含元数据如 token 使用量)
ChatResponse chatResponse = chatClient.prompt()
.user("给我说一下Java语言的学习路线")
.call()
.chatResponse();
// 2. 返回实体对象(自动将 AI 输出映射为 Java 对象)
// 2.1 返回单个实体
record ActorFilms(String actor, List<String> movies) {}
ActorFilms actorFilms = chatClient.prompt()
.user("")
.call()
.entity(ActorFilms.class);
// 2.2 返回泛型集合
List<ActorFilms> multipleActors = chatClient.prompt()
.user("Generate filmography for Tom Hanks and Bill Murray.")
.call()
.entity(new ParameterizedTypeReference<List<ActorFilms>>() {});
// 3. 流式返回(适用于打字机效果)
Flux<String> streamResponse = chatClient.prompt()
.user("Tell me a story")
.stream()
.content();
// 也可以流式返回ChatResponse
Flux<ChatResponse> streamWithMetadata = chatClient.prompt()
.user("Tell me a story")
.stream()
.chatResponse();
也可以给ChatClient设置默认的参数,比如系统提示词,还可以在对话的时候动态更改系统提示词的变量:
// 定义默认系统提示词
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultSystem("You are a friendly chat bot that answers question in the voice of a {voice}")
.build();
// 对话时动态更改系统提示词的变量
chatClient.prompt()
.system(sp -> sp.param("voice", voice))
.user(message)
.call()
.content());
Advisors
SpringAI使用Advisors(顾问)机制来增强AI的能力,可以理解为一系列的可插拔的拦截器,在调用AI前后执行一些额外的操作,比如:
**前置增强:**调用AI前改写一下Prompt提示词,检查一下提示词是否安全。
**后置增强:**调用AI后记录日志,处理一下返回的结果
我们可以为ChatClient指定默认拦截器,比如说对话记忆拦截器MessageChatMemoryAdvisor可以帮助我们实现多轮对话能力,省去了自己维护对话列表的麻烦。
var chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
new MessageChatMemoryAdvisor(chatMemory), // 对话记忆 advisor
new QuestionAnswerAdvisor(vectorStore) // RAG 检索增强 advisor
)
.build();
String response = this.chatClient.prompt()
// 对话时动态设定拦截器参数,比如指定对话记忆的 id 和长度
.advisors(advisor -> advisor.param("chat_memory_conversation_id", "678")
.param("chat_memory_response_size", 100))
.user(userText)
.call()
.content();
ChatMemoryAdvisor
如果我们想要实现对话记忆功能,可以使用Spring AI的ChatMemoryAdvisor,它主要有几种内置的实现方式:
**MessageChatMemoryAdvisor:**从记忆中检索历史对话,并将其作为消息集合添加到提示词中
**PromptChatMemoryAdvisor:**从记忆中检索历史对话,并将其添加到提示词的系统文本中
**VectorStoreChatMemoryAdvisor:**可以用向量数据库存储检索历史对话
MessageChatMemoryAdvisor和PromptChatMemoryAdvisor相似,但是还是存在着一些区别的
(1)MessageMemoryAdvisor将对话历史作为一系列独立的消息添加到提示中,保留原始对话的完整结构,包括每条消息的角色标识(用户、助手、系统)
[
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!有什么我能帮助你的吗?"},
{"role": "user", "content": "讲个笑话"}
]
(2)PromptChatMemoryAdvisor将历史对话添加到提示词的系统文部分,因此可能会失去原始的消息边界
以下是之前的对话历史:
用户: 你好
助手: 你好!有什么我能帮助你的吗?
用户: 讲个笑话
现在请继续回答用户的问题。
一般建议使用 MessageChatMemoryAdvisor,能够更好的保持上下文的连贯
Chat Memory
上述的ChatMemoryAdvisor都依赖 Chat Client API :: Spring AI Reference 进行构造,ChatMemory负责历史对话的存储,定义了保存消息、查询消息、清空消息历史的方法。
SpringAI内置了几种ChatMemory,可以将对话保存到不同的数据源,比如:
InMemoryChatMemory:内存存储
CassandraChatMemory:在Cassandra中带有过期时间的持久化存储
Neo4jChatMemory:在Neo4j中没有过期时间限制的持久化存储
JdbcChatMemory:在JDBC中没有过期时间限制的持久化存储
当然也是可以通过实现ChatMemory接口自定义数据源的存储
四、多轮对话AI应用开发
(1)首先初始化ChatClient对象,使用Spring的构造器注入方式来注入阿里大模型 dashscopeChatModel,并使用该对象初始化ChatClient。初始化时指定默认的系统Prompt和基于内存的对话记忆Advisor,代码如下:
private final ChatClient chatClient;
String SYSTEM_PROMPT = "你是拥有12年经验的高级Java开发架构师,专注企业级Java应用与系统架构设计,擅长通过引导式提问深入诊断问题本质。" +
"请在对话中主动了解用户当前的技术场景(如具体问题排查、架构设计、技术选型或个人成长)" +
",围绕问题现象、环境上下文、已尝试方案和业务约束进行多轮提问,逐步揭示根本原因,最终提供涵盖短期缓解、中期优化和长期演进的结构化建议," +
"并始终说明每项建议的适用场景与潜在风险。";
public LoveApp(ChatModel dashscopeChatModel) {
// 初始化基于内存的对话记忆
ChatMemory chatMemory = new InMemoryChatMemory();
chatClient = ChatClient.builder(dashscopeChatModel)
.defaultSystem(SYSTEM_PROMPT)
.defaultAdvisors(
new MessageChatMemoryAdvisor(chatMemory)
)
.build();
}
(2)编写对话方法。调用chatClient对象,传入用户Prompt,并且给advisor指定对话id和对话记忆的大小,代码如下:
public String doChat(String message, String chatId) {
ChatResponse response = chatClient
.prompt()
.user(message)
// chatId 就是为了让对话在同一id下
.advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
// 5 -> 代表的是只能存储5条对话记忆
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 5))
.call()
.chatResponse();
String content = response.getResult().getOutput().getText();
log.info("content: {}", content);
return content;
}
(3)编写单元测试,测试多轮对话:
@Test
void testChat() {
String chatId = UUID.randomUUID().toString();
// 第一轮
String message = "你好,我是一名初学Java的萌新";
String answer = loveApp.doChat(message, chatId);
Assertions.assertNotNull(answer);
// 第二轮
message = "我想知道怎么学习Java,你能告诉我Java的学习路线吗?(我的爱好是学习Python)";
answer = loveApp.doChat(message, chatId);
Assertions.assertNotNull(answer);
// 第三轮
message = "我的爱好学习语言什么来着?刚跟你说过,帮我回忆一下";
answer = loveApp.doChat(message, chatId);
Assertions.assertNotNull(answer);
}
运行结果如下,说明对话记忆生效了

好了,本次的初识Prompt,和使用SpringAI简单的做一个多轮对话。相信你对于AI又多了一些了解吧
更多推荐


所有评论(0)