news 2026/4/18 5:33:18

langchain4j 构建条件工作流

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
langchain4j 构建条件工作流

一.背景

1.业务场景驱动

        随着大语言模型(LLM)在企业级应用中的落地深化,基于 langchain4j 开发的智能应用(如招聘助手、智能客服、文档分析系统等)不再局限于 “单一输入→单一输出” 的线性交互模式,而是需要处理多分支、多条件、动态决策的复杂业务逻辑:

  • 以招聘场景为例:系统需根据候选人简历匹配度(如 “完全匹配 / 部分匹配 / 不匹配”),自动触发不同流程 —— 匹配则发送面试邀约、安排线下面试;部分匹配则发起二次评估;不匹配则发送标准化拒信。
  • 以智能客服为例:需根据用户问题类型(如 “咨询 / 投诉 / 售后”)、用户等级(VIP / 普通),分流至不同的对话链(如转接人工、自动解答、推送解决方案文档)。这些场景的核心诉求是:基于预设条件(如文本分析结果、元数据标签、外部系统状态)驱动工作流分支执行,实现 LLM 应用的自动化、标准化落地。

2.传统实现的技术痛点

        在未基于 langchain4j 构建条件工作流时,企业级 LLM 应用开发存在以下问题:

  1. 逻辑耦合严重:条件判断(如 “简历是否符合岗位要求”)与 LLM 调用、外部系统交互(如发送邮件、更新数据库)的代码混杂,难以维护和扩展;
  2. 灵活性不足:硬编码的条件分支无法快速适配业务规则变更(如招聘流程调整、客服分流规则修改),每次变更需重新开发、测试;
  3. 与 langchain4j 生态脱节:常规工作流框架(如 Flowable、Camunda)无法原生适配 langchain4j 的核心组件(如 Document、ChatMemory、Tool),需大量适配代码,增加开发成本;
  4. 可观测性差:条件分支的执行轨迹、LLM 调用结果、异常节点难以追溯,不利于问题排查和流程优化。

3.langchain4j 构建条件工作流的核心价值

        langchain4j 作为轻量级、专为 LLM 应用设计的框架,其组件化、可插拔的特性天然适配条件工作流的构建需求:

  1. 生态原生适配:可直接复用 langchain4j 的 Document 处理、Tool 调用、LLM 交互能力,无需跨框架适配;
  2. 轻量化部署:相较于重型工作流引擎,基于 langchain4j 构建的条件工作流无需额外中间件,契合微服务 / 轻量级应用的部署需求;
  3. 动态决策能力:可将 LLM 本身作为条件判断的核心(如通过 LLM 分析文本内容生成决策标签),实现 “AI 驱动的智能工作流”;
  4. 易于扩展:通过 langchain4j 的 Chain、Agent 等组件,可快速扩展工作流节点(如添加日志记录、异常重试、外部 API 调用)。

        综上,基于 langchain4j 构建条件工作流,能够解决企业级 LLM 应用从 “单点能力” 到 “全流程自动化” 的落地痛点,实现业务规则与 LLM 能力的深度融合,提升应用的稳定性、灵活性和可维护性。

二.具体实现

1.创建java工程,引入依赖

<dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j</artifactId> <version>1.10.0</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai</artifactId> <version>1.10.0</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-agentic</artifactId> <version>1.10.0-beta18</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-embeddings-bge-small-en-v15-q</artifactId> <version>1.10.0-beta18</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.5.12</version> </dependency>

2.创建大模型工具类

package com.example; import dev.langchain4j.model.chat.ChatModel; import dev.langchain4j.model.openai.OpenAiChatModel; import static dev.langchain4j.model.openai.OpenAiChatModelName.GPT_4_O_MINI; public class ChatModelProvider { public static ChatModel createChatModel() { return OpenAiChatModel.builder() .baseUrl("xxx") .apiKey("xxx") .modelName("gpt-4.1-2025-04-14") .logRequests(true) .logResponses(true) .build(); } }

3.创建工具类

package com.example; import dev.langchain4j.agent.tool.P; import dev.langchain4j.agent.tool.Tool; import java.util.ArrayList; import java.util.Date; import java.util.List; public class OrganizingTools { @Tool public Date getCurrentDate(){ return new Date(); } @Tool("根据指定的岗位编号,查询需出席该岗位线下面试的人员名单及对应的电子邮箱地址") public List<String> getInvolvedEmployeesForInterview(@P("job description ID") String jobDescriptionId){ // dummy implementation for demo return new ArrayList<>(List.of( "Anna Bolena: hiring.manager@company.com", "Chris Durue: near.colleague@company.com", "Esther Finnigan: vp@company.com")); } @Tool("根据员工的电子邮箱地址创建日程条目") public void createCalendarEntry(@P("list of employee email addresses") List<String> emailAddress, @P("meeting topic") String topic, @P("start date and time in format yyyy-mm-dd hh:mm") String start, @P("end date and time in format yyyy-mm-dd hh:mm") String end){ // dummy implementation for demo System.out.println("*** CALENDAR ENTRY CREATED ***"); System.out.println("Topic: " + topic); System.out.println("Start: " + start); System.out.println("End: " + end); } @Tool public int sendEmail(@P("list of recipient email addresses") List<String> to, @P("list of CC email addresses") List<String> cc, @P("emailsubject") String subject, @P("body") String body){ // dummy implementation for demo System.out.println("*** EMAIL SENT ***"); System.out.println("To: " + to); System.out.println("Cc: " + cc); System.out.println("Subject: " + subject); System.out.println("Body: " + body); return 1234; // dummy email ID } @Tool public void updateApplicationStatus(@P("job description ID") String jobDescriptionId, @P("candidate (first name, last name)") String candidateName, @P("new application status") String newStatus){ // dummy implementation for demo System.out.println("*** APPLICATION STATUS UPDATED ***"); System.out.println("Job Descirption ID: " + jobDescriptionId); System.out.println("Candidate Name: " + candidateName); System.out.println("New Status: " + newStatus); } }

4.创建RAG检索类

package com.example; import dev.langchain4j.data.document.Document; import dev.langchain4j.data.document.splitter.DocumentSplitters; import dev.langchain4j.data.segment.TextSegment; import dev.langchain4j.internal.Utils; import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.rag.content.retriever.ContentRetriever; import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever; import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; import dev.langchain4j.model.embedding.onnx.bgesmallenv15q.BgeSmallEnV15QuantizedEmbeddingModel; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import static dev.langchain4j.data.document.loader.FileSystemDocumentLoader.loadDocument; public class RagProvider { public static ContentRetriever loadHouseRulesRetriever() { Document doc = Document.from(""" 吸烟区限定:仅允许在停车场后方指定的户外区域吸烟。 办公静音时段:每日下午 2 点至 4 点为办公静音时段,在此期间请避免在开放办公区大声交谈。 会议室预约:员工使用会议室须提前 24 小时通过内部日历系统进行预约。 自行车停放:自行车需停放至主楼外指定的自行车停放架处。 外来访客登记:所有外来访客进入办公区域前,须到前台出示有效身份证件办理登记手续。 访客证件佩戴:所有外来访客均需领取临时访客证,并全程将证件佩戴在显眼位置。 """); EmbeddingModel embeddingModel = new BgeSmallEnV15QuantizedEmbeddingModel(); InMemoryEmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore<>(); EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() .documentSplitter(DocumentSplitters.recursive(200, 10)) .embeddingModel(embeddingModel) .embeddingStore(store) .build(); ingestor.ingest(List.of(doc)); return EmbeddingStoreContentRetriever.builder() .embeddingStore(store) .embeddingModel(embeddingModel) .maxResults(2) .minScore(0.8) .build(); } public static Path toPath(String relativePath) { try { URL fileUrl = Utils.class.getClassLoader().getResou
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/12 19:32:02

实时进度条显示当前处理状态:HeyGem任务可视化做得有多好?

HeyGem 任务可视化&#xff1a;实时进度条如何重塑 AI 视频生成体验 在数字人视频生成的世界里&#xff0c;等待曾经是一种煎熬。 你上传一段音频、选好多个视频模板&#xff0c;点击“开始”后&#xff0c;页面却陷入沉默——没有提示、没有反馈&#xff0c;甚至连一个动效都没…

作者头像 李华
网站建设 2026/4/17 18:20:22

全网最全8个一键生成论文工具,自考党轻松搞定毕业论文!

全网最全8个一键生成论文工具&#xff0c;自考党轻松搞定毕业论文&#xff01; AI 工具如何让论文写作更轻松 在当前的学术环境中&#xff0c;越来越多的学生开始借助 AI 工具来完成论文写作任务。这些工具不仅能够帮助用户快速生成内容&#xff0c;还能有效降低 AIGC 率&#…

作者头像 李华
网站建设 2026/4/17 22:17:57

OGG音频也能处理:小众格式用户的福音来了

OGG音频也能处理&#xff1a;小众格式用户的福音来了 在数字人技术逐渐走入日常的今天&#xff0c;一个看似微不足道的技术细节&#xff0c;往往能决定用户体验的“最后一公里”是否顺畅。比如——你有没有遇到过这样的情况&#xff1a;手头有一段从会议录音、语音助手或WebRTC…

作者头像 李华
网站建设 2026/4/16 13:06:16

从数组到Span:提升数据转换效率300%,你还在用传统方式吗?

第一章&#xff1a;从数组到Span&#xff1a;性能变革的起点在现代高性能计算场景中&#xff0c;数据访问效率直接决定系统吞吐能力。传统的数组操作虽然简单直观&#xff0c;但在跨方法传递、内存复制和边界检查方面存在显著开销。.NET 引入的 Span<T> 类型正是为解决此…

作者头像 李华
网站建设 2026/4/12 21:39:07

Transformer Key Concepts

Types of position encoding Types of masks in transformer Role of sqrt(d_model) in transformer PreNorm vs PostNorm RmsNorm vs other forms of normalization

作者头像 李华