本章导读:你向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:控制模型行为的参数,如temperaturemaxTokens等。

4.1.3 四种Message角色详解

在Spring AI中,消息被抽象为几个类,每种类型对应对话中的一个特定角色,“同一句话,放在不同消息角色里,效果可能完全不同”——Prompt的本质不是提问,而是精确表达任务。

消息类型 说明 典型用途
SystemMessage 系统提示词,设定AI角色和行为 “你是一个专业的Java技术专家”
UserMessage 用户消息,实际的问题或指令 “什么是Spring AI?”
AssistantMessage AI的回复消息 记录对话历史
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通常包含多个关键要素,常见的设计框架是“角色+任务+格式+示例”的四段式结构。

要素 说明 示例
角色 告诉模型“你是谁” “你是一个专业的Java技术助手”
任务 你要做什么 “回答技术问题、帮助debug代码”
约束 什么能做、什么不能做 “不确定的内容要说明,不要编造”
格式 用什么格式返回 “使用Markdown格式,代码用代码块包裹”
示例 给一两个示例让模型参考 Few-shot Prompting

在代码中,可以通过ChatClient.BuilderdefaultSystem()来配置默认的系统消息。这样配置后,这段文本会作为基础角色设定,注入到每次对话的上下文中。

@RestController
public class SystemPromptController {

    private final ChatClient chatClient;

    public SystemPromptController(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. 创建模板,使用{}包裹占位符
PromptTemplate template = new PromptTemplate("你好,我叫{name},今年{age}岁");

// 2. 填充变量,Map的key对应模板中的占位符
Prompt prompt = template.create(Map.of("name", "张三", "age", "25"));

// 3. 调用AI
String result = chatClient.prompt(prompt).call().content();
// 输出:你好,我叫张三,今年25岁

4.3.2 在ChatClient中直接使用模板

ChatClient也支持在链式调用中直接使用模板,语法更加简洁:

@GetMapping("/introduce")
public String introduce(@RequestParam String name, @RequestParam int age) {
    return chatClient.prompt()
            .user(u -> u.text("你好,我叫{name},今年{age}岁")
                .param("name", name)
                .param("age", age))
            .call()
            .content();
}

4.3.3 SystemPromptTemplate:系统消息专用模板

SystemPromptTemplate是专门用于创建系统消息的模板,用法与PromptTemplate几乎一致:

// 创建系统消息模板
SystemPromptTemplate systemTemplate = new SystemPromptTemplate(
    "你是一个{profession}专家,擅长{skill}"
);

// 填充变量,生成SystemMessage
Message systemMessage = 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加载模板
    Resource resource = new ClassPathResource("prompts/qa-template.txt");
    String templateContent = resource.getContentAsString(StandardCharsets.UTF_8);
    
    PromptTemplate promptTemplate = new PromptTemplate(templateContent);
    Prompt prompt = 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 控制输出随机性,越低越确定,越高越有创意 0~2(不同模型范围略有差异) 代码生成用0.10.3,创意写作用0.81.0
maxTokens 限制模型输出的最大Token数量 取决于模型上下文窗口 短问答用500,长内容生成用2000+
topP 核采样,选择概率和质量达到P的token集 0~1 通常与temperature配合,推荐调整其中一个即可
frequencyPenalty 惩罚重复token,抑制模型重复内容 -2~2 需要多样化输出时可设为0.5~1.0
presencePenalty 惩罚已出现过的主题,鼓励引入新话题 -2~2 需要探索新话题时可设为0.5~1.0

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快速构建配置,兼容所有厂商:

ChatOptions options = 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")
public class ConfigurableChatController {

    private final ChatClient chatClient;

    public ConfigurableChatController(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:也可以从模板文件中加载系统提示词
        String finalSystemPrompt = "你是一个" + 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 {
        
        // 从外部文件加载系统消息模板
        Resource resource = new ClassPathResource("prompts/system-template.txt");
        String systemTemplateStr = resource.getContentAsString(StandardCharsets.UTF_8);
        SystemPromptTemplate systemTemplate = new SystemPromptTemplate(systemTemplateStr);
        
        // 创建系统消息
        Message systemMessage = 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配置temperaturemaxTokens等参数来控制生成行为。上面的示例已给出正确示范。

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参数参考
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐