夜雨聆风学习资料网

ARTICLE · 1055242

[AI工程] Spring AI第四篇:提示词使用与设置技巧(2.0)

[AI工程] Spring AI第四篇:提示词使用与设置技巧(2.0)

💡 很多人把大模型效果不好归因于“模型不够强”,但实际项目里,更常见的问题是:任务描述模糊、上下文缺失、输出格式不可控。

同一个模型,提示词从“帮我写篇文章”改成“面向什么读者、写多长、按什么结构、禁止输出什么”,结果往往会有明显差异。

Spring AI 2.0 把提示词、消息角色、模板渲染和 ChatClient 做成了统一的工程能力。这篇文章从基础 Message、PromptTemplate 到可复用 Prompt 文件,讲清楚提示词该怎么写。


1. Prompt 不只是一个字符串

在 Spring AI 中,一次模型调用由多个 Message 组成。不同消息角色承担不同责任:

类型
作用
典型内容
SystemMessage
定义全局规则与行为边界
身份、语气、输出格式、安全限制
UserMessage
用户当前输入
问题、任务、材料、需求
AssistantMessage
模型历史回复
对话上下文
ToolResponseMessage
工具执行结果
天气、订单、数据库查询结果

可以简单理解成:

SystemMessage  --> 规定 AI 应该怎样做UserMessage    --> 告诉 AI 这次要做什么ToolResponse   --> 给 AI 提供外部事实Assistant      --> 保留此前的对话结果

系统提示词不是万能指令。它的职责是定义长期稳定的规则;当前任务、用户材料和动态参数,应该放在用户消息或工具结果中。

String content = chatClient.prompt()        .system("""                你是程序员GC的技术助手。                回答使用中文,结论优先。                不确定的信息必须明确说明。                """)        .user("解释 Spring AI 中 Advisor 的作用。")        .call()        .content();

2. PromptTemplate:把动态内容从字符串拼接中解放出来

提示词通常不能写死。例如生成影评、产品介绍、知识库问答时,角色、风格、主题和上下文都需要动态传入。

Spring AI 2.0 默认使用 StTemplateRenderer,基于 StringTemplate。默认变量分隔符是 {}

String systemText = """        你是一个友好的 AI 助手。        你的名字是 {name}。        请使用 {voice} 的风格回答用户问题。        """;PromptTemplate template = PromptTemplate.builder()        .template(systemText)        .build();String systemPrompt = template.render(        Map.of(                "name""程序员GC",                "voice""专业但易懂"        ));Prompt prompt = new Prompt(List.of(        new SystemMessage(systemPrompt),        new UserMessage("请解释什么是 RAG")));ChatResponse response = chatModel.call(prompt);System.out.println(        response.getResult().getOutput().getText());

如果使用 ChatClient,代码会更自然:

String answer = ChatClient.create(chatModel)        .prompt()        .user(user -> user                .text("列出 5 部由 {director} 执导的代表电影。")                .param("director""周星驰"))        .call()        .content();

模板变量解决“动态内容”;System Prompt 解决“长期规则”;User Prompt 解决“当前任务”。

官方说明:Spring AI Prompt 文档。


3. JSON 与模板冲突时:自定义分隔符

默认 {} 很方便,但当 Prompt 中包含 JSON Schema、JSON 示例或代码片段时,容易和变量占位符混淆。

这时可以改用 <变量名>

PromptTemplate template = PromptTemplate.builder()        .renderer(                StTemplateRenderer.builder()                        .startDelimiterToken('<')                        .endDelimiterToken('>')                        .build()        )        .template("""                请介绍 5 部 <director> 的代表电影。                输出 JSON:                {                  "movies": [                    {"name": "", "year": 0}                  ]                }                """)        .build();String prompt = template.render(        Map.of("director""周星驰"));System.out.println(prompt);

ChatClient 也可以在单次请求中指定渲染器:

String answer = ChatClient.create(chatModel)        .prompt()        .user(user -> user                .text("列出 5 部 <director> 的电影。")                .param("director""周星驰"))        .templateRenderer(                StTemplateRenderer.builder()                        .startDelimiterToken('<')                        .endDelimiterToken('>')                        .build()        )        .call()        .content();

如果 Prompt 完全没有变量,或内容本身包含大量 {},也可以考虑 NoOpTemplateRenderer,避免无意义的模板解析。


4. Prompt 文件化:别把长提示词塞进 Java 代码

系统提示词一旦超过几十行,继续放在 Java 字符串里会越来越难维护。更实用的方式是将它们放到资源目录。

目录结构:

src/main/resources/  prompts/    tech-blog-system.st

tech-blog-system.st

你是程序员GC的技术博客助手。任务:- 用中文回答;- 优先给出结论;- 使用 Markdown;- 涉及不确定事实时明确标注;- 避免营销语气。当前文章主题:{topic}目标读者:{audience}

在 ChatClient 中加载:

@SpringBootTestclass PromptFileTest {    @Test    void testPrompt(            @Autowired DeepSeekChatModel chatModel,            @Value("classpath:/prompts/tech-blog-system.st")            Resource systemResource    ) {        ChatClient chatClient = ChatClient.builder(chatModel)                .defaultSystem(systemResource)                .build();        String content = chatClient.prompt()                .system(system -> system                        .param("topic""Spring AI Advisor")                        .param("audience""Java 后端开发者"))                .user("请写一个 300 字左右的入门介绍。")                .call()                .content();        System.out.println(content);    }}

这样做有三个好处:

  • Prompt 可以独立 Review、测试和版本管理;
  • 产品、运营与开发可以协作修改;
  • 避免系统提示词散落在 Controller、Service 和测试类中。

5. 提示词设计:从“让模型猜”变成“给模型明确任务”

5.1 指令要明确

❌ 模糊写法:

写一篇文章。

✅ 清晰写法:

写一篇约 800 字的中文技术文章。主题:Spring AI 中的 ChatClient。读者:有 Spring Boot 基础的 Java 开发者。结构:引言、三个核心能力、一个代码示例、总结。风格:专业直接,避免营销用语。输出:Markdown。

提示词可以套用这个公式:

角色设定  + 具体任务  + 必要上下文  + 输出格式  + 限制条件  + 示例参考

例如旅游助手:

# 角色你是一位专业、耐心的旅行规划师。# 任务根据用户预算、天数、出发城市和偏好设计旅行路线。# 输出格式1. 每日行程2. 交通建议3. 住宿区域建议4. 预算估算5. 注意事项# 限制- 只讨论旅行相关内容;- 不提供违法或高风险活动建议;- 信息不确定时明确提示用户核实。

5.2 用结构降低歧义

Markdown 标题、列表、表格和 XML 标签都可以帮助模型理解任务层次。

<背景>用户是 Java 后端开发者,刚接触 Spring AI。</背景><任务>解释 ChatClient 与 ChatModel 的区别。</任务><输出要求>- 使用表格;- 给出一个最小代码示例;- 控制在 500 字以内。</输出要求>

比起“你懂我的意思吧”“写详细一点”“差不多就行”,这种表达更可测试、更稳定。

5.3 常见任务的 Prompt 方向

场景
关键约束
文本摘要
原文范围、目标字数、保留哪些信息
问答
可用资料、事实边界、不足时如何回答
文本分类
分类标签、分类标准、固定输出格式
对话助手
身份、语气、权限、升级人工的条件
代码生成
技术栈、版本、接口、异常处理、测试要求
结构化输出
JSON Schema、字段含义、缺失字段处理规则

6. 高级技巧:Few-shot、ReAct 与工程边界

提示词不是越长越好,而是要让模型得到完成任务所需的最小充分信息。

Few-shot:给模型一个正确示例

将用户问题分类为:订单、退款、物流、其他。示例:输入:我的快递什么时候到?输出:物流输入:我想取消订单。输出:订单现在开始分类:输入:退款多久到账?输出:

Few-shot 特别适合分类、格式转换、固定文风和结构化输出。

CoT 与 ReAct:不要把“思考过程”当成用户内容

CoT 更适合拆解复杂问题;ReAct 是“推理 + 工具调用”的模式。

用户问题  --> 分析需要什么信息  --> 调用工具查询  --> 根据工具结果回答

在工程实践中,更重要的是让模型调用受控工具、返回可验证结果,而不是要求它输出冗长的思维过程。涉及订单、支付、数据库等业务时,工具权限、参数校验、审计与幂等性必须由应用负责。

Prompt 可以影响模型行为,但不能替代权限控制、数据校验和业务规则。


最后总结

提示词工程不是“写几句更有魔力的话”,而是把模型任务描述成可执行、可验证、可维护的规则。

  • 如果只是简单问答: 明确任务、上下文和输出格式。

  • 如果是企业级应用: 将 System Prompt 文件化、模板化,并通过 ChatClient 统一管理。

  • 如果涉及工具、RAG 与 Agent: 用户输入、检索内容和工具结果都应视为不可信数据;权限与关键决策必须留在应用侧。

对于 Java 开发者,Spring AI 2.0 的价值在于:把 PromptTemplate、Message、ChatClient、Advisor 和 Tool Calling 组织成一套工程化能力,而不是散落的字符串拼接。

参考资料 & 致谢

[1] Spring AI Prompt 官方文档 [2] Spring AI ChatClient 文档 [3] Spring AI Prompt Engineering Patterns [4] Spring AI 官方项目 [5] 提示词使用与设置技巧参考资料(语雀)

相关学习资料