☰
Java工程师AI应用开发指南:LangChain4j从入门到生产实践
2026/10/26 6:14:04 网站建设 项目流程

1. 从Java工程师到AI应用开发:为什么LangChain4j值得你花时间

如果你是一名写了几年Java的开发者,最近大概率会有一种隐隐的焦虑:身边做Python的同事开始聊RAG、聊Agent、聊大模型应用开发,而自己还在写CRUD、调MyBatis的XML、处理各种业务接口。不是说这些工作没价值,而是当AI应用开发成为新的技术增量时,Java工程师如果一直站在门外看,迟早会发现自己能碰到的项目类型越来越窄。

LangChain4j就是在这个背景下值得认真对待的一个东西。它是LangChain的Java版本,把大模型调用、提示词模板、对话记忆、Embedding向量化、向量库检索、Agent工具调用这些能力,用Java开发者熟悉的方式封装了起来。你不需要切到Python生态,不需要重新学一套工程化体系,用Maven引入依赖、用Spring Boot组织服务、用你熟悉的接口和实现类思维,就能把一个大模型应用跑起来。

我写这篇东西的出发点很简单:网上关于LangChain4j的中文资料,要么是官方文档的翻译,要么是跑一个Hello World就结束,真正从Java工程师视角讲清楚“这东西怎么用、为什么这么设计、实际项目里会遇到什么”的内容很少。所以下面我会按照一个真实项目的推进节奏来展开——从环境搭建、模型接入、提示词工程,到Embedding与RAG、Agent工具调用,再到实际踩过的坑和性能调优。每一部分都会解释设计意图,而不是只给代码。

这篇文章适合两类人:一类是有Java基础、想快速进入AI应用开发领域的工程师;另一类是在项目里已经接到“接入大模型”需求、但不知道从哪下手的开发者。如果你连Maven和Spring Boot都没用过,建议先把Java Web的基础打一打再来看,不然中间一些工程化的部分会有点吃力。

2. 环境准备与依赖选型:别一上来就被版本问题卡住

2.1 JDK版本与构建工具的实际选择

LangChain4j对JDK的最低要求是JDK 8,但我在实际项目里强烈建议用JDK 17或更高。原因有两个:一是LangChain4j的很多新特性(比如Record类型的消息封装、Stream流式处理的优化)在JDK 17上体验更好;二是现在主流的Spring Boot 3.x本身就要求JDK 17起步,你如果还在用JDK 8,后面集成Spring Boot的时候会非常别扭。

构建工具用Maven还是Gradle都行,但考虑到国内Java团队Maven的普及率更高,我下面的示例统一用Maven。如果你用Gradle,依赖坐标是一样的,只是写法不同。

Maven的核心依赖其实就几个,我列一个最小可用的配置:

<dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j</artifactId> <version>0.35.0</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai</artifactId> <version>0.35.0</version> </dependency>

这里有个细节要注意:langchain4j是核心包,包含了提示词模板、对话记忆、链式调用这些基础能力;langchain4j-open-ai是模型接入包,用来对接兼容OpenAI接口的大模型服务。如果你用的是其他模型(比如通义千问、智谱、DeepSeek),它们大多提供了兼容OpenAI协议的接口,所以这个包基本是通用的。

版本号方面,LangChain4j迭代很快,0.35.0是我写这篇文章时比较稳定的一个版本。不建议用太老的版本,因为早期版本在流式输出和工具调用上有一些已知问题。也不建议盲目追最新版,新版本偶尔会有API变动,如果你是在生产项目里用,锁定一个稳定版本更稳妥。

2.2 API Key的配置方式与安全实践

很多教程会把API Key直接硬编码在代码里,这在本地跑Demo没问题,但一旦进入团队协作或者生产环境,就是安全隐患。我推荐的做法是用环境变量或者配置文件加占位符的方式。

本地开发时,可以在IDE的运行配置里设置环境变量,比如OPENAI_API_KEY。代码里这样读:

String apiKey = System.getenv("OPENAI_API_KEY");

如果是Spring Boot项目,更规范的做法是放在application.yml里,通过@Value或者@ConfigurationProperties注入:

langchain4j: openai: api-key: ${OPENAI_API_KEY} base-url: https://api.example.com/v1 model-name: gpt-4o-mini

这里base-url很关键。如果你用的是国内的大模型服务,需要把它改成对应服务商的接口地址。很多新手在这一步卡住,是因为直接用了默认的OpenAI地址,结果网络不通或者认证失败。记住:LangChain4j本身不绑定任何一家模型服务,它只是按照OpenAI的接口协议去发请求,具体请求发到哪里,由base-url决定。

提示:API Key不要提交到Git仓库。即使是私有仓库,也建议用.gitignore排除本地配置文件,团队共享的配置用环境变量或者配置中心管理。

2.3 第一个可运行的最小示例

环境准备好之后,先跑一个最小示例验证链路是否通。不要一上来就搞RAG和Agent,先把“能调用模型并拿到回复”这件事跑通。

import dev.langchain4j.model.openai.OpenAiChatModel; import dev.langchain4j.model.chat.ChatLanguageModel; public class FirstDemo { public static void main(String[] args) { ChatLanguageModel model = OpenAiChatModel.builder() .apiKey(System.getenv("OPENAI_API_KEY")) .baseUrl("https://api.example.com/v1") .modelName("gpt-4o-mini") .build(); String answer = model.generate("用一句话解释什么是Java的面向对象编程"); System.out.println(answer); } }

这段代码跑通之后,你会看到模型返回的一段文字。如果报错,大概率是三个原因:API Key不对、base-url写错、模型名称服务商不支持。排查的时候先把异常信息完整打印出来,LangChain4j的异常信息通常比较清晰,会告诉你到底是认证失败还是连接超时。

我个人的经验是,第一次跑通这个Demo之后,不要急着往下走,先花十分钟把OpenAiChatModel.builder()里的参数看一遍。除了apiKey、baseUrl、modelName,还有temperature、maxTokens、timeout这些参数,它们直接影响模型输出的风格和成本。比如temperature设成0.0会让输出更确定,适合做信息抽取;设成0.8会让输出更发散,适合做创意生成。这些参数的含义和OpenAI官方一致,LangChain4j只是做了Java侧的封装。

3. 提示词模板与对话记忆:让模型输出可控、可复用

3.1 为什么需要提示词模板而不是字符串拼接

刚开始用的时候,很多人会直接写model.generate("帮我翻译这段话:" + text)。这样写能跑,但有两个问题:一是提示词散落在代码各处,改起来麻烦;二是当提示词里有多个变量时,字符串拼接容易出错,尤其是变量里包含特殊字符的时候。

LangChain4j提供了PromptTemplate来解决这个问题。它的思路和MyBatis的#{}占位符很像,用{{变量名}}来标记需要替换的部分:

import dev.langchain4j.model.input.PromptTemplate; import java.util.Map; PromptTemplate template = PromptTemplate.from( "你是一名资深Java面试官,请针对以下知识点出一道面试题:{{topic}}。" + "题目难度为{{level}},并给出参考答案。" ); Prompt prompt = template.apply(Map.of( "topic", "Java并发编程中的volatile关键字", "level", "中级" )); String result = model.generate(prompt.text());

这样写的好处是提示词和业务逻辑分离了。你可以把常用的提示词模板抽到一个专门的类或者配置文件里,业务代码只负责传参。团队协作的时候,产品经理或者运营同学想调整提示词,也不需要改Java代码,改配置就行。

3.2 对话记忆的几种实现与选型

单轮对话很简单,但真实场景里用户往往需要多轮交互。比如用户先问“Java有哪些集合类”,接着问“ArrayList和LinkedList有什么区别”,再问“那它们在多线程环境下安全吗”。如果每次请求都是独立的,模型就不知道“它们”指的是什么。

LangChain4j的ChatMemory就是解决这个问题的。它会把历史对话保存下来,每次请求时把历史消息一起发给模型。常用的实现有两种:

  • MessageWindowChatMemory:按消息条数保留最近N条,超出就丢弃最早的。适合大多数对话场景,实现简单,内存占用可控。
  • TokenWindowChatMemory:按Token数量保留,更精确地控制上下文长度,但需要模型支持Token计数。

我一般用MessageWindowChatMemory,设置窗口大小为10到20条。太小了模型记不住上下文,太大了会增加Token消耗,而且模型对太早的历史信息本来就不敏感。

import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.service.AiServices; interface Assistant { String chat(String userMessage); } Assistant assistant = AiServices.builder(Assistant.class) .chatLanguageModel(model) .chatMemory(MessageWindowChatMemory.withMaxMessages(10)) .build(); assistant.chat("Java有哪些集合类?"); assistant.chat("ArrayList和LinkedList有什么区别?"); assistant.chat("那它们在多线程环境下安全吗?");

这里用到了AiServices,它是LangChain4j里非常核心的一个类。你定义一个接口,它帮你生成实现,把模型调用、记忆管理、工具调用这些逻辑都串起来。这种“声明式”的用法对Java开发者来说很亲切,有点像Spring Data JPA的Repository接口。

3.3 系统提示词与角色设定的实战技巧

系统提示词(System Message)决定了模型的“人设”和回答风格。很多人忽略这一点,结果模型回答得过于笼统或者跑题。一个好的系统提示词应该包含三部分:角色定义、任务边界、输出格式要求。

举个例子,如果你要做一个Java面试题生成器,系统提示词可以这样写:

你是一名有十年经验的Java技术面试官。 你的任务是针对用户给出的知识点生成面试题。 要求: 1. 题目要贴近真实面试场景,不要过于理论化 2. 每道题附带参考答案,答案要简洁准确 3. 如果知识点范围太大,先追问用户想聚焦哪个方向 4. 输出格式为:题目、参考答案、考察点

在LangChain4j里,系统提示词可以通过@SystemMessage注解来设置:

interface InterviewAssistant { @SystemMessage("你是一名有十年经验的Java技术面试官...") String generateQuestion(String topic); }

实测下来,系统提示词写得越具体,模型输出越稳定。但也不要写得太长,超过500字之后边际收益就下降了,而且会占用上下文窗口。我的经验是控制在200到400字之间,把最关键的约束说清楚就行。

4. Embedding与RAG:让模型能回答私有知识

4.1 Embedding到底是什么,用生活化的方式理解

Embedding这个词听起来很技术,但它的本质很简单:把一段文字转换成一串数字(向量),这串数字代表了这段文字的“语义坐标”。语义相近的文字,它们的向量在空间里的距离也近。

打个比方,假设我们把所有文字都放在一个巨大的地图上,“Java”和“编程语言”这两个词的位置会比较近,“Java”和“咖啡”的位置会比较远(虽然Java确实也是一种咖啡)。Embedding模型做的就是给每段文字算出这个地图上的坐标。

为什么需要Embedding?因为大模型本身只知道它训练时见过的知识。你公司内部的文档、产品手册、客服话术,这些私有知识模型是不知道的。你当然可以把整篇文档塞进提示词里让模型读,但文档一长,Token消耗巨大,而且模型对超长上下文的理解能力也会下降。Embedding加向量检索的思路是:先把文档切成小块,每块算出向量存起来;用户提问时,把问题也算出向量,然后找出最相似的几块文档,只把这些相关片段发给模型。这样既省Token,又提高了回答的准确性。

4.2 文档切分策略:切得好不好直接决定RAG效果

文档切分(Chunking)是RAG里最容易被低估的环节。很多人随便按固定长度切,结果切出来的片段语义不完整,检索效果很差。

LangChain4j提供了DocumentSplitter,常用的策略有按段落切、按句子切、按固定字符数切。我的经验是:

  • 对于结构清晰的文档(比如Markdown、技术手册),优先按标题层级切,每个小节作为一个片段。
  • 对于连续文本(比如小说、论文),按段落切,每段控制在300到500字。
  • 如果段落本身就很长,再按句子切,但不要切得太碎,否则丢失上下文。
import dev.langchain4j.data.document.splitter.DocumentSplitters; import dev.langchain4j.data.document.Document; import dev.langchain4j.data.segment.TextSegment; import java.util.List; Document document = Document.from(yourText); List<TextSegment> segments = DocumentSplitters.recursive(500, 50).split(document);

这里的recursive(500, 50)表示每段最大500个字符,相邻段之间重叠50个字符。重叠是为了避免一个完整的句子被切断后,前后两段都丢失关键信息。这个参数不是固定的,要根据你的文档特点调。技术文档可以设小一点,叙事类文档可以设大一点。

注意:切分粒度太细会导致检索到的片段缺乏上下文,模型回答时容易断章取义;切分粒度太粗会导致检索精度下降,因为一个片段里混了太多不相关的信息。实际项目里我一般会准备两三组参数,用真实问题测一下检索效果再定。

4.3 向量库选型:从内存到生产环境

LangChain4j支持多种向量库,选哪个取决于你的数据量和部署环境。我列一个对比:

向量库适用场景优点缺点
InMemoryEmbeddingStore本地开发、Demo零配置,启动即用重启数据丢失,不适合生产
Chroma中小规模、快速验证部署简单,社区活跃大规模性能一般
Milvus大规模生产环境性能强,支持分布式运维复杂度高
PostgreSQL + pgvector已有PG的团队复用现有数据库,事务支持好向量检索性能不如专用库
Redis已有Redis的团队部署简单,速度快向量功能相对基础

新手入门我建议先用InMemoryEmbeddingStore把流程跑通,理解RAG的完整链路。等要上生产了,再根据团队的技术栈选一个。如果团队已经在用PostgreSQL,pgvector是个很务实的选择,不用额外维护一套向量数据库。

import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; import dev.langchain4j.store.embedding.EmbeddingStore; import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>(); EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() .documentSplitter(DocumentSplitters.recursive(500, 50)) .embeddingModel(embeddingModel) .embeddingStore(embeddingStore) .build(); ingestor.ingest(document);

EmbeddingStoreIngestor把切分、向量化、存储三步串起来了,一行代码完成入库。这种封装对新手很友好,但你要知道它背后做了什么,不然出了问题不好排查。

4.4 检索增强生成的完整链路与常见瓶颈

RAG的完整链路是:用户提问 → 问题向量化 → 向量库检索相似片段 → 把片段和问题一起组装成提示词 → 发给模型 → 返回答案。

这个链路里最常见的瓶颈有三个:

第一个是检索不准。用户问“怎么配置数据库连接池”,检索出来的却是“数据库索引优化”的片段。原因可能是Embedding模型对中文技术术语的理解不够好,或者切分粒度不合适。解决办法是换一个中文效果更好的Embedding模型,或者调整切分策略。

第二个是检索到了但模型没用上。有时候检索结果明明包含答案,但模型回答时却忽略了。这通常是提示词的问题,需要在提示词里明确要求“请基于以下参考资料回答,如果参考资料中没有相关信息,请如实说明”。

第三个是Token超限。检索回来的片段太多,加上历史对话,超过了模型的上下文窗口。解决办法是限制检索返回的片段数量(一般3到5个),或者用更激进的摘要策略。

import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever; import dev.langchain4j.service.AiServices; interface KnowledgeAssistant { String answer(String question); } KnowledgeAssistant assistant = AiServices.builder(KnowledgeAssistant.class) .chatLanguageModel(model) .contentRetriever(EmbeddingStoreContentRetriever.builder() .embeddingStore(embeddingStore) .embeddingModel(embeddingModel) .maxResults(5) .minScore(0.7) .build()) .build();

maxResults(5)表示最多返回5个片段,minScore(0.7)表示相似度低于0.7的片段直接丢弃。这两个参数是控制检索质量的关键,建议根据实际数据调优。

5. Agent与工具调用:让模型从“会说”到“会做”

5.1 Agent的本质:模型决定调用哪个工具

普通的大模型调用是“你问我答”,模型只能输出文字。Agent不一样,它可以让模型决定调用外部工具,比如查数据库、调API、执行计算。模型输出的不再是最终答案,而是一个“我要调用某个工具”的指令,LangChain4j解析这个指令,执行对应的Java方法,把结果再喂给模型,模型再决定下一步。

这个机制的核心价值在于:模型的知识是静态的,但工具可以访问实时数据。比如你问“今天北京的天气怎么样”,模型本身不知道,但如果有一个查天气的工具,Agent就能调用它拿到实时数据。

在LangChain4j里,定义一个工具非常简单,用@Tool注解标记一个方法就行:

import dev.langchain4j.agent.tool.Tool; public class WeatherTool { @Tool("查询指定城市的当前天气") public String getWeather(String city) { // 实际项目中这里调用天气API return city + "今天晴,气温25度"; } }

然后把工具注册到AiServices里:

interface WeatherAssistant { String chat(String message); } WeatherAssistant assistant = AiServices.builder(WeatherAssistant.class) .chatLanguageModel(model) .tools(new WeatherTool()) .build(); assistant.chat("北京今天天气怎么样?");

模型会识别出需要调用getWeather工具,传入“北京”,拿到结果后再组织语言回复用户。

5.2 工具设计的粒度与边界

工具设计有个常见的误区:把工具做得太粗,一个工具干很多事。比如一个executeSql(String sql)工具,让模型直接生成SQL执行。这样做很危险,模型可能生成删库的SQL,也可能生成性能极差的查询。

正确的做法是把工具做细,每个工具只做一件明确的事,并且在工具内部做好参数校验和权限控制。比如:

  • queryOrderStatus(String orderId):只查订单状态,不接受任意SQL
  • calculateShippingFee(String province, double weight):只算运费,参数有明确类型
  • searchProduct(String keyword, int page):只搜商品,分页参数有上限

这样设计的好处是模型不容易误用,而且每个工具的输入输出都是可控的。我在实际项目里还会给工具加日志,记录每次调用的参数和结果,方便排查问题。

5.3 多工具协作与调用链的调试

当Agent注册了多个工具时,模型需要决定调用哪个、按什么顺序调用。比如用户问“帮我查一下订单12345的状态,如果已发货就查一下物流”,模型需要先调queryOrderStatus,根据结果再决定是否调queryLogistics。

这个过程中最容易出问题的是:模型调用了错误的工具,或者传了错误的参数。调试的时候,我一般会打开LangChain4j的日志,把每次工具调用的请求和响应都打出来。LangChain4j用的是SLF4J,配置一下日志级别就行:

logging: level: dev.langchain4j: DEBUG

日志里会显示模型输出的工具调用指令、实际执行的Java方法、返回的结果。如果模型调错了工具,通常是工具的描述不够清晰。@Tool注解里的描述要写得具体,告诉模型这个工具是干什么的、什么时候用。描述写得越清楚,模型选对的概率越高。

提示:工具方法的参数类型尽量用String、int、double这些基础类型,避免用复杂的自定义对象。模型对复杂参数的理解能力有限,容易传错。

6. 实战踩坑记录:那些文档里不会写的问题

6.1 流式输出的线程安全问题

流式输出(Streaming)是提升用户体验的重要手段,模型生成一个字就返回一个字,不用等全部生成完。LangChain4j支持流式调用,但在Web环境里用的时候要注意线程安全。

我遇到过一个典型问题:在Spring Boot的Controller里用流式输出,多个用户同时请求时,回复内容会串。原因是流式回调是在模型客户端的线程里执行的,如果直接往HttpServletResponse里写,没有做好隔离,就会出现并发问题。

解决办法是用StreamingResponseHandler,把每个请求的流式响应绑定到独立的上下文:

model.generate("你的问题", new StreamingResponseHandler<>() { @Override public void onNext(String token) { // 把token推送到当前请求的SSE连接 emitter.send(token); } @Override public void onComplete(Response<String> response) { emitter.complete(); } @Override public void onError(Throwable error) { emitter.completeWithError(error); } });

配合Spring的SseEmitter使用,每个请求有自己的emitter实例,就不会串了。

6.2 Token消耗的监控与成本控制

大模型调用是按Token计费的,如果不加监控,月底账单可能会吓你一跳。LangChain4j的响应对象里包含了Token使用信息:

Response<String> response = model.generate("你的问题"); int inputTokens = response.tokenUsage().inputTokenCount(); int outputTokens = response.tokenUsage().outputTokenCount();

我一般会在项目里做一个简单的Token统计,按用户、按接口维度记录消耗。如果发现某个接口的Token消耗异常高,通常是提示词写得太长,或者RAG检索返回的片段太多。

控制成本的手段有几个:一是用更便宜的模型处理简单任务,复杂任务再用贵模型;二是压缩提示词,去掉不必要的说明;三是限制RAG返回的片段数量和长度;四是设置maxTokens上限,防止模型生成过长的回复。

6.3 模型返回格式不稳定的处理

让模型输出JSON格式是常见需求,但模型有时候会在JSON外面加一段解释文字,或者JSON格式不合法。LangChain4j提供了AiServices的返回类型支持,可以直接把模型输出映射成Java对象:

interface JsonAssistant { Person extractPerson(String text); } class Person { String name; int age; String occupation; }

LangChain4j会自动在提示词里加入格式说明,并尝试把模型输出解析成Person对象。但实测下来,模型偶尔还是会返回不合法的JSON。我的做法是在解析失败时做一次重试,重试时在提示词里强调“只输出JSON,不要任何其他文字”。如果重试还失败,就降级到人工处理或者返回默认值。

6.4 中文场景下的Embedding模型选择

做中文RAG的时候,Embedding模型的选择非常关键。OpenAI的text-embedding-ada-002对中文的支持一般,检索中文文档时经常出现语义相近但检索不到的情况。国内有几个中文效果不错的Embedding模型,比如智谱的embedding-2、百度的Embedding-V1,在中文语义相似度任务上表现更好。

LangChain4j支持自定义Embedding模型,只要实现EmbeddingModel接口就行。如果你用的模型服务商提供了兼容OpenAI的Embedding接口,也可以直接用OpenAiEmbeddingModel,改一下base-url和modelName即可。

选Embedding模型的时候,不要只看排行榜,要用你自己的数据测。准备20到30个真实问题,人工标注每个问题应该检索到哪些文档片段,然后对比不同模型的召回率。这个方法虽然土,但最靠谱。

7. 从Demo到生产:工程化落地的几个关键决策

7.1 模型调用的超时与重试策略

生产环境里,模型调用超时是常态。网络抖动、服务商限流、模型负载高,都可能导致请求失败。如果不做超时和重试,用户体验会很差。

LangChain4j的模型构建器支持设置超时:

OpenAiChatModel model = OpenAiChatModel.builder() .apiKey(apiKey) .baseUrl(baseUrl) .modelName(modelName) .timeout(Duration.ofSeconds(30)) .maxRetries(2) .build();

timeout设成30秒是个比较平衡的值,太短了正常请求也可能超时,太长了用户等不起。maxRetries(2)表示失败后重试2次,加上首次请求一共3次。重试的时候要注意,如果是因为限流失败,重试间隔最好递增,避免加重服务端压力。

7.2 提示词版本管理与A/B测试

提示词是AI应用的核心资产之一,但很多团队把它散落在代码里,改了就改了,没有版本管理。我建议把提示词抽到独立的配置文件或者数据库里,每次修改都记录版本号和修改原因。

更进一步,可以做A/B测试:同一批用户随机分配两组提示词,对比回答质量和用户满意度。LangChain4j本身不提供A/B测试功能,但你可以自己在业务层实现——根据用户ID哈希取模,决定用哪个版本的提示词。

7.3 日志、监控与问题追溯

AI应用的日志比普通应用更重要,因为模型的行为有不确定性。我一般会记录这几类信息:

  • 每次请求的完整提示词(包括系统提示词、历史对话、RAG检索结果)
  • 模型的原始输出
  • Token消耗
  • 耗时
  • 如果涉及工具调用,记录调用的工具名和参数

这些日志在排查问题时非常有用。比如用户反馈“回答不准确”,你可以回溯当时的提示词和检索结果,判断是检索没找到相关文档,还是模型理解错了。

注意:记录日志的时候要注意脱敏,提示词里可能包含用户隐私信息,不要明文存储。

7.4 并发场景下的资源隔离

如果你的应用同时服务多个用户,要考虑资源隔离。比如给VIP用户分配更快的模型,给免费用户用便宜的模型;或者限制每个用户的并发请求数,防止某个用户把配额占满。

LangChain4j的模型实例本身是线程安全的,可以在多个线程间共享。但如果你用了ChatMemory,每个用户需要有独立的记忆实例,不能共享。我一般用ConcurrentHashMap按用户ID管理记忆实例,并设置过期时间,避免内存泄漏。

8. 一些个人体会与后续可扩展的方向

LangChain4j给我的最大感受是:它没有试图把Java工程师变成Python工程师,而是用Java开发者熟悉的方式来解决AI应用开发的问题。接口、注解、构建器、依赖注入,这些概念你本来就会,学习成本主要花在理解大模型的特性和RAG、Agent这些新范式上,而不是花在学一门新语言上。

如果你已经跟着上面的内容把Demo跑通了,下一步可以尝试这几个方向:一是把RAG接到真实的业务文档上,比如公司内部的技术Wiki或者产品手册,看看检索效果如何;二是给Agent加更多工具,比如查数据库、调内部API,让它能处理更复杂的任务;三是研究一下模型微调,如果你的场景对回答风格有特殊要求,微调可能比提示词工程更有效。

我在实际项目里踩过的坑远不止上面写的这些,但那些坑大多和具体的业务场景绑定,写出来参考价值有限。真正通用的经验是:不要迷信模型的能力,该做的校验、该设的边界、该留的降级方案,一个都不能少。模型是概率性的,工程是确定性的,把两者结合好,才能做出靠谱的AI应用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询