You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 12:27:45