SpringAI实现Reread(Advisor)

文章目录

1_Advisor介绍

Advisor 是 SpringAI 基于 AOP 机制实现与大模型对话过程的增强、拦截、修改等功能的关键组件。

所有的增强通知都需要实现 Advisor 接口。

常见的组件有如下几种:

  • Advisor:顶级接口,继承了 Ordered,可以定义拦截器(Advisor)的顺序。
  • ChatClientRequest:表示由 ChatClient 处理的请求,最终用于构建要发送到 LLM 的Prompt。
  • ChatClientResponse:表示由 ChatClient 返回的响应。
  • CallAdvisor:非流式场景的接口(增强 ChatClientRequest 和 ChatClientResponse)。
  • StreamAdvisor:流式场景的接口(增强 ChatClientRequest 和 ChatClientResponse)。
  • CallAdvisorChain:非流式场景的实例链,用于编排链中下一个 CallAdvisor 实例的执行。
  • StreamAdvisorChain:流式场景的实例链,用于编排链中下一个 StreamAdvisor 实例的执行。

常见的实现有如下几种:

  • BaseAdvisor:基础 Advisor,实现了 CallAdvisor, StreamAdvisor 两个接口。
  • SimpleLoggerAdvisor:日志记录的 Advisor。
  • MessageChatMemoryAdvisor:会话记忆的 Advisor。
  • SafeGuardAdvisor:拦截敏感词的 Advisor。
  • VectorStoreChatMemoryAdvisor:从 VectorStore 检索内存并将其添加到提示的系统文本中的Advisor。

最基本的两个接口是 CallAdvisor, StreamAdvisor,但有时并不知晓到底应该选择流式还是非流式,所以一般情况下都会一起实现。

而 BaseAdvisor 这个接口就可以方便我们快速创建自定义的 Advisor,只用实现其中的 beforeafter 方法。

根据以下源码可知,无论是流式还是非流式都会在请求传递给下一个 Advisor 前后分别调用 beforeafter 方法:

java 复制代码
public interface BaseAdvisor extends CallAdvisor, StreamAdvisor {

	Scheduler DEFAULT_SCHEDULER = Schedulers.boundedElastic();

	@Override
	default ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
		ChatClientRequest processedChatClientRequest = before(chatClientRequest, callAdvisorChain);
		ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(processedChatClientRequest);
		return after(chatClientResponse, callAdvisorChain);
	}

	@Override
	default Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest,
			StreamAdvisorChain streamAdvisorChain) {
		Flux<ChatClientResponse> chatClientResponseFlux = Mono.just(chatClientRequest)
			.publishOn(getScheduler())
			.map(request -> this.before(request, streamAdvisorChain))
			.flatMapMany(streamAdvisorChain::nextStream);

		return chatClientResponseFlux.map(response -> {
			if (AdvisorUtils.onFinishReason().test(response)) {
				response = after(response, streamAdvisorChain);
			}
			return response;
		}).onErrorResume(error -> Flux.error(new IllegalStateException("Stream processing failed", error)));
	}

	@Override
	default String getName() {
		return this.getClass().getSimpleName();
	}

	/**
	 * Logic to be executed before the rest of the advisor chain is called.
	 */
	ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain);

	/**
	 * Logic to be executed after the rest of the advisor chain is called.
	 */
	ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain);

	/**
	 * Scheduler used for processing the advisor logic when streaming.
	 */
	default Scheduler getScheduler() {
		return DEFAULT_SCHEDULER;
	}

}

总结:简单来说就是责任链模式。

2_reread介绍

重读策略的核心在于让 LLMs 重新审视输入的问题,这借鉴了人类解决问题的思维方式。

通过这种方式,LLMs 能够更深入地理解问题,发现复杂的模式,从而在各种推理任务中表现得更加强大。

可以按照如下方式编写提示词模版:

properties 复制代码
{Input_Query}
Read the question again: {Input_Query}

论文链接地址:https://arxiv.org/pdf/2309.06275

3_实现 ReReadingAdvisor

基于 BaseAdvisor 来实现自定义的 Advisor,避免了重复代码的编写,只需专注自己的业务即可:

java 复制代码
@SuppressWarnings("all")
public class ReReadingAdvisor implements BaseAdvisor {

    private static final String DEFAULT_RE2_ADVISE_TEMPLATE = """
            {re2_input_query}
            Read the question again: {re2_input_query}
            """;

    @Override
    public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {
        // 拿到之前的提示词
        String userPrompt = chatClientRequest.prompt().getUserMessage().getText();
        String re2InputQuery = PromptTemplate.builder().template(DEFAULT_RE2_ADVISE_TEMPLATE)
                .variables(Map.of("re2_input_query", userPrompt))
                .build().render();
        return chatClientRequest.mutate() // 复制一个新的chatClientRequest
                .prompt(chatClientRequest.prompt().augmentUserMessage(re2InputQuery)).build();
    }

    @Override
    public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
        return chatClientResponse;
    }

    @Override
    public int getOrder() {
        return 0;
    }

}

还可以进行扩展比如增加一个设置 Order 的方法。

相关推荐
居然JuRan6 分钟前
大模型瘦身术:量化与蒸馏技术全解析
人工智能
艾莉丝努力练剑7 分钟前
【优选算法必刷100题】第031~32题(前缀和算法):连续数组、矩阵区域和
大数据·人工智能·线性代数·算法·矩阵·二维前缀和
不去幼儿园14 分钟前
【启发式算法】灰狼优化算法(Grey Wolf Optimizer, GWO)详细介绍(Python)
人工智能·python·算法·机器学习·启发式算法
培风图南以星河揽胜14 分钟前
Java实习模拟面试|离散数学|概率论|金融英语|数据库实战|职业规划|期末冲刺|今日本科计科要闻速递:技术分享与学习指南
java·面试·概率论
能鈺CMS15 分钟前
能鈺CMS · 虚拟发货源码
java·大数据·数据库
sheji341618 分钟前
【开题答辩全过程】以 环保监督管理系统为例,包含答辩的问题和答案
java·eclipse
不会玩电脑的Xin.21 分钟前
Web请求乱码解决方案
java·javaweb
Billow_lamb30 分钟前
Spring Boot2.x.x 全局错误处理
java·spring boot·后端
remaindertime32 分钟前
基于Ollama和Spring AI:实现本地大模型对话与 RAG 功能
人工智能·后端·ai编程
编程火箭车33 分钟前
【Java SE 基础学习打卡】14 Java 注释
java·编程规范·代码注释·api文档·代码可读性·javadoc·文档注释