跳到主要内容

提示词

Deepseek 3.2 中英对照 Prompts

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

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

随着 Spring AI 的发展,它将引入更高层次的抽象来与 AI 模型交互。本节中描述的基础类,就其角色和功能而言,可以与 JDBC 相类比。例如,ChatModel 类类似于 JDK 中的核心 JDBC 库。ChatClient 类则类似于 JdbcClient,它构建在 ChatModel 之上,并通过 Advisor 提供更高级的构造,以便考虑与模型的过往交互、用额外的上下文文档增强提示,并引入智能体行为。

提示结构在人工智能领域内已经历了演变。最初,提示仅仅是简单的字符串。随着时间的推移,它们逐渐发展成包含特定输入的占位符,例如“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 消息 API

这些角色通过 MessageType 有效映射,具体如下所述。

角色

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

主要角色包括:

  • 系统角色(System Role):指导 AI 的行为和回应风格,设定 AI 如何解读和回应用户输入的参数或规则。这类似于在开启对话前向 AI 提供指令。

  • 用户角色(User Role):代表用户的输入——即他们向 AI 提出的问题、发出的命令或作出的陈述。这一角色是基础性的,因为它构成了 AI 回应的依据。

  • 助手角色(Assistant Role):即 AI 对用户输入的回应。它不仅仅是答案或反应,对于维持对话的连贯性至关重要。通过追踪 AI 之前的回应(其“助手角色”消息),系统确保了互动的一致性和上下文相关性。助手消息可能还包含函数工具调用请求信息。这就像是 AI 中的一项特殊功能,在需要时使用,以执行特定任务,如计算、获取数据或其他超出纯对话范围的操作。

  • 工具/函数角色(Tool/Function Role):工具/函数角色专注于响应工具调用助手消息,返回额外的信息。

角色在Spring AI中以枚举类型表示,如下所示

public enum MessageType {

USER("user"),

ASSISTANT("assistant"),

SYSTEM("system"),

TOOL("tool");

...
}

提示词模板

Spring AI中提示词模板化的一个关键组件是 PromptTemplate 类,它旨在简化结构化提示词的创建,随后这些提示词会被发送给AI模型进行处理。

public class PromptTemplate implements PromptTemplateActions, PromptTemplateMessageActions {

// Other methods to be discussed later
}

该类使用 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专注于创建和渲染提示字符串,代表了提示生成的最基本形式。

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>,其中映射的键是提示模板中的占位符名称,而值则是要插入的动态内容。

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> 参数,其中每个映射条目代表提示模板中的一个占位符及其关联的动态值。

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

示例用法

以下是一个取自 AI Workshop on PromptTemplates 的简单示例。

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 on Roles

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

此示例展示了如何使用SystemPromptTemplate构建Prompt实例:首先创建一个带有系统角色的Message,并传入占位符值。随后将用户角色的消息与系统角色的消息组合形成提示。最后将该提示传递给ChatModel以获取生成式响应。

使用自定义模板渲染器

你可以通过实现 TemplateRenderer 接口并将其传递给 PromptTemplate 构造函数来使用自定义模板渲染器。你也可以继续使用默认的 StTemplateRenderer,但使用自定义配置。

默认情况下,模板变量通过 {} 语法标识。如果你打算在提示中包含 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>.
""")
.build();

String prompt = promptTemplate.render(Map.of("composer", "John Williams"));

使用资源替代原始字符串

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 回应的期望格式,例如 JSON。但请注意,AI 可能并不总是严格遵守此格式。例如,它可能在 JSON 数据前加上类似 "这是您的 JSON" 的短语,或者有时生成一个不准确的类 JSON 结构。

在构建提示词时,为AI提供预期的问题与回答格式示例会非常有益。这种做法有助于AI“理解”您查询的结构与意图,从而生成更精确、更相关的回答。虽然本文档未深入探讨这些技巧,但它们为进一步探索AI提示工程提供了一个起点。

以下是供进一步研究的资源列表。

简单技巧

  • 文本摘要:
    将长篇文本浓缩为简洁的摘要,抓住关键点和核心思想,同时省略次要细节。

  • 问答系统:
    专注于从给定文本中根据用户提出的问题推导出具体答案。其核心在于精确定位并提取与查询相关的信息。

  • 文本分类:
    根据文本内容进行分析,将其系统地归类到预定义的类别或组别中。

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

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

高级技巧

  • Zero-shotFew-shot Learning:
    使模型能够在几乎没有或完全没有特定问题类型的先例情况下做出准确的预测或响应,利用已学习的泛化能力理解和处理新任务。

  • 思维链(Chain-of-Thought):
    连接多个 AI 响应,以创建连贯且具有上下文感知的对话。它帮助 AI 保持讨论的线索,确保相关性和连续性。

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

Microsoft 指导

  • 提示创建与优化框架:
    Microsoft 提供了一种结构化的方法来开发和优化提示。该框架指导用户创建有效的提示,以引导 AI 模型产生期望的响应,从而优化交互的清晰度和效率。

令牌

令牌(Tokens)在人工智能模型处理文本时至关重要,它们作为桥梁将人类理解的语言转化为AI模型能够处理的格式。这一转换过程包含两个阶段:输入时词语被转换为令牌,输出时这些令牌再被转换回词语。

分词(Tokenization)是将文本分解为词元(tokens)的过程,这是人工智能模型理解和处理语言的基础。人工智能模型正是通过这种分词后的格式来理解提示并作出回应。

为了更好地理解令牌(token),你可以把它们看作是单词的一部分。通常,一个令牌大约相当于四分之三个单词。例如,莎士比亚的全集大约有90万字,这大约会转换为120万个令牌。

使用OpenAI 分词器 UI进行实验,看看单词是如何转换成标记的。

代币除了在AI处理中扮演技术角色外,在实际应用层面也具有重要影响,尤其在计费与模型能力方面:

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

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

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

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