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 的方法。

相关推荐
1G18 分钟前
openclaw控制浏览器/自动化的playwright MCP + Mcporter方案实现
人工智能
踩着两条虫23 分钟前
VTJ.PRO 双向代码转换原理揭秘
前端·vue.js·人工智能
扉川川24 分钟前
OpenClaw 架构解析:一个生产级 AI Agent 是如何设计的
前端·人工智能
顺风尿一寸28 分钟前
从 Java NIO poll 到 Linux 内核 poll:一次系统调用的完整旅程
java
程途知微31 分钟前
JVM运行时数据区各区域作用与溢出原理
java
星浩AI40 分钟前
让模型自己写 Skills——从素材到自动生成工作流
人工智能·后端·agent
华仔啊3 小时前
为啥不用 MP 的 saveOrUpdateBatch?MySQL 一条 SQL 批量增改才是最优解
java·后端
千寻girling5 小时前
Python 是用来做 AI 人工智能 的 , 不适合开发 Web 网站 | 《Web框架》
人工智能·后端·算法
AI攻城狮5 小时前
OpenClaw 里 TAVILY_API_KEY 明明写在 ~/.bashrc,为什么还是失效?一次完整排查与修复
人工智能·云原生·aigc
xiaoye20185 小时前
Lettuce连接模型、命令执行、Pipeline 浅析
java