提示词
约 5548 字大约 18 分钟
2026-09-15
📌 原文地址:https://docs.spring.io/java-ai/reference/api/prompt.html 📌 原文版本:Java AI Reference v2.0.1(Stable 稳定版)
提示词是引导 AI 模型生成特定输出的输入内容。这些提示词的设计和措辞会显著影响模型的响应结果。
在 Java AI 中,与 AI 模型交互的最底层,处理提示词的方式有点类似于 Spring MVC 中的"视图(View)"管理。这涉及创建带有动态内容占位符的大量文本,然后根据用户请求或应用中的其他代码替换这些占位符。另一个类似的类比是包含某些表达式占位符的 SQL 语句。
随着 Java AI 的发展,它将引入更高级别的抽象来与 AI 模型交互。本节中介绍的基础类在角色和功能上可以类比为 JDBC(Java 数据库连接):例如,ChatModel 类类似于 JDK 中的核心 JDBC 库;ChatClient 类类似于 JdbcClient,它构建在 ChatModel 之上,并通过 Advisor(顾问)提供更高级的构造,以考虑与模型的过往交互、用额外的上下文文档增强提示词,并引入代理行为(agentic behavior)。
提示词的结构在 AI 领域经过了一段时间的演变:最初,提示词只是简单的字符串;随着时间的推移,它们发展为包含特定输入的占位符,例如 AI 模型可以识别的 "USER:"(用户);OpenAI 通过将多个消息字符串分类为不同的角色,在提示词中引入了更多的结构,然后再交给 AI 模型处理。
白话解读
把 Java AI 和 JDBC 类比一下就很好懂:ChatModel 相当于最底层的 JDBC 驱动——能干活但用起来琐碎;ChatClient 相当于更好用的 JdbcClient——封装了常用操作;而 Advisor 则像是 Spring 的 AOP 切面,能在请求进出模型前后"加料"。
API 概览(API Overview)
Prompt(提示词)
使用 ChatModel(聊天模型)的 call() 方法很常见,它接收一个 Prompt 实例并返回一个 ChatResponse(聊天响应)。
Prompt 类充当有序 Message 对象序列和请求 ChatOptions(聊天选项)的容器。每条 Message 在提示词中体现独特的角色,在内容和意图上有所不同。这些角色可以包含多种元素,从用户询问到 AI 生成的响应再到相关的背景信息。这种安排使得与 AI 模型的交互能够变得复杂而细致,因为提示词是由多条消息构建的,每条消息都被分配了特定的角色来参与对话。
以下是 Prompt 类的精简版本,为简洁起见省略了构造函数和实用方法:
// Prompt 类:表示发给 AI 模型的一次提示请求
// 实现了 ModelRequest 接口,泛型为 List<Message>
public class Prompt implements ModelRequest<List<Message>> {
// 消息列表:组成此次提示的多条消息
private final List<Message> messages;
// 聊天选项:例如温度、最大令牌数等模型调用参数
private ChatOptions chatOptions;
}便捷方法(Convenience Methods)
Prompt 类提供了几种按角色访问消息的便捷方法。
单条消息访问:
getUserMessage():返回提示词中最后一条用户消息,如果不存在则返回一个空的UserMessage;getSystemMessage():返回提示词中第一条系统消息,如果不存在则返回一个空的SystemMessage;getLastUserOrToolResponseMessage():返回最后一条用户消息或工具响应消息,用于保持对话连续性。
多条消息访问:
getUserMessages():返回提示词中所有用户消息的列表,保留它们的顺序;getSystemMessages():返回提示词中所有系统消息的列表,保留它们的顺序。
白话解读
这些方法特别适用于多轮对话场景,或者当你需要按角色处理消息时。例如,你可以用 getUserMessages() 一次性拿到所有用户说过的话,用来分析用户的提问历史。
Message(消息)
Message 接口封装了 Prompt 的文本内容、一组元数据属性以及称为 MessageType(消息类型)的分类。

该接口定义如下:
// 内容接口:表示消息中的文本内容及元数据
public interface Content {
// 获取文本内容
String getContent();
// 获取元数据,例如时间戳、来源等附加信息
Map<String, Object> getMetadata();
}
// 消息接口:继承自 Content,并增加消息类型
public interface Message extends Content {
// 获取消息类型,例如 USER、ASSISTANT 等
MessageType getMessageType();
}多模态消息类型还实现了 MediaContent 接口,提供 Media 内容对象的列表:
// 多媒体内容接口:除了文本还可以携带图片、音频等媒体
public interface MediaContent extends Content {
// 获取媒体对象集合
Collection<Media> getMedia();
}Message 接口的各种实现对应于 AI 模型可以处理的不同类别的消息。模型根据对话角色区分消息类别,这些角色通过 MessageType 有效映射,如下所述。
角色(Roles)
每条消息都被分配一个特定的角色。这些角色对消息进行分类,阐明提示词中每个片段的上下文和目的。这种结构化的方法增强了与 AI 通信的细微差别和有效性,因为提示词的每个部分都在交互中扮演一个独特而明确的角色。
主要角色包括:
- 系统角色(System Role):指导 AI 的行为和响应风格,为 AI 解释和回复输入设置参数或规则。类似于在发起对话之前向 AI 提供指令。
- 用户角色(User Role):代表用户的输入——他们向 AI 提出的问题、命令或陈述。这是基础角色,因为它构成了 AI 响应的基础。
- 助手角色(Assistant Role):AI 对用户输入的响应。不仅仅是答案或反应,它对于维护对话流程至关重要。通过跟踪 AI 之前的响应(其"助手角色"消息),系统确保交互连贯且上下文相关。助手消息还可能包含函数工具调用请求信息——就像 AI 中的一个特殊功能,在需要时用于执行特定功能,例如计算、获取数据或执行其他不仅仅是交谈的任务。
- 工具/函数角色(Tool/Function Role):工具/函数角色专注于返回响应于工具调用助手消息的附加信息。
角色在 Java AI 中表示为枚举,如下所示:
// 消息类型枚举:定义提示中可能出现的角色
public enum MessageType {
USER("user"), // 用户:发起提问的一方
ASSISTANT("assistant"), // 助手:AI 模型自身的回复
SYSTEM("system"), // 系统:用于设置 AI 行为准则
TOOL("tool"); // 工具:工具调用的返回结果
...
}提示词模板(PromptTemplate)
Java AI 中提示词模板的关键组件是 PromptTemplate 类,旨在方便创建结构化提示词,然后将其发送给 AI 模型进行处理。
// 提示词模板类:用于生成带占位符的提示
// 实现了 PromptTemplateActions 和 PromptTemplateMessageActions 接口
public class PromptTemplate implements PromptTemplateActions, PromptTemplateMessageActions {
// 其他方法稍后讨论
}该类使用 TemplateRenderer(模板渲染器)API 来渲染模板。默认情况下,Java AI 使用 StTemplateRenderer 实现,该实现基于 Terence Parr 开发的开源 StringTemplate 引擎。模板变量由 {} 语法标识,但你也可以配置分隔符以使用其他语法。
// 模板渲染器接口:接收模板字符串和变量映射,返回渲染后字符串
// 它本质上是一个接收两个参数、返回一个结果的函数(BiFunction)
public interface TemplateRenderer extends BiFunction<String, Map<String, Object>, String> {
@Override
// 第一个参数:模板字符串;第二个参数:变量名 -> 变量值 的映射
String apply(String template, Map<String, Object> variables);
}Java AI 使用 TemplateRenderer 接口处理模板字符串中的变量替换,默认实现使用 StringTemplate。如果需要自定义逻辑,可以提供自己的 TemplateRenderer 实现。对于不需要模板渲染的场景(例如模板字符串已经完整),可以使用提供的 NoOpTemplateRenderer。
白话解读
模板渲染器本质上就是"找占位符 → 填值"的工具。Java AI 默认用 {} 标记占位符,但如果你要在提示词里写 JSON,那 {} 会和 JSON 自身的花括号冲突,所以可以换成 <> 之类的符号来避免冲突。
该类实现的接口支持提示词创建的不同方面:
- PromptTemplateStringActions:专注于创建和渲染提示词字符串,代表最基本的提示词生成形式;
- PromptTemplateMessageActions:专为通过生成和操作 Message 对象进行提示词创建而设计;
- PromptTemplateActions:旨在返回 Prompt 对象,可以传递给 ChatModel 以生成响应。
虽然这些接口在许多项目中可能不会被广泛使用,但它们展示了提示词创建的不同方法。
实现的接口如下:
// 字符串操作接口:直接渲染出字符串结果
public interface PromptTemplateStringActions {
// 无变量渲染:直接返回模板字符串(适用于无占位符的模板)
String render();
// 带变量渲染:用模型 Map 中的值替换模板中的占位符
String render(Map<String, Object> model);
}- 方法
String render():将提示词模板渲染为最终字符串格式,无需外部输入,适用于没有占位符或动态内容的模板; - 方法
String render(Map<String, Object> model):增强渲染功能以包含动态内容。它使用Map<String, Object>,其中映射键是提示词模板中的占位符名称,值是要插入的动态内容。
// 消息操作接口:用于创建 Message 对象,支持多媒体
public interface PromptTemplateMessageActions {
// 创建普通消息:仅含文本内容
Message createMessage();
// 创建多媒体消息:同时携带媒体(例如图片)
Message createMessage(List<Media> mediaList);
// 创建带变量的消息:用模型 Map 中的值替换占位符
Message createMessage(Map<String, Object> model);
}- 方法
Message createMessage():创建不带附加数据的Message对象,用于静态或预定义的消息内容; - 方法
Message createMessage(List<Media> mediaList):创建带有静态文本和媒体内容的Message对象; - 方法
Message createMessage(Map<String, Object> model):扩展消息创建以集成动态内容,接受Map<String, Object>,其中每个条目代表消息模板中的占位符及其对应的动态值。
// 完整提示操作接口:扩展自字符串操作,可直接产出 Prompt 对象
public interface PromptTemplateActions extends PromptTemplateStringActions {
// 静态创建:无变量,直接产出 Prompt
Prompt create();
// 带聊天选项的静态创建:可设置模型参数(温度、top-p 等)
Prompt create(ChatOptions modelOptions);
// 带变量的创建:使用模型 Map 填充模板
Prompt create(Map<String, Object> model);
// 带变量和聊天选项的创建:同时填充模板和设置模型参数
Prompt create(Map<String, Object> model, ChatOptions modelOptions);
}- 方法
Prompt create():生成不带外部数据输入的Prompt对象,适合静态或预定义的提示词; - 方法
Prompt create(ChatOptions modelOptions):生成不带外部数据输入且具有聊天请求特定选项的Prompt对象; - 方法
Prompt create(Map<String, Object> model):扩展提示词创建功能以包含动态内容,接受Map<String, Object>,其中每个映射条目是提示词模板中的占位符及其关联的动态值; - 方法
Prompt create(Map<String, Object> model, ChatOptions modelOptions):扩展提示词创建功能以包含动态内容以及聊天请求的特定选项。
使用示例(Example Usage)
下面展示了从 AI Workshop 中关于 PromptTemplates 的一个简单示例:
// 创建一个带两个占位符的提示词模板:{adjective} 形容词、{topic} 主题
PromptTemplate promptTemplate = new PromptTemplate("Tell me a {adjective} joke about {topic}");
// 使用 Map 填充变量,生成 Prompt 对象
Prompt prompt = promptTemplate.create(Map.of("adjective", adjective, "topic", topic));
// 调用 chatModel 发送 Prompt 并获取第一条生成结果
return chatModel.call(prompt).getResult();下面展示了从 AI Workshop 中关于 Roles 的另一个示例:
// 用户消息:让 AI 介绍三位著名海盗
String userText = """
Tell me about three famous pirates from the Golden Age of Piracy and why they did.
Write at least a sentence for each pirate.
""";
// 构造用户角色消息
Message userMessage = new UserMessage(userText);
// 系统消息:定义 AI 助手的身份和说话风格
// 模板里使用 {name} 和 {voice} 作为占位符
String systemText = """
You are a helpful AI assistant that helps people find information.
Your name is {name}
You should reply to the user's request with your name and also in the style of a {voice}.
""";
// 用 SystemPromptTemplate 创建系统消息
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemText);
Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice));
// 将系统消息和用户消息组合成一个 Prompt
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
// 调用 chatModel 并获取所有生成结果
List<Generation> response = chatModel.call(prompt).getResults();这展示了如何通过使用 SystemPromptTemplate 创建具有系统角色的 Message 并传入占位符值来构建 Prompt 实例。然后将具有用户角色的消息与具有系统角色的消息组合以形成提示词,最后将提示词传递给 ChatModel 以获得生成响应。
白话解读
上面两段代码展示了两种典型用法:第一种是单字符串模板,适合简单提示;第二种是分角色构造(系统消息 + 用户消息),可以更好地约束 AI 的行为和输出风格,比如让 AI 自报家门、按某种口吻回答。
使用自定义模板渲染器(Using a Custom Template Renderer)
你可以通过实现 TemplateRenderer 接口并将其传递给 PromptTemplate 构造函数来使用自定义模板渲染器。你也可以继续使用默认的 StTemplateRenderer,但使用自定义配置。
默认情况下,模板变量由 {} 语法标识。如果你打算在提示词中包含 JSON,可能需要使用不同的语法以避免与 JSON 语法冲突。例如,你可以使用 < 和 > 分隔符:
// 自定义模板渲染器:使用 '<' 和 '>' 作为变量定界符,避免与 JSON 中的 {} 冲突
PromptTemplate promptTemplate = PromptTemplate.builder()
.renderer(StTemplateRenderer.builder() // 配置渲染器
.startDelimiterToken('<') // 起始定界符
.endDelimiterToken('>') // 结束定界符
.build())
.template("""
Tell me the names of 5 movies whose soundtrack was composed by <composer>.
""") // <composer> 是占位符
.build();
// 传入变量值,渲染模板
String prompt = promptTemplate.render(Map.of("composer", "John Williams"));使用资源代替原始字符串(Using Resources Instead of Raw Strings)
Java AI 支持 org.springframework.core.io.Resource 抽象,因此你可以将提示词数据放在一个文件中,该文件可以直接在 PromptTemplate 中使用。例如,你可以在 Spring 管理的组件中定义一个字段来获取 Resource:
// 通过 @Value 注解从 classpath 加载 system-message.st 资源文件
@Value("classpath:/prompts/system-message.st")
private Resource systemResource;然后将该资源直接传递给 SystemPromptTemplate:
// 使用资源文件直接构造 SystemPromptTemplate
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);提示词工程(Prompt Engineering)
在生成式 AI 中,提示词的创建是开发人员的一项关键任务。这些提示词的质量和结构显著影响 AI 输出的有效性。投入时间和精力设计深思熟虑的提示词可以大大改善 AI 的结果。
在 AI 社区中,分享和讨论提示词是一种常见的做法。这种协作方法不仅营造了共同的学习环境,还能够识别和使用高效的提示词。
该领域的研究通常涉及分析和比较不同的提示词,以评估它们在各种情况下的有效性。例如,一项重要的研究表明,以"深呼吸并逐步解决这个问题(Take a deep breath and work on this problem step-by-step)"开头的提示词显著增强了问题解决效率。这突显了精心选择的语言对生成式 AI 系统性能的影响。
掌握提示词的最有效用法,特别是随着 AI 技术的快速发展,是一个持续的挑战。你应该认识到提示词工程的重要性,并考虑利用社区和研究的见解来改进提示词创建策略。
创建有效的提示词(Creating Effective Prompts)
在开发提示词时,整合几个关键组件以确保清晰度和有效性非常重要:
- 指令(Instructions):向 AI 提供清晰直接的指令,类似于你与人的交流方式。这种清晰度对于帮助 AI "理解"预期内容至关重要;
- 外部上下文(External Context):在必要时包含相关的背景信息或对 AI 响应的具体指导。这种"外部上下文"构建了提示词的框架,并帮助 AI 把握整体场景;
- 用户输入(User Input):这是简单的部分——用户的直接请求或问题构成提示词的核心;
- 输出指示器(Output Indicator):这方面可能会比较棘手。它涉及指定 AI 响应的所需格式,例如 JSON。但是,请注意,AI 可能并不总是严格遵守此格式。例如,它可能在实际 JSON 数据之前添加"这是你的 JSON"之类的短语,或者有时生成不准确的 JSON 状结构。
在为 AI 提供预期问答格式的示例时,在制作提示词时是非常有益的。这种做法有助于 AI "理解"查询的结构和意图,从而产生更精确和相关的响应。
白话解读
一条好提示词 = 指令 + 背景 + 用户问题 + 输出格式要求,四个要素各司其职。最容易被忽视的是"输出指示器"——你以为说了"给我 JSON"模型就会乖乖给 JSON,实际上模型经常在前面加一句客套话,或者给出格式歪掉的 JSON,这也是后面 Structured Output(结构化输出)功能要解决的问题。
简单技巧(Simple Techniques)
- 文本摘要(Text Summarization):将大量文本缩减为简洁的摘要,捕获关键点和主要思想,同时省略不太关键的细节;
- 问答(Question Answering):专注于根据用户提出的问题从提供的文本中得出特定答案,围绕响应查询定位和提取相关信息;
- 文本分类(Text Classification):根据预定义的类别或组系统地对文本进行分类,分析文本并将其分配到基于内容的最合适类别;
- 对话(Conversation):创建交互式对话,AI 可以与用户进行来回通信,模拟自然对话流程;
- 代码生成(Code Generation):根据特定的用户需求或描述生成功能性代码片段,将自然语言指令转换为可执行代码。
高级技巧(Advanced Techniques)
- 零样本、少样本学习(Zero-shot, Few-shot Learning):使模型能够以最少或没有特定问题类型的先前示例进行准确的预测或响应,使用学到的泛化能力来理解和处理新任务;
- 思维链(Chain-of-Thought):链接多个 AI 响应以创建连贯且上下文感知的对话。它帮助 AI 保持讨论的线索,确保相关性和连续性;
- ReAct(推理 + 行动,Reason + Act):在这种方法中,AI 首先分析(推理)输入,然后确定最适当的行动或响应过程。它将理解与决策相结合。
Microsoft 指导(Microsoft Guidance)
- 提示词创建和优化框架(Framework for Prompt Creation and Optimization):Microsoft 提供了一种结构化方法来开发和优化提示词。该框架指导用户创建有效的提示词,以从 AI 模型中引出所需的响应,优化交互的清晰度和效率。
令牌(Tokens)
令牌(Token,也叫"词元")在 AI 模型如何处理文本方面至关重要,它是将我们理解中的单词转换为 AI 模型可以处理的格式的桥梁。这种转换分两个阶段进行:单词在输入时转换为令牌,然后这些令牌在输出时再转换回单词。
令牌化(Tokenization)是将文本分解为令牌的过程,是 AI 模型理解和处理语言的基础。AI 模型使用这种令牌化格式来理解和响应提示词。
为了更好地理解令牌,可以将它们视为单词的一部分。通常,一个令牌代表大约四分之三个单词。例如,莎士比亚的全部作品,总计约 90 万个单词,将转换为约 120 万个令牌。
白话解读
简单来说,令牌就是 AI 眼中"单词"的最小单位。比如中文里"你好"可能被拆成两个令牌。AI 不是按人类意义上的"字"或"词"来理解文本,而是按令牌数。可以使用 OpenAI Tokenizer UI 直观地查看一段文字会被拆成哪些令牌。
令牌除了在 AI 处理中的技术作用外,还有实际的意义,特别是在计费和模型能力方面:
- 计费(Billing):AI 模型服务通常根据令牌使用情况计费。输入(提示词)和输出(响应)都贡献于总令牌数,使得较短的提示词更具成本效益;
- 模型限制(Model Limits):不同的 AI 模型具有不同的令牌限制,定义了它们的"上下文窗口"——它们一次可以处理的最大信息量。例如,GPT-3 的限制是 4K 令牌,而其他模型如 Claude 2 和 Meta Llama 2 的限制为 100K 令牌,一些研究模型可以处理多达 100 万个令牌;
- 上下文窗口(Context Window):模型的令牌限制决定了其上下文窗口,超过此限制的输入不会被模型处理。仅发送最小有效的信息集进行处理至关重要。例如,当询问"哈姆雷特"时,无需包含莎士比亚所有其他作品的令牌;
- 响应元数据(Response Metadata):AI 模型响应的元数据包括使用的令牌数,这是管理使用和成本的重要信息。