LangChain4J 跟 SpringAi 差不多, 都提供了提示词工程、对话记忆、结构化输出、RAG、MCP 等功能


接入LangChain4J

依赖

        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j</artifactId>
        </dependency>

        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-reactor</artifactId>
        </dependency>

低层次接口

提供了 Basics(大模型、提示词模板、对话记忆等) 和 RAG(向量模型、向量数据库、文本载入分割工具) 两种低层次接口

    @GetMapping("/chat01")
    public String chat01() {
        return chatModel.chat("你好,你是谁?");
    }

    @GetMapping("/stream01")
    public Flux<String> stream01(HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        return Flux.create(sink -> {
            streamingChatModel.chat("你好,我是谁?", new StreamingChatResponseHandler() {
                @Override
                public void onPartialResponse(String partialResponse) {
                    sink.next(partialResponse);
                }

                @Override
                public void onCompleteResponse(ChatResponse completeResponse) {
                    sink.complete();
                }

                @Override
                public void onError(Throwable error) {
                    sink.error(error);
                }
            });
        });
    }

    /**
     * 对话记忆,基于手动保存
     */
    @GetMapping("/memory01")
    public String memory01(@RequestParam("message") String message, HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        List<ChatMessage> messages = List.of(
                new SystemMessage("""
                        # 角色
                        你是一个美食推荐博主
              
                        # 推荐前提问
                        1.询问喜好
                        2.询问忌口
                        """),
                new UserMessage("给我推荐今晚该吃什么"),
                new AiMessage("请问你有什么喜欢吃的菜系和有什么不喜欢吃或忌口的吗?"),
                new UserMessage("我不喜欢吃香菜、芹菜"),
                new AiMessage("了解,我推荐你今晚吃一道家常菜,西红柿炒鸡蛋"),
                new UserMessage(message)
        );
        return chatModel.chat(messages).aiMessage().text();
    }

    /**
     * 对话记忆,基于MessageWindowChatMemory
     */
    @GetMapping("/memory02")
    public String memory02(@RequestParam("message") String message, HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        MessageWindowChatMemory memory = MessageWindowChatMemory.withMaxMessages(10);
        memory.add(
                new SystemMessage("""
                        # 角色
                        你是一个美食推荐博主
              
                        # 推荐前提问
                        1.询问喜好
                        2.询问忌口
                        """),
                new UserMessage("给我推荐今晚该吃什么"),
                new AiMessage("请问你有什么喜欢吃的菜系和有什么不喜欢吃或忌口的吗?"),
                new UserMessage("我不喜欢吃香菜、芹菜"),
                new AiMessage("了解,我推荐你今晚吃一道家常菜,西红柿炒鸡蛋"),
                new UserMessage(message)
        );
        return chatModel.chat(memory.messages()).aiMessage().text();
    }

    /**
     * 结构化输出
     */
    @GetMapping("/structure")
    public String structure(@RequestParam("language") String language, HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");

        // 构造Json结构
        ResponseFormat format = ResponseFormat.builder().type(ResponseFormatType.JSON).jsonSchema(
                JsonSchema.builder().name("Book").rootElement(
                        JsonObjectSchema.builder()
                                .addStringProperty("name", "书名")
                                .addStringProperty("author", "作者")
                                .addStringProperty("publisher", "出版社")
                                .addNumberProperty("price", "价格,以分为单位,人民币")
                                .build()
                ).build()
        ).build();

        ChatRequest request = ChatRequest.builder().responseFormat(format).messages(
                UserMessage.from("给我推荐几本%s路线学习的书,输出格式为Json".formatted(language))
        ).build();

        return chatModel.chat(request).aiMessage().text();
    }

    /**
     * 工具使用
     */
    @GetMapping("tools")
    public String tools(@RequestParam("city") String city, HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        // 构造工具清单
        List<ToolSpecification> tools = ToolSpecifications.toolSpecificationsFrom(TemperatureTools.class);

        // 让ai生成调用工具的指令
        List<ChatMessage> messages = new ArrayList<>();
        UserMessage message = UserMessage.from("查询中国%s今天的天气".formatted(city));
        messages.add(message);
        ChatRequest request = ChatRequest.builder().messages(message).toolSpecifications(tools).toolChoice(ToolChoice.AUTO).build();
        AiMessage aiMessage = chatModel.chat(request).aiMessage();
        messages.add(aiMessage);

        // 调用工具并获取工具返回信息
        List<ToolExecutionRequest> toolExecutionRequests = aiMessage.toolExecutionRequests();
        toolExecutionRequests.forEach(toolExecutionRequest -> {
            DefaultToolExecutor executor = new DefaultToolExecutor(new TemperatureTools(), toolExecutionRequest);
            String result = executor.execute(toolExecutionRequest, UUID.randomUUID().toString());
            ToolExecutionResultMessage toolMessage = ToolExecutionResultMessage.from(toolExecutionRequest, result);
            messages.add(toolMessage);
        });

        // 把用户信息和工具返回结果一起交给ai生成最终回复
        ChatRequest finalRequest = ChatRequest.builder().messages(messages).toolSpecifications(tools).build();
        return chatModel.chat(finalRequest).aiMessage().text();
    }

高层次接口

AI Services(和 Spring Data JPA 类似, 只需显式定义接口, 可以自定义加入 Menory、Tools、RAG 等)

Controller

    @GetMapping("/chat01")
    public String chat01() {
        return aiService.chat("你好,你是谁?");
    }

    @GetMapping("/stream01")
    public Flux<String> stream01(HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        return aiService.stream("你好,我是谁?");
    }

    @GetMapping("/prompt")
    public Flux<String> prompt(HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        return aiService.prompt("推荐一道美食");
    }

Service

@AiService
public interface LangChainAiService {
    String chat(String msg);

    Flux<String> stream(String msg);

    @SystemMessage("你是我的美食推荐bot,推荐前询问我的喜好、忌口")
    Flux<String> prompt(String msg);

    @UserMessage("请推荐一本{{language}}入门书")
    Book structure(@V("language") String language);
}
@AiService
public interface MemoryAiService {

    Flux<String> stream(@MemoryId String memoryId, @UserMessage String msg);

}

提示词模板、结构化输出

    @GetMapping("/structure")
    public Book structure(HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        return aiService.structure("Java");
    }

对话记忆

    @GetMapping("/memory")
    public Flux<String> memory(@RequestParam("memory-id") String memoryId, @RequestParam("msg") String msg, HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        MemoryAiService service = AiServices.builder(MemoryAiService.class)
                .streamingChatModel(stream).chatMemoryProvider(id ->
                        MessageWindowChatMemory.withMaxMessages(10)
                ).build();
        return service.stream(memoryId, msg);
    }

    @GetMapping("/memory")
    public Flux<String> memory(@RequestParam("memory-id") String memoryId, @RequestParam("msg") String msg, HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        MemoryAiService service = AiServices.builder(MemoryAiService.class)
                .streamingChatModel(stream).chatMemoryProvider(id ->
                        MessageWindowChatMemor
                                .builder()
                                .id(memoryId)
                                .chatMemoryStore(custom)
                                .build()
                ).build();
        return service.stream(memoryId, msg);
    }

工具调用

    /**
     * 工具调用
     */
    @GetMapping("/tools")
    public String tools(@RequestParam("city") String city, HttpServletResponse response){
        response.setCharacterEncoding("UTF-8");
        LangChainAiService service = AiServices
                .builder(LangChainAiService.class)
                .tools(new TemperatureTools())
                .chatModel(model)
                .build();
        return service.chat("中国%s今天天气怎么样?".formatted(city));
    }