第4天 - Prompt 工程与模板引擎
系列: Spring AI Alibaba 技术博客系列
日期: 2026-04-28
难度: ⭐⭐⭐
前置知识: ChatModel 基础使用、AI Model 体系
目录
- 什么是 Prompt 工程?
- Spring AI Alibaba 中的 Prompt 模型
- PromptTemplate 模板引擎详解
- 高级模板功能
- Structured Prompt 结构化提示词
- Prompt 工程最佳实践
- 实战案例:多场景 Prompt 管理
- 常见问题与调试技巧
- 总结
1. 什么是 Prompt 工程?
Prompt 工程(Prompt Engineering)是一门研究如何设计和优化输入提示词(Prompt),以引导大语言模型生成高质量、符合预期输出的技术与艺术。
在 Spring AI Alibaba 应用中,Prompt 工程绝非”随便写几句话让 AI 回答”那么简单。一个精心设计的 Prompt 可以:
- 显著提升输出质量:同样的模型,好的 Prompt 和差的 Prompt 产出差距可能天壤之别
- 减少幻觉(Hallucination):通过约束和结构化指令,降低模型编造信息的概率
- 控制输出格式:确保输出符合下游处理所需的格式(JSON、XML、Markdown 等)
- 提高可维护性:将 Prompt 从代码中解耦,使其可配置、可测试、可版本管理
为什么需要模板引擎?
在实际项目中,我们经常遇到以下痛点:
❌ 硬编码在代码中:难以修改,不利于协作
❌ 拼接字符串:容易出错,缺乏类型安全
❌ 重复内容多:相似的 Prompt 无法复用
❌ 无法动态化:参数无法灵活替换
Spring AI Alibaba 提供了完善的 PromptTemplate 模板引擎,优雅地解决了上述问题。
2. Spring AI Alibaba 中的 Prompt 模型
在深入模板引擎之前,我们先理清 Spring AI Alibaba 中 Prompt 相关的核心类及其关系:
┌─────────────────────────────────────────────────┐
│ Prompt │
│ ┌─────────────────┐ ┌───────────────────────┐ │
│ │ SystemMessage │ │ UserMessage │ │
│ │ (系统指令) │ │ (用户输入 + 变量) │ │
│ └─────────────────┘ └───────────────────────┘ │
│ ┌───────────────────────┐ │
│ │ AssistantMessage │ │
│ │ (对话历史/模型回复) │ │
│ └───────────────────────┘ │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ PromptTemplate │
│ 模板文件(.txt/.yaml) + 变量占位符 → Prompt │
└─────────────────────────────────────────────────┘
核心类说明
| 类名 | 作用 | 说明 |
|---|---|---|
Message | 消息接口 | 所有消息类型的父接口 |
SystemMessage | 系统消息 | 设定模型的行为和角色 |
UserMessage | 用户消息 | 用户的输入内容 |
AssistantMessage | 助手消息 | 模型的回复或对话历史 |
Prompt | 提示词封装 | 包含一组 Message,发送给模型 |
PromptTemplate | 模板引擎 | 将模板+变量生成 Prompt |
ChatPromptTemplate | 对话模板 | 支持多轮对话的模板 |
3. PromptTemplate 模板引擎详解
3.1 基础使用
Maven 依赖
确保你的项目已引入 Spring AI Alibaba 依赖:
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0.2</version>
</dependency>
最简单的模板
步骤一:创建模板文件
在 src/main/resources/prompts/ 目录下创建 greeting.txt:
你是一个友好的助手。请用 {language} 语言,用 {tone} 的语气,
向 {name} 打招呼。请确保回答不超过 50 个字。
模板中使用 {variableName} 语法定义变量占位符。
步骤二:在 Java 代码中使用
package com.example.demo.controller;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Value;
import java.util.Map;
@RestController
@RequestMapping("/api/prompt")
public class PromptTemplateController {
@Autowired
private ChatModel chatModel;
@Value("classpath:/prompts/greeting.txt")
private Resource greetingTemplate;
@GetMapping("/greet")
public String greet(
@RequestParam(defaultValue = "中文") String language,
@RequestParam(defaultValue = "热情") String tone,
@RequestParam(defaultValue = "老兰") String name) {
// 1. 创建模板实例,传入 Resource
PromptTemplate template = new PromptTemplate(greetingTemplate);
// 2. 替换变量,生成 Prompt
Prompt prompt = template.create(Map.of(
"language", language,
"tone", tone,
"name", name
));
// 3. 调用模型
ChatResponse response = chatModel.call(prompt);
return response.getResult().getOutput().getText();
}
}
请求示例:
curl "http://localhost:8080/api/prompt/greet?language=中文&tone=幽默&name=老兰"
输出示例:
嘿,老兰!今天过得怎么样?我这心情好得就像中了彩票一样,有啥事儿尽管说,包在我身上!😄
3.2 系统消息 + 用户消息的组合模板
实际场景中,我们通常需要同时设置系统提示词和用户提示词。这时可以使用 ChatPromptTemplate:
模板文件 src/main/resources/prompts/code-review.yaml:
messages:
- role: SYSTEM
content: |
你是一位资深的 Java 代码审查专家,拥有 15 年以上的开发经验。
你的审查标准包括:
1. 代码规范性(命名、格式、注释)
2. 性能问题(循环、IO、内存)
3. 安全隐患(SQL注入、XSS、敏感信息泄露)
4. 架构设计(职责单一、依赖倒置)
请用 Markdown 格式输出审查报告,包含以下章节:
- 📋 总体评价
- ✅ 优点
- ❌ 问题(按严重程度排序)
- 💡 改进建议
- role: USER
content: "请审查以下 Java 代码:\n{code}"
Java 代码:
package com.example.demo.service;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.ChatPromptTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;
import java.util.Map;
@Service
public class CodeReviewService {
@Autowired
private ChatModel chatModel;
@Value("classpath:/prompts/code-review.yaml")
private Resource codeReviewTemplate;
public String reviewCode(String sourceCode) {
// ChatPromptTemplate 会自动解析 YAML 中的多消息结构
ChatPromptTemplate template = new ChatPromptTemplate(codeReviewTemplate);
Prompt prompt = template.create(Map.of("code", sourceCode));
ChatResponse response = chatModel.call(prompt);
return response.getResult().getOutput().getText();
}
}
3.3 消息参数(Message Parameters)
除了简单变量替换,Spring AI 还支持为模板中的消息设置额外参数(metadata):
import org.springframework.ai.chat.messages.MessageParameter;
// 为特定消息设置 temperature 等参数
ChatPromptTemplate template = new ChatPromptTemplate(
"""
messages:
- role: SYSTEM
content: "你是一个专业的翻译助手。"
- role: USER
content: "请将以下内容翻译成 {targetLanguage}:{text}"
parameters:
temperature: 0.3
"""
);
// YAML 中也可以定义参数
4. 高级模板功能
4.1 条件渲染
Spring AI Alibaba 的模板引擎支持条件渲染,根据变量值决定是否渲染某段内容:
你是一个{role}助手。
{{#if includeExamples}}
以下是一些示例:
{{examples}}
{{/if}}
{{#unless includeExamples}}
请直接回答问题,不需要提供示例。
{{/unless}}
用户问题:{question}
代码使用:
PromptTemplate template = new PromptTemplate(templateResource);
// 不渲染示例部分
Prompt prompt1 = template.create(Map.of(
"role", "翻译",
"includeExamples", false,
"question", "什么是 AI?"
));
// 渲染示例部分
Prompt prompt2 = template.create(Map.of(
"role", "翻译",
"includeExamples", true,
"examples", "示例1:...\n示例2:...",
"question", "什么是 AI?"
));
4.2 循环渲染
当需要渲染列表数据时,可以使用循环语法:
你是一个技术文档助手。请根据以下 API 列表生成文档:
{{#each apis}}
## {{name}}
- **路径:** {{path}}
- **方法:** {{method}}
- **描述:** {{description}}
{{/each}}
请确保文档格式统一,每个 API 都包含请求参数和响应示例。
代码使用:
record ApiDoc(String name, String path, String method, String description) {}
List<ApiDoc> apis = List.of(
new ApiDoc("用户登录", "/api/auth/login", "POST", "用户认证登录"),
new ApiDoc("获取用户信息", "/api/users/{id}", "GET", "获取指定用户的详细信息"),
new ApiDoc("更新用户资料", "/api/users/{id}", "PUT", "更新用户的个人资料")
);
Prompt prompt = template.create(Map.of("apis", apis));
生成效果:
你是一个技术文档助手。请根据以下 API 列表生成文档:
## 用户登录
- **路径:** /api/auth/login
- **方法:** POST
- **描述:** 用户认证登录
## 获取用户信息
- **路径:** /api/users/{id}
- **方法:** GET
- **描述:** 获取指定用户的详细信息
## 更新用户资料
- **路径:** /api/users/{id}
- **方法:** PUT
- **描述:** 更新用户的个人资料
请确保文档格式统一,每个 API 都包含请求参数和响应示例。
4.3 多语言 Prompt 管理
对于国际化应用,可以在不同目录下管理不同语言的 Prompt:
src/main/resources/
└── prompts/
├── zh/
│ ├── greeting.txt
│ └── code-review.yaml
├── en/
│ ├── greeting.txt
│ └── code-review.yaml
└── ja/
├── greeting.txt
└── code-review.yaml
@Service
public class I18nPromptService {
@Autowired
private ApplicationContext applicationContext;
public Prompt loadPrompt(String language, String templateName, Map<String, Object> variables) {
// 根据语言加载对应模板
String resourcePath = String.format("classpath:/prompts/%s/%s.txt", language, templateName);
Resource templateResource = applicationContext.getResource(resourcePath);
PromptTemplate template = new PromptTemplate(templateResource);
return template.create(variables);
}
}
5. Structured Prompt 结构化提示词
5.1 什么是 Structured Prompt?
Spring AI 2.0+ 引入了结构化 Prompt 的概念,允许使用更丰富的结构来组织提示词,包括多个消息、工具定义、输出格式约束等。
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.messages.AssistantMessage;
import java.util.List;
// 构建包含对话历史的结构化 Prompt
Prompt prompt = new Prompt(List.of(
new SystemMessage("""
你是一位专业的 Java 技术面试官。
请根据候选人的回答进行评估,并给出评分(1-10分)和评语。
"""),
new UserMessage("请解释 Java 中的垃圾回收机制。"),
new AssistantMessage("垃圾回收(GC)是 Java 虚拟机自动管理内存的机制..."),
new UserMessage("那么 G1 收集器和 CMS 收集器有什么区别呢?")
));
ChatResponse response = chatModel.call(prompt);
5.2 输出格式约束
通过系统提示词强制模型输出特定格式:
@Service
public class JsonOutputService {
@Autowired
private ChatModel chatModel;
public String extractEntities(String text) {
String systemPrompt = """
你是一个信息抽取专家。请从用户输入的文本中提取以下信息:
- 人名(person)
- 地点(location)
- 时间(date)
- 组织(organization)
你必须严格按照以下 JSON Schema 格式输出,不要包含任何其他内容:
{
"entities": {
"persons": ["字符串数组"],
"locations": ["字符串数组"],
"dates": ["字符串数组"],
"organizations": ["字符串数组"]
}
}
如果某类信息不存在,对应的数组为空数组 []。
""";
Prompt prompt = new Prompt(List.of(
new SystemMessage(systemPrompt),
new UserMessage("请分析:2026年4月28日,张三在北京参加了阿里巴巴组织的AI技术大会。")
));
ChatResponse response = chatModel.call(prompt);
return response.getResult().getOutput().getText();
}
}
5.3 使用 Bean Output Converter(推荐方式)
Spring AI 提供了更优雅的方式将模型输出映射为 Java Bean:
package com.example.demo.dto;
// 定义输出 Bean
public class EntityExtractionResult {
private List<String> persons;
private List<String> locations;
private List<String> dates;
private List<String> organizations;
// getters, setters, toString...
public List<String> getPersons() { return persons; }
public void setPersons(List<String> persons) { this.persons = persons; }
public List<String> getLocations() { return locations; }
public void setLocations(List<String> locations) { this.locations = locations; }
public List<String> getDates() { return dates; }
public void setDates(List<String> dates) { this.dates = dates; }
public List<String> getOrganizations() { return organizations; }
public void setOrganizations(List<String> organizations) { this.organizations = organizations; }
}
@Service
public class StructuredOutputService {
@Autowired
private ChatModel chatModel;
public EntityExtractionResult extractEntities(String text) {
// 使用 BeanOutputConverter 自动将 JSON 映射为 Java 对象
BeanOutputConverter<EntityExtractionResult> converter =
new BeanOutputConverter<>(EntityExtractionResult.class);
String formatInstructions = converter.getFormatInstructions();
String systemPrompt = """
你是一个信息抽取专家。请从用户输入的文本中提取人名、地点、时间和组织。
%s
如果某类信息不存在,对应字段为空数组。
""".formatted(formatInstructions);
Prompt prompt = new Prompt(List.of(
new SystemMessage(systemPrompt),
new UserMessage(text)
));
ChatResponse response = chatModel.call(prompt);
// 自动解析 JSON 并映射为 Java Bean
return converter.convert(response.getResult().getOutput().getText());
}
}
这是 Spring AI 推荐的 Structured Output 方式,类型安全且易于维护。
6. Prompt 工程最佳实践
6.1 角色设定(Role Prompting)
始终在系统消息中为模型设定明确的角色:
# ✅ 好的做法
messages:
- role: SYSTEM
content: |
你是一位拥有 20 年经验的高级软件架构师,
精通分布式系统、微服务架构和云原生技术。
你擅长用简洁清晰的方式解释复杂的技术概念。
- role: USER
content: "{question}"
# ❌ 不好的做法
messages:
- role: SYSTEM
content: "你是一个助手。" # 过于笼统,模型行为不可控
6.2 提供上下文和示例(Few-shot Prompting)
通过提供少量示例,可以显著提升模型输出的准确性和一致性:
你是一个 SQL 生成助手。请根据用户的自然语言描述生成对应的 SQL 查询。
表结构:
- users(id, name, email, department_id, created_at)
- departments(id, name, location)
示例:
用户输入:找出所有在北京部门的员工
SQL:SELECT u.* FROM users u JOIN departments d ON u.department_id = d.id WHERE d.location = '北京'
用户输入:统计每个部门的员工数量
SQL:SELECT d.name, COUNT(u.id) as count FROM departments d LEFT JOIN users u ON d.id = u.department_id GROUP BY d.id, d.name
用户输入:{naturalLanguage}
SQL:
6.3 分步思考(Chain of Thought)
对于复杂任务,引导模型分步思考:
请按以下步骤解决问题:
1. 首先,理解用户的需求,复述核心问题
2. 其次,分析问题涉及的技术要点
3. 然后,给出解决方案的详细步骤
4. 最后,总结关键要点和注意事项
用户问题:{question}
6.4 输出格式约束
明确指定输出格式,避免模型自由发挥:
请按以下格式回答:
## 概述
[2-3句话概括核心答案]
## 详细分析
[分点说明,每点不超过 100 字]
## 代码示例
[如有,提供可运行的代码]
## 注意事项
- [列出关键注意事项]
用户问题:{question}
6.5 温度参数调优
不同的任务需要不同的 temperature 设置:
| 场景 | Temperature | 说明 |
|---|---|---|
| 代码生成 | 0.1-0.3 | 需要确定性输出 |
| 翻译 | 0.2-0.4 | 需要准确一致 |
| 摘要 | 0.3-0.5 | 平衡准确性和创造性 |
| 创意写作 | 0.7-0.9 | 需要多样性和创意 |
| 头脑风暴 | 0.8-1.0 | 最大化创意输出 |
在 Spring AI 中设置 temperature:
Prompt prompt = new Prompt(List.of(
new SystemMessage("你是一个创意写作助手。"),
new UserMessage("请写一个关于 AI 的短故事。")
), ChatOptions.builder()
.temperature(0.8)
.maxTokens(500)
.build());
6.6 Prompt 长度管理
- 注意 Token 限制:不同模型有不同的上下文窗口,超出会被截断
- 精简冗余信息:去掉不必要的描述,保留核心指令
- 优先级排序:最重要的指令放在最前面(模型对开头和结尾的内容更敏感)
7. 实战案例:多场景 Prompt 管理
下面展示一个完整的实战案例:在 Spring Boot 项目中统一管理多个场景的 Prompt。
7.1 项目结构
src/main/
├── java/com/example/demo/
│ ├── config/
│ │ └── PromptConfig.java # Prompt 配置
│ ├── service/
│ │ └── PromptService.java # Prompt 服务
│ └── controller/
│ └── PromptController.java # API 接口
└── resources/
└── prompts/
├── system-rules.txt # 全局系统规则
├── scenarios/
│ ├── code-review.yaml # 代码审查
│ ├── sql-generator.txt # SQL 生成
│ ├── document-summarizer.txt # 文档摘要
│ └── bug-analyzer.yaml # Bug 分析
└── examples/
└── code-review-examples.txt # Few-shot 示例
7.2 配置类
package com.example.demo.config;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class PromptConfig {
@Value("classpath:/prompts/system-rules.txt")
private Resource systemRules;
@Value("classpath:/prompts/scenarios/code-review.yaml")
private Resource codeReviewPrompt;
@Value("classpath:/prompts/scenarios/sql-generator.txt")
private Resource sqlGeneratorPrompt;
@Value("classpath:/prompts/scenarios/document-summarizer.txt")
private Resource documentSummarizerPrompt;
@Value("classpath:/prompts/scenarios/bug-analyzer.yaml")
private Resource bugAnalyzerPrompt;
/**
* 将所有 Prompt 模板注册为 Bean,方便注入使用
*/
@Bean
public Map<String, Resource> promptTemplates() {
Map<String, Resource> templates = new HashMap<>();
templates.put("code-review", codeReviewPrompt);
templates.put("sql-generator", sqlGeneratorPrompt);
templates.put("document-summarizer", documentSummarizerPrompt);
templates.put("bug-analyzer", bugAnalyzerPrompt);
return templates;
}
}
7.3 Prompt 服务
package com.example.demo.service;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.chat.prompt.ChatPromptTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class PromptService {
private static final Logger log = LoggerFactory.getLogger(PromptService.class);
@Autowired
private ChatModel chatModel;
@Autowired
private Map<String, Resource> promptTemplates;
// 模板缓存,避免重复加载
private final Map<String, PromptTemplate> templateCache = new ConcurrentHashMap<>();
/**
* 执行 Prompt 模板调用
*
* @param scenarioName 场景名称
* @param variables 模板变量
* @return AI 回复结果
*/
public String execute(String scenarioName, Map<String, Object> variables) {
Resource templateResource = promptTemplates.get(scenarioName);
if (templateResource == null) {
throw new IllegalArgumentException("未知的 Prompt 场景: " + scenarioName);
}
try {
// 获取或创建模板实例
PromptTemplate template = templateCache.computeIfAbsent(scenarioName,
key -> createTemplate(templateResource));
// 创建 Prompt 并调用模型
Prompt prompt = template.create(variables);
log.info("执行 Prompt 场景: {}, 变量: {}", scenarioName, variables.keySet());
ChatResponse response = chatModel.call(prompt);
String result = response.getResult().getOutput().getText();
log.info("Prompt 场景 {} 执行完成,结果长度: {}", scenarioName, result.length());
return result;
} catch (Exception e) {
log.error("Prompt 场景 {} 执行失败", scenarioName, e);
throw new RuntimeException("Prompt 执行失败: " + e.getMessage(), e);
}
}
private PromptTemplate createTemplate(Resource resource) {
String filename = resource.getFilename();
if (filename != null && filename.endsWith(".yaml")) {
return new ChatPromptTemplate(resource);
}
return new PromptTemplate(resource);
}
}
7.4 Controller 层
package com.example.demo.controller;
import com.example.demo.service.PromptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api/ai")
public class PromptController {
@Autowired
private PromptService promptService;
@PostMapping("/code-review")
public Map<String, String> codeReview(@RequestBody CodeReviewRequest request) {
String result = promptService.execute("code-review", Map.of(
"code", request.code(),
"language", request.language() != null ? request.language() : "Java"
));
return Map.of("result", result);
}
@PostMapping("/sql")
public Map<String, String> generateSql(@RequestBody SqlRequest request) {
String result = promptService.execute("sql-generator", Map.of(
"description", request.description(),
"schema", request.schema()
));
return Map.of("sql", result);
}
@PostMapping("/summarize")
public Map<String, String> summarize(@RequestBody SummarizeRequest request) {
String result = promptService.execute("document-summarizer", Map.of(
"content", request.content(),
"maxLength", request.maxLength() != null ? request.maxLength() : "200"
));
return Map.of("summary", result);
}
// Request 记录类
public record CodeReviewRequest(String code, String language) {}
public record SqlRequest(String description, String schema) {}
public record SummarizeRequest(String content, String maxLength) {}
}
7.5 模板文件示例
prompts/scenarios/document-summarizer.txt:
你是一位专业的文档摘要专家。请将以下文档内容进行摘要。
要求:
1. 保留文档的核心观点和关键信息
2. 摘要长度不超过 {maxLength} 字
3. 使用清晰的条理结构(分点列出)
4. 如果文档包含代码,请保留关键代码片段的说明
5. 不要添加原文中没有的信息
待摘要的文档:
---
{content}
---
8. 常见问题与调试技巧
8.1 Prompt 调试方法
@Service
public class PromptDebugger {
@Autowired
private ChatModel chatModel;
/**
* 打印完整的 Prompt 内容,方便调试
*/
public void debugPrompt(String templatePath, Map<String, Object> variables) {
Resource resource = new ClassPathResource(templatePath);
PromptTemplate template = new PromptTemplate(resource);
Prompt prompt = template.create(variables);
// 打印渲染后的 Prompt
System.out.println("=== 渲染后的 Prompt ===");
prompt.getMessages().forEach(msg -> {
System.out.println("[" + msg.getMessageType() + "]: " + msg.getText());
System.out.println("---");
});
// 调用模型
ChatResponse response = chatModel.call(prompt);
System.out.println("=== AI 回复 ===");
System.out.println(response.getResult().getOutput().getText());
}
}
8.2 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 变量未被替换 | 变量名不匹配 | 检查模板中的 {name} 和代码中的 key 是否一致 |
| 输出格式不符合预期 | 缺少格式约束 | 在系统提示词中明确指定输出格式 |
| 模型回答太短 | Token 限制或 temperature 过低 | 调整 maxTokens 和 temperature 参数 |
| 模型编造信息(幻觉) | 缺少约束或上下文不足 | 添加 “如果不知道,请明确说明” 的指令 |
| Prompt 过长导致截断 | 超出模型上下文窗口 | 精简 Prompt 或使用分块处理 |
| YAML 模板解析失败 | 缩进错误或格式不对 | 检查 YAML 缩进(必须使用空格,不能用 Tab) |
8.3 Prompt 版本管理建议
// 在文件名中加入版本号
// prompts/v1/code-review.yaml
// prompts/v2/code-review.yaml
@Service
public class VersionedPromptService {
private final Map<String, Resource> promptVersions = Map.of(
"code-review-v1", new ClassPathResource("/prompts/v1/code-review.yaml"),
"code-review-v2", new ClassPathResource("/prompts/v2/code-review.yaml")
);
public String execute(String scenario, String version, Map<String, Object> variables) {
String key = scenario + "-" + version;
Resource resource = promptVersions.get(key);
// ...
}
}
9. 总结
本文深入介绍了 Spring AI Alibaba 中的 Prompt 工程与模板引擎,核心要点如下:
核心知识点回顾
- PromptTemplate 是最基础的模板引擎,支持简单的变量替换(
{variable}语法) - ChatPromptTemplate 支持 YAML 格式的多消息模板(System + User + Assistant)
- 条件渲染(
{{#if}})和循环渲染({{#each}})让模板更加灵活 - Structured Prompt 通过
BeanOutputConverter实现类型安全的结构化输出 - Prompt 工程最佳实践:角色设定、Few-shot 示例、分步思考、格式约束、温度调优
Prompt 设计检查清单
在将 Prompt 投入生产环境之前,请确认:
- 是否设定了明确的角色?
- 是否提供了足够的上下文?
- 是否有输出格式约束?
- 是否考虑了边界情况?
- 是否进行了充分的测试?
- 温度等参数是否合理?
- Prompt 长度是否在模型限制内?
下一篇预告
第5天我们将探讨 系统提示词与用户提示词的最佳实践,包括如何设计高效的全局系统规则、如何在不同场景间共享和复用系统提示词,以及系统提示词的安全注意事项。