提示词

提示词是引导 AI 模型生成特定输出的输入。这些提示词的设计和措辞会显著影响模型的响应。

在与 Spring AI 中的 AI 模型进行最低级别的交互时,处理提示词的方式与管理 Spring MVC 中的“视图”有些类似。这涉及创建包含动态内容占位符的扩展文本。然后,根据用户请求或应用程序中的其他代码替换这些占位符。另一个类比是包含某些表达式占位符的 SQL 语句。

随着 Spring AI 的发展,它将引入更高级别的抽象来与 AI 模型交互。本节中描述的基础类在作用和功能方面可以比作 JDBC。例如,ChatModel 类类似于 JDK 中的核心 JDBC 库。ChatClient 类可以比作 JdbcClient,它构建在 ChatModel 之上,并通过 Advisor 提供更高级的结构来考虑与模型的过去交互,使用其他上下文文档增强提示词,并引入代理行为。

提示词的结构在 AI 领域随着时间推移而发展。最初,提示词是简单的字符串。随着时间的推移,它们发展到包含特定输入的占位符,例如“USER:”,AI 模型会识别这些占位符。OpenAI 通过将多个消息字符串分类为不同的角色来为提示词引入更多结构,然后由 AI 模型处理这些字符串。

API 概述

提示词

通常使用 ChatModelcall() 方法,该方法接受一个 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 接口封装了 Prompt 的文本内容、一组元数据属性以及称为 MessageType 的分类。

接口定义如下:

public interface Content {

	String getContent();

	Map<String, Object> getMetadata();
}

public interface Message extends Content {

	MessageType getMessageType();
}

多模态消息类型也实现了 MediaContent 接口,提供了一个 Media 内容对象的列表。

public interface MediaContent extends Content {

	Collection<Media> getMedia();

}

Message 接口的各种实现对应于 AI 模型可以处理的不同类别的消息。模型根据对话角色区分消息类别。

Spring AI Message API

如下所述,这些角色实际上由 MessageType 映射。

角色

每个消息都被分配了一个特定的角色。这些角色对消息进行分类,为 AI 模型阐明提示词每个部分的上下文和目的。这种结构化的方法增强了与 AI 通信的细致性和有效性,因为提示词的每个部分都在交互中扮演着独特且明确的角色。

主要角色包括:

  • 系统角色:指导 AI 的行为和响应风格,设置 AI 如何解释和回复输入的参数或规则。这类似于在开始对话之前向 AI 提供说明。

  • 用户角色:表示用户的输入——他们对 AI 提出的问题、命令或陈述。此角色至关重要,因为它构成了 AI 响应的基础。

  • 助手角色:AI 对用户输入的响应。它不仅仅是一个答案或反应,它对于保持对话的流畅性至关重要。通过跟踪 AI 之前的回复(其“助手角色”消息),系统确保交互连贯且与上下文相关。助手消息还可以包含函数工具调用请求信息。这就像 AI 中的一项特殊功能,在需要时用于执行特定功能,例如计算、获取数据或其他超出简单对话的任务。

  • 工具/函数角色:工具/函数角色侧重于响应工具调用助手消息返回其他信息。

角色在 Spring AI 中表示为枚举,如下所示:

public enum MessageType {

	USER("user"),

	ASSISTANT("assistant"),

	SYSTEM("system"),

	TOOL("tool");

    ...
}

提示词模板

Spring AI 中提示词模板的关键组件是 PromptTemplate 类。此类使用由 Terence Parr 开发的 OSS StringTemplate 引擎来构建和管理提示词。PromptTemplate 类旨在促进创建结构化提示词,然后将其发送到 AI 模型进行处理。

public class PromptTemplate implements PromptTemplateActions, PromptTemplateMessageActions {

    // Other methods to be discussed later
}

此类实现的接口支持提示词创建的不同方面:

PromptTemplateStringActions 侧重于创建和呈现提示词字符串,代表提示词生成的***基本形式。

PromptTemplateMessageActions 专为通过生成和操作 Message 对象来创建提示词而设计。

PromptTemplateActions 旨在返回 Prompt 对象,该对象可以传递给 ChatModel 以生成响应。

虽然这些接口在许多项目中可能不会被广泛使用,但它们展示了创建提示词的不同方法。

实现的接口包括:

public interface PromptTemplateStringActions {

	String render();

	String render(Map<String, Object> model);

}

方法 String render():在没有外部输入的情况下将提示词模板呈现为最终的字符串格式,适用于没有占位符或动态内容的模板。

方法 String render(Map<String, Object> model):增强渲染功能以包含动态内容。它使用一个 Map<String, Object>,其中 map 的键是提示模板中的占位符名称,值是要插入的动态内容。

public interface PromptTemplateMessageActions {

	Message createMessage();

    Message createMessage(List<Media> mediaList);

	Message createMessage(Map<String, Object> model);

}

方法 Message createMessage():创建一个不带额外数据的 Message 对象,用于静态或预定义的消息内容。

方法 Message createMessage(List<Media> mediaList):创建一个包含静态文本和媒体内容的 Message 对象。

方法 Message createMessage(Map<String, Object> model):扩展消息创建以集成动态内容,接受一个 Map<String, Object>,其中每个条目代表消息模板中的占位符及其对应的动态值。

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);

}

方法 Prompt create():生成一个不带外部数据输入的 Prompt 对象,非常适合静态或预定义的提示。

方法 Prompt create(ChatOptions modelOptions):生成一个不带外部数据输入,并带有特定聊天请求选项的 Prompt 对象。

方法 Prompt create(Map<String, Object> model):扩展提示创建功能以包含动态内容,接受一个 Map<String, Object>,其中每个 map 条目是提示模板中的占位符及其关联的动态值。

方法 Prompt create(Map<String, Object> model, ChatOptions modelOptions):扩展提示创建功能以包含动态内容,接受一个 Map<String, Object>,其中每个 map 条目是提示模板中的占位符及其关联的动态值,以及特定聊天请求选项。

示例用法

下面显示了一个来自 AI 工作坊关于提示模板 的简单示例。

PromptTemplate promptTemplate = new PromptTemplate("Tell me a {adjective} joke about {topic}");

Prompt prompt = this.promptTemplate.create(Map.of("adjective", adjective, "topic", topic));

return chatModel.call(prompt).getResult();

下面显示了另一个来自 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(this.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(this.systemText);
Message systemMessage = this.systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice));

Prompt prompt = new Prompt(List.of(this.userMessage, this.systemMessage));

List<Generation> response = chatModel.call(this.prompt).getResults();

这展示了如何使用 SystemPromptTemplate 创建一个包含系统角色的 Message 并传入占位符值来构建 Prompt 实例。然后将具有角色 user 的消息与具有角色 system 的消息组合起来形成提示。然后将提示传递给 ChatModel 以获取生成式响应。

使用资源而不是原始字符串

Spring AI 支持 org.springframework.core.io.Resource 抽象,因此您可以将提示数据放在一个文件中,该文件可以直接在 PromptTemplate 中使用。例如,您可以在 Spring 托管组件中定义一个字段来检索 Resource

@Value("classpath:/prompts/system-message.st")
private Resource systemResource;

然后将该资源直接传递给 SystemPromptTemplate

SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);

提示工程

在生成式 AI 中,提示的创建是开发人员的一项关键任务。这些提示的质量和结构会显著影响 AI 输出的有效性。投入时间和精力设计周到的提示可以极大地改善 AI 的结果。

在 AI 社区中,分享和讨论提示是一种常见的做法。这种协作方法不仅创建了一个共享的学习环境,而且还导致了识别和使用高效提示。

这方面的研究通常涉及分析和比较不同的提示,以评估它们在各种情况下的有效性。例如,一项重要的研究表明,以“深呼吸,一步一步解决这个问题”开头提示可以显著提高解决问题的效率。这突出了精心选择的语言对生成式 AI 系统性能的影响。

掌握提示的最有效用法,特别是在 AI 技术快速发展的情况下,是一个持续的挑战。您应该认识到提示工程的重要性,并考虑利用社区和研究的见解来改进提示创建策略。

创建有效的提示

在开发提示时,务必整合几个关键组件以确保清晰度和有效性。

  • 指令:向 AI 提供清晰而直接的指令,就像您与人交流一样。这种清晰度对于帮助 AI“理解”预期内容至关重要。

  • 外部上下文:如有必要,包含相关的背景信息或 AI 响应的具体指导。此“外部上下文”构建了提示,并帮助 AI 掌握整体场景。

  • 用户输入:这是简单的一部分 - 用户的直接请求或问题构成了提示的核心。

  • 输出指示器:这方面可能比较棘手。它涉及指定 AI 响应的所需格式,例如 JSON。但是,请注意,AI 并不总是严格遵守此格式。例如,它可能会在实际 JSON 数据之前添加诸如“这是您的 JSON”之类的短语,或者有时生成类似 JSON 但不准确的结构。

在构建提示时,提供 AI 预期问答格式的示例可能非常有益。此做法有助于 AI“理解”查询的结构和意图,从而产生更准确和相关的响应。虽然本文档没有深入探讨这些技术,但它们为进一步探索 AI 提示工程提供了起点。

以下是供进一步调查的资源列表。

简单技术

  • 文本摘要:
    将大量文本缩减为简洁的摘要,捕捉关键点和主要思想,同时省略不太重要的细节。

  • 问答:
    专注于根据用户提出的问题从提供的文本中获取特定答案。它旨在针对查询确定并提取相关信息。

  • 文本分类:
    系统地将文本分类到预定义的类别或组中,分析文本并根据其内容将其分配到最合适的类别。

  • 对话:
    创建交互式对话,AI 可以与用户进行来回交流,模拟自然的对话流程。

  • 代码生成:
    根据特定的用户需求或描述生成功能代码片段,将自然语言指令转换为可执行代码。

高级技术

  • 零样本学习少样本学习:
    使模型能够在对特定问题类型几乎没有或没有先验示例的情况下做出准确的预测或响应,使用学习到的泛化来理解和执行新任务。

  • 思维链:
    链接多个 AI 响应以创建连贯且上下文相关的对话。它帮助 AI 维持讨论的主题,确保相关性和连续性。

  • ReAct(推理 + 行动):
    在此方法中,AI 首先分析(推理)输入,然后确定最合适的行动方案或响应。它将理解与决策相结合。

Microsoft 指导

  • 提示创建和优化框架:
    Microsoft 提供了一种结构化的提示开发和完善方法。此框架指导用户创建有效的提示,以从 AI 模型中引出所需的响应,优化交互以实现清晰度和效率。

令牌

令牌在 AI 模型如何处理文本方面至关重要,它充当桥梁,将单词(我们理解的单词)转换为 AI 模型可以处理的格式。此转换分两个阶段进行:输入时将单词转换为令牌,然后在输出时将这些令牌转换回单词。

标记化,即把文本分解成令牌的过程,是 AI 模型理解和处理语言的基础。AI 模型使用此标记化格式来理解和响应提示。

为了更好地理解令牌,可以将其视为单词的一部分。通常,一个令牌大约代表四分之三的单词。例如,莎士比亚的全部作品,总计约 900,000 个单词,将转换为约 120 万个令牌。

尝试使用 OpenAI 标记化 UI 来查看单词如何转换为令牌。

令牌除了在 AI 处理中的技术作用外,还有实际意义,尤其是在计费和模型功能方面。

  • 计费:AI 模型服务通常根据令牌使用情况进行计费。输入(提示)和输出(响应)都会计入总令牌数,因此较短的提示更具成本效益。

  • 模型限制:不同的 AI 模型具有不同的令牌限制,定义了它们的“上下文窗口” - 它们一次可以处理的最大信息量。例如,GPT-3 的限制是 4K 个令牌,而其他模型如 Claude 2 和 Meta Llama 2 的限制为 100K 个令牌,一些研究模型可以处理多达 100 万个令牌。

  • 上下文窗口:模型的令牌限制决定了其上下文窗口。超过此限制的输入不会被模型处理。务必仅发送处理所需的最小有效信息集。例如,当询问“哈姆雷特”时,无需包含莎士比亚其他所有作品中的令牌。

  • 响应元数据:来自 AI 模型的响应的元数据包括使用的令牌数,这是管理使用情况和成本的重要信息。