AI框架之LangChain4j讲解
是LangChainPython) 的 Java 移植版,遵循其原设计理念,灵活、模块化、非侵入式,是通用性很强的工具包。虽然是的 Java 实现,但并非简单移植,而是融合了 LangChain、Haystack、LlamaIndex 等框架的理念,专为 Java 开发者优化。以LLM为中心,注重模块化和灵活性,提供了统一的 API 来适配多种 LLM 和向量存储,适合需要高度定制化的场景。更灵活
文章目录
1 LangChain4j
1.1 概述
1.1.1 简介
LangChain4J
:是 LangChain
(Python
) 的 Java 移植版,遵循其原设计理念,灵活、模块化、非侵入式,是通用性很强的工具包。虽然 LangChain4j
是 Python LangChain
的 Java 实现,但并非简单移植,而是融合了 LangChain、Haystack、LlamaIndex 等框架的理念,专为 Java 开发者优化。
以 LLM
为中心,注重模块化和灵活性,提供了统一的 API 来适配多种 LLM 和向量存储,适合需要高度定制化的场景。更灵活,模块化设计让开发者可以自由组合功能,适合构建复杂、定制化的 AI 工作流。
1.1.2 核心理念与背景
LangChain4j
旨在让 Java 开发者能够轻松集成 LLM、向量存储、外部工具和记忆管理,构建复杂的 AI 驱动应用(如聊天机器人、RAG 系统、代理工作流等)。
设计理念:
- 模块化:提供松耦合的组件(如模型集成、向量存储、工具调用),开发者可按需组合。
- 统一 API:为不同的 LLM 和向量存储提供一致的接口,降低适配成本。
- Java 友好:使用 Java 的习惯(如 Builder 模式),并支持 GraalVM 以优化云原生部署。
社区与生态:由活跃的开源社区维护(GitHub 仓库:langchain4j/langchain4j),支持广泛的 LLM 提供商和向量存储,更新频繁。
1.1.3 与spring ai 对比
特性维度 | LangChain4J | Spring AI |
---|---|---|
背景与生态 | 源自流行的 Python LangChain 项目,社区驱动。 | Spring 官方项目,是 Spring 生态的一部分,有VMware/Pivotal背书。 |
设计哲学 | 工具包 (Library):提供构建AI应用所需的各种工具和组件,高度模块化,灵活性强。 | 框架 (Framework):提供一站式的解决方案,遵循约定优于配置,与Spring生态无缝集成。 |
编程模型 | 传统的 API 调用风格。需要自己管理组件(如模型、记忆、工具)的创建和组装。 | 深度集成 Spring Boot 的编程模型(如自动配置、@EnableAi、@Bean)。大量使用 AOT(提前编译) 原生支持。 |
依赖注入 | 不强制,可以选择任何DI容器(如Spring, Guice)来管理它的组件,或者不用。 | 强制且深度依赖 Spring 的 IOC 容器。所有核心组件(如ChatClient, EmbeddingClient) 都是 Spring Bean。 |
模块化 | 极其模块化。每个功能(如与不同模型的连接器、工具、记忆存储)都是独立的模块,按需引入,避免膨胀。 | 相对集中。虽然功能也在拆分,但更倾向于提供一个“全家桶”式的 starter(如spring-ai-openai-spring-boot-starter)。 |
特色功能 | Agent(智能体) 功能非常强大和灵活,支持多种Agent类型和工具调用模式。 | Spring 生态集成是其最大特色(如AI+Spring Data Vector DB,AI+Spring Security)。输出结构化(强制JSON格式)做得非常优雅。 |
总的来说:
Spring AI
优势在于:和Spring
生态深度集成、配置优雅、企业级稳定,非常适合现有 Spring 项目快速接入 -LangChain4j
则更偏向模块化、面向复杂流程、丰富的 AI 工具链和 RAG 支持,但使用复杂、文档和学习成本更高
1.2 架构&核心功能
1.2.1 架构
LangChain4j
的架构基于模块化设计,主要组件包括:
ChatLanguageModel
:用于对话和生成文本,于和对话模型(LLM,比如 GPT-4, Claude)交互,生成回复。EmbeddingModel
:生成文本的向量表示,方便做 RAG。
支持 OpenAI Embedding、Ollama Embedding、本地模型等。EmbeddingStore
:存储和检索向量(与向量数据库集成),也可以接入向量数据库(Pinecone、Weaviate、Milvus、Redis、Elasticsearch、Postgres…)。DocumentLoader/Splitter
:加载和切分文档。PromptTemplate
:管理动态提示。ChatMemory
:维护会话上下文,可以是 InMemory,也可以是持久化存储。Tools
:外部工具集成,比如调用 API、数据库、搜索引擎、计算器等。Agents
:协调多步骤任务,比如决定什么时候调用工具、什么时候直接回复用户。
支持 ReAct(推理 + 行动)模式、工具调用、任务规划等Chain
:在LangChain4j
里也有Chain
(比如ConversationalRetrievalChain
),用来把模型、Prompt、Memory、Retriever 组合成完整流程。Retriever
:从向量库中检索相关文档(通常和 RAG 配合),它在架构里通常和 EmbeddingStore 搭配使用。
1.2.2 核心功能
- 模型接入
支持多种 LLM 提供商:OpenAI、Azure OpenAI、Anthropic Claude、Ollama、本地模型(如 Llama2, Mistral)等。支持 Embedding 模型(向量化)和 Chat/Completion 模型。
提供统一的ChatLanguageModel
和EmbeddingModel
接口,适配不同模型的聊天和嵌入生成 - 提示模板 Prompt 管理
支持动态生成提示,允许开发者通过模板定义输入格式,注入变量以构建上下文。
示例:PromptTemplate
可以生成类似“根据 {{context}} 回答 {{question}}”的提示。
可以用 注解方式 定义 Prompt:
public interface Assistant {
@SystemMessage("你是一个专业助手")
@UserMessage("帮我写一个关于 {topic} 的故事")
String writeStory(String topic);
}
LangChain4j
会自动拼接成完整 prompt
并调用模型。
- RAG(检索增强生成)
内置 文档解析器(PDF、Word、TXT),支持通过向量存储进行语义搜索,将外部知识注入 LLM,增强回答的准确性和上下文相关性。
支持的向量存储:Pinecone、Milvus、Chroma、Weaviate、Qdrant 等。
流程:- 将文档切分为块(
chunking
) - 使用嵌入模型生成向量。
- 存储到向量数据库。
- 查询时检索相关文档并注入到 LLM 的上下文中。
- 将文档切分为块(
- Agent(智能体)
支持代理架构,LLM 可根据任务动态选择工具或执行多步骤推理。
LangGraph4j:引入类似 Python LangChain 的 LangGraph,支持循环工作流和状态管理。
提供 ReAct、工具调用(Function Calling)等模式。允许 LLM 调用外部工具(如 API、数据库、计算函数),实现动态任务处理。 - 记忆管理
提供会话记忆功能,支持上下文感知的对话。
类型:- MessageWindowChatMemory:限制记忆窗口大小,保留最近的 n 条消息。
- TokenWindowChatMemory:基于 token 数量限制记忆。
- 其他特性
支持文本、图像输入输出
例如可以让 LLM 看图回答问题。
流式输出(Streaming),类似 ChatGPT 的打字机效果,适合做实时对话应用
1.3 简单使用
pom依赖
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>0.23.0</version>
</dependency>
代码示例
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.DocumentLoader;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.pinecone.PineconeEmbeddingStore;
import dev.langchain4j.service.AiServices;
public class RAGExample {
public static void main(String[] args) {
// 配置 OpenAI 模型
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey("openai-api-key")
.build();
// 配置 Pinecone 向量存储
EmbeddingStore embeddingStore = PineconeEmbeddingStore.builder()
.apiKey("your-pinecone-api-key")
.environment("us-west1-gcp")
.index("your-index-name")
.build();
// 加载文档
Document document = DocumentLoader.loadDocument("path/to/document.txt");
// 创建 AI 服务
interface Assistant {
String answer(String query);
}
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(model)
.embeddingStore(embeddingStore)
.build();
// 查询
String response = assistant.answer("What is the main topic of the document?");
System.out.println(response);
}
}
1.4 LangChain 构建模块
LangChain
为我们的应用程序提供了多种价值主张,这些功能以模块化组件的形式提供。模块化组件不仅提供了有用的抽象,还包含了一系列操作语言模型的实现
1.4.1 模型输入/输出
在使用任何语言模型时,我们需要具备与其交互的能力。LangChain
提供了必要的构建模块,例如模板化提示的能力,以及动态选择和管理模型输入的能力。此外,我们还可以使用输出解析器从模型输出中提取信息:
提示模板(Prompt Templates
)是用于生成语言模型提示的预定义配方,可以包括指令、少样本示例[16]和特定上下文:
PromptTemplate promptTemplate = PromptTemplate
.from("Tell me a {{adjective}} joke about {{content}}..");
Map<String, Object> variables = new HashMap<>();
variables.put("adjective", "funny");
variables.put("content", "computers");
Prompt prompt = promptTemplate.apply(variables);
1.4.2 内存
通常,一个利用大型语言模型(LLM
)的应用程序会有一个对话界面。对话的一个重要方面是能够引用对话中先前的信息。这种存储过去交互信息的能力称为内存:
LangChain
提供了一些关键功能,可以为应用程序添加内存。例如,我们需要能够从内存中读取信息以增强用户输入,同时还需要将当前运行的输入和输出写入内存:
ChatMemory chatMemory = TokenWindowChatMemory.withMaxTokens(300, new OpenAiTokenizer(GPT_3_5_TURBO));
chatMemory.add(userMessage("你好,我叫 测试"));
AiMessage answer = model.generate(chatMemory.messages()).content();
System.out.println(answer.text()); // 你好 测试!今天我能为您做些什么?
chatMemory.add(answer);
chatMemory.add(userMessage("我叫什么名字?"));
AiMessage answerWithName = model.generate(chatMemory.messages()).content();
System.out.println(answerWithName.text()); // 您的名字是 测试。
chatMemory.add(answerWithName);
在这里,我们使用 TokenWindowChatMemory
实现了固定窗口聊天内存,它允许我们读取和写入与语言模型交换的聊天消息。
LangChain 还提供更复杂的数据结构和算法,以便从内存中返回选定的消息, 而不是返回所有内容。例如,它支持返回过去几条消息的摘要,或者仅返回与当前运行相关的消息。
1.4.3 检索
大型语言模型通常是在大量的文本语料库上进行训练的。因此,它们在处理通用任务时表现得非常高效,但在处理特定领域任务时可能效果不佳。为了解决这一问题,我们需要在生成阶段检索相关的外部数据,并将其传递给语言模型。
这个过程被称为检索增强生成(Retrieval Augmented Generation,RAG
)。RAG 有助于将模型的生成过程与相关且准确的信息结合,同时也让我们更深入地了解模型的生成过程。LangChain 提供了构建 RAG 应用程序所需的核心组件:
首先,LangChain
提供了文档加载器 FileSystemDocumentLoader
,用于从存储位置检索文档。然后,LangChain
还提供了转换器,用于进一步处理文档,例如将大型文档分割成更小的块:
Document document = FileSystemDocumentLoader.loadDocument("simpson's_adventures.txt");
DocumentSplitter splitter = DocumentSplitters.recursive(100, 0, new OpenAiTokenizer(GPT_3_5_TURBO));
List<TextSegment> segments = splitter.split(document);
在这里,使用 FileSystemDocumentLoader
从文件系统中加载文档。然后使用 OpenAiTokenizer 将文档分割成更小的段落。
为了提高检索效率,这些文档通常会被转换成嵌入(embeddings),并存储在向量数据库中。LangChain 支持多种嵌入提供商和方法,并与几乎所有主流的向量存储集成:
EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel();
List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
embeddingStore.addAll(embeddings, segments);
在这里,我们使用 AllMiniLmL6V2EmbeddingModel
为文档段落创建嵌入,然后将嵌入存储在内存中的向量存储中。
现在,我们的外部数据已经以嵌入的形式存储在向量存储中,可以从中进行检索。LangChain 支持多种检索算法,例如简单的语义搜索和更复杂的集成检索器(ensemble retriever):
String question = "Who is Simpson?";
// 假设该问题的答案包含在我们之前处理的文档中。
Embedding questionEmbedding = embeddingModel.embed(question).content();
int maxResults = 3;
double minScore = 0.7;
List<EmbeddingMatch<TextSegment>> relevantEmbeddings = embeddingStore.findRelevant(questionEmbedding, maxResults, minScore);
我们为用户的问题生成嵌入,然后使用该问题的嵌入从向量存储中检索相关的匹配项。现在,我们可以将检索到的相关内容作为上下文,添加到我们打算发送给模型的提示中。
1.5 LangChain 复杂应用
LangChain
还提供了构建更复杂应用程序的组件。例如,我们可以使用链(Chains
)和代理(Agents
)来构建更加自适应、功能增强的应用程序。
1.5.1 链(Chains)
通常,一个应用程序需要按特定顺序调用多个组件。在 LangChain
中,这被称为链(Chain
)。链简化了开发更复杂应用程序的过程,并使调试、维护和改进更加容易。
链还可以组合多个链来构建更复杂的应用程序,这些应用程序可能需要与多个语言模型交互。LangChain
提供了创建此类链的便捷方式,并内置了许多预构建链:
ConversationalRetrievalChain chain = ConversationalRetrievalChain.builder()
.chatLanguageModel(chatModel)
.retriever(EmbeddingStoreRetriever.from(embeddingStore, embeddingModel))
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.promptTemplate(PromptTemplate
.from("Answer the following question to the best of your ability: {{question}}\n\nBase your answer on the following information:\n{{information}}"))
.build();
在这里,我们使用了预构建的链 ConversationalRetrievalChain
,它允许我们将聊天模型与检索器、内存和提示模板结合使用。现在,我们可以简单地使用该链来执行用户查询:
String answer = chain.execute("Who is Simpson?");
该链提供了默认的内存和提示模板,我们可以根据需要进行覆盖。创建自定义链也非常容易。链的能力使我们能够更轻松地实现复杂应用程序的模块化实现。
1.5.2 代理(Agents)
LangChain
还提供了更强大的结构,例如代理(Agent)。与链不同,代理将语言模型用作推理引擎,以确定应该采取哪些操作以及操作的顺序。我们还可以为代理提供访问合适工具的权限,以执行必要的操作。
在 LangChain4j
中,代理作为 AI 服务(AI Services)提供,用于声明性地定义复杂的 AI 行为。让我们看看如何通过提供一个计算器工具,为 AI 服务赋能,从而使语言模型能够执行计算。
首先,我们定义一个包含一些基本计算功能的类,并用自然语言描述每个函数,这样模型可以理解:
public class AIServiceWithCalculator {
static class Calculator {
@Tool("Calculates the length of a string")
int stringLength(String s) {
return s.length();
}
@Tool("Calculates the sum of two numbers")
int add(int a, int b) {
return a + b;
}
}
接下来,我们定义一个接口,用于构建我们的 AI 服务。这里的接口相对简单,但也可以描述更复杂的行为:
interface Assistant {
String chat(String userMessage);
}
然后,我们使用 LangChain4j
提供的构建器工厂,通过定义的接口和工具创建一个 AI 服务:
Assistant assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(OpenAiChatModel.withApiKey(<OPENAI_API_KEY>))
.tools(new Calculator())
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
现在,我们可以向语言模型发送包含计算任务的问题:
String question = "What is the sum of the numbers of letters in the words \"language\" and \"model\"?";
String answer = assistant.chat(question);
System.out.println(answer); // The sum of the numbers of letters in the words "language" and "model" is 13.
运行这段代码后,我们会发现语言模型现在能够执行计算。
需要注意的是,语言模型在执行某些任务时可能会遇到困难,例如需要时间和空间概念的任务或复杂的算术操作。然而,我们可以通过为模型提供必要的工具来解决这些问题。
更多推荐
所有评论(0)