如何配置Spring AI将格式信息置于Prompt开头或手动生成该信息?
解决方案:调整Spring AI结构化输出的Prompt位置以适配OpenAI缓存
一、当前Spring AI的限制
截至当前版本,Spring AI没有内置配置项可以直接将结构化输出的格式信息移至Prompt开头。官方默认逻辑是将格式约束追加在用户Prompt之后,虽能保证用户输入优先级,但与OpenAI Prompt缓存要求(静态前缀匹配)冲突。
二、手动调整方案
方法1:自定义Prompt模板,手动前置格式信息
直接生成结构化输出的格式描述,将其作为Prompt前缀拼接用户可变内容:
import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.parser.BeanOutputParser; import org.springframework.ai.prompt.Prompt; import org.springframework.ai.prompt.PromptTemplate; // 定义返回的DTO类 record UserInfo(String name, Integer age) {} public class CustomPromptDemo { public static void main(String[] args) { OpenAiChatModel chatModel = new OpenAiChatModel(new OpenAiApi("your-api-key")); // 生成结构化输出的格式解析器与约束文本 BeanOutputParser<UserInfo> parser = new BeanOutputParser<>(UserInfo.class); String formatInstructions = parser.getFormatInstructions(); // 构建格式信息前置的Prompt模板 String promptTemplateStr = """ {formatInstructions} 请提取以下内容中的用户信息: {userInput} """; PromptTemplate promptTemplate = new PromptTemplate(promptTemplateStr); promptTemplate.add("formatInstructions", formatInstructions); promptTemplate.add("userInput", "用户张三今年25岁"); // 执行请求并解析结果 Prompt prompt = promptTemplate.create(); String response = chatModel.call(prompt).getResult().getOutput().getContent(); UserInfo userInfo = parser.parse(response); } }
方法2:全局拦截调整,自定义ChatModel包装类
如果需要全局生效,可编写包装类拦截所有请求,自动将格式信息移至开头:
import org.springframework.ai.chat.ChatModel; import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; public class CacheOptimizedChatModel implements ChatModel { private final ChatModel delegate; public CacheOptimizedChatModel(ChatModel delegate) { this.delegate = delegate; } @Override public ChatResponse call(Prompt prompt) { // 判断是否包含结构化输出格式指令 boolean hasStructuredOutput = prompt.getInstructions().stream() .anyMatch(instr -> instr.getContent().contains("format instructions")); if (hasStructuredOutput) { StringBuilder prefixContent = new StringBuilder(); StringBuilder userContent = new StringBuilder(); // 分离格式指令与用户内容 prompt.getInstructions().forEach(instr -> { if (instr.getContent().contains("format instructions")) { prefixContent.append(instr.getContent()).append("\n\n"); } else { userContent.append(instr.getContent()).append("\n\n"); } }); // 重新拼接生成新Prompt Prompt newPrompt = new Prompt(prefixContent.append(userContent).toString()); return delegate.call(newPrompt); } return delegate.call(prompt); } }
在配置类中替换默认ChatModel:
@Configuration public class AiConfig { @Bean public ChatModel chatModel(OpenAiApi openAiApi) { OpenAiChatModel delegate = new OpenAiChatModel(openAiApi); return new CacheOptimizedChatModel(delegate); } }
三、注意事项
- 确保格式信息完全静态,每次请求的前缀内容完全一致,才能触发OpenAI的Prompt缓存
- 可通过OpenAI API返回的
usage.prompt_tokens_details.cached_tokens字段验证缓存命中情况
内容的提问来源于stack exchange,提问作者dur
相关产品推荐
相关产品推荐

