侧边栏壁纸
博主头像
牧云

怀璧慎显,博识谨言。

  • 累计撰写 129 篇文章
  • 累计创建 13 个标签
  • 累计收到 8 条评论

目 录CONTENT

文章目录

提示词工程

秋之牧云
2026-03-16 / 0 评论 / 0 点赞 / 0 阅读 / 0 字

1. 基于“助手角色”实现聊天记忆功能

核心原理:定义一个以会话 ID 为 key,List<Message>为值的 Map, 将用户输入作为“用户消息”存入该 Map 中,将模型响应作为“助手消息”存入该 Map 中。

@RestController
@RequestMapping("/v6/ai")
public class AliyunBailianController {

    // 存储聊天对话
    private Map<String, List<Message>> chatMemoryStore = new ConcurrentHashMap<>();

    /**
     * 普通对话
     */
    @GetMapping("/generate")
    public String generate(@RequestParam(value = "message", defaultValue = "你是谁?") String message,
                           @RequestParam(value = "chatId") String chatId) {
        // 根据 chatId 获取对话记录
        List<Message> messages = chatMemoryStore.get(chatId);
        // 若不存在,则初始化一份
        if (CollectionUtils.isEmpty(messages)) {
            messages = new ArrayList<>();
            chatMemoryStore.put(chatId, messages);
        }

        // 添加 “用户角色消息” 到聊天记录中
        messages.add(new UserMessage(message));

        // 构建提示词
        Prompt prompt = new Prompt(messages);
        // 一次性返回结果
        String responseText = chatModel.call(prompt).getResult().getOutput().getText();

        // 添加 “助手角色消息” 到聊天记录中
        messages.add(new AssistantMessage(responseText));

        return responseText;
    }

}

2. 提示词模版

2.1. 提示词模版的组成

2.1.1. 固定部分

  • 角色定义: 明确要求 AI 扮演什么角色

  • 核心任务: 清晰说明需要 AI 做什么

  • 输出格式要求: 严格规定输出的结构和样式

  • 风格与语气: 指定所需的写作风格

  • 背景信息/上下文: 提供必要的背景知识或限制条件

  • 思考过程/步骤(可选): 引导 AI 按照特定逻辑或步骤思考

  • 示例(Few-Shot Learning,可选): 提供几个输入-输出的例子,让 AI 更清楚地理解任务模式。

2.1.2. 用户填充部分

  • 用特定的符号(如 {}, [], {{变量名}}, $变量名)标记出来

  • 代表每次使用时需要用户或系统动态填入的具体内容

  • 例如:[主题][目标语言][产品名称][客户姓名][具体数据]

3. Demo

3.1. 在代码中定义提示词模版

@RestController
@RequestMapping("/v7/ai")
public class PromptTemplateController {

    @Resource
    private OpenAiChatModel chatModel;

    /**
     * 智能代码生成
     * @param message
     * @param lang
     * @return
     */
    @GetMapping(value = "/generateStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<AIResponse> generateStream(@RequestParam(value = "message") String message,
                                           @RequestParam(value = "lang") String lang) {
        // 提示词模板
        String template = """
                你是一位资深 {lang} 开发工程师。请严格遵循以下要求编写代码:
                1. 功能描述:{description}
                2. 代码需包含详细注释
                3. 使用业界最佳实践
                """;

        PromptTemplate promptTemplate = new PromptTemplate(template);

        // 填充提示词占位符,转换为 Prompt 提示词对象
        Prompt prompt = promptTemplate.create(Map.of("description", message, "lang", lang));

        // 流式输出
        return chatModel.stream(prompt)
                .mapNotNull(chatResponse -> {
                    Generation generation = chatResponse.getResult();
                    String text = generation.getOutput().getText();
                    return AIResponse.builder().v(text).build();
                });
    }


}

3.2. 从文件中读取提示词

@RestController
@RequestMapping("/v7/ai")
public class PromptTemplateController {

    @Resource
    private OpenAiChatModel chatModel;

    @Value("classpath:/prompts/code-assistant.st")
    private org.springframework.core.io.Resource templateResource;

    /**
     * 智能代码生成
     * @param message
     * @param lang
     * @return
     */
    @GetMapping(value = "/generateStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<AIResponse> generateStream(@RequestParam(value = "message") String message,
                                           @RequestParam(value = "lang") String lang) {
        // 提示词模板
        PromptTemplate promptTemplate = new PromptTemplate(templateResource);

        // 省略...
    }
}

3.3. 自定义占位符

默认情况下,Spring AI 中提示词模板中的占位符为 {} 花括号。

// 提示词模板
PromptTemplate promptTemplate = PromptTemplate.builder()
    .renderer(StTemplateRenderer.builder()
              .startDelimiterToken('<')
              .endDelimiterToken('>').build()) // 自定义占位符
    .template("""
            你是一位资深 <lang> 开发工程师。请严格遵循以下要求编写代码:
            1. 功能描述:<description>
            2. 代码需包含详细注释
            3. 使用业界最佳实践
            """)
    .build();

3.4. 设置多角色

@RestController
@RequestMapping("/v7/ai")
public class PromptTemplateController {

    @Resource
    private OpenAiChatModel chatModel;

    // 省略...

    /**
     * 智能代码生成 3
     * @param message
     * @param lang
     * @return
     */
    @GetMapping(value = "/generateStream3", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<AIResponse> generateStream3(@RequestParam(value = "message") String message,
                                            @RequestParam(value = "lang") String lang) {

        // 系统角色提示词模板
        String systemPrompt = """
                你是一位资深 {lang} 开发工程师, 已经从业数十年,经验非常丰富。
                """;
        SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemPrompt);
        // 填充提示词占位符,并转换为 Message 对象
        Message systemMessage = systemPromptTemplate.createMessage(Map.of("lang", lang));

        // 用户角色提示词模板
        String userPrompt = """
                请严格遵循以下要求编写代码:
                1. 功能描述:{description}
                2. 代码需包含详细注释
                3. 使用业界最佳实践
                """;
        PromptTemplate promptTemplate = new PromptTemplate(userPrompt);
        // 填充提示词占位符,并转换为 Message 对象
        Message userMessage = promptTemplate.createMessage(Map.of("description", message));


        // 组合多角色消息,构建提示词 Prompt
        Prompt prompt = new Prompt(List.of(systemMessage, userMessage));

        // 流式输出
        return chatModel.stream(prompt)
                .mapNotNull(chatResponse -> {
                    Generation generation = chatResponse.getResult();
                    String text = generation.getOutput().getText();
                    return AIResponse.builder().v(text).build();
                });
    }
}

3.5. 结构化输出

3.5.1. 演员代表作

转 Java 对象

@JsonPropertyOrder({"actor", "movies"})
public record ActorFilmography(String actor, List<String> movies) {
}
@RestController
@RequestMapping("/v8/ai")
public class StructuredOutputController {

    @Resource
    private ChatClient chatClient;

    /**
     * 示例1: BeanOutputConverter - 获取演员电影作品集
     * @param name
     * @return
     */
    @GetMapping("/actor/films")
    public ActorFilmography generate(@RequestParam(value = "name") String name) {
        // 一次性返回结果
        return chatClient.prompt()
                .user(u -> u.text("""
                                请为演员 {actor} 生成包含5部代表作的电影作品集,
                                只包含 {actor} 担任主演的电影,不要包含任何解释说明。
                                """)
                        .param("actor", name))
                .call()
                .entity(ActorFilmography.class);
    }

}

3.5.2. 获取编程语言信息

转 Map

@RestController
@RequestMapping("/v8/ai")
public class StructuredOutputController {

    @Resource
    private ChatClient chatClient;


    /**
     * 示例2: MapOutputConverter - 获取编程语言信息
     * @param language
     * @return
     */
    @GetMapping("/language-info")
    public Map<String, Object> getLanguageInfo(@RequestParam(value = "lang") String language) {

        String userText = """
                请提供关于编程语言 {language} 的结构化信息,包含以下字段:"
                name (语言名称), "
                popularity (流行度排名,整数), "
                features (主要特性,字符串数组), "
                releaseYear (首次发布年份). "
                不要包含任何解释说明,直接输出 JSON 格式数据。
                """;

        return chatClient.prompt()
                .user(u -> u.text(userText).param("language", language))
                .call()
                .entity(new MapOutputConverter());
    }

}

3.5.3. 获取城市列表

转 List

@RestController
@RequestMapping("/v8/ai")
public class StructuredOutputController {

    @Resource
    private ChatClient chatClient;

    /**
     * 示例3: ListOutputConverter - 获取城市列表
     * @param country
     * @return
     */
    @GetMapping("/city-list")
    public List<String> getCityList(@RequestParam(value = "country") String country) {

        return chatClient.prompt()
                .user(u -> u.text(
                                """
                                列出 {country} 的8个主要城市名称。
                                不要包含任何编号、解释或其他文本,直接输出城市名称列表。
                                """)
                        .param("country", country))
                .call()
                .entity(new ListOutputConverter(new DefaultConversionService()));
    }
}

3.5.4. 获取书籍信息

转复杂 Java 对象

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book {
    /**
     * 书名
     */
    private String title;

    /**
     * 作者
     */
    private String author;

    /**
     * 发布年份
     */
    private Integer publishYear;

    /**
     * 类型
     */
    private List<String> genres;

    /**
     * 简介
     */
    private String description;
}
@RestController
@RequestMapping("/v8/ai")
public class StructuredOutputController {

    @Resource
    private ChatClient chatClient;

    /**
     * 使用低级 API 的 BeanOutputConverter - 获取书籍信息
     * @param bookTitle
     * @return
     */
    @GetMapping("/book-info")
    public Book getBookInfo(@RequestParam(value = "name") String bookTitle) {

        // 使用 BeanOutputConverter 定义输出格式
        BeanOutputConverter<Book> converter = new BeanOutputConverter<>(Book.class);

        // 提示词模板
        String template = """
                请提供关于书籍《{bookTitle}》的详细信息:
                1. 作者姓名
                2. 出版年份
                3. 主要类型(数组)
                4. 书籍描述(不少于50字)
                
                不要包含任何解释说明,直接按指定格式输出。
                {format}
                """;

        // 创建 Prompt
        PromptTemplate promptTemplate = new PromptTemplate(template);
        Prompt prompt = promptTemplate.create(Map.of(
                "bookTitle", bookTitle,
                "format", converter.getFormat()
        ));

        // 调用模型并转换结果
        String result = chatClient.prompt(prompt)
                .call()
                .content();

	    // 结构化转换
        return converter.convert(result);
    }
}

0

评论区