AI 实战篇:Spring-AI再更新!细细讲下Advisors
在2024年10月8日,Spring AI再次进行了更新,虽然当时版别仍为非安稳版别(1.0.0-M3),但博主将持续重视这些动态,并从盛行的智能体视角深化解析其技能底层。现在,Spring AI仍处于小众状况,没有经过开源社区多年的保护和安稳化进程,这与现已较为老练的Spring结构构成鲜明对比。即便是Spring AI的安稳版别(1.0.0-SNAPSHOT),在常见的maven库房中也难以找到,仍需经过Spring的jfrog库房进行拜访。
好的,咱们不再绕圈子,直接进入主题。在1.0.0-M3版别中,进行了许多重要的更新,我将逐个详细解说这些特性。今日的重点是深化解析Advisors的概念,由于它与咱们当时作业中所运用的一些技能有许多类似之处,可以协助咱们更简略地了解相关内容。因而,我信任经过这部分的解说,咱们将能更好地把握Spring AI的中心功用。现在,就让咱们开端吧!
什么是 Spring AI Advisors?
Spring AI Advisor的中心功用在于阻拦并或许修正AI运用程序中谈天恳求和呼应流的组件。在这个体系中,AroundAdvisor是要害参与者,它答应开发人员在这些交互进程中动态地转化或运用信息。
运用Advisor的首要优势包含:
- 重复使命的封装:可以将常见的生成式AI形式打包成可重用的单元,简化开发进程。
- 数据转化:增强发送给言语模型(LLM)的数据,并优化回来给客户端的呼应格局,以进步交互质量。
- 可移植性:创立可跨不同模型和用例作业的可重用转化组件,提高代码的灵敏性和习惯性。
或许你会觉得,这与咱们在Spring中运用AspectJ的办法较为类似。实践上,当你阅读完今日的文章后,会发现这不过是换了个称号罢了,首要功用其实是共同的,都是为了增强运用程序的才能。
Advisors VS Advised
这儿咱们简略弄清一下“Advisors”和“Advised”这两个术语。实践上,它们之间并没有直接关系,仅仅由于在检查源码时,咱们常常会遇到这两个词。Advisors是指咱们创立的各种增强功用类,它们担任对恳求链路进行不同的处理。而“Advised”则是一个形容词,用来描绘某个类已不再是一般类,它经过增强后具有了新的特性。虽然它们也对恳求类进行了增强,但这种增强首要是经过特色搬迁的办法完成的。
下面的图示将协助进一步了解这一点。
Advisors怎么运作
假如你曾经编写过AspectJ的注解类,那么你应该可以很简略地推测出Advisors是怎么运作的。在Advisor体系中,各个Advisor以链式结构运转,序列中的每个Advisor都有时机对传入的恳求和传出的呼应进行处理。这种链式处理机制保证了每个Advisor可以在恳求和呼应流中添加自己的逻辑,然后完成更灵敏和可定制的功用。
为了协助了解这一流程,下面咱们先来看一下官方供给的流程图。这张图详细展现了各个Advisor怎么在恳求链中进行交互,以及它们怎么协同作业以增强全体功用。
我来大致解说一下整个流程:
首要,咱们会封装各种恳求参数装备,如前面AdvisedRequest的截图所示,这儿就不再详细阐明。接下来,链中的每个Advisor都会处理恳求,或许对其进行修正,并将履行流程转发给链中的下一个Advisor。值得留意的是,某些Advisor也可以挑选不调用下一个实体,然后阻挠恳求持续传递。
终究,Advisor将恳求发送到Chat Model。谈天模型的呼应将经过Advisor链传递回原恳求途径,构成原始上下文和主张上下文的组合。每个Advisor都有时机处理或修正这个呼应,保证其契合预期。最终,体系将回来一个AdvisedResponse给客户端。
接下来,咱们将深化探讨怎么实践运用Advisor。
运用 Advisor
内嵌的Advisor
作为Spring AI的一部分,体系内置了多个官方Advisor示例,这些示例不只数量不多,并且功用各异,可以很好地展现Advisor的实践运用场景。咱们无妨一起来逐个检查这些内置Advisor的作用和特色,深化了解它们怎么在恳求处理链中发挥各自的功用。
-
MessageChatMemoryAdvisor是咱们之前提到过的一个常用类,它在恳求处理流程中扮演着重要的人物。这个Advisor的首要功用是将用户提出的问题和模型的答复添加到前史记载中,然后构成一个上下文回忆的增强机制。经过这种办法,体系可以更好地了解用户的需求,供给愈加连接和相关的呼应。
- 需求留意的是,并非一切的AI模型都支撑这种上下文回忆的存储和办理办法。某些模型或许没有完成相应的前史记载功用,因而在运用MessageChatMemoryAdvisor时,保证所运用的模型具有此支撑是至关重要的。
-
PromptChatMemoryAdvisor的功用在MessageChatMemoryAdvisor的基础上进一步增强,其首要作用在于上下文谈天记载的处理办法。与MessageChatMemoryAdvisor不同,PromptChatMemoryAdvisor并不将上下文记载直接传入messages参数中,而是奇妙地将其封装到systemPrompt提示词中。这一规划使得不管所运用的模型是否支撑messages参数,体系都可以有效地添加上下文前史回忆。
-
QuestionAnswerAdvisor的首要功用是履行RAG(Retrieval-Augmented Generation)检索,这一进程触及对知识库的高效调用。当用户提出问题时,QuestionAnswerAdvisor会首要对知识库进行检索,并将匹配到的相关引证文本添加到用户发问的后边,然后为生成的答复供给更为丰厚和精确的上下文。
- 此外,该Advisor设定了一个默许提示词,旨在保证答复的质量和相关性。假如在知识库中无法找到匹配的文本,体系将回绝答复用户的问题。
-
SafeGuardAdvisor的中心功用是进行灵敏词校验,以保证体系在处理用户输入时的安全性和合规性。当用户提交的信息触发了灵敏词机制,SafeGuardAdvisor将立即对该恳求进行半途阻拦,防止持续调用大型模型进行处理。
-
SimpleLoggerAdvisor:这是一个用于日志打印的东西,咱们之前现已对其进行了操练和深化了解,因而在这儿不再赘述。
-
VectorStoreChatMemoryAdvisor:该组件完成了长时间回忆功用,可以将每次用户提出的问题及模型的答复存储到向量数据库中。在用户每次发问时,体系会进行一次检索,将检索到的信息累加到体系提示词的后边,以便为大模型供给更精确的上下文提示。但是,这儿需求留意的是,假如没有妥善保护
chat_memory_conversation_id
,或许会导致无限制的写入和检索,然后引发潜在的灾难性bug。因而,保证这一标识的办理和更新至关重要,以防止体系的不安稳性和数据紊乱。
在这儿,咱们首要评论 chat_memory_conversation_id
参数,它在一切 Advisor 中都是一个要害要素。咱们有必要为每位用户妥善保护这个参数,防止每次默许生成新 ID,以防在向量数据库中发生许多废物数据。此外,保护好该参数后,可以在后台运用它整理向量数据库中的旧数据。需求留意的是,这儿运用的是存储在 metadata 中的 chat_memory_conversation_id
,而不是简略的 ID,因而在删去和整理时,需先进行查询。
自界说Advisor
其实,咱们之前现已完成过一个简略的日志记载 Advisor。今日,咱们将依据 Re-Reading (Re2)技能,打造一个更高档的 Advisor。实践上,了解这一进程并不杂乱,中心在于提早对恳求的问题进行包装。
关于有爱好的同学,我引荐你们检查 Re2 技能的相关完成及作用,详细内容可以参阅这篇论文:Re2技能完成。
此外,关于提示词的优化,假如你对这方面特别感爱好,我主张你阅读一下免费开源的博客文档,这儿有许多有价值的资源可以参阅:提示词攻略。
接下来,咱们不再烦琐,直接来看一下官方的示例代码:
public class ReReadingAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
private static final String DEFAULT_USER_TEXT_ADVISE = """
{re2_input_query}
Read the question again: {re2_input_query}
""";
@Override
public String getName() {
return this.getClass().getSimpleName();
}
@Override
public int getOrder() {
return 0;
}
private AdvisedRequest before(AdvisedRequest advisedRequest) {
String inputQuery = advisedRequest.userText(); //original user query
Map<String, Object> params = new HashMap<>(advisedRequest.userParams());
params.put("re2_input_query", inputQuery);
return AdvisedRequest.from(advisedRequest)
.withUserText(DEFAULT_USER_TEXT_ADVISE)
.withUserParams(params)
.build();
}
@Override
public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
return chain.nextAroundCall(before(advisedRequest));
}
@Override
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
return chain.nextAroundStream(before(advisedRequest));
}
}
可以看到,在这儿的完成中,实践上并没有过多的代码编写,仅仅是声明晰一个大局的文本模板,并在恳求之前对其进行了简略的封装。这种规划思路的中心在于经过模板的预处理,提高恳求的有效性和上下文的相关性。依据 Re2 的官方阐明,这种办法不只可以简化代码结构,还能明显提高模型的答复作用。
同享参数Advisor
在之前的解说中,例如咱们评论的 messageChatMemoryAdvisor
,在 Bean 声明时,实践上是专门为参数装备编写了默许设置。虽然如此,咱们依然可以经过参数传入的办法进行动态装备,这种灵敏性让咱们可以依据实践需求调整参数。你可以传入任何所需的参数,并在重写的办法中进行读取和运用,然后使 Advisor 愈加灵敏和习惯不同场景的需求。
在这儿,咱们将以官方的 messageChatMemoryAdvisor
为例,展现曾经的写法,以便更好地了解这一装备进程。
ChatDataPO functionGenerationByText(@RequestParam("userInput") String userInput) {
OpenAiChatOptions openAiChatOptions = OpenAiChatOptions.builder()
.withModel("hunyuan-pro").withTemperature(0.5).build();
String content = this.myChatClientWithSystem
.prompt()
.system("请你作为一个小雨的AI小帮手,请将东西回来的数据格局化后以友爱的办法回复用户的问题。拟定的旅行攻略要有航班、酒店、火车信息")
.user(userInput)
.options(openAiChatOptions)
.advisors(messageChatMemoryAdvisor,myLoggerAdvisor,promptChatKnowledageAdvisor)
//装备类如下:
@Bean
MessageChatMemoryAdvisor messageChatMemoryAdvisor() {
InMemoryChatMemory chatMemory = new InMemoryChatMemory();
return new MessageChatMemoryAdvisor(chatMemory,"123",10);
}
传递参数的作用可以经过以下办法进行改写,详细完成中省掉了一些冗余的重复代码,以便愈加明晰地展现首要逻辑:
.advisors(messageChatMemoryAdvisor,myLoggerAdvisor,promptChatKnowledageAdvisor)
.advisors(advisor -> advisor.param("chat_memory_conversation_id", "678")
.param("chat_memory_response_size", 100))
这样,咱们就可以实时读取参数,并在调用之前进行恰当的装备,下面是详细的源码示例:
官方现已为咱们封装了常用的读取模板,供给了一系列高效且易于运用的功用接口。咱们只需直接调用这些预界说的模板,便可以快速完成所需的操作。
更新参数
除了在开端调用之前设置一些同享参数外,咱们还可以在运转期间动态调整这些参数,以便更好地习惯实时改变的需求和环境:
@Override
public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
this.advisedRequest = advisedRequest.updateContext(context -> {
context.put("aroundCallBefore" + getName(), "AROUND_CALL_BEFORE " + getName()); // Add multiple key-value pairs
context.put("lastBefore", getName()); // Add a single key-value pair
return context;
});
// Method implementation continues...
}
今日的Advisors介绍就到这儿,期望能为你带来一些新的启示和考虑。
总结
Spring AI Advisors 供给了一种强壮而灵敏的办法,旨在明显增强你的 AI 运用程序的功用和功能。经过充分运用这一 API,你可以创立出更杂乱、可重用且易于保护的 AI 组件,然后提高开发功率和体系的可扩展性。
不管你是在施行自界说逻辑以满意特定事务需求,办理对话前史记载以优化用户体会,仍是改善模型推理以取得更精确的成果,Advisors 都能为你供给简练且高效的解决方案。这种灵敏性使得开发者可以快速呼应改变,一起坚持代码的整齐和可读性,进而为用户供给愈加流通和智能的体会。
我是尽力的小雨,一名 Java 服务端码农,潜心研究着 AI 技能的奥妙。我酷爱技能交流与共享,对开源社区充满热情。一起也是一位腾讯云创造之星、阿里云专家博主、华为如此享专家、掘金优异作者。
💡 我将不惜共享我在技能道路上的个人探究与经历,期望能为你的学习与生长带来一些启示与协助。
🌟 欢迎重视尽力的小雨!🌟