赞
踩
我们构建一个aiService的bean, 触发请求:
- @CrossOrigin
- @Slf4j
- @RestController
- class ChatController {
- @Resource
- private AssistantService assistantService;
-
- @GetMapping("/ai/assistant")
- public String assistant(@RequestParam(defaultValue = "What can you do for me?") String message) {
- String response = assistantService.chat(message);
- log.info("Received message: {}, generated response: {}", message, response);
- return response;
- }
- }
定义AiService:
- @AiService
- public interface AssistantService {
-
- @SystemMessage(value = {
- "you are system assistant, you can help me to do some works in this system.",
- "if user want to generate table data, must input the table name and the number of rows."
- })
- String chat(String message);
- }
这里debug调用chat方法的时候实际上debug会走到dev.langchain4j.service.DefaultAiServices#build中的匿名InvocationHandler.invoke()方法
核心业务逻辑就是根据根据构建的aiService信息调用大模型的api:
如果有对当前的aiService定义添加tool工具, 那么会tool工具调用对应的大模型function calling的api, 这样模型会知道何时才会去调用系统内部的工具类.
实际上就是 langchain4j-spring-boot-starter 的自动装配流程
starter主要作用就是确保所有必要的库和配置都被自动包含,从而减少了手动添加依赖和配置的工作量。
主要对 RAG的配置和AiService的配置
启动自动装配: 类 dev.langchain4j.spring.LangChain4jAutoConfig
LangChain4jAutoConfig
@Import注解用于导入其他的配置类,使得这些配置类中的bean定义可以被当前配置类所在的Spring容器中识别和管理。在这里,它导入了AiServicesAutoConfig
和RagAutoConfig
两个配置类,这意味着这两个配置类中的所有bean定义都将被包含在当前Spring容器中。
AiServicesAutoConfig的核心功能: 定义自定义Bean处理器
- @Bean
- BeanFactoryPostProcessor aiServicesRegisteringBeanFactoryPostProcessor() {
- return beanFactory -> {
-
- // all components available in the application context
- String[] chatLanguageModels = beanFactory.getBeanNamesForType(ChatLanguageModel.class);
- String[] streamingChatLanguageModels = beanFactory.getBeanNamesForType(StreamingChatLanguageModel.class);
- String[] chatMemories = beanFactory.getBeanNamesForType(ChatMemory.class);
- String[] chatMemoryProviders = beanFactory.getBeanNamesForType(ChatMemoryProvider.class);
- String[] contentRetrievers = beanFactory.getBeanNamesForType(ContentRetriever.class);
- String[] retrievalAugmentors = beanFactory.getBeanNamesForType(RetrievalAugmentor.class);
-
- Set<String> tools = new HashSet<>();
- for (String beanName : beanFactory.getBeanDefinitionNames()) {
- try {
- Class<?> beanClass = Class.forName(beanFactory.getBeanDefinition(beanName).getBeanClassName());
- for (Method beanMethod : beanClass.getDeclaredMethods()) {
- if (beanMethod.isAnnotationPresent(Tool.class)) {
- tools.add(beanName);
- }
- }
- } catch (Exception e) {
- // TODO
- }
- }
-
- findAiServices(beanFactory).forEach(aiServiceClass -> {
-
- if (beanFactory.getBeanNamesForType(aiServiceClass).length > 0) {
- // User probably wants to configure AI Service bean manually
- // TODO or better fail because user should not annotate it with @AiService then?
- return;
- }
-
- GenericBeanDefinition aiServiceBeanDefinition = new GenericBeanDefinition();
- aiServiceBeanDefinition.setBeanClass(AiServiceFactory.class);
- aiServiceBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(aiServiceClass);
- MutablePropertyValues propertyValues = aiServiceBeanDefinition.getPropertyValues();
-
- AiService aiServiceAnnotation = aiServiceClass.getAnnotation(AiService.class);
-
- addBeanReference(
- ChatLanguageModel.class,
- aiServiceAnnotation,
- aiServiceAnnotation.chatModel(),
- chatLanguageModels,
- "chatModel",
- "chatLanguageModel",
- propertyValues
- );
-
- addBeanReference(
- StreamingChatLanguageModel.class,
- aiServiceAnnotation,
- aiServiceAnnotation.streamingChatModel(),
- streamingChatLanguageModels,
- "streamingChatModel",
- "streamingChatLanguageModel",
- propertyValues
- );
-
- addBeanReference(
- ChatMemory.class,
- aiServiceAnnotation,
- aiServiceAnnotation.chatMemory(),
- chatMemories,
- "chatMemory",
- "chatMemory",
- propertyValues
- );
-
- addBeanReference(
- ChatMemoryProvider.class,
- aiServiceAnnotation,
- aiServiceAnnotation.chatMemoryProvider(),
- chatMemoryProviders,
- "chatMemoryProvider",
- "chatMemoryProvider",
- propertyValues
- );
-
- addBeanReference(
- ContentRetriever.class,
- aiServiceAnnotation,
- aiServiceAnnotation.contentRetriever(),
- contentRetrievers,
- "contentRetriever",
- "contentRetriever",
- propertyValues
- );
-
- addBeanReference(
- RetrievalAugmentor.class,
- aiServiceAnnotation,
- aiServiceAnnotation.retrievalAugmentor(),
- retrievalAugmentors,
- "retrievalAugmentor",
- "retrievalAugmentor",
- propertyValues
- );
-
- if (aiServiceAnnotation.wiringMode() == EXPLICIT) {
- propertyValues.add("tools", toManagedList(asList(aiServiceAnnotation.tools())));
- } else if (aiServiceAnnotation.wiringMode() == AUTOMATIC) {
- propertyValues.add("tools", toManagedList(tools));
- } else {
- throw illegalArgument("Unknown wiring mode: " + aiServiceAnnotation.wiringMode());
- }
-
- BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
- registry.registerBeanDefinition(lowercaseFirstLetter(aiServiceClass.getSimpleName()), aiServiceBeanDefinition);
- });
- };
- }
构建对应的实例:
实际调用的dev.langchain4j.service.DefaultAiServices#build
, 根据实际的配置信息构建对应的aiService的bean对象
ChatLanguageModel
、StreamingChatLanguageModel
、ChatMemory
、ChatMemoryProvider
、ContentRetriever
和RetrievalAugmentor
类型的 Bean 名称。@Tool
注解的 Bean,并将它们的名称添加到tools
集合中。@AiService
注解的类。GenericBeanDefinition
,其类为AiServiceFactory
,并为其构造函数提供一个参数(即当前的@AiService
注解的类)。@AiService
注解中的属性,查找和添加与这些属性相对应的 Bean 引用到AiServiceFactory
Bean 的属性中。@AiService
注解的wiringMode
属性,将相应的工具(从@Tool
注解中获取的或手动指定的)添加到AiServiceFactory
Bean 的属性中。GenericBeanDefinition
注册到 Spring 的BeanDefinitionRegistry
中。@AiService
去构建一个可以由AiServiceFactory
创建的BeanDefinition。dev.langchain4j.model.openai.OpenAiChatModel#generate(java.util.List<dev.langchain4j.data.message.ChatMessage>, java.util.List<dev.langchain4j.agent.tool.ToolSpecification>, dev.langchain4j.agent.tool.ToolSpecification)
设置前置message:
设置工具: 在前面讲到过, 在构建aiService的时候会将@Tool
注释的工具封装成ToolSpecification
工具添加到 AiServiceContext
的属性toolSpecifications
中, 当工具如果不为空就会告诉大模型可以用工具有哪些; 最后根据大模型的反馈调用具体的工具类.
封装请求:
dev.langchain4j.model.openai.OpenAiChatModel#generate(java.util.List<dev.langchain4j.data.message.ChatMessage>, java.util.List<dev.langchain4j.agent.tool.ToolSpecification>, dev.langchain4j.agent.tool.ToolSpecification)
处理返回的response: 这时候知道需要调用的工具有哪些
循环调用ai模型, 根据模型反馈需要调用的工具链并将结果告诉模型, 最后根据模型的response返回最终的执行结果给用户
核心源码调用流程:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。