LangChain4j实战

LangChain4j进阶实战(三):Function Calling赋能复杂业务流程编排 本文深入探讨了如何利用LangChain4j的Function Calling机制高效编排复杂业务流程。通过电商客服和跨境物流订单处理等实战案例,详细展示了函数注册、流程控制、异常处理及性能优化的最佳实践,帮助开发者实现70%以上的流程变更效率提升。 阅读详情

一、什么是 LangChain4j?

目前主流的 Java AI 开发框架有 Spring AI 和 LangChain4j,它们都提供了很多 开箱即用的 API 来帮你调用大模型、实现 AI 开发常用的功能,上期我们学了SpringAi,今天我们要学的是:

  • 对话记忆
  • 结构化输出
  • RAG 知识库
  • 工具调用
  • MCP
  • SSE 流式输出

二、开始实操

1、新建项目

打开 IDEA 开发工具,新建一个 Spring Boot 项目,Java 版本尽量选择 21(因为 LangChain4j 最低支持 17 版本):
在这里插入图片描述
选择依赖,使用 3.5.x 版本的 Spring Boot,引入 Spring MVC 和 Lombok 注解库:
在这里插入图片描述

2、AI 对话 - ChatModel

ChatModel 是最基础的概念,负责和 AI 大模型交互。

首先需要引入至少一个 AI 大模型依赖,这里选择国内的阿里云大模型,提供了和 Spring Boot 项目的整合依赖包,比较方便:

<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
    <version>1.1.0-beta7</version>
</dependency>

需要到 阿里云百炼平台 获取大模型调用 key,注意不要泄露!
在这里插入图片描述
回到项目,在配置文件中添加大模型配置,指定模型名称和 API Key:

langchain4j:
  community:
    dashscope:
      chat-model:
        model-name: qwen-max
        api-key: <You API Key here>

有了 ChatModel 后,创建一个 AiCodeHelper 类,引入自动注入的 qwenChatModel,编写简单的对话代码,并利用 Lombok 注解打印输出结果日志:

@Service
@Slf4j
publicclass AiCodeHelper {

    @Resource
    private ChatModel qwenChatModel;

    public String chat(String message) {
        UserMessage userMessage = UserMessage.from(message);
        ChatResponse chatResponse = qwenChatModel.chat(userMessage);
        AiMessage aiMessage = chatResponse.aiMessage();
        log.info("AI 输出:" + aiMessage.toString());
        return aiMessage.text();
    }
}

编写单元测试【在chat方法中按alt+回车】

@SpringBootTest
class AiCodeHelperApplicationTests {

    @Resource
    public AiCodeHelper aiCodeHelper;

    @Test
    void chat() {
        String chat = aiCodeHelper.chat("你好,我是程序员鱼亮");
        System.out.println(chat);
    }
}

以 Debug 模式运行单元测试,成功运行并查看输出:
在这里插入图片描述

3、多模态【候补】

多模态是指能够同时处理、理解和生成多种不同类型数据的能力,比如文本、图像、音频、视频、PDF 等等。
LangChain4j 中使用多模态的方法很简单,用户消息中是可以添加图片、音视频、PDF 等媒体资源的。

4、系统提示词 - SystemMessage

系统提示词是设置 AI 模型行为规则和角色定位的隐藏指令,用户通常不能直接看到。系统 Prompt 相当于给 AI 设定人格和能力边界,也就是告诉 AI “你是谁?你能做什么?”。
修改 chat 方法,代码如下:

private static final String SYSTEM_MESSAGE = """
        你是编程领域的小助手,帮助用户解答编程学习和求职面试相关的问题,并给出建议。重点关注 4 个方向:
        1. 规划清晰的编程学习路线
        2. 提供项目学习建议
        3. 给出程序员求职全流程指南(比如简历优化、投递技巧)
        4. 分享高频面试题和面试技巧
        请用简洁易懂的语言回答,助力用户高效学习与求职。
        """;

public String chat(String message) {
    SystemMessage systemMessage = SystemMessage.from(SYSTEM_MESSAGE);
    UserMessage userMessage = UserMessage.from(message);
    ChatResponse chatResponse = qwenChatModel.chat(systemMessage, userMessage);
    AiMessage aiMessage = chatResponse.aiMessage();
    log.info("AI 输出:" + aiMessage.toString());
    return aiMessage.text();
}

5、AI 服务 - AI Service

在学习更多特性前,我们要了解 LangChain4j 最重要的开发模式 —— AI Service,提供了很多高层抽象的、用起来更方便的 API,把 AI 应用当做服务来开发。

使用 AI Service

首先引入 langchain4j 依赖:

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

然后创建一个编程助手 AI Service 服务,采用声明式开发方法,编写一个对话方法,然后可以直接通过 @SystemMessage 注解定义系统提示词。

public interface AiCodeHelperService {

    @SystemMessage("你是一位编程小助手")
    String chat(String userMessage);
}

不过由于我们提示词较长,写到注解里很不优雅,所以单独在 resources 目录下新建文件 system-prompt.txt 来存储系统提示词。

public interface AiCodeHelperService {

    @SystemMessage(fromResource = "system-prompt.txt")
    String chat(String userMessage);
}

然后我们需要编写工厂类,用于创建 AI Service:

@Configuration
public class AiCodeHelperServiceFactory {

    @Resource
    private ChatModel qwenChatModel;

    @Bean
    public AiCodeHelperService aiCodeHelperService() {
        return AiServices.create(AiCodeHelperService.class, qwenChatModel);
    }
}

调用 AiServices.create 方法就可以创建出 AI Service 的实现类了,背后的原理是利用 Java 反射机制创建了一个实现接口的代理对象,代理对象负责输入和输出的转换,比如把 String 类型的用户消息参数转为 UserMessage 类型并调用 ChatModel,再将 AI 返回的 AiMessage 类型转换为 String 类型作为返回值。

6、Spring Boot 项目中使用

如果你觉得手动调用 create 方法来创建 Service 比较麻烦,在 Spring Boot 项目中可以引入依赖:

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

然后给 AI Service 加上 @AiService 注解,就能自动创建出服务实例了:

@AiService
public interface AiCodeHelperService {

    @SystemMessage(fromResource = "system-prompt.txt")
    String chat(String userMessage);
}

记得注释掉之前工厂类的 @Configuration 注解,否则会出现 Bean 冲突

7、会话记忆 - ChatMemory

会话记忆是指让 AI 能够记住用户之前的对话内容,并保持上下文连贯性,这是实现 AI 应用的核心特性。

使用会话记忆

LangChain4j 为我们提供了开箱即用的 MessageWindowChatMemory 会话记忆,最多保存 N 条消息,多余的会自动淘汰。创建会话记忆后,在构造 AI Service 设置 chatMemory:

@Configuration
public class AiCodeHelperServiceFactory {

    @Resource
    private ChatModel qwenChatModel;

    @Bean
    public AiCodeHelperService aiCodeHelperService() {
        // 会话记忆
        ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
        AiCodeHelperService aiCodeHelperService = AiServices.builder(AiCodeHelperService.class)
                .chatModel(qwenChatModel)
                .chatMemory(chatMemory)
                .build();
        return aiCodeHelperService;
    }
}

8、结构化输出

结构化输出有 3 种实现方式:

  • 利用大模型的 JSON schema
  • 利用 Prompt + JSON Mode
  • 利用 Prompt

比如我们增加一个 让 AI 生成学习报告 的方法,AI 需要输出学习报告对象,包含名称和建议列表:

@SystemMessage(fromResource = "system-prompt.txt")
Report chatForReport(String userMessage);

// 学习报告
record Report(String name, List<String> suggestionList){}

编写单元测试:


void chatForReport() {
    String userMessage = "你好,我是程序员鱼亮,学编程两年半,请帮我制定学习报告";
    AiCodeHelperService.Report report = aiCodeHelperService.chatForReport(userMessage);
    System.out.println(report);
}

运行单元测试,效果很不错:
在这里插入图片描述
如果你发现 AI 有时无法生成准确的 JSON,那么可以采用 JSON Schema 模式,直接在请求中约束 LLM 的输出格式。这是目前最可靠、精确度最高的结构化输出实现。

ResponseFormat responseFormat = ResponseFormat.builder()
        .type(JSON)
        .jsonSchema(JsonSchema.builder()
                .name("Person")
                .rootElement(JsonObjectSchema.builder()
                        .addStringProperty("name")
                        .addIntegerProperty("age")
                        .addNumberProperty("height")
                        .addBooleanProperty("married")
                        .required("name", "age", "height", "married") 
                        .build())
                .build())
        .build();
ChatRequest chatRequest = ChatRequest.builder()
        .responseFormat(responseFormat)
        .messages(userMessage)
        .build();

9、检索增强生成 - RAG

RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合信息检索技术和 AI 内容生成的混合架构,可以解决大模型的知识时效性限制和幻觉问题。

简单来说,RAG 就像给 AI 配了一个 “小抄本”,让 AI 回答问题前先查一查特定的知识库来获取知识,确保回答是基于真实资料而不是凭空想象。很多企业也基于 RAG 搭建了自己的智能客服,可以用自己积累的领域知识回复用户。

RAG 的完整工作流程如下:
在这里插入图片描述
LangChain 提供了 3 种 RAG 的实现方式,我把它称为:极简版、标准版、进阶版。

极简版 RAG

极简版适合快速查看效果,首先需要引入额外的依赖,里面包含了内置的离线 Embedding 模型,开箱即用:

<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-easy-rag</artifactId>
    <version>1.1.0-beta7</version>
</dependency>

示例代码如下,使用内置的文档加载器读取文档,然后利用内置的 Embedding 模型将文档转换成向量,并存储在内置的 Embedding 内存存储中,最后给 AI Service 绑定默认的内容检索器。

// RAG
// 1. 加载文档
List<Document> documents = FileSystemDocumentLoader.loadDocuments("src/main/resources/docs");
// 2. 使用内置的 EmbeddingModel 转换文本为向量,然后存储到自动注入的内存 embeddingStore 中
EmbeddingStoreIngestor.ingest(documents, embeddingStore);
// 构造 AI Service
AiCodeHelperService aiCodeHelperService = AiServices.builder(AiCodeHelperService.class)
        .chatModel(qwenChatModel)
        .chatMemory(chatMemory)
        // RAG:从内存 embeddingStore 中检索匹配的文本片段
        .contentRetriever(EmbeddingStoreContentRetriever.from(embeddingStore))
        .build();

可以看到,极简版的特点是 “一切皆默认”,实际开发中,为了更好的效果,建议采用标准版或进阶版。

标准版 RAG

下面来试试标准版 RAG 实现,为了更好地效果,我们需要:

  • 加载 Markdown 文档并按需切割
  • Markdown 文档补充文件名信息
  • 自定义 Embedding 模型
  • 自定义内容检索器

在 Spring Boot 配置文件中添加 Embedding 模型配置,使用阿里云提供的 text-embedding-v4 模型:

langchain4j:
  community:
    dashscope:
      chat-model:
        model-name:qwen-max
        api-key:<YouAPIKeyhere>
      embedding-model:
        model-name:text-embedding-v4
        api-key:<YouAPIKeyhere>

新建 rag.RagConfig,编写 RAG 相关的代码,执行 RAG 的初始流程并返回了一个定制的内容检索器 Bean:

/**
 * 加载 Rag
 */
@Configuration
public class RagConfig {

    @Resource
    private EmbeddingModel qwenEmbeddingModel;

    @Resource
    private EmbeddingStore<TextSegment> embeddingStore;

    @Bean
    public ContentRetriever contentRetriever(){
        // ---- Rag -----
        // 1、加载文件
        List<Document> document = FileSystemDocumentLoader.loadDocuments("src/main/resources/docs");
        // 2、文档切割:每个文档按照段落进行分割,最大1000个字符 每次最多重叠200个字符
        DocumentByParagraphSplitter documentByParagraphSplitter =
                new DocumentByParagraphSplitter(1000, 200);
        // 3、自定义文档加载器 把文档转化成向量并保存到向量数据库中
        EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
                // 切割
                .documentSplitter(documentByParagraphSplitter)
                // 为了提高文档的质量,为每个切割后的文档碎片 TextSegment 添加文档名称作为元信息[file_name是固定写法,不可改]
                .textSegmentTransformer(textSegment -> {
                    return TextSegment.from(textSegment.metadata().getString("file_name") +
                            "\n" + textSegment.text(), textSegment.metadata());
                })
                // 使用向量数据模型
                .embeddingModel(qwenEmbeddingModel)
                // 保存向量数据
                .embeddingStore(embeddingStore)
                .build();
        // 加载文档
        ingestor.ingest(document);
        // 4、自定义内容加载器
        EmbeddingStoreContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore)
                .embeddingModel(qwenEmbeddingModel)
                .maxResults(5) // 最多返回5个结果
                .minScore(0.75) // 过滤分数小于 0.75的结果
                .build();
        return contentRetriever;
    }
}

然后在构建 AI Service 时绑定内容检索器:

@Resource
private ContentRetriever contentRetriever;

@Bean
public AiCodeHelperService aiCodeHelperService() {
    // 会话记忆
    ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
    // 构造 AI Service
    AiCodeHelperService aiCodeHelperService = AiServices.builder(AiCodeHelperService.class)
            .chatModel(qwenChatModel)
            .chatMemory(chatMemory)
            .contentRetriever(contentRetriever) // RAG 检索增强生成
            .build();
    return aiCodeHelperService;
}

编写单元测试:

@Test
void chatWithRag() {
    String chat = aiCodeHelperService.chat("怎么学习java?有哪些常见的面试题?");
    System.out.println(chat);
}

Debug 运行,能够看到分割的文档片段,部分文档片段有内容重叠:
在这里插入图片描述
可以在对话记忆中看到实际发送的、增强后的 Prompt:
在这里插入图片描述
在这里插入图片描述
回答效果也是符合预期的:
在这里插入图片描述

获取引用源文档

如果能够给 AI 的回答下面展示回答来源,更容易增加内容的可信度
在 LangChain4j 中,实现这个功能很简单。在 AI Service 中新增方法,在原本的返回类型外封装一层 Result 类,就可以获得封装后的结果,从中能够获取到 RAG 引用的源文档、以及 Token 的消耗情况等等。

@SystemMessage(fromResource = "system-prompt.txt")
Result<String> chatWithRag(String userMessage);

修改单元测试,输出更多信息:

@Test
void chatWithRag() {
    Result<String> result = aiCodeHelperService.chatWithRag("怎么学习 Java?有哪些常见面试题?");
    String content = result.content();
    List<Content> sources = result.sources();
    System.out.println(content);
    System.out.println(sources);
}

执行效果如图,获取到了引用的源文档信息:
在这里插入图片描述

10、工具调用 - Tools

工具调用(Tool Calling)可以理解为让 AI 大模型 借用外部工具 来完成它自己做不到的事情。

跟人类一样,如果只凭手脚完成不了工作,那么就可以利用工具箱来完成。

工具可以是任何东西,比如网页搜索、对外部 API 的调用、访问外部数据、或执行特定的代码等。

比如用户提问 “帮我查询上海最新的天气”,AI 本身并没有这些知识,它就可以调用 “查询天气工具”,来完成任务。

需要注意的是,工具调用的本质 并不是 AI 服务器自己调用这些工具、也不是把工具的代码发送给 AI 服务器让它执行,它只能提出要求,表示 “我需要执行 XX 工具完成任务”。而真正执行工具的是我们自己的应用程序,执行后再把结果告诉 AI,让它继续工作。
第一步:先引入 Jsoup 库:

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.20.1</version>
</dependency>

然后在 tools 包下编写工具,通过 @Tool 注解就能声明工具了,注意 要认真编写工具和工具参数的描述,这直接决定了 AI 能否正确地调用工具。

@Slf4j
publicclass InterviewQuestionTool {
   /**
     * 从面试网站获取关键词相关的面试题列表
     *
     * @param keyword 搜索关键词(如"redis"、"java多线程")
     * @return 面试题列表,若失败则返回错误信息
     */
    @Tool(name = "interviewQuestionSearch", value = """
            Retrieves relevant interview questions from mianshiya.com based on a keyword.
            Use this tool when the user asks for interview questions about specific technologies,
            programming concepts, or job-related topics. The input should be a clear search term.
            """
    )
    public String searchInterviewQuestions(@P(value = "the keyword to search") String keyword) {
        List<String> questions = new ArrayList<>();
        // 构建搜索URL(编码关键词以支持中文)
        String encodedKeyword = URLEncoder.encode(keyword, StandardCharsets.UTF_8);
        String url = "https://www.mianshiya.com/search/all?searchText=" + encodedKeyword;
        // 发送请求并解析页面
        Document doc;
        try {
            doc = Jsoup.connect(url)
                    .userAgent("Mozilla/5.0")
                    .timeout(5000)
                    .get();
        } catch (IOException e) {
            log.error("get web error", e);
            return e.getMessage();
        }
        // 提取内容
        Elements questionElements = doc.select(".ant-table-cell > a");
        questionElements.forEach(el -> questions.add(el.text().trim()));
        return String.join("\n", questions);
    }
}

给 AI Service 绑定工具:

// 构造 AI Service
AiCodeHelperService aiCodeHelperService = AiServices.builder(AiCodeHelperService.class)
        .chatModel(qwenChatModel)
        .chatMemory(chatMemory)
        .contentRetriever(contentRetriever) // RAG 检索增强生成
        .tools(new InterviewQuestionTool()) // 工具调用
        .build();

编写单元测试,验证工具的效果:

@Test
void chatWithTools() {
    String result = aiCodeHelperService.chat("有哪些常见的计算机网络面试题?");
    System.out.println(result);
}

Debug 运行,发现 AI 调用了工具:
在这里插入图片描述
工具检索到了题目列表:
在这里插入图片描述
可以通过 Debug 看到 AI Service 加载了工具:
在这里插入图片描述

11、模型上下文协议 - MCP

MCP(Model Context Protocol,模型上下文协议)是一种开放标准,目的是增强 AI 与外部系统的交互能力。MCP 为 AI 提供了与外部工具、资源和服务交互的标准化方式,让 AI 能够访问最新数据、执行复杂操作,并与现有系统集成。

可以将 MCP 想象成 AI 应用的 USB 接口。就像 USB 为设备连接各种外设和配件提供了标准化方式一样,MCP 为 AI 模型连接不同的数据源和工具提供了标准化的方法。
请添加图片描述
简单来说,通过 MCP 协议,AI 应用可以轻松接入别人提供的服务来实现更多功能,比如查询地理位置、操作数据库、部署网站、甚至是支付等等。

刚刚我们通过工具调用实现了面试题的搜索,下面我们利用 MCP 实现 全网搜索内容,这也是一个典型的 MCP 应用场景了。

引入依赖:

<!-- https://mvnrepository.com/artifact/dev.langchain4j/langchain4j-mcp -->
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-mcp</artifactId>
    <version>1.1.0-beta7</version>
</dependency>

在配置文件中新增 API Key 的配置:

bigmodel:
  api-key: <Your Api Key>

新建 mcp.McpConfig,按照官方的开发方式,初始化和 MCP 服务的通讯,并创建 McpToolProvider 的 Bean:

@Configuration
publicclass McpConfig {

    @Value("${bigmodel.api-key}")
    private String apiKey;

    @Bean
    public McpToolProvider mcpToolProvider() {
        // 和 MCP 服务通讯
        McpTransport transport = new HttpMcpTransport.Builder()
                .sseUrl("https://open.bigmodel.cn/api/mcp/web_search/sse?Authorization=" + apiKey)
                .logRequests(true) // 开启日志,查看更多信息
                .logResponses(true)
                .build();
        // 创建 MCP 客户端
        McpClient mcpClient = new DefaultMcpClient.Builder()
                .key("yupiMcpClient")
                .transport(transport)
                .build();
        // 从 MCP 客户端获取工具
        McpToolProvider toolProvider = McpToolProvider.builder()
                .mcpClients(mcpClient)
                .build();
        return toolProvider;
    }
}

注意,上面我们是通过 SSE 的方式调用 MCP。如果你是通过 npx 或 uvx 本地启动 MCP 服务,需要先安装对应的工具,并且利用下面的配置建立通讯:

McpTransport transport = new StdioMcpTransport.Builder()
    .command(List.of("/usr/bin/npm", "exec", "@modelcontextprotocol/server-everything@0.6.2"))
    .logEvents(true) // only if you want to see the traffic in the log
    .build();

在 AI Service 中应用 MCP 工具:

@Resource
private McpToolProvider mcpToolProvider;

// 构造 AI Service
AiCodeHelperService aiCodeHelperService = AiServices.builder(AiCodeHelperService.class)
        .chatModel(qwenChatModel)
        .chatMemory(chatMemory)
        .contentRetriever(contentRetriever) // RAG 检索增强生成
        .tools(new InterviewQuestionTool()) // 工具调用
        .toolProvider(mcpToolProvider) // MCP 工具调用
        .build();

编写单元测试:

@Test
void chatWithMcp() {
    String result = aiCodeHelperService.chat("什么是程序员鱼皮的编程导航?");
    System.out.println(result);
}

在这里插入图片描述

12、护轨 - Guardrail

分为输入护轨(input guardrails)和输出护轨(output guardrails),可以在请求 AI 前和接收到 AI 的响应后执行一些额外操作,比如调用 AI 前鉴权、调用 AI 后记录日志。请添加图片描述
让我们小试一把,在调用 AI 前进行敏感词检测,如果用户提示词包含敏感词,则直接拒绝。

新建 guardrail.SafeInputGuardrail,实现 InputGuardrail 接口:

/**
 * 安全检测输入护轨
 */
publicclass SafeInputGuardrail implements InputGuardrail {

    privatestaticfinal Set<String> sensitiveWords = Set.of("kill", "evil");

    /**
     * 检测用户输入是否安全
     */
    
    public InputGuardrailResult validate(UserMessage userMessage) {
        // 获取用户输入并转换为小写以确保大小写不敏感
        String inputText = userMessage.singleText().toLowerCase();
        // 使用正则表达式分割输入文本为单词
        String[] words = inputText.split("\\W+");
        // 遍历所有单词,检查是否存在敏感词
        for (String word : words) {
            if (sensitiveWords.contains(word)) {
                return fatal("Sensitive word detected: " + word);
            }
        }
        return success();
    }
}

修改 AI Service,使用输入护轨:

({SafeInputGuardrail.class}) // 添加输入护轨
public interface AiCodeHelperService {

编写单元测试,写一个包含敏感词的提示词:


void chatWithGuardrail() {
    String result = aiCodeHelperService.chat("kill the game");
    System.out.println(result);
}

运行并查看效果,会触发输入检测,直接抛出异常:
在这里插入图片描述
当然,除了输入护轨,也可以编写输出护轨,对 AI 的响应结果进行检测。

13、日志和可观测性

开启日志的方法很简单,直接构造模型时指定开启、或者直接编写 Spring Boot 配置,支持打印 AI 请求和响应日志。

但并不是所有的 ChatModel 都支持,比如我测试下来 QwenChatModel 就不支持。这时只能把希望交给可观测性了。

可观测性

可以通过自定义 Listener 获取 ChatModel 的调用信息,比较灵活。

新建 listener.ChatModelListenerConfig,输出请求、响应、错误信息:

@Configuration
@Slf4j
publicclass ChatModelListenerConfig {
    
    @Bean
    ChatModelListener chatModelListener() {
        returnnew ChatModelListener() {
            @Override
            public void onRequest(ChatModelRequestContext requestContext) {
                log.info("onRequest(): {}", requestContext.chatRequest());
            }

            @Override
            public void onResponse(ChatModelResponseContext responseContext) {
                log.info("onResponse(): {}", responseContext.chatResponse());
            }

            @Override
            public void onError(ChatModelErrorContext errorContext) {
                log.info("onError(): {}", errorContext.error().getMessage());
            }
        };
    }
}

但是只定义 Listener 好像对 QwenChatModel 不起作用,所以我们需要手动构造自定义的 QwenChatModel。

新建 model.QwenChatModelConfig,构造 ChatModel 对象并绑定 Listener:


(prefix = "langchain4j.community.dashscope.chat-model")

publicclass QwenChatModelConfig {

    private String modelName;

    private String apiKey;

    
    private ChatModelListener chatModelListener;

    
    public ChatModel myQwenChatModel() {
        return QwenChatModel.builder()
                .apiKey(apiKey)
                .modelName(modelName)
                .listeners(List.of(chatModelListener))
                .build();
    }
}

然后,我们将AiCodeHelpServiceFactory中引用的ChatModel 的名称改为 myQwenChatModel,防止和 Spring Boot 自动注入的 ChatModel 冲突。

再次调用 AI,就能看到很多信息了:
在这里插入图片描述

14、AI 服务化

AI 的能力基本开发完成,但是目前只支持本地运行,需要编写一个接口提供给前端调用,让 AI 能够成为一个服务。

我们平时开发的大多数接口都是同步接口,也就是等后端处理完再返回。但是对于 AI 应用,特别是响应时间较长的对话类应用,可能会让用户失去耐心等待,因此推荐使用 SSE(Server-Sent Events)技术实现实时流式输出,类似打字机效果,大幅提升用户体验。

SSE 流式接口开发

LangChain 提供了 2 种方式来支持流式响应(注意,流式响应不支持结构化输出)。

一种方法是 TokenStream,先让 AI 对话方法返回 TokenStream,然后创建 AI Service 时指定流式对话模型 StreamingChatModel:

interface Assistant {

    TokenStream chat(String message);
}

StreamingChatModel model = OpenAiStreamingChatModel.builder()
    .apiKey(System.getenv("OPENAI_API_KEY"))
    .modelName(GPT_4_O_MINI)
    .build();

Assistant assistant = AiServices.create(Assistant.class, model);

TokenStream tokenStream = assistant.chat("Tell me a joke");

tokenStream.onPartialResponse((String partialResponse) -> System.out.println(partialResponse))
    .onRetrieved((List<Content> contents) -> System.out.println(contents))
    .onToolExecuted((ToolExecution toolExecution) -> System.out.println(toolExecution))
    .onCompleteResponse((ChatResponse response) -> System.out.println(response))
    .onError((Throwable error) -> error.printStackTrace())
    .start();

我个人会更喜欢另一种方法,使用 Flux 代替 TokenStream,熟悉响应式编程的同学应该对 Flux 不陌生吧?让 AI 对话方法返回 Flux 响应式对象即可。

首先需要引入响应式包依赖:

<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-reactor</artifactId>
    <version>1.1.0-beta7</version>
</dependency>

然后给 AI Service 增加流式对话方法,这里顺便支持下多用户的会话记忆:

// 流式对话
Flux<String> chatStream( int memoryId,  String userMessage);

由于要用到流式模型,需要增加流式模型配置:

langchain4j:
  community:
    dashscope:
      streaming-chat-model:
        model-name: qwen-max
        api-key: <Your Api Key>

构造 AI Service 时指定流式对话模型(自动注入即可),并且补充会话记忆提供者:
在这里插入图片描述
最后,编写 Controller 接口。为了方便测试,这里使用 Get 请求:


("/api")
public class AiController {

    
    private AiCodeHelperService aiCodeHelperService;

    ("/chat")
    public Flux<ServerSentEvent<String>> chat(int memoryId, String userMessage){
        return aiCodeHelperService.chatStream(memoryId, userMessage)
                .map(chunk  -> ServerSentEvent.<String>builder()
                        .data(chunk).build()
                );
    }
}

增加服务器配置,指定后端端口和接口路径前缀:

server:
  port: 8081
  servlet:
    context-path: /api

使用ApiFox测试:
在这里插入图片描述

后端支持跨域

为了让前端项目能够顺利调用后端接口,我们需要在后端配置跨域支持。在 config 包下创建跨域配置类,代码如下:

/**
 * 全局跨域配置
 */

publicclass CorsConfig implements WebMvcConfigurer {

    
    public void addCorsMappings(CorsRegistry registry) {
        // 覆盖所有请求
        registry.addMapping("/**")
                // 允许发送 Cookie
                .allowCredentials(true)
                // 放行哪些域名(必须用 patterns,否则 * 会和 allowCredentials 冲突)
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .exposedHeaders("*");
    }
}

注意,如果 .allowedOrigins(“*”) 与 .allowCredentials(true) 同时配置会导致冲突,因为出于安全考虑,跨域请求不能同时允许所有域名访问和发送认证信息(比如 Cookie)。

15、AI 生成前端

我们可以利用 AI 来快速生成前端代码,极大提高开发效率。这里鱼皮使用 主流 AI 开发工具 Trae,挑战不写一行代码,生成符合要求的前端项目。你应该使用 Windows 支持的命令来完成任务。

提示词

首先准备一段详细的 Prompt,一般要包括需求、技术选型、后端接口信息,还可以提供一些原型图、后端代码等。

你是一位专业的前端开发,请帮我根据下列信息来生成对应的前端项目代码。

## 需求

应用为《AI 编程小助手》,帮助用户解答编程学习和求职面试相关的问题,并给出建议。

只有一个页面,就是主页:页面风格为聊天室,上方是聊天记录(用户信息在右边,AI 信息在左边),下方是输入框,进入页面后自动生成一个聊天室 id,用于区分不同的会话。通过 SSE 的方式调用 chat 接口,实时显示对话内容。

## 技术选型

1. Vue3 项目
2. Axios 请求库

## 后端接口信息

接口地址前缀:http://localhost:8081/api

## SpringBoot 后端接口代码


("/api")
public class AiController {

    ("/chat")
    public Flux<ServerSentEvent<String>> chat(int memoryId, String message) {
        return aiCodeHelperService.chatStream(memoryId, message)
                .map(chunk -> ServerSentEvent.<String>builder()
                        .data(chunk)
                        .build());
    }
}

开发

打开“Trae”软件,创建ai-code-helper-frontend项目,将上述提示此给到Builder,直接生成好,我们还可以根据自己的想法不断的优化
在这里插入图片描述

查看效果

在这里插入图片描述
请添加图片描述

【大模型】SpringBoot整合LangChain4j实现RAG检索实战详解 SpringBoot整合LangChain4j实现RAG检索实战详解 阅读详情

相关推荐

LangChain开源大型语言模型(LLM)框架

LangChain是由Harrison Chase开发的一个框架,迎合了大型语言模型(LLM)的需求。在OpenAI的GPT-3取得巨大成功后,它的受欢迎程度激增,可以说,GPT-3.5和GPT-4更是如此。2022年10月底推出的LangChain利用了这些进展,提供了一个专门用于创建依赖LLM的生产级应用程序的平台,而不仅仅是实验性的。 LangChain的关键理念是将不同的部件连接或 "链条 "在一起,形成复杂的应用。这些链子可以使用许多模块的碎片,如各种提示的模板(如聊天回复、ELI5等)、与不同LLM模型的连接(如GPT-3、BLOOM等)、代理(使用LLM来决定做什么--我们今天晚些时候将讨论gpt-engineer项目在这个类别中的情况)以及记忆(短期和长期)。所有这些都是以标准应用框架的形式提供的。 langchain4j/langchain4j则是langchain的一个Java封装器--一个类固醇。整个项目已经在引导用户,并专注于清楚地展示使用的可能性(在项目中你会发现一个带有例子的目录),并且创造者宣布进一步的发展。

LangChain4j实战之九:高级API(AI Services)实例的创建方式

使用高级API服务时,对应的实现类的bean如何创建?这里提供了三种方法供您选择

程序员欣宸的博客 1832

从零搭建Synopsys EDA环境:Ubuntu系统下的完整安装与避坑指南

本文详细介绍了在Ubuntu系统下从零搭建Synopsys EDA环境的完整流程,包括系统配置、安装文件准备、核心工具安装、License配置及常见问题排查。特别针对数字IC设计场景,提供了VCS、Verdi、Design Compiler等EDA Tools的安装避坑指南和性能优化建议,帮助开发者高效搭建稳定的开发环境。

weixin_33738982的博客 491

LangChain4j实战之七:聊天记忆,高级API版

用LangChain4j的高级API解决聊天记忆问题,还有分用户的记忆,以及记忆内容的持久化

程序员欣宸的博客 1250

Mediapipe手势识别实战——基于关节角度计算实现动态手势分类

本文详细介绍了如何利用Mediapipe进行手势识别,通过计算手指关节弯曲角度实现动态手势分类。文章从基础原理出发,讲解了关节角度计算的数学方法,并提供了Python实现代码。同时,还分享了数字手势和OK手势的识别实战经验,以及性能优化和工程实践中的关键技巧,帮助开发者快速掌握实时手势识别技术。

weixin_26891599的博客 403

Allegro File菜单实战指南:从导入网表到导出BOM的完整流程

PCB设计离不开对EDA软件菜单体系的深入理解。在Cadence Allegro中,File菜单是数据流转的中枢,它不仅是新建、打开、保存的基础入口,更是连接原理图与版图、设计与制造的关键桥梁。工程师常遇到的“导入网表报错”“导出BOM为空”“板框不闭合”等高频问题,大多源于对File菜单中Import与Export功能的理解不到位。从基础概念出发,先厘清Import(网表、DXF、IPC)与Export(BOM、ODB++、DXF)的工作原理,再结合版本兼容、封装库路径、单位设置等工程细节,即可构建完整的

weixin_32950759的博客 296

LangChain4j实战-Java AI应用开源框架之LangChain4j和Spring AI

LangChain4j实战-Java AI应用开源框架之LangChain4j和Spring AI

和时间做朋友,终身学习 2159

[Stanford CS217]CUDA中矩阵乘法GEMM优化的最关键点:向量内积和向量外积分别取行和取列

对于矩阵C中的每一个tile,矩阵A与矩阵B中的tile只需要读取一次,这样就可以达到 O(N) 的计算强度。理想情况下性能应当受限于算术吞吐量,确切来说对于一个大型方阵,即 M=N=K ,矩阵乘法的数学操作复杂度为 O(N3) ,而数据量为 O(N2) ,同时计算强度为 N (这里的计算强度可以被理解为同一个数据被重复利用的次数)。GEMM就是指计算 C=A∗B+C ,其中 A 、B和C都是矩阵,A是 M×K 的矩阵,B是 K×N 的矩阵, C是 M×N 的矩阵。这就是向量内积和向量外积的最大区别。

weixin_55035144的博客 1427

LangChain4j系列:LangChain4j ChatMemory聊天记忆详解与实战

LangChain4j系列:LangChain4j ChatMemory聊天记忆详解与实战

优快云_224022的博客 4277

永不消逝的电波 -GSM空口信号的接收与解码

一开始接触软件无线电应该是从很早之前对RFID洗衣卡的解密开始的,后来接触人员定位,慢慢的打开了这样一扇大门。。。。 翻到之前做的GSM信号的接收解码,今天就对其过程做一个记录,也不是什么新颖的内容,权当是记录和回忆了。 对于GSM网络的相关内容,不是非常的熟悉,如有不正确的,欢迎批评指正。

Jack.Xu 专栏 1万+

基于STM32的智能垃圾分类机器人

垃圾分类是城市管理的重要环节,传统人工分拣效率低、成本高。本文设计了一款基于STM32的智能垃圾分类机器人,通过视觉识别与机械臂协同作业,实现垃圾自动分类与投放,适用于社区、公共场所等场景,可显著提升垃圾分类效率。:STM32H743(480MHz Cortex-M7,支持AI加速)本系统实现了垃圾分拣的自动化,显著提升了分类效率。OV5640摄像头(500万像素,支持JPEG输出)支持4类垃圾识别(可回收/厨余/有害/其他)问题讨论,stm32的资料领取可以私信!超声波避障(探测距离0.1-4m)

stm32d1219的博客 1820

LangChain4j实战之一:准备工作

《LangChain4j实战系列》是欣宸在AI应用领域的原创教程,面向Java开发者介绍如何将AI能力集成到微服务中,从本篇开始,欣宸将陪伴您一同学习AI应用开发

程序员欣宸的博客 1436

MLC-LLM框架在安卓设备上的模型编译与部署全流程解析

本文详细解析了如何利用MLC-LLM框架在安卓设备上编译与部署大语言模型。从环境搭建、模型获取与量化编译,到最终的安卓应用打包与调试,提供了完整的实战流程。重点介绍了如何通过MLC-LLM将大型模型优化为适合移动端运行的格式,实现本地、低延迟、隐私安全的AI应用部署。

weixin_29251337的博客 475

LangChain4j从入门到实战

LangChain4j从入门到实战项目全套视频课程,涵盖LangChain4j+ollama+RAG,Java传统项目AI智能化升级

写代码的渣渣苏 4230

Vue3 + uniapp 踩坑:iOS 真机调试一直报错Maximum call stack size exceeded,和(in promise)MiniProgramError err的解决方法

不是你写错代码,是微信开发工具和 iOS 真机调试的兼容问题。开个空项目都报错,勾个设置就没问题,不是你,是工具的锅。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 1267

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

SpringBoot 整合Langchain4j 对接主流大模型实战详解

当下随着各种AI大模型的应用市场深入到各个领域,AI赋能为业务的拓展和商业价值的延伸带来了无限的可能。大厂商陆续推出对市面尚主流AI大模型的接入和支持,方便普通用户或开发者快速体验最新的大模型能力,以DeepSeek为例,像阿里云,硅基流动,智谱等众多厂商,都已经提供了与其对接的方式。与此同时,为了应用开发者能够基于自身的业务快速对接各类AI大模型API能力,更灵活的拓展自身的业务能力。

计算机毕业论文源码,学生个人网页制作html源码。贴近用户做网络推广和互联网优化。 1497

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

SpringBoot整合Langchain4j调用主流大模型实战详解

摘要: 本文介绍如何基于SpringBoot整合Langchain4j框架,实现与通义千问、DeepSeek等主流大模型的高效对接。Langchain4j凭借多模型适配、Spring生态融合等优势,成为Java开发者集成AI能力的首选工具。文章详细讲解环境配置(依赖引入、YAML参数设计)、通义千问/DeepSeek的对接实现(配置类、服务层封装、REST接口),以及通过动态配置切换模型的技术方案,帮助开发者快速构建灵活可扩展的AI应用。代码示例涵盖模型初始化、提示词生成和响应处理等核心流程。

没事学AI的博客 1284

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

LangChain4j 实战开发终极指南:从入门到精通

LangChain4j 是一个功能强大的 Java AI 开发库,它提供了丰富的工具和接口,帮助开发者轻松构建各种 AI 应用。本指南将带您从入门到精通,掌握 LangChain4j 的核心功能和实战技巧,让您能够快速开发出高效、智能的 AI 应用程序。 ## 为什么选择 LangChain4j? LangChain4j 作为一款优秀的 AI 开发框架,具有以下显著优势: - **丰富的集成

gitblog_01042的博客 638

AI - Java AI - LangChain4J 实战

是一个专为Java开发者设计的开源库,旨在简化将大型语言模型(LLM)集成到Java应用程序中的过程统一API:LangChain4j提供了一个标准化的API,使得开发者可以方便地接入15+个主流的LLM提供商(如OpenAI、Google 、阿里、 智谱)和15+个向量嵌入存储(Qdrant、Pinecone、Milvus)。这意味着开发者无需学习每个API的细节,可以轻松切换不同的模型和存储,而无需重写代码。综合工具箱。

Starrier 1364
上一篇: Spring AI
下一篇: 谁还加班做PPT?WPS灵犀AI全自动教程,老板以为你熬夜了!
智汇风渔AI小白
博客等级 码龄7年 1万+粉丝 179原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智汇风渔AI小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值