本章导读:你向AI提问的方式,直接决定了它回答的质量。就像与人沟通一样,把需求表达得越清楚,对方理解得就越准确。在Spring AI中,提示词的设计和构造有着一套完整的体系——从基础的四种消息角色,到动态的提示模板,再到工程化的最佳实践。本章将带你系统掌握这些知识,让你从“随意提问”进阶到“专业引导”。
4.1 提示词的本质
4.1.1 什么是Prompt
Prompt(提示词)是我们与AI沟通的唯一方式。你可以把它理解为给AI下达的指令或提出的问题——就像和人沟通一样,你说得越清楚,对方理解得就越准确,Prompt写得好不好,直接决定了AI输出的质量。
一个有效的Prompt并非简单的提问,而是指令与上下文的巧妙融合。它告诉模型你期望它做什么(指令),并为其提供完成任务所需的背景信息(上下文)。模型接收到Prompt后,会基于其庞大的知识库和对语言的理解,生成符合你指令和上下文的输出。
从API层面来看,Spring AI中的Prompt并非简单的字符串,而是一个容器。Prompt类充当一系列有组织的Message对象和请求ChatOptions的容器。每条Message在提示中都包含一个独特的角色,其内容和意图不同。这些角色可以包含各种元素,从用户查询到AI生成的对相关背景信息的响应。这种安排支持与AI模型进行复杂而详细的交互。
4.1.2 Prompt的组成要素
一个完整的Prompt在Spring AI中由两个核心部分组成:
消息列表(
List<Message>):包含系统消息、用户消息、助手消息等,每条消息都有特定的角色。ChatOptions:控制模型行为的参数,如
temperature、maxTokens等。
4.1.3 四种Message角色详解
在Spring AI中,消息被抽象为几个类,每种类型对应对话中的一个特定角色,“同一句话,放在不同消息角色里,效果可能完全不同”——Prompt的本质不是提问,而是精确表达任务。
SystemMessage | ||
UserMessage | ||
AssistantMessage | ||
ToolResponseMessage |
SystemMessage(系统消息) 是最重要也最容易被忽视的消息类型。它在对话开始前设定AI的身份、行为准则、输出风格或知识边界,通常用户不可见,但深刻影响AI的所有响应。简而言之,SystemMessage告诉模型“你是谁”和“你应该怎么做”。
UserMessage(用户消息) 承载用户的真实需求——问题、指令或陈述,这是AI生成响应的直接依据。
AssistantMessage(助理消息) 代表AI模型对之前消息的回复,主要用于构造多轮对话历史。
ToolResponseMessage(工具响应消息) 将工具执行的结果反馈给AI,供其生成最终回答。框架的@Tool注解已经封装了这一细节,开发者通常无需直接操作。
理解这些消息类型是构造高质量Prompt的基础。SystemMessage设定角色和边界,UserMessage提出具体任务,两者分工明确,切忌将所有内容混入UserMessage。
4.2 提示工程基础
提示工程(Prompt Engineering)是指通过精心设计提示词来优化AI模型输出效果的一套方法论。以下是三种核心的提示工程技术。
4.2.1 零样本提示(Zero-shot Prompting)
零样本提示是最基础的提示模式:直接向AI模型发出指令,不提供任何示例,让模型依靠其训练时获得的知识来理解和执行任务。第三章中的所有示例都属于零样本提示。
@GetMapping("/chat")public String chat(@RequestParam(defaultValue = "你好") String msg) {return chatClient.prompt() .user(msg) .call() .content();}4.2.2 少样本提示(Few-shot Prompting)
与零样本相反,少样本提示在发出指令的同时提供一两个示例,让模型参考示例的格式和风格来生成输出。这种方式在需要模型模仿特定输出格式时特别有效。
以下示例展示了如何让AI输出固定格式的回答:
@GetMapping("/classification")public String classifySentiment(@RequestParam String review) {return chatClient.prompt() .system(""" 你是一个情感分析助手。将用户评论分类为「好评」「中评」或「差评」。 示例: 输入:"这个产品质量太差了,完全不符合预期" 输出:差评 输入:"物超所值,非常满意!" 输出:好评 输入:"还行吧,没有特别惊艳但也不差" 输出:中评 """) .user(review) .call() .content();}4.2.3 思维链提示(Chain-of-Thought)
对于复杂推理问题,思维链提示通过引导模型“一步一步思考”来提升推理准确性。在提示中要求模型展示推理过程,而非直接给出结论。
@GetMapping("/reasoning")public String solveProblem(@RequestParam String problem) {return chatClient.prompt() .system(""" 你是一个逻辑推理专家。对于复杂问题,请按以下步骤分析: 1. 拆解问题的关键要素 2. 逐步推理每个要素的逻辑 3. 综合得出结论 请一步一步分析,不要直接给出最终答案。 """) .user(problem) .call() .content();}4.2.4 系统提示词的设计原则
一个高质量的SystemMessage通常包含多个关键要素,常见的设计框架是“角色+任务+格式+示例”的四段式结构。
在代码中,可以通过ChatClient.Builder的defaultSystem()来配置默认的系统消息。这样配置后,这段文本会作为基础角色设定,注入到每次对话的上下文中。
@RestControllerpublicclassSystemPromptController {privatefinal ChatClient chatClient;publicSystemPromptController(ChatClient.Builder builder) {this.chatClient = builder .defaultSystem(""" 你是一个专业的Java技术助手。 职责: - 回答Java、Spring Boot相关的技术问题 - 帮助用户理解代码原理 - 提供最佳实践建议 规则: - 代码示例使用Java 17+语法 - 回答简洁,不要过度解释 - 不确定的内容要说明,不要编造 """) .build(); }@GetMapping("/ask")public String ask(@RequestParam String question) {return chatClient.prompt() .user(question) .call() .content(); }}4.3 使用PromptTemplate动态构造提示
在实际开发中,我们经常需要根据不同的用户输入动态构造提示词。如果不用模板,每个问题都要写一个新的请求,代码会变得重复、难以维护。
PromptTemplate正是为解决这个问题而设计。使用模板,你可以定义一套包含占位符的提示结构,运行时动态替换占位符的值,实现“一套模板,复用多个场景”。
4.3.1 基本语法
PromptTemplate是Spring AI中用于创建带变量模板的Prompt的核心类,使用{}包裹占位符:
// 1. 创建模板,使用{}包裹占位符PromptTemplatetemplate=newPromptTemplate("你好,我叫{name},今年{age}岁");// 2. 填充变量,Map的key对应模板中的占位符Promptprompt= template.create(Map.of("name", "张三", "age", "25"));// 3. 调用AIStringresult= chatClient.prompt(prompt).call().content();// 输出:你好,我叫张三,今年25岁4.3.2 在ChatClient中直接使用模板
ChatClient也支持在链式调用中直接使用模板,语法更加简洁:
@GetMapping("/introduce")public String introduce(@RequestParam String name, @RequestParamint age) {return chatClient.prompt() .user(u -> u.text("你好,我叫{name},今年{age}岁") .param("name", name) .param("age", age)) .call() .content();}4.3.3 SystemPromptTemplate:系统消息专用模板
SystemPromptTemplate是专门用于创建系统消息的模板,用法与PromptTemplate几乎一致:
// 创建系统消息模板SystemPromptTemplatesystemTemplate=newSystemPromptTemplate("你是一个{profession}专家,擅长{skill}");// 填充变量,生成SystemMessageMessagesystemMessage= systemTemplate.createMessage(Map.of("profession", "Java","skill", "后端开发"));// 输出:你是一个Java专家,擅长后端开发4.3.4 从外部文件加载模板
将模板内容从代码中分离到外部文件,是一种良好的工程实践。模板与代码解耦,便于维护和版本控制。
步骤一:创建模板文件
在src/main/resources/prompts/目录下创建模板文件qa-template.txt:
请基于以下背景信息回答问题。【背景信息】{context}【问题】{question}【要求】- 如果背景信息中包含答案,请基于背景信息回答- 如果背景信息中不包含答案,请明确告知用户"根据现有信息无法回答"- 回答要简洁、准确步骤二:在Java代码中加载
import org.springframework.core.io.ClassPathResource;import org.springframework.ai.chat.prompt.PromptTemplate;@GetMapping("/rag-answer")public String answerWithTemplate(@RequestParam String context,@RequestParam String question)throws IOException {// 从resources/prompts/qa-template.txt加载模板Resourceresource=newClassPathResource("prompts/qa-template.txt");StringtemplateContent= resource.getContentAsString(StandardCharsets.UTF_8);PromptTemplatepromptTemplate=newPromptTemplate(templateContent);Promptprompt= promptTemplate.create(Map.of("context", context,"question", question ));return chatClient.prompt(prompt).call().content();}这种模板文件化管理方式将提示词与Java代码分离,对于提示词较长、格式复杂或需要频繁调整的场景,强烈推荐使用。
4.4 模型参数详解
在发起请求与大模型交互时,可以配置各类核心参数,以此控制模型的生成行为、回复长度、随机性等关键特性。
ChatOptions是Spring AI中管理这些配置的核心接口,它继承了ModelOptions根接口。ChatOptions除了一个默认实现类DefaultChatOptions外,还有两个子接口:ToolCallingChatOptions(工具调用配置)和StructuredOutputChatOptions(结构化输出配置)。
4.4.1 核心参数说明
temperature | |||
maxTokens | |||
topP | |||
frequencyPenalty | |||
presencePenalty |
temperature(温度)详解:温度参数可能是影响模型输出最直观的配置。低温度(0.1-0.3)使模型倾向于选择高概率的token,输出更确定、更稳定;高温度(0.8-1.0)增加低概率token被选中的机会,输出更多样、更有创意。
maxTokens(最大输出长度):限制模型单次回复的Token数量上限,同时直接影响API调用成本(按Token计费)。
4.4.2 在ChatClient中配置参数
ChatClient提供了链式的参数配置方式:
@GetMapping("/code")public String generateCode(@RequestParam String requirement) {return chatClient.prompt() .user(requirement) .options(ChatOptions.builder() .temperature(0.2D) // 低温度保证代码准确 .maxTokens(1000) // 限制输出长度 .topP(0.95D) // 核采样 .build()) .call() .content();}也可以通过ChatOptions.builder快速构建配置,兼容所有厂商:
ChatOptionsoptions= ChatOptions.builder() .model("qwen-plus") // 指定模型 .temperature(0.1D) // 低随机性,适合精准回答 .maxTokens(1024) // 最大输出1024 Token .topP(0.9D) // 核采样参数 .stopSequences(List.of("###")) // 停止词 .build();4.4.3 厂商专属配置
每个大模型厂商都有自己独有的API参数,Spring AI为特定厂商大模型提供了不同的ChatOptions实现类。例如,如果使用通义千问并通过DashScope OpenAI兼容端点接入,标准ChatOptions即可满足大部分需求;如果直接使用其他厂商的专有SDK,可以使用对应的专属配置类。
4.5 综合实战:可配置的智能问答助手
现在,让我们将本章所学内容整合起来,构建一个完整的可配置智能问答助手。这个助手支持:
动态系统提示词(用户可指定AI角色) 模型参数可调节 从外部模板文件加载提示词 支持多轮对话参数配置
4.5.1 完整代码实现
@RestController@RequestMapping("/configurable")publicclassConfigurableChatController {privatefinal ChatClient chatClient;publicConfigurableChatController(ChatClient.Builder builder) {this.chatClient = builder.build(); }/** * 核心问答接口 * @param question 用户问题 * @param systemPrompt 可选系统提示词,默认值为"你是一个乐于助人的AI助手" * @param role AI角色,用于模板替换 * @param temperature 温度参数,控制输出随机性,默认0.7 * @param maxTokens 最大输出Token数,默认500 */@GetMapping("/ask")public String ask(@RequestParam String question,@RequestParam(defaultValue = "你是一个乐于助人的AI助手") String systemPrompt,@RequestParam(defaultValue = "普通助手") String role,@RequestParam(defaultValue = "0.7")double temperature,@RequestParam(defaultValue = "500")int maxTokens) {// 方式1:直接使用传入的系统提示词// 方式2:也可以从模板文件中加载系统提示词StringfinalSystemPrompt="你是一个" + role + "。" + systemPrompt;return chatClient.prompt() .system(finalSystemPrompt) .user(question) .options(ChatOptions.builder() .temperature(temperature) .maxTokens(maxTokens) .build()) .call() .content(); }/** * 使用模板文件的问答接口(演示外部模板加载) */@GetMapping("/ask-with-template")public String askWithTemplate(@RequestParam String role,@RequestParam String topic,@RequestParam String question)throws IOException {// 从外部文件加载系统消息模板Resourceresource=newClassPathResource("prompts/system-template.txt");StringsystemTemplateStr= resource.getContentAsString(StandardCharsets.UTF_8);SystemPromptTemplatesystemTemplate=newSystemPromptTemplate(systemTemplateStr);// 创建系统消息MessagesystemMessage= systemTemplate.createMessage(Map.of("role", role,"topic", topic ));// 构建完整的Prompt并调用return chatClient.prompt() .messages(systemMessage) .user(question) .options(ChatOptions.builder() .temperature(0.7) .maxTokens(800) .build()) .call() .content(); }}其中src/main/resources/prompts/system-template.txt代码如下:
你是一个{role},专注于{topic}领域。你的回答风格:- 专业、准确、简洁- 使用中文回答- 如果需要代码示例,请使用适当的代码块格式请基于以上角色和领域知识,回答用户的问题。4.5.2 测试示例
测试1:零样本问答
GET /configurable/ask?question=什么是Spring AI&role=Java专家&temperature=0.2输出结果会更确定、更专业,适合技术问答场景。
测试2:带创意的回答
GET /configurable/ask-with-template?question=讲一下Java面向对象&role=Java专家&topic=编程输出结果按照你的文件的风格展示。
4.6 常见误区与最佳实践
4.6.1 ❌ 误区一:把控制参数写进提示词
很多人写Prompt时喜欢加这些句子:
“请保持稳定输出” “不要超过200字” “回答要随机一些”
这些说法不是完全没用,但Spring AI提供了更专业的控制方式——通过ChatOptions配置temperature、maxTokens等参数来控制生成行为。上面的示例已给出正确示范。
4.6.2 ❌ 误区二:提示词和代码不分家
当提示词内容较长时,散落在Java代码字符串中既不便于维护,也难以协作。正确的做法是将提示词模板抽取到外部文件(src/main/resources/prompts/目录下)或数据库中,实现提示词与代码的分离。
4.6.3 ✅ 良好提示词的标准
一个工程化良好的Prompt在代码中应具备以下特征:
┌─────────────────────────────────────────────────────┐│ SystemMessage: 角色 + 任务 + 约束 + 格式 │├─────────────────────────────────────────────────────┤│ UserMessage: 具体问题(支持参数化) │├─────────────────────────────────────────────────────┤│ ChatOptions: temperature / maxTokens 等参数配置 │└─────────────────────────────────────────────────────┘将这些要素拆开、参数化,而不是揉成一团堆在一个字符串里,这才是工程化的方式。
4.7 本章小结
在本章中,我们系统学习了Spring AI的提示词体系:
四种消息类型:
SystemMessage设定AI角色,UserMessage承载用户任务,AssistantMessage记录AI回复,ToolResponseMessage反馈工具执行结果提示工程基础:零样本提示、少样本提示(通过示例让模型模仿)、思维链提示(引导模型一步步思考)
PromptTemplate:使用模板实现提示词参数化和复用,支持从外部文件加载,将提示词与Java代码分离模型参数:
temperature控制输出随机性,maxTokens限制输出长度,ChatOptions统一管理综合实战:构建了一个完整的可配置智能问答助手,演示了上述所有技术的整合使用
最佳实践:区分系统消息和用户消息、使用
ChatOptions控制参数、提示词与代码分离
提示词质量在很大程度上决定了AI应用的输出质量。从本章开始,你不再是“随意提问”的用户,而是能够“专业引导”AI的开发者。
下一章,我们将学习如何让AI输出结构化的数据——将模型返回的自然语言直接映射为Java对象,让AI的输出真正可以被业务代码直接消费。
参考来源
Spring AI官方Prompt API文档 Spring AI ChatClient源码分析 阿里云DashScope API参数参考
项目源码请私信回复:Spring AI
夜雨聆风