1.什么是提示词
在人工智能领域,提示词(Prompt
)扮演着至关重要的角色,它宛如一把精准的钥匙,为 AI 大模型开启理解之门。作为向模型输入的关键信息或引导性语句,提示词能够助力模型迅速洞悉问题需求,进而生成精准的回答。
具体而言,提示词功效显著。一方面,它能提升回答准确性,补充背景资料、设定限定条件,让答案更切题,削减模糊性;另一方面,可把控回答走向,限定篇幅、雕琢风格、拿捏语气。甚至还可以激发模型创意潜能,带来新颖思路与独特想法。
从类型上划分,提示词通常可归为两大类别:描述型提示词 与指令型提示词。
描述型提示词恰似一位细致的画师,通过对事物全方位、多层次的刻画,如外观的勾勒、特征的描摹、功能的阐述以及用途与背景的交代等,引导模型生成贴合需求的内容。
指令型提示词则像一位果断的指挥官,以简洁有力的指令,直白地告知模型需要执行的任务,诸如撰写特定主题的文章、运用特定手法描述场景或是按照指定格式回答问题等,让模型清晰知晓行动方向。
2.Spring AI中的提示词(Prompt)
在 Spring AI
中,处理 Prompt
类似于在 Spring MVC
中管理 VIEW
,需要创建动态内容占位符,这些占位符会根据用户请求或应用程序中的其他代码进行替换,从而生成完整的 Prompt
并传递给模型。也类似包含表达式占位符的 SQL
语句。
最初Prompt
只是简单的字符串,但随着技术发展,其逐渐包含了特定输入的占位符,例如 "USER:
" 等,以便模型能够识别不同部分的信息。OpenAI
进一步推动了 Prompt
结构的发展,将多个消息字符串分类为不同角色,如系统角色(SYSTEM
)、用户角色(USER
)、助手角色(ASSISTANT
)和工具 / 函数。
第三章介绍到ChatClient
对象提供了三个prompt()重载方法:
java
chatClient.prompt() .user(("你给我说一个笑话") .call() .content();
chatClient.prompt(new Prompt("你给我说一个笑话")) .call() .content();
chatClient.prompt("你给我说一个笑话") .call() .content();
上述三种方法实现的功能一样,向AI大模型发送用户的消息你给我说一个笑话
。这就是提示词,针对这个提示词可以做进一步的优化,让大模型更清楚的明白你的需求,比如你给我说一个周星驰风格的笑话
。
3.核心接口
3.1 Prompt接口
Prompt
是一个 ModelRequest
表示 AI 模型请求中使用的提示词,它封装了 Message
对象列表和可选的模型请求选项。下面的例子让OpenAI
讲一个笑话使用ChatModel
几种调用写法。
java
//第一种方法
chatModel.call("讲一个笑话");
//第二种方法
chatModel.call(new Prompt("讲一个笑话"));
//第三种方法
var openAiChatOptions = OpenAiChatOptions.builder()
.model("gpt-3.5-turbo")
.temperature(0.4)
.maxTokens(200)
.build();
var prompt = new Prompt("讲一个笑话", openAiChatOptions);
chatModel.call(prompt);
//第四种方法
chatModel.call(new UserMessage("讲一个笑话"));
3.2 Message接口
Message
接口封装了提示词文本、元数据属性集合以及消息分类 MessageType
。每个Message
在提示词中都有不同角色的作用,其内容和意图各不相同。这些角色可以涵盖各种元素,从用户查询到人工智能生成的响应,再到相关背景信息。
对话模型根据根据对话角色区分消息类别,角色与MessageType
有对应映射关系:
角色 | MessageType |
描述 | Content |
---|---|---|---|
SYSTEM(系统) | SystemMessage | 引导人工智能的行为和响应风格,为人工智能如何解释和回复输入设置参数或规则。这类似于在开始对话之前向人工智能提供指令。 | Message |
USER(用户) | UserMessage | 向人工智能提出的问题、命令或陈述。 | Message Media |
ASSISTANT(助手) | AssistantMessage | 人工智能对用户输入的响应。 | Message Media ToolCall |
TOOL(工具) | ToolResponseMessage | 响应工具调用AssistantMessage时返回附加信息。 | Message ToolResponse |
SYSTEM
、USER
和 ASSISTANT
之间存在密切的相互关系。USER
向 SYSTEM
发送请求,SYSTEM
生成 ASSISTANT
响应用户的请求。在这个过程中,SYSTEM
需要理解用户的意图,并根据其生成合适的响应`。
java
@GetMapping("/ai/joke")
public String joke(@RequestParam(value = "message", defaultValue = "给我讲个笑话") String message) {
SystemMessage systemMessage = new SystemMessage("你是一个会讲笑话的智能助手,可以讲各种风格的笑话,比如爆笑、冷笑话、黑色幽默等");
UserMessage userMessage = new UserMessage(message);
List<Message> messages = List.of(systemMessage, userMessage);
String content = this.chatClient.prompt().messages(messages).call().content();
return content;
}
上述例子传给大模型2个Message
,一个是用户的需求UserMessage
,另一个是系统消息SystemMessage
。
Message
接口定义如下
java
public interface Content {
String getContent();
Map<String, Object> getMetadata();
}
public interface Message extends Content {
MessageType getMessageType();
}
多模态消息类型还实现了 MediaContent
接口,提供了媒体内容对象的列表。
java
public interface MediaContent extends Content {
Collection<Media> getMedia();
}
media
字段包含了多模态的输入输出,UserMessage
AssistantMessage
实现MediaContent
接口。
4. PromptTemplate提示词模版
4.1 什么是提示词模板
它是一种预先设计好结构与格式的文本模板,其核心目的是引导大模型生成符合特定需求的输出。开发者通过精心构建这些模板,将任务相关的关键信息、指令以及期望的回答格式等要素融入其中,使得模型在接收到基于模板生成的提示词后,能精准理解任务意图并产出高质量的回复。
在 Spring AI 中,提示模板关键组件PromptTemplate
类。这个类使用由 Terence Parr 开发的开源StringTemplate引擎来构建和管理提示。PromptTemplate
类可以便捷的创建提示词,然后将这些提示词发送到人工智能模型进行处理。
例如现在需要设计一个讲笑话的智能机器人,用户只需要输入笑话的类型和主题,可以生成不同类型的笑话,adjective
topic
参数用户可以动态传入如下所示:
java
@GetMapping("/ai/joke")
public String joke(@RequestParam(value = "adjective", defaultValue = "冷笑话") String adjective,
@RequestParam(value = "topic", defaultValue = "小明") String topic) {
PromptTemplate promptTemplate = new PromptTemplate("给我讲一个{adjective}笑话关于{topic}");
Prompt prompt = promptTemplate.create(Map.of("adjective", adjective, "topic", topic));
return chatClient.prompt(prompt).call().content();
}
shell
❯ curl http://localhost:8080/ai/joke
{ "completion": "小明走进图书馆,问图书管理员:"请问这里有没有那本《冷笑话大全》?" 管理员回答:"有的,不过这本书有点冷,你要小心别着凉了。" 小明疑惑地问:"着凉?一本书怎么会让人着凉呢?" 管理员微笑着说:"因为它是一本冷笑话大全啊!"" }
4.2核心接口
PromptTemplate
类实现了多个接口,这些接口提供了不同的方式创建提示词。
PromptTemplateStringActions
提供最基本的字符串提示词创建。
接口定义:
java
public interface PromptTemplateStringActions {
//将提示模板渲染为最终字符串格式,无需外部输入,适用于没有占位符或动态内容的模板。
String render();
//增强了渲染功能以包含动态内容。它使用 Map<String, Object>,其中映射键是提示模板中的占位符名称,
// 值是要插入的动态内容。
String render(Map<String, Object> model); }
使用方法:
java
//将提示模板渲染为最终字符串格式,无需外部输入,适用于没有占位符或动态内容的模板。
PromptTemplate promptTemplate = new PromptTemplate("没有占位符的提示词");
String promptStr = promptTemplate.render();
System.out.printf("promptStr=%s\n", promptStr);
//增强了渲染功能以包含动态内容。它使用 Map<String, Object>,其中映射键是提示模板中的占位符名称,
// 值是要插入的动态内容。
PromptTemplate promptTemplate1 = new PromptTemplate("有占位符提示词,占位符{str}");
String promptStr1 = promptTemplate1.render(Map.of("str", "helloword"));
System.out.printf("promptStr1=%s\n", promptStr1);
//结果:
promptStr=没有占位符的提示词
promptStr1=有占位符提示词,占位符helloword
PromptTemplateMessageActions
生成和操作Message
对象来创建提示词。
接口定义:
java
public interface PromptTemplateMessageActions {
//建一个没有附加数据的消息对象,用于静态或预定义的消息内容。
Message createMessage();
//扩展消息创建以集成动态内容,接受 Map<String, Object>,其中每个条目代表消息模板中的占位符及其对应的动态值。
Message createMessage(List<Media> mediaList);
//创建具有静态文本和媒体内容的消息对象。
Message createMessage(Map<String, Object> model);
}
使用方法:
java
//建一个没有附加数据的消息对象,用于静态或预定义的消息内容。
PromptTemplate promptTemplate2 = new PromptTemplate("messageActions createMessage静态消息");
Message message = promptTemplate2.createMessage();
System.out.printf("message=%s\n", message.getText());
//扩展消息创建以集成动态内容,接受 Map<String, Object>,其中每个条目代表消息模板中的占位符及其对应的动态值。
PromptTemplate promptTemplate3 = new PromptTemplate("messageActions createMessage,占位符{str}");
Message message1 = promptTemplate3.createMessage(Map.of("str", "helloword"));
System.out.printf("message1=%s\n", message1.getText());
//创建具有静态文本和媒体内容的消息对象。
PromptTemplate promptTemplate = new PromptTemplate("静态文本和媒体内容");
List<Media> mediaList = new ArrayList<>();
mediaList.add(0, new Media(new MimeType("img"), new URL("https://docs.spring.io/spring-ai/reference/_/img/spring-logo.svg")));
UserMessage message2 = (UserMessage) promptTemplate.createMessage(mediaList);
System.out.printf("new message2=%s\n", message2.getMedia().get(0).getData());
//结果:
message=messageActions createMessage静态消息
message1=messageActions createMessage,占位符helloword
new message2=https://docs.spring.io/spring-ai/reference/_/img/spring-logo.svg
PromptTemplateActions
返回Prompt
对象,对象可以传递给ChatModel
以生成响应。
java
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);
}
使用方法:
java
//无需外部数据输入即可生成提示对象,非常适合静态或预定义提示。
PromptTemplate promptTemplate1 = new PromptTemplate("静态或预定义提示");
Prompt prompt = promptTemplate1.create();
System.out.printf("prompt=%s\n", prompt.getContents());
//生成一个无需外部数据输入但带有对话模型请求特定选项的 Prompt 对象。
ZhiPuAiChatOptions chatOptions = new ZhiPuAiChatOptions();
chatOptions.setModel("model");
chatOptions.setMaxTokens(1024);
PromptTemplate promptTemplate4 = new PromptTemplate("带有聊天请求特定选项的 Prompt 对象。");
Prompt prompt1 = promptTemplate4.create(chatOptions);
System.out.printf("prompt1=%s\n", prompt1.getContents());
//扩展提示创建功能以包含动态内容,采用 Map<String, Object>,其中每个映射条目都是提示模板中的占位符及其关联的动态值。
PromptTemplate promptTemplate5 = new PromptTemplate("PromptTemplateActions,占位符{str}");
Message message3 = promptTemplate5.createMessage(Map.of("str", "helloword"));
System.out.printf("message3=%s\n", message3.getText());
//模型特定选项+动态参数
Prompt prompt2 = promptTemplate5.create(Map.of("str", "helloword"), chatOptions);
System.out.printf("prompt2=%s\n", prompt2.getContents());
4.3 多角色提示词
提示词模板也可以应用于不同类型的角色消息,比如SystemMessage
。同时可以讲多个角色Message
组合一起构建Prompt
。
java
@GetMapping("/ai")
String generation() {
String name = "三国百晓生";
String userText = """
讲一讲赤壁之战的故事
""";
Message userMessage = new UserMessage(userText);
String systemText = """
你是一个能讲历史故事的智能助手,尤其精通三国历史。
你的名字是{name}。
""";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemText);
Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name));
Prompt prompt = new Prompt(List.of(userMessage, systemMessage));
String content = chatClient.prompt(prompt)
.advisors(new SimpleLoggerAdvisor()).call().content();
return content;
}
上述例子通过使用系统提示模板(SystemPromptTemplate
)创建一个带有系统角色的消息并传入占位符值来构建(Prompt
)实例。然后将带有用户角色的消息与系统角色的消息相结合构建最终的提示词传给大模型。
4.4 提示词从资源文件导入
Spring AI 支持org.springframework.core.io.Resource
,因此可以将提示词数据放在一个文件中,该文件可直接在PromptTemplate
中使用。例如,可以 使用 @Value
注解从配置文件或环境中注入 Resource
对象。
java
@Value("classpath:/prompts/system-message.st")
private Resource systemResource;
在项目resource/prompts
目录新建文件system-message.st
内容如下:
你是一个能讲历史故事的智能助手,尤其精通三国历史。
你的名字是{name}。
完整例子:
java
@Value("classpath:/prompts/system-message.st")
private Resource systemResource;
@GetMapping("/ai")
String ai() {
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", "智能助手"));
Prompt prompt = new Prompt(List.of(systemMessage));
String content = chatClient.prompt(prompt)
.advisors(new SimpleLoggerAdvisor()).call().content();
return content;
}
5.提示词工程
在生成式人工智能领域,提示词的创建对于开发者而言是一项至关重要的任务。这些提示词的质量和结构会显著影响人工智能输出的效果。花时间和精力精心设计提示词,能大幅提升人工智能给出的结果。 在人工智能社区中,分享和讨论提示词是一种常见做法。这种协作方式不仅营造了共享的学习环境,还能发现并使用高效的提示词。 该领域的研究通常涉及分析和比较不同的提示词,以评估它们在各种情境下的有效性。例如,一项重要研究表明,以 "深呼吸,然后一步步解决这个问题" 作为提示词的开头,能显著提高解决问题的效率。这凸显了精心挑选的措辞对生成式人工智能系统性能的影响。 随着人工智能技术的迅速发展,掌握提示词的最有效用法是一项持续的挑战。你应当认识到提示工程的重要性,并考虑借鉴社区经验和研究成果,改进提示词创建策略。
5.1 创建有效提示词
在创建提示词时,几个关键要素保证清晰和有效至关重要:
- 指令:向 AI 提供清晰直接的指令,类似与人交流的方式。这种清晰度对于帮助 AI"理解"预期内容至关重要。
- 上下文背景 :添加相关背景信息或人工智能响应的具体指导。这些External Context 构成了
Prompt
的框架,并帮助人工智能掌握整体情况。 - 用户输入:用户的直接请求或问题构成了提示词的核心。
- 输出指示器 :指定人工智能回复所需的格式,比如
JSON
格式。不过要注意,人工智能可能并不总是严格遵循这种格式。例如,它可能会在实际的JSON
数据前加上类似"这是你的 JSON"这样的短语,或者有时生成一个类似JSON
但不准确的结构。
在构思提示词时,为人工智能提供预期问答格式的示例会大有效果。这种做法有助于人工智能 "理解" 你提问的结构和意图,从而给出更精确、贴切的回复。
5.2提示词技巧
详细了解提示词工程,可以访问提示词工程指南https://www.promptingguide.ai/ 如下是一些有用的技巧资源。
5.2.1简单技术
-
文本摘要
将大量文本精简为简洁的摘要,捕捉关键点和主要思想,同时省略不太重要的细节。
-
问答
侧重于根据用户提出的问题从提供的文本中得出具体答案。它旨在针对查询精确定位和提取相关信息。
-
文本分类
系统地将文本分类到预定义的类别或组中,分析文本并根据其内容将其分配给最合适的类别。
-
对话
创建交互式对话,使人工智能能够与用户进行来回交流,模拟自然的对话流程。
-
代码生成
根据特定的用户需求或描述生成功能性代码片段,将自然语言指令转化为可执行代码。
5.2.2高级技巧
-
零样本、少样本学习
使模型能够在几乎没有或完全没有特定问题类型的先验示例的情况下做出准确预测或响应,利用所学 的通用知识来理解并处理新任务。
-
思维链
将多个人工智能回复联系起来,以创建连贯且有上下文感知的对话。它有助于人工智能保持讨论的主 线,确保相关性和连续性。
-
ReAct(推理 + 行动)
在这种方法中,人工智能首先分析输入(原因),然后确定最合适的行动或响应方案。它将理解与决策结合在一起。
6.Tokens
Tokens
在人工智能模型处理文本的过程中至关重要,它就像一座桥梁,将我们所理解的单词转换为人工智能模型能够处理的格式。这种转换分两个阶段进行:输入时,单词被转换为Tokens
;输出时,这些Tokens
再转换回单词。
标记化(Tokenization
),即将文本分解为Tokens的过程,是人工智能模型理解和处理语言的基础。人工智能模型通过这种标记化格式来理解并回应提示。
为了更好地理解Tokens,可以将它们看作是单词的片段。通常,一个标记大约代表四分之三个单词。例如,莎士比亚的全部作品大约有 90 万个单词,转换后大约为 120 万个标记。
可以使用 OpenAI Tokenizer UI进行测试,看看单词是如何转换为标记的。
Tokens除了在人工智能处理中发挥技术作用外,在计费和模型能力方面还有实际影响:
计费:人工智能模型服务通常根据Tokens
使用量计费。输入(提示)和输出(回复)都会计入总Tokens
数,因此较短的提示更具成本效益。
模型限制:不同的人工智能模型有不同的标记限制,这决定了它们的 "上下文窗口",即它们一次能够处理的最大信息量。例如,GPT-3
的限制是4000个标记,而像Claude2
和Meta Llama2
等其他模型的限制是10万个Tokens
,一些研究模型最多可以处理100万个Tokens
。
上下文窗口:模型的Tokens
限制决定了其上下文窗口。超过此限制的输入将不会被模型处理。发送仅够处理的最少有效信息集至关重要。例如,当询问关于《哈姆雷特》的问题时,无需包含莎士比亚其他所有作品的Tokens
。
回复元数据:人工智能模型回复的元数据包括使用的Tokens
数量,这是管理使用情况和成本的关键信息。
7.总结
本文围绕与大模型进行有效沟通的提示词(Prompt
)技术全面展开,涵盖基础概念、Spring AI
中的应用、核心接口、提示词模板、提示词工程以及 Tokens
相关知识,为利用提示词优化大模型应用提供了系统性指导。