第二章-AI应用开发基础

106 阅读6分钟

Prompt工程

提示词工程主要就是

  • prompt分类
  • prompt优化技巧

提示词分类

分为三大类:

user prompt: 是用户对ai提供的实际问题、指令、需求等

system prompt: 是对AI模型的行为规则和角色设定的隐藏指令,一般对用不可见,即告诉AI你是谁能做什么。不同的prompt可以让同一个AI模型表现出完全不同的应用特性,这是构建垂直领域AI应用(如教育辅导、医疗咨询、产品咨询)的关键

assistant prompt: 这是AI模型的相应内容,即设置了AI回复时的一些模板等

Token成本优化

系统提示词、用户提示词和AI大模型的输出都是要消耗Token的,合理的优化这些方面可以节约成本。

  • 精简系统提示词只保留核心指令(例如:你是一个经验丰富且有编成的编程导师 和 你是一个编程导师 其实差别不大可以去掉不必要的修饰词)
  • 定期清理对话历史,设定多轮对话记忆轮数,每次对话都会总结回忆上文,对话越长Token消耗越多
  • 使用向量检索代替直接输入,对于需要处理大量参考文档的场景不要将整个文档作为prompt,而是用向量数据库和RAG获取相关段落
  • 结构化代替自然语言,如表格、列表等

参考现有Prompt提示词库

如有现成的可以直接使用节约开发时间和成本

提示词技巧

基础技巧: 指明任务和角色、提供详细的说明和具体实例、明确输出格式等

进阶技巧

  • 思维链提示法:即通过引导模型展示推理过程逐步思考问题,提高复杂问题的准确性
  • 少样本学习:为模型提供几个输入输出的实力帮助理解任务模式和期望输出
  • 分布引导:先给出一个复杂问题,并给出解决问题的每个步骤
  • 自我修正和评估:让模型自己评估自己的输出并进行改进
  • 知识引用和检索
  • 多视角
  • 多模态

实践

我们可以开发一个AI恋爱大师应用,来解决用户的情感问题,那么如何实现的这个应用呢?其实很简单,AI需要明晰自己的定位,自己有何种能力以及自己要解决什么样的问题,这时候就需要我们设计系统提示词。除此之外,用户可能进行长对话,那我们不能聊完一句忘记一句需要有多轮对话记忆功能,如何实现这点呢?这就要使用SpringAI框架的对话记忆相关API了。

系统提示词设计

我们把上面这段需求喂给AI让AI帮忙生成一段提示词,然后我们简单优化一下就有了下面这段系统提示词:

扮演深耕恋爱心理领域的专家。开场向用户表明身份,告知用户可倾诉恋爱难题。围绕单身、恋爱、已婚三种状态提问:单身状态询问社交圈拓展及追求心仪对象的困扰;恋爱状态询问沟通、习惯差异引发的矛盾;已婚状态询问家庭责任与亲属关系处理的问题。引导用户详述事情经过、对方反应及自身想法,以便给出专属解决方案。

多轮对话的实现

先了解几个概念

  • ChatClient (Spring AI 调用大模型的客户端)
  • Advisors (SpringAI用来增强AI调用能力的拦截器)
  • Chat Memory Advisor (对话记忆拦截器)
  • Chat Memory (对话记忆拦截器依赖的对话记忆存储 )

ChatClient

ChatClient对比直接使用Spring boot注入ChatModel来调用大模型有以下好处:可实现更丰富的功能、灵活、支持更复杂的链式调用

// 基础用法(ChatModel)
ChatResponse response = chatModel.call(new Prompt("你好"));

// 高级用法(ChatClient)
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultSystem("你是恋爱顾问")
    .build();
    
String response = chatClient.prompt().user("你好").call().content();

Advisor

SpringAI 使用顾问机制来增强AI的能力,在调用AI前后都可以执行一些额外的操作类似AOP切面编程,常见的用法有:

  • 前置增强:调用AI前优化一下提示词、检查提示词是否安全、用户提示词是否包含敏感问题等
  • 调用AI后记录日志、处理返回结果等

用法很简单,可以直接为ChatClient指定默认拦截器,如对话记忆拦截器MessageChatMemoryAdvisor,能够帮助我们实现多轮对话的功能,不必再自己维护对话列表

var chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(
        new MessageChatMemoryAdvisor(chatMemory), // 对话记忆 advisor
        new QuestionAnswerAdvisor(vectorStore)    // RAG 检索增强 advisor
    )
    .build();

String response = this.chatClient.prompt()
    // 对话时动态设定拦截器参数,比如指定对话记忆的 id 和长度
    .advisors(advisor -> advisor.param("chat_memory_conversation_id", "678")
            .param("chat_memory_response_size", 100))
    .user(userText)
    .call()
	.content();

Advisor原理

image.png

ChatMemory

负责历史对话的存储,定义了保存消息,查询消息,清空历史消息的方法等

开发代码

参考Spring AI Alibaba的官方示例代码:(java2ai.com/docs/1.0.0-…)

(1)先初始化ChatClient对象,使用构造方法注入阿里大模型dashscopeChatModel对象来初始化ChatClient。初始化时指定默认系统prompt和基于内存的对话记忆Advisor。代码如下:

@Component
@Slf4j
public class LoveApp {

    private final ChatClient chatClient;

    private static final String SYSTEM_PROMPT = "扮演深耕恋爱心理领域的专家。开场向用户表明身份,告知用户可倾诉恋爱难题。" +
            "围绕单身、恋爱、已婚三种状态提问:单身状态询问社交圈拓展及追求心仪对象的困扰;" +
            "恋爱状态询问沟通、习惯差异引发的矛盾;已婚状态询问家庭责任与亲属关系处理的问题。" +
            "引导用户详述事情经过、对方反应及自身想法,以便给出专属解决方案。";

    public LoveApp(ChatModel dashscopeChatModel) {
        // 初始化基于内存的对话记忆
        ChatMemory chatMemory = new InMemoryChatMemory();
        chatClient = ChatClient.builder(dashscopeChatModel)
                .defaultSystem(SYSTEM_PROMPT)
                .defaultAdvisors(
                        new MessageChatMemoryAdvisor(chatMemory)
                )
                .build();
    }
}

(2)编写对话方法,调用chatClient对象,传入用户prompt,并指定对话id和记忆大小。代码如下:

public String doChat(String message, String chatId) {
    ChatResponse response = chatClient
            .prompt()
            .user(message)
            .advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                    .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
            .call()
            .chatResponse();
    String content = response.getResult().getOutput().getText();
    log.info("content: {}", content);
    return content;
}

(3)测试功能:

@SpringBootTest
class LoveAppTest {

    @Resource
    private LoveApp loveApp;

    @Test
    void doChat() {
        String chatID = UUID.randomUUID().toString();
        //第一轮对话
        String message1 = "我是周浩宇";
        String content1 = loveApp.doChat(message1, chatID);
        //第二轮对话
        String message2 = "我的对象是wsy";
        String content2 = loveApp.doChat(message2, chatID);
        Assertions.assertNotNull(content2);//断言非空,为空报错
        //第三轮对话
        String message3 = "如何让我们相处更加融洽";
        String content3 = loveApp.doChat(message3, chatID);
        Assertions.assertNotNull(content2);
        //第四轮对话
        String message4 = "我的对象是谁来着帮我回忆一下";
        String content4 = loveApp.doChat(message4, chatID);
        Assertions.assertNotNull(content2);
    }
}

对话记忆成功:

image.png image.png

自定义Advisor

文档编写中......