文章目录
- Prompts
Prompts
提示词是指导 AI 模型生成特定输出的输入。这些提示词的设计和措辞对模型的效果有很大影响。
在 Spring AI 中与 AI 模型交互中,处理 Spring AI 中的提示词与管理 Spring MVC 中的"视图"有些相似。这涉及创建包含动态内容占位符的泛文本。然后根据用户请求或应用程序中的其他代码替换这些占位符。也比较像包含某些表达式占位符的 SQL 语句。
随着 Spring AI 的发展,它将引入更高级的抽象层,用于与 AI 模型进行交互。就其角色和功能而言,本节中描述的基础类可以类比于 JDBC。例如,ChatClient类类似于 JDK 中的核心 JDBC 库。在此基础上,Spring AI 可以提供类似于JdbcTemplate、Spring 数据仓库的辅助类,以及考虑与模型的过去交互的更高级构造,如 ChatEngines 和 Agents。
在 AI 领域内,提示词的结构随着时间的推移而发展。最初,提示词是简单的字符串。随着时间的推移,它们发展到包括特定输入的占位符,比如"USER:",这是 AI 模型可以识别的。OpenAI 通过将多个消息字符串分类为不同角色,然后再由 AI 模型处理,为提示词引入了更多结构。
API 概述
Prompt
常见的做法是使用 ChatClient 的 generate方法,该方法接受一个Prompt实例并返回一个ChatResponse。
Prompt 类作为一个有序的 Message 对象容器,每个 Message 对象是整个提示词的一个部分。每个 Message 在提示词中扮演着独特的角色,其内容和意图各不相同。这些角色可以涵盖各种元素,从用户查询到 AI 生成的响应或相关背景信息。这种安排使得与 AI 模型进行复杂和详细的交互成为可能,因为提示词由多个消息构成,每个消息在对话中扮演着特定的角色。
下面是 Prompt 类的缩减版本,为简洁起见省略了构造函数和实用方法:
java
public class Prompt {
private final List<Message> messages;
// constructors and utility methods omitted
}
Message
Message 接口封装了一个文本消息,作为 Map 的属性集合,一个称为 MessageType 的分类,以及一个媒体对象列表,用于那些多模型的模型。该接口定义如下:
java
public interface Message {
String getContent();
List<Media> getMedia();
Map<String, Object> getProperties();
MessageType getMessageType();
}
Message接口的各种实现对应着 AI 模型可以处理的不同消息类别。一些模型,比如来自 OpenAI 的模型,根据对话角色区分消息类别。这些角色通过MessageType进行有效映射,如下所讨论。
Roles(角色)
人工智能中提示词的演变已经从基础的、直接的文本格式,逐步演变为更有条理、更复杂的模式,具有特定的角色和结构。
最初,提示词只是简单的字符串 - 只是文本行。随着时间的推移,这种情况发展到在这些字符串中包含特定的占位符,比如"用户:",AI 模型可以识别并相应地回应。这是朝着更有结构的提示词迈出的一步。
OpenAI 随后引入了一种更有组织的方法。在他们的模型中,提示词不仅仅是单个字符串,而是一系列消息。每条消息,虽然仍然以文本形式存在,但被分配了一个特定的角色。这些角色对消息进行分类,澄清了每个提示词段的上下文和目的,以便 AI 模型更好地理解。这种结构化方法增强了与 AI 的交流的微妙性和有效性,因为提示词的每个部分在交互中扮演着独特和明确定义的角色。
主要角色包括:
- System Role:为 AI 解释和回复设置参数或规则可以指导 AI 的行为和响应风格。这类似于在开始对话之前向 AI 提供指示词。
- User Role:代表用户的输入 - 他们对 AI 的问题、命令或陈述。这个角色是基础,因为它构成了 AI 回应的基础。
- Assistant Role:AI 对用户输入的响应。不仅仅是一个答案或反应,对于维持对话流程至关重要。通过跟踪 AI 之前的响应("Assistant Role"消息),系统确保连贯且与上下文相关的互动。
- Function Role:该角色处理对话过程中的特定任务或操作。虽然系统角色设置了 AI 的整体行为,功能角色专注于执行用户要求的某些操作或命令。就像 AI 中的特殊功能,有时需要用于执行特定功能,如计算、获取数据或其他不仅限于对话的任务。该角色使 AI 除了对话响应外还能提供实用帮助。
在 Spring AI 中,角色被表示为下面所示的枚举。
java
public enum MessageType {
USER("user"),
ASSISTANT("assistant"),
SYSTEM("system"),
FUNCTION("function");
private final String value;
MessageType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static MessageType fromValue(String value) {
for (MessageType messageType : MessageType.values()) {
if (messageType.getValue().equals(value)) {
return messageType;
}
}
throw new IllegalArgumentException("Invalid MessageType value: " + value);
}
}
PromptTemplate
Spring AI 中提示词模板化的关键组件是 PromptTemplate 类。该类使用由 Terence Parr 开发的 StringTemplate 引擎来构建和管理提示词。 PromptTemplate 类旨在促进结构化提示词的创建,然后将其发送到 AI 模型进行处理。
java
public class PromptTemplate implements PromptTemplateActions, PromptTemplateMessageActions {
// Other methods to be discussed later
}
该类实现的不同接口支持提示词创建的不同场景:
PromptTemplateStringActions专注于创建和呈现字符串提示词,代表提示词生成的最基本形式。
PromptTemplateMessageActions专为创建和呈现消息对象的方式来创建提示词。
PromptTemplateActions旨在返回提示词对象,该对象可以传递给 ChatClient 以生成响应。
尽管这些接口在许多项目中可能不会被广泛使用,但它们展示了创建提示词的不同方法。
已实现的接口是:
java
public interface PromptTemplateStringActions {
String render();
String render(Map<String, Object> model);
}
String render()方法:将提示词模板呈现为最终字符串格式,不需要外部输入,适用于没有占位符或动态内容的模板。
String render(Map<String, Object> model) 方法:增强呈现功能包括动态内容。它使用一个 Map,其中 map 键是提示模板中的占位符名称,值是要插入的动态内容。
java
public interface PromptTemplateMessageActions {
Message createMessage();
Message createMessage(Map<String, Object> model);
}
Message createMessage() 方法:创建一个没有数据输入的消息对象,用于静态或预定义的消息内容。
Message createMessage(Map<String, Object> model) 方法:消息创建集成动态内容,接受一个 Map,其中每个条目表示消息模板中的占位符及其对应的动态值。
java
public interface PromptTemplateActions extends PromptTemplateStringActions {
Prompt create();
Prompt create(Map<String, Object> model);
}
Prompt create()方法:生成一个没有外部数据输入的提示词对象,适用于静态或预定义的提示词。
Prompt create(Map<String, Object> model) 方法:扩展提示词创建功能,集成动态内容,接受一个 Map,其中每个映射条目是提示词模板中的占位符及其关联的动态值。
示例用法
下面是从 AI Workshop on PromptTemplates 中提取的一个简单示例。
java
PromptTemplate promptTemplate = new PromptTemplate("Tell me a {adjective} joke about {topic}");
Prompt prompt = promptTemplate.create(Map.of("adjective", adjective, "topic", topic));
return chatClient.call(prompt).getResult();
下面是从 AI Workshop on Roles 中提取的另一个示例。
java
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 = chatClient.call(prompt).getResults();
这显示了如何通过使用 SystemPromptTemplate 来构建 Message ,并传递占位符来创建 Message 的 Prompt 实例。然后将具有 user 角色的消息与具有 system 角色的消息组合以形成提示词。然后将提示词传递给 ChatClient 以获得生成的响应。
使用资源而不是原始字符串
Spring AI 支持 org.springframework.core.io.Resource 抽象,因此您可以将提示词数据放入文件中,该文件可以直接在 PromptTemplates 中使用。例如,您可以在 Spring 管理的组件中定义一个字段来获取对应资源。
java
@Value("classpath:/prompts/system-message.st")
private Resource systemResource;
然后直接将该资源传递给 SystemPromptTemplate 。
java
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
Prompt Engineering
在生成式人工智能中,为开发人员创建提示词是一项至关重要的任务。这些提示词的质量和结构显著影响人工智能输出的有效性。投入时间和精力设计周到的提示词可以极大地改善人工智能的结果。
在 AI 社区中,分享和讨论提示词是一种常见做法。这种协作方式不仅创造了一个共享学习环境,还有助于识别和使用高效的提示词。
这一领域的研究通常涉及分析和比较不同的提示词,以评估它们在各种情况下的有效性。例如,一项重要研究表明,以"深呼吸,一步一步地解决这个问题"开头的提示显著提高了解决问题的效率。这突显了精心选择描述方式对生成式 AI 系统性能的影响。
随着 AI 技术的快速发展,掌握提示词的有效性是一个持续的挑战。您应该认识到提示词工程的重要性,并考虑利用社区和研究的见解来改进提示词创建策略。
创建有效的提示
在开发提示词时,重要的是整合几个关键组成部分,以确保清晰和有效:
- 说明:向 AI 提供清晰直接的说明,类似于您与人交流的方式。这种清晰描述对于帮助 AI "理解"预期的内容至关重要。
- 外部环境:在必要时包含相关背景信息或特定指导,以帮助 AI 理解响应。这种"外部环境"框定了提示词并帮助 AI 把握整体情景。
- 用户输入:这是直接的部分 - 用户的直接请求或问题构成了提示词的核心。
- 输出指示器:这个方面可能有点复杂。它涉及指定 AI 响应的期望格式,比如 JSON。但要注意,AI 可能并不总是严格遵守这种格式。例如,它可能会在实际的 JSON 数据之前添加诸如"这是您的 JSON"之类的短语,或者有时生成一个不准确的类似 JSON 结构。
为 AI 提供预期问题和答案格式的示例在制作提示词时可能非常有益。这种做法有助于 AI "理解"您查询的结构和意图,从而产生更精确更相关的响应。虽然本文档并未深入探讨这些技术,但它们为进一步探索 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 首先分析(推理)输入,然后确定最合适的行动或响应。它结合了理解和决策。
微软指导
Framework for Prompt Creation and Optimization: 微软提供了一种结构化方法来开发和完善提示词。该框架指导用户创建有效的提示词,从而引发 AI 模型所需的响应,优化交互以提高清晰度和效率。
Tokens(令牌)
在 AI 模型处理文本方面中,令牌至关重要,充当将单词(我们理解的方式)转换为 AI 模型可以处理的格式的桥梁。这种转换分为两个阶段:单词在输入时被转换为令牌,然后这些令牌在输出时再转换回单词。
分词,即将文本分解为令牌的过程,对于 AI 模型理解和语言处理至关重要。AI 模型使用这种令牌化格式来理解并回应提示词。
要更好地理解令牌,请将它们视为单词的部分。通常,一个标记代表大约四分之三个单词。例如,莎士比亚的全部作品,总计约 90 万字,将转换为约 120 万个令牌。
可以尝试使用 OpenAI Tokenizer UI 进行实验,看看单词如何转换为令牌。
令牌在AI处理中的技术角色之外还具有实际意义,特别是在计费和模型功能方面。
- 计费:AI 模型服务通常基于令牌使用量进行计费。输入(提示词)和输出(响应)都会影响总令牌数量,因此较短的提示词更具成本效益。
- 模型限制:不同的 AI 模型具有不同的令牌数量限制,定义了它们的"上下文窗口" - 即一次可以处理的最大信息量。例如,GPT-3 的限制是 4K 个令牌,而其他模型如 Claude 2 和 Meta Llama 2 的限制为 100K 个令牌,一些研究模型可以处理高达 1 百万个令牌。
- 上下文窗口:模型的令牌限制决定了其上下文窗口。超过此限制的输入不会被模型处理。仅发送最小有效信息集进行处理至关重要。例如,询问"哈姆雷特"时,无需包含莎士比亚其他作品的令牌。
- 响应元数据:来自 AI 模型的响应元数据也囊括在所使用的令牌数量,这是管理使用量和成本的重要信息。