AI超级智能开发系列从入门到上天第四篇:AI应用方案设计

一:系统提示词设计

搞系统提示词可以让豆包写。

然后可以到阿里云百炼的文本调试里面去做,内容调试

二:多轮对话实现

1:Chat Client API

功能比直接调用ChatModel功能更丰富。Chat Client需要用到Chet Model对象。

指定大模型+指定系统预设+指定配置参数,右边的聊天框就是一个chat client。

1:ChatClient两种创建方式

复制代码
// 方式1:使用构造器注入
@Service
public class ChatService {
    private final ChatClient chatClient;
    
    public ChatService(ChatClient.Builder builder) {
        this.chatClient = builder
            .defaultSystem("你是恋爱顾问")
            .build();
    }
}

// 方式2:使用建造者模式
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultSystem("你是恋爱顾问")
    .build();

推荐第二种。

2:Chat Client多种响应格式

返回整个文本,返回Java实体,返回List集合,流式返回

返回实体的这种形式被称为结构化输出。

复制代码
// ChatClient支持多种响应格式
// 1. 返回 ChatResponse 对象(包含元数据如 token 使用量)
ChatResponse chatResponse = chatClient.prompt()
    .user("Tell me a joke")
    .call()
    .chatResponse();

// 2. 返回实体对象(自动将 AI 输出映射为 Java 对象)
// 2.1 返回单个实体
record ActorFilms(String actor, List<String> movies) {}
ActorFilms actorFilms = chatClient.prompt()
    .user("Generate the filmography for a random actor.")
    .call()
    .entity(ActorFilms.class);

// 2.2 返回泛型集合
List<ActorFilms> multipleActors = chatClient.prompt()
    .user("Generate filmography for Tom Hanks and Bill Murray.")
    .call()
    .entity(new ParameterizedTypeReference<List<ActorFilms>>() {});

// 3. 流式返回(适用于打字机效果)
Flux<String> streamResponse = chatClient.prompt()
    .user("Tell me a story")
    .stream()
    .content();

// 也可以流式返回ChatResponse
Flux<ChatResponse> streamWithMetadata = chatClient.prompt()
    .user("Tell me a story")
    .stream()
    .chatResponse();

3:ChatClient设置默认参数

复制代码
// 定义默认系统提示词
ChatClient chatClient = ChatClient.builder(chatModel)
        .defaultSystem("You are a friendly chat bot that answers question in the voice of a {voice}")
        .build();

// 对话时动态更改系统提示词的变量
chatClient.prompt()
        .system(sp -> sp.param("voice", voice))
        .user(message)
        .call()
        .content());

支持指定的对话选项、默认拦截器、默认函数调用等等。

2:Advisors

Spring AI 使用 Advisors(顾问) 机制增强 AI 能力,可理解为可插拔的拦截器,在调用 AI 前后执行额外操作:

  • 前置增强:调用 AI 前改写 Prompt 提示词、检查提示词安全性
  • 后置增强:调用 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")
         // 最多拿100条记录获取上下文。  
        .param("chat_memory_response_size", 100))
    .user(userText)
    .call()
	.content();

拦截器的作用:敏感词校验、添加上下文记忆、通过RAG进行检索增强。等等。拦截器是责任链的形式进行调用。

拦截器是有顺序的,顺序越小越先执行getOrder()。不是通过代码的编写顺序决定的。

Spring AI Advisors 模式核心内容提取

Advisors 分为 流式(Streaming)非流式(Non-Streaming) 两种模式:

  • 二者在用法上无明显区别 ,仅返回值不同
  • 若要自主实现 Advisors,为保证通用性,最好同时实现流式和非流式的环绕通知方法。

1:Spring内置的Advisors

Spring AI ChatMemoryAdvisor 内置实现方式提取

要实现对话记忆功能,可使用 Spring AI 的 ChatMemoryAdvisor,它主要有以下几种内置实现:

  • MessageChatMemoryAdvisor :从记忆中检索历史对话,并将其作为消息集合添加到提示词中。
  • PromptChatMemoryAdvisor :从记忆中检索历史对话,并将其添加到提示词的系统文本中。
  • VectorStoreChatMemoryAdvisor :使用向量数据库来存储和检索历史对话。

2:MessageChatMemoryAdvisor 与 PromptChatMemoryAdvisor 核心区别提取

两者用法类似,但存在以下关键差异:

  1. MessageChatMemoryAdvisor

    • 将对话历史作为一系列独立消息添加到提示中

    • 保留原始对话的完整结构,包含每条消息的角色标识(用户 / 助手 / 系统)

    • 示例格式: json

      复制代码
      [
        {"role": "user", "content": "你好"},
        {"role": "assistant", "content": "你好!有什么我能帮助你的吗?"},
        {"role": "user", "content": "讲个笑话"}
      ]
  2. PromptChatMemoryAdvisor

    • 将对话历史添加到提示词的系统文本部分

    • 可能会丢失原始的消息边界

    • 示例格式:

      复制代码
      以下是之前的对话历史:
      用户:你好
      助手:你好!有什么我能帮助你的吗?
      用户:讲个笑话
      
      现在请继续回答用户的问题。

一般情况下,更推荐使用 MessageChatMemoryAdvisor ,它更符合大多数现代 LLM 的对话模型设计,能更好地保持上下文连贯性。

**3:**MessageChatMemoryAdvisor

MessageChatMemoryAdvisor对象创建需要ChatMemory对象。

4:Chat Memory 核心内容提取

所有 ChatMemoryAdvisor 都依赖 Chat Memory 进行构造,它负责历史对话的存储,定义了保存消息、查询消息、清空消息历史的方法。

ChatMemory 接口定义
复制代码
public interface ChatMemory {

    // TODO: consider a non-blocking interface for streaming usages

    default void add(String conversationId, Message message) {
        this.add(conversationId, List.of(message));
    }

    void add(String conversationId, List<Message> messages);

    List<Message> get(String conversationId, int lastN);

    void clear(String conversationId);
}
Spring AI 内置 Chat Memory 实现

Spring AI 提供了多种可将对话保存到不同数据源的内置实现:

  • InMemoryChatMemory:内存存储
  • CassandraChatMemory:在 Cassandra 中带有过期时间的持久化存储
  • Neo4jChatMemory:在 Neo4j 中没有过期时间限制的持久化存储
  • JdbcChatMemory:在 JDBC 中没有过期时间限制的持久化存储

我们可以通过chatmemory来实现自己的持久化。(增删查)还是要我们自己保存下来。

所谓对话记忆的实现原理,就是每次对话把上下文发一遍,就有记忆了。

相关推荐
Amnesia0_01 小时前
C++中的IO流
开发语言·c++
2401_891482172 小时前
C++模块化编程指南
开发语言·c++·算法
暮冬-  Gentle°2 小时前
自定义类型转换机制
开发语言·c++·算法
2301_816651222 小时前
嵌入式C++低功耗设计
开发语言·c++·算法
架构师沉默2 小时前
Java 终于有自己的 AI Agent 框架了?
java·后端·架构
程序员爱酸奶2 小时前
ThreadLocal内存泄漏深度解析
java
czlczl200209252 小时前
JVM创建对象过程
java·开发语言
qq_416018722 小时前
分布式缓存一致性
开发语言·c++·算法
SuperEugene2 小时前
Vue Router 实战规范:path/name/meta 配置 + 动态 / 嵌套路由,统一团队标准|状态管理与路由规范篇
开发语言·前端·javascript·vue.js·前端框架