提示(Prompts) 是引导 AI 模型生成特定输出的输入。这些提示的设计和措辞显著影响模型的响应。
在 Spring AI 中与 AI 模型交互的最基础层级,处理提示的方式有些类似于在 Spring MVC 中管理"视图"(View)。这涉及创建包含占位符的扩展文本,用于动态内容。这些占位符随后会根据用户请求或应用程序中的其他代码被替换。另一个类比是包含表达式占位符的 SQL 语句。
随着 Spring AI 的发展,它将引入更高级别的抽象来与 AI 模型交互。本节描述的基础类在角色和功能上可类比于 JDBC:
ChatModel
类类似于 JDK 中的核心 JDBC 库ChatClient
类类似于构建在ChatModel
之上的JdbcClient
,通过 Advisor 提供更高级的结构,用于考虑与模型的过往交互、用额外上下文文档增强提示,并引入代理行为(agentic behavior)
提示结构在 AI 领域已随时间演变:
最初:提示是简单字符串
随后:引入特定输入的占位符(如 "USER:")
当前:OpenAI 通过将多个消息字符串分类为不同角色(roles)进一步结构化提示
API 概览
Prompt 类
通常使用 ChatModel
的 call()
方法,它接受 Prompt
实例并返回 ChatResponse
。
Prompt
类充当有序 Message
对象序列和请求 ChatOptions
的容器。每个 Message
在提示中体现一个独特角色,其内容和意图各不相同。这些角色可包含多种元素:用户查询、AI 生成的响应、相关背景信息等。这种安排支持与 AI 模型的复杂交互,因为提示由多个消息构成,每个消息在对话中扮演特定角色。
以下是 Prompt
类的简化版本(为简洁省略构造函数和工具方法):
public class Prompt implements ModelRequest<List<Message>> {
private final List<Message> messages; // 消息列表
private ChatOptions chatOptions; // 聊天选项
}
Message 接口Message
接口封装了提示的文本内容、元数据属性集合和称为 MessageType
的分类:
public interface Content {
String getContent(); // 获取内容
Map<String, Object> getMetadata(); // 获取元数据
}
public interface Message extends Content {
MessageType getMessageType(); // 获取消息类型
}
多模态消息类型还实现 MediaContent
接口,提供媒体内容对象列表:
public interface MediaContent extends Content {
Collection<Media> getMedia(); // 获取媒体内容
}
Message
接口的各种实现对应 AI 模型可处理的不同消息类别。模型根据对话角色区分消息类别
这些角色通过 MessageType
有效映射。
角色(Roles)
每条消息被分配特定角色,用于为 AI 模型分类消息、阐明上下文和目的。这种结构化方法增强了与 AI 沟通的细微差别和有效性:
系统角色(System Role):指导 AI 的行为和响应风格,设定解释和回复输入的规则。类似于对话前向 AI 提供指令。
用户角色(User Role):代表用户输入(问题、命令或陈述)。这是 AI 响应的基础。
助手角色(Assistant Role):AI 对用户输入的响应。不仅是答案,对维护对话流至关重要。通过跟踪 AI 之前的响应(其 "助手角色" 消息),系统确保连贯且上下文相关的交互。助手消息可能包含函数工具调用(Function Tool Call)请求信息。
工具/函数角色(Tool/Function Role):专注于响应工具调用助手消息返回额外信息。
角色在 Spring AI 中表示为枚举:
public enum MessageType {
USER("user"), // 用户
ASSISTANT("assistant"), // 助手
SYSTEM("system"), // 系统
TOOL("tool"); // 工具
// ...
}
PromptTemplate 类
Spring AI 中提示模板化的关键组件是 PromptTemplate
类,旨在简化结构化提示的创建:
public class PromptTemplate implements PromptTemplateActions, PromptTemplateMessageActions {
// 其他方法将在后面讨论
}
此类使用 TemplateRenderer
API 渲染模板。默认情况下,Spring AI 使用基于 Terence Parr 开发的开源 StringTemplate 引擎的 StTemplateRenderer
实现。模板变量由 {}
语法标识,但也可配置其他分隔符。
public interface TemplateRenderer extends BiFunction<String, Map<String, Object>, String> {
@Override
String apply(String template, Map<String, Object> variables); // 应用模板和变量
}
Spring AI 使用 TemplateRenderer
接口处理变量代入模板字符串的实际替换。默认实现使用 [StringTemplate]。如需自定义逻辑,可实现自己的 TemplateRenderer
。对于不需要模板渲染的场景(如模板字符串已完整),可使用提供的 NoOpTemplateRenderer
。
使用自定义分隔符的示例
PromptTemplate promptTemplate = PromptTemplate.builder()
.renderer(StTemplateRenderer.builder()
.startDelimiterToken('<') // 起始分隔符
.endDelimiterToken('>') // 结束分隔符
.build())
.template("""
Tell me the names of 5 movies whose soundtrack was composed by <composer>.
""")
.build();
String prompt = promptTemplate.render(Map.of("composer", "John Williams")); // 渲染结果
实现接口
此类实现的接口支持提示创建的不同方面:
PromptTemplateStringActions:
专注于创建和渲染提示字符串(最基本的提示生成形式)
public interface PromptTemplateStringActions {
String render(); // 无外部输入的渲染
String render(Map<String, Object> model); // 带动态内容的渲染
}
PromptTemplateMessageActions:
专为通过生成和操作
Message
对象创建提示
public interface PromptTemplateMessageActions {
Message createMessage(); // 创建无数据的消息
Message createMessage(List<Media> mediaList); // 创建带媒体内容的消息
Message createMessage(Map<String, Object> model); // 创建带动态内容的消息
}
PromptTemplateActions:
设计用于返回可传递给
ChatModel
生成响应的Prompt
对象
public interface PromptTemplateActions extends PromptTemplateStringActions {
Prompt create(); // 创建无输入的提示
Prompt create(ChatOptions modelOptions); // 创建带聊天选项的提示
Prompt create(Map<String, Object> model); // 创建带动态内容的提示
Prompt create(Map<String, Object> model, ChatOptions modelOptions); // 创建带内容和选项的提示
}
使用示例
基础示例(来自 AI Workshop)
PromptTemplate promptTemplate = new PromptTemplate("Tell me a {adjective} joke about {topic}");
Prompt prompt = promptTemplate.create(Map.of("adjective", adjective, "topic", topic));
return chatModel.call(prompt).getResult(); // 调用模型获取结果
角色使用示例(来自 AI Workshop)
// 用户消息
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); // 用户角色消息
// 系统消息模板
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 = new SystemPromptTemplate(systemText);
// 创建系统角色消息(带动态参数)
Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice));
// 组合提示
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
// 获取生成结果
List<Generation> response = chatModel.call(prompt).getResults();
使用自定义模板渲染器
可通过实现 TemplateRenderer
接口并将其传递给 PromptTemplate
构造函数来使用自定义渲染器。也可继续使用默认的 StTemplateRenderer
但自定义配置:
PromptTemplate promptTemplate = PromptTemplate.builder()
.renderer(StTemplateRenderer.builder()
.startDelimiterToken('<')
.endDelimiterToken('>')
.build())
.template("Tell me the names of 5 movies whose soundtrack was composed by <composer>.")
.build();
String prompt = promptTemplate.render(Map.of("composer", "John Williams"));
使用资源代替原始字符串
Spring AI 支持 org.springframework.core.io.Resource
抽象,可将提示数据放入文件中直接使用:
@Value("classpath:/prompts/system-message.st") // 注入资源
private Resource systemResource;
// 直接使用资源创建系统提示模板
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
提示工程(Prompt Engineering)
在生成式 AI 中,创建提示对开发者至关重要。提示的质量和结构显著影响 AI 输出的效果。投入时间设计周到的提示可极大改善 AI 结果。
创建有效提示的关键要素
开发提示时需整合多个关键组件以确保清晰有效:
指令(Instructions):
向 AI 提供清晰直接的指令(类似于与人沟通)
明确性对帮助 AI "理解" 期望至关重要
外部上下文(External Context):
必要时包含相关背景信息或对 AI 响应的具体指导
为提示提供框架,帮助 AI 掌握整体场景
用户输入(User Input):
用户直接请求或问题(构成提示的核心)
输出指示符(Output Indicator):
指定 AI 响应的期望格式(如 JSON)
注意:AI 可能不严格遵守格式(例如在 JSON 前添加 "here is your JSON")
提供问答格式的示例对 AI "理解" 查询结构和意图非常有益
提示工程技术分类
基础技术
文本摘要(Text Summarization):
将冗长文本压缩为简明摘要,捕捉要点和主要思想,省略次要细节
问答(Question Answering):
根据用户问题从提供文本中提取具体答案,精确定位相关信息
文本分类(Text Classification):
系统地将文本分类到预定义类别,基于内容分配最合适类别
对话(Conversation):
创建交互式对话,使 AI 能与用户进行自然流畅的交流
代码生成(Code Generation):
根据特定需求生成功能代码片段,将自然语言指令转化为可执行代码
高级技术
零样本/少样本学习(Zero-shot, Few-shot Learning):
模型在极少或没有特定问题类型示例的情况下做出准确预测
利用学习到的泛化能力理解新任务
思维链(Chain-of-Thought):
链接多个 AI 响应以创建连贯且上下文感知的对话
帮助 AI 保持讨论主线,确保相关性和连续性
ReAct(推理+行动,Reason + Act):
AI 先分析(推理)输入,再确定最合适的行动或响应
结合理解与决策
Microsoft Guidance 框架:
用于创建和优化提示的结构化方法
指导用户构建能引发 AI 模型期望响应的有效提示
令牌(Tokens)
令牌在 AI 模型处理文本的方式中至关重要,充当将单词转换为 AI 可处理格式的桥梁:
输入阶段:单词被转换为令牌
处理阶段:AI 模型使用令牌化格式理解和响应
输出阶段:令牌被转换回单词
令牌化(Tokenization) 是将文本分解为令牌的过程,是 AI 模型理解和处理语言的基础。通常:
1 个令牌 ≈ 3/4 个单词
示例:莎士比亚全集(约 90 万字)≈ 120 万令牌
实际意义
计费(Billing):
AI 模型服务通常基于令牌使用量计费
输入(提示)和输出(响应)均计入总令牌数(更短的提示更具成本效益)
模型限制(Model Limits):
不同 AI 模型有不同令牌限制(定义其"上下文窗口")
示例限制:
GPT-3:4K 令牌
Claude 2 / Llama 2:100K 令牌
研究模型:最高 100 万令牌
上下文窗口(Context Window):
模型的令牌限制决定其上下文窗口
超过此限制的输入不会被处理
关键:仅发送处理所需的最少有效信息集(例如询问《哈姆雷特》时无需包含莎士比亚所有作品)
响应元数据(Response Metadata):
AI 模型响应的元数据包含使用的令牌数
这是管理使用量和成本的重要信息
评论区