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

相关推荐
得物技术几秒前
得物知识问答:复合检索 Agent 的系统设计实践
人工智能·后端·ai编程
机器学习之心2 分钟前
基于改进鲸鱼优化算法的CNN-BiLSTM-MATT短期电力负荷预测模型
人工智能·算法·cnn·cnn-bilstm-matt·短期电力负荷预测
南方程序猴3 分钟前
Codex 将再次重置:GPT-6.0 发布前的黑暗时刻
人工智能·gpt·ai·ai编程
一线数智7 分钟前
从百度搜索到AI推荐 制造业正在迎来新的获客方式
人工智能
andongni20311 分钟前
SpringBoot 入门实验报告
java·spring boot·后端
黄鸭部落格ShiYuYuanFang12 分钟前
【分享】软考每日一练与解析-计组 8/12
java·开发语言
天天爱吃肉821813 分钟前
【工程师笔记|新能源整车电控一次过CISPR25/BCI,汽车EMC/EMI落地十大核心设计技巧】
大数据·人工智能·笔记·python·汽车
9i编程17 分钟前
SKILL 四大铁律准则:从「AI 选择性执行 SKILL」到「铁律强制闭环」
人工智能·openai·ai编程
后端小肥肠22 分钟前
自研长篇小说写作 Skills:参考文风 + 自动续篇 + 剧情连续性检测
人工智能·aigc·agent
AI语宙漫游指南22 分钟前
开源一个「照妖镜」Skill,从会话日志到“真身与灵魂”对比
人工智能·agent