提示词 (Prompt)

引言

在生成式 AI 应用中,Prompt(提示)是与大型语言模型(LLM)交互的核心输入格式。Prompt 的设计不仅决定了模型理解任务的准确度,还直接影响生成结果的风格、长度、结构与可控性。随着模型能力和应用场景的不断丰富,Prompt 的结构也从最初的简单文本演进为支持多角色区分、占位符模板化、工具调用等高级特性。本文将系统梳理 Prompt 的演变历程、角色(Role)分类及职责、Spring AI 中 Prompt API 的详细使用方法,以及 PromptTemplate 的模板化实践,并在每个小节提供详尽的文字说明和代码示例。


一、Prompt 的演变历程

Prompt 的演进大致可分为三个阶段:

1.1 初始阶段:简单字符串

背景与特点
  • 实现方式:直接将用户意图用自然语言表述为一条字符串,传入模型。

  • 示例

    复制代码
    Tell me a joke about cats.
  • 优点:上手门槛低,无需额外框架或结构。

  • 局限:无法提供上下文区分,也难以对输出进行精细控制,适用于单轮、简单的任务。

使用场景
  • 问答类 Chatbot 的一次性查询
  • 单条文本生成,如自动撰写短消息或标语

1.2 占位符与参数化

为什么要参数化?
  • 当同一类任务需要多次调用时,硬编码不同 Prompt 会导致维护成本高。
  • 参数化模板能够通过动态替换变量,提高 Prompt 的复用性和可维护性。
基本示例
复制代码
String template = "Tell me a {adjective} joke about {topic}.";
String adjective = "silly";
String topic = "robots";
// 使用简单替换实现参数化
String prompt = template
    .replace("{adjective}", adjective)
    .replace("{topic}", topic);
System.out.println(prompt);
// 输出:Tell me a silly joke about robots.
详细说明
  1. 模板定义 :使用 {} 包裹的占位符表示可替换的变量位置。
  2. 动态赋值:在运行时根据业务逻辑提供不同的变量值。
  3. 字符串替换 :示例中使用 String.replace 实现,适合简单场景,但无法处理复杂数据类型(如列表、条件逻辑)。
参数化模板的优势
  • 高复用性:一份模板可支持多种输入组合。
  • 易于维护:模板与参数分离,修改模板无需改动调用代码。
  • 结构化:在模板中明确标识可变部分,增强可读性。

1.3 多角色消息流与上下文管理

角色化的必要性

随着对话式应用需求的增长,仅靠单条字符串难以维护多轮对话上下文,也无法区分系统指令与用户输入。引入角色(Role)后,每条消息都可携带角色标签,明确其在对话中的作用。

角色类型
  • SYSTEM:设定对话基调、行为规范或全局约束。
  • USER:真实用户的提问或指令。
  • ASSISTANT:模型的回复,用于记录对话历史和保持连贯性。
  • TOOL:外部工具或函数调用的输入/输出信息。
示例代码
复制代码
List<Message> messages = new ArrayList<>();

// 系统角色:初始化对话规则
messages.add(new SystemMessage("You are a professional travel planner. Provide concise and accurate travel advice."));

// 用户角色:用户发起需求
messages.add(new UserMessage("I want to visit Xinjiang in autumn. What do you recommend?"));

// 模型角色:占位符,由模型生成后再添加到列表
// messages.add(new AssistantMessage(modelOutput));

// 下一轮用户角色:继续提问
// messages.add(new UserMessage("What is the weather forecast there for the next 3 days?"));
详细说明
  1. SystemMessage :通过 new SystemMessage(...) 创建,内容为对模型的全局指令。
  2. UserMessage :通过 new UserMessage(...) 创建,内容为用户输入。
  3. AssistantMessage:模型生成的回复,由程序接收并添加,用于多轮对话。
  4. Role 的作用:模型在生成回复时会根据角色区分系统指令与用户对话,确保响应符合预期。

二、Prompt 中的角色(Role)详解

在 Spring AI 中,MessageType 枚举将角色明确定义为四种类型:

复制代码
public enum MessageType {
  SYSTEM("system"),   // 系统指令
  USER("user"),       // 用户输入
  ASSISTANT("assistant"), // 模型回复
  TOOL("tool");       // 工具调用
}

2.1 系统角色(System Role)

  • 主要职责:向模型提供身份设定、业务规则或回答风格。

  • 典型指令

    • "You are a helpful assistant that speaks formally."
    • "Only answer questions related to legal advice."
代码示例
复制代码
Message system = new SystemMessage(
    "You are a financial advisor. Provide concise and accurate investment suggestions in JSON format."
);
扩展说明
  • 可以通过添加元数据(metadata)为系统消息携带额外信息,如时间戳或优先级。
  • 多个 SystemMessage 可按顺序添加,以分阶段设定对话规则。

2.2 用户角色(User Role)

  • 主要职责:传达用户的具体需求或问题。
  • 设计要点:用户消息应清晰、简洁,避免含糊不清的描述。
代码示例
复制代码
Message user = new UserMessage(
    "Please recommend three budget-friendly hotels in Tokyo for a family of four."
);
扩展说明
  • 可在用户消息中列出多步操作,模型会尝试按顺序执行。
  • 对于复杂查询,建议在一条消息中明确所有必要参数,如时间、人数、预算等。

2.3 助手角色(Assistant Role)

  • 主要职责:承载模型的回复内容,同时可触发工具调用。
  • 触发工具调用 :当模型需要执行计算或外部操作时,会在 AssistantMessage 中返回 function_call 字段,表明后续需调用对应工具。
代码示例
复制代码
// 模型生成的回复示例
Message assistant = new AssistantMessage(
    "The weather in Xinjiang for the next 3 days is:\nDay1: 18°C, sunny\nDay2: 20°C, partly cloudy\nDay3: 17°C, light rain"
);
扩展说明
  • 在接收到 AssistantMessage 后,程序应检查是否存在工具调用请求,并根据 tool_namearguments 调用相应工具。
  • 回复中可包含结构化内容(如 JSON、表格等),便于后续解析。

2.4 工具/功能角色(Tool/Function Role)

  • 主要职责:传递外部工具执行结果,如 API 调用、数据库查询或数学运算。
  • 示例场景:天气查询、汇率转换、订单查询等。
代码示例
复制代码
// 工具调用返回示例
Message tool = new ToolMessage(
    "{ \"location\": \"Xinjiang\", \"forecast\": [18,20,17] }"
);
扩展说明
  • ToolMessagecontent 通常为 JSON 格式,程序解析后将结果注入下一次 Prompt。
  • 多个工具调用可串联使用,形成复杂工作流。

三、Spring AI 的 Prompt API 深入

Spring AI 提供了 PromptMessage 等核心类,简化 Prompt 构建与调用流程。以下示例演示完整调用链路,并对关键方法进行详细解读。

3.1 构建 Prompt 实例

复制代码
// 1. 导入依赖:确保已引入 spring-ai 相关库

// 2. 构建消息列表
List<Message> messages = new ArrayList<>();
messages.add(system);
messages.add(user);

// 3. 创建 Prompt 对象
Prompt prompt = new Prompt(messages)
    // 4. 可选:设置 ChatOptions,如温度、最大 Token
    .options(ChatOptions.builder()
        .temperature(0.5)
        .maxTokens(500)
        .build());
详细说明
  1. 消息列表初始化 :使用 ArrayList 存储各角色消息,顺序决定 Prompt 的上下文顺序。
  2. Prompt 构造函数new Prompt(List<Message>) 将消息列表复制到内部。
  3. 链式调用 :通过 options(...) 方法设置模型参数,如温度(temperature)控制随机性,maxTokens 控制最大生成长度。

3.2 调用 ChatModel

复制代码
// 5. 使用 ChatModel 或 ChatClient 发起调用
ChatResponse response = chatModel.call(prompt);

// 6. 解析响应
String reply = response.getResult().getOutput().getContent();
System.out.println("Assistant: " + reply);
详细说明
  • ChatModel.call() :接收 Prompt,发送给底层 LLM 服务,并返回 ChatResponse
  • ChatResponse:封装模型生成的所有输出,包括文本、工具调用请求、Usage 信息等。
  • getResult().getOutput().getContent():提取生成文本内容。

3.3 动态添加消息

在多轮对话中,可根据上一轮回复动态添加消息:

复制代码
// 上一轮回复添加为 AssistantMessage
messages.add(new AssistantMessage(reply));

// 下一轮用户输入
messages.add(new UserMessage("Thank you. Can you also suggest local cuisines?"));

// 重新构建并调用 Prompt
Prompt nextPrompt = new Prompt(messages);
ChatResponse nextResp = chatModel.call(nextPrompt);
详细说明
  • 将模型回复添加到消息列表,保证上下文连贯性。
  • 新的用户消息与历史消息一起传入,模型能够参考整个对话历史。

四、PromptTemplate:高级模板化设计

当 Prompt 逻辑复杂、参数众多时,手动拼接字符串或管理消息列表容易出错。PromptTemplate 基于 StringTemplate 引擎提供了灵活的模板化方案。

复制代码
public class PromptTemplate implements PromptTemplateActions, PromptTemplateMessageActions {
    private final String template;
    public PromptTemplate(String template) {
        this.template = template;
    }
    // render/create 方法由接口提供,具体由 StringTemplate 引擎实现
}

4.1 渲染为纯文本

复制代码
// 无参数模板
PromptTemplate staticTpl = new PromptTemplate("List three facts about the moon.");
String staticPrompt = staticTpl.render();
// staticPrompt == "List three facts about the moon."

// 带参数模板
PromptTemplate paramTpl = new PromptTemplate("List three {adjective} facts about the {subject}.");
String dynamicPrompt = paramTpl.render(Map.of(
    "adjective", "interesting",
    "subject", "moon"
));
// dynamicPrompt == "List three interesting facts about the moon."
详细说明
  • render():直接输出模板内容,适用于静态 Prompt。
  • render(Map):根据键值对替换占位符,适用于简单参数化需求。

4.2 生成 Message 对象

复制代码
// 仅文本消息
Message msg1 = paramTpl.createMessage(Map.of(
    "adjective", "funny",
    "subject", "penguins"
));
// msg1.getContent() -> "List three funny facts about the penguins."
// 默认角色为 USER,可通过重载指定其他角色
详细说明
  • createMessage(Map) :将渲染后的文本封装为 UserMessage,简化消息构建流程。
  • 也可使用重载方法传入媒体列表(图片、音频等),扩展多模态能力。

4.3 生成完整 Prompt

复制代码
// 创建带系统和用户消息的模板
String tpl = "<system>You are {role}.</system>" +
             "<user>Summarize the following text: {text}</user>";
PromptTemplate fullTpl = new PromptTemplate(tpl);

Map<String,Object> data = Map.of(
    "role", "an academic writer",
    "text", "Artificial intelligence is transforming industries..."
);

// 生成 Prompt 并设置选项
Prompt fullPrompt = fullTpl
    .create(data)
    .options(ChatOptions.builder().maxTokens(200).build());

ChatResponse fullResp = chatModel.call(fullPrompt);
System.out.println(fullResp.getResult().getOutput().getContent());
详细说明
  • 模板标签 :使用 <system><user> 标签将渲染内容分配给不同角色。
  • create(Map) :渲染并拆分消息为多条 Message,并封装为 Prompt
  • options(...):为该 Prompt 单独设置模型参数。

五、进阶用法与最佳实践

  1. 分层组织 Prompt

    • 将不同职责的消息(系统、用户、助手、工具)分层组织,保持清晰的调用链路。
    • 例如,先添加所有系统级指令,再添加用户级查询,最后再执行工具调用。
  2. 语义化占位符

    • 使用有意义的占位符名称,如 {userName}{startDate},提高模板可读性和可维护性。
  3. 模板管理

    • 将常用 PromptTemplate 存储在配置文件或数据库中,支持在线更新和版本控制。
    • 结合 CI/CD 流程,实现模板自动化测试与回滚。
  4. 工具调用预留

    • 在模板中预先定义工具调用点,如 Please fetch current weather via function_call.
    • 模型在生成时会自动触发 function_call,程序接收后执行相应工具。
  5. 性能与成本控制

    • 缓存静态 Prompt 或已渲染模板,减少重复渲染开销。
    • 控制消息列表长度,定期对历史对话进行摘要或清理,避免超出模型上下文窗口。
  6. 安全与合规

    • 对用户输入进行校验和清洗,避免注入恶意指令。
    • 记录 Prompt 与响应日志,确保可审计、可追溯。

六、总结

Prompt 设计是生成式 AI 应用的基础,直接影响模型的行为和输出质量。通过理解 Prompt 的演变历程、合理划分角色、熟练使用 Spring AI 提供的 Prompt API 与 PromptTemplate 工具,开发者可以构建出灵活、高效且易维护的对话系统。结合最佳实践,您将能够在各类业务场景中充分发挥 LLM 的潜力,为用户带来流畅、智能的交互体验。


本文示例基于 Spring AI 框架及 StringTemplate 引擎,旨在为开发者提供系统的 Prompt 设计指导。

相关推荐
努力也学不会java1 小时前
【设计模式】 原型模式
java·设计模式·原型模式
方渐鸿1 小时前
【2024】k8s集群 图文详细 部署安装使用(两万字)
java·运维·容器·kubernetes·k8s·运维开发·持续部署
学亮编程手记1 小时前
K8S v1.33 版本主要新特性介绍
java·容器·kubernetes
SEO_juper2 小时前
大型语言模型SEO(LLM SEO)完全手册:驾驭搜索新范式
人工智能·语言模型·自然语言处理·chatgpt·llm·seo·数字营销
Haven-2 小时前
Java-面试八股文-JVM篇
java·jvm·面试
我真的是大笨蛋2 小时前
JVM调优总结
java·jvm·数据库·redis·缓存·性能优化·系统架构
wjs0403 小时前
Git常用的命令
java·git·gitlab
攻城狮7号3 小时前
腾讯混元翻译模型Hunyuan-MT-7B开源,先前拿了30个冠军
人工智能·hunyuan-mt-7b·腾讯混元翻译模型·30个冠军
superlls3 小时前
(算法 哈希表)【LeetCode 349】两个数组的交集 思路笔记自留
java·数据结构·算法
zezexihaha3 小时前
从“帮写文案”到“管生活”:个人AI工具的边界在哪?
人工智能