☰
使用 JavaFX + LangChain4J 构建流式聊天桌面应用:javafx-example 实战指南
2026/10/3 2:19:35 网站建设 项目流程
  • 示例工程

【免费下载链接】langchain4j-examples

项目地址:https://gitcode.com/GitHub_Trending/la/langchain4j-examples
点击查看免费下载

导读

本指南基于langchain4j-examples仓库中的 javafx-example/README.md,完整讲解如何用 JavaFX 构建一个原生桌面聊天界面,并通过 LangChain4J 的流式(Streaming)能力把 OpenAI 的回复逐字实时渲染到界面上。文章从环境准备、项目结构到核心源码逐层拆解:你将掌握 JavaFX 属性绑定如何驱动流式 Token 渲染、LangChain4JTokenStream的三段式回调模型、AiServices+ 窗口消息记忆的配置方式,最终能以此为起点搭建自己的桌面端 AI 助手。

一、项目定位:为什么要用 JavaFX 做 LangChain4J 桌面端

javafx-example是一个独立的 Maven 模块(见 javafx-example/pom.xml),其目标非常聚焦:演示如何用 JavaFX 用户界面把 OpenAI 流式返回的答案,一边接收一边可视化。核心诉求有三点:

  • 答案在收到过程中就实时渲染到右侧文本框;
  • 同一答案同步呈现在左侧历史表格(Table)中;
  • 每次提问的历史记录(时间戳、问题、答案、完成状态)都保留在表格里,且模型通过聊天记忆维持多轮对话上下文。

从源码结构看(javafx-example/src/main/java/),该模块把 UI、流式处理、业务服务、数据模型分层得很清晰,共 7 个 Java 类:Launcher、ChatApp、AnswerService、Assistant、CustomStreamingResponseHandler、SearchAction、ApiKeys。这种「UI 层只负责展示、服务层只负责编排模型、模型类只负责声明接口」的组织方式,是后续构建任何桌面 AI 应用都可以复用的骨架。

二、环境准备与运行方式

运行该示例的前提是使用自带 JavaFX 的 JDK。README 给出的推荐方式是使用 SDKMAN 安装 Azul Zulu 的 JavaFX 捆绑版 JDK,然后直接通过 Maven JavaFX 插件启动:

$ sdk install java 21.fx-zulu $ mvn javafx:run

围绕这两条命令,结合 pom.xml 补充几个可验证的细节:

  • Java 编译级别为 17:maven.compiler.source/target均为17,而运行示例用 21 版捆绑 JDK,说明代码可在 Java 17+ 上编译、用含 JavaFX 的 JDK 运行。
  • JavaFX 依赖:仅引入javafx-controls(版本21.0.1),因为界面只用到了控件层(Application、Scene、TableView、TextArea、TextField、Button等),无需javafx-fxml。
  • LangChain4J 依赖:langchain4j(核心库)与langchain4j-open-ai(OpenAI 模型实现)均为1.17.0,流式能力由OpenAiStreamingChatModel提供。
  • 日志依赖:log4j-api+log4j-slf4j2-impl(2.22.1),用于记录启动、请求、流式完成等过程日志。
  • 启动入口:javafx-maven-plugin(版本0.0.8)配置了<mainClass>Launcher</mainClass>,Launcher只是一个转发入口,main直接调用ChatApp.main(args)启动 JavaFX 应用。

注意:模型调用需要有效的 OpenAI API Key。源码 ApiKeys.java 中默认使用"demo"占位 Key,注释说明可前往 OpenAI 平台获取自己的 Key 后替换OPENAI_API_KEY常量。

三、整体架构:一条提问在 UI 与模型之间的流转链路

应用虽小,却构成了一条完整的「用户输入 → 线程化服务 → 流式模型 → JavaFX 主线程更新 UI」链路,可以用下面这张运行时截图直观对照:

(截图来自 javafx-example/screenshot.png,界面包含标题「JavaFX Chat Langchain4J Demo」、输入区「What is your question?」、带 Timestamp/Question/Answer/Finished 四列的表格,以及右侧完整回答文本框。)

链路各环节对应源码如下:

  1. UI 事件触发:ChatApp.java 中,TextField的setOnAction(回车)与Search按钮的setOnAction都指向doSearch(input.getText()),空输入会被直接忽略。
  2. 新建会话记录并绑定:doSearch创建一个新的SearchAction(question),加入ObservableList<SearchAction> data,同时执行lastAnswer.textProperty().bind(searchAction.getAnswerProperty())—— 右侧文本框直接与这条记录的答案属性绑定。
  3. 后台线程执行:new Thread(() -> docsAnswerService.ask(searchAction)).start()把模型调用放到独立线程,避免阻塞 JavaFX 的 UI 线程。
  4. 流式回调:AnswerService调用assistant.chat(...)得到TokenStream,分别注册onPartialResponse、onCompleteResponse、onError回调。
  5. 回主线程更新:CustomStreamingResponseHandler.java 中每个回调都通过Platform.runLater(...)把 UI 更新切回 JavaFX Application Thread,这是 JavaFX 并发编程的硬性要求——模型线程不能直接操作 UI。

这个设计同时回答了 README 里强调的「如何用 JavaFX bindings 处理流式答案」:把模型返回的 Token 追加写入 JavaFX 属性,再由属性绑定自动推送到界面。

四、核心代码逐层拆解

4.1 入口与界面装配:ChatApp

ChatApp.java 是整个应用的 UI 核心,几个值得注意的装配细节:

  • 全局单例数据源:FXCollections.observableArrayList()的data作为TableView的 items,表格各列通过setCellValueFactory绑定SearchAction的 JavaFX 属性(时间戳、问题、答案、完成状态四列分别设了 250/250/300/50 的最小宽度)。
  • 启动即初始化:start()中先插入一条new SearchAction("Application started", true),再插入"Initializing search engine, please stand by..."的初始化条目,并用lastAnswer.textProperty().bind(initAction.getAnswerProperty())让右侧文本框显示初始化进度,随后在新线程中执行docsAnswerService.init(initAction)。
  • 布局:VBox外层 15px 内边距,依次放入标题 Label(25px 加粗)、输入区HBox(输入框 500px 宽 + Search 按钮)、以及new HBox(table, lastAnswer)构成的「左表右文」双栏布局;lastAnswer.setWrapText(true)保证长答案自动换行。

4.2 流式模型与记忆装配:AnswerService

AnswerService.java 负责模型生命周期管理,是 README 中「模型保持对话记忆」这一承诺的直接实现:

StreamingChatModel model = OpenAiStreamingChatModel.builder() .apiKey(ApiKeys.OPENAI_API_KEY) .modelName(GPT_4_O_MINI) // 静态导入自 OpenAiChatModelName .build(); assistant = AiServices.builder(Assistant.class) .streamingChatModel(model) .chatMemory(MessageWindowChatMemory.withMaxMessages(10)) // 最多保留 10 条消息 .build();

关键点:

  • 流式模型:使用OpenAiStreamingChatModel,模型名取自OpenAiChatModelName.GPT_4_O_MINI;
  • AI 服务:通过AiServices.builder(Assistant.class)把流式模型和聊天记忆注入到Assistant接口,得到可调用的代理对象;
  • 窗口记忆:MessageWindowChatMemory.withMaxMessages(10)说明记忆采用「消息窗口」策略,只保留最近 10 条消息,既维持多轮上下文又控制 Token 开销;
  • 初始化流程:init会先appendAnswer("Initiating..."),模型构建完成后追加"Done"并调用setFinished(),让表格中该行「Finished」列变为true。

4.3 流式回调的三种形态:CustomStreamingResponseHandler

CustomStreamingResponseHandler.java 演示了 LangChain4JTokenStream最核心的三段式回调:

回调触发时机UI 行为
onNext(String token)收到一个流式分片 TokenPlatform.runLater内action.appendAnswer(token),答案逐字增长
onComplete(ChatResponse response)整个响应结束打印完整响应日志与答案长度,action.setFinished()置完成标志
onError(Throwable error)出错日志记录错误,答案尾部追加"\nSomething went wrong: " + error.getMessage(),并置完成标志

这里有一个值得学习的健壮性细节:onError并不会让界面卡在「加载中」,而是把错误信息作为答案的一部分追加显示,同时把Finished置为true,保证表格状态始终一致。

4.4 AI 服务接口:Assistant

Assistant.java 是 LangChain4J 的 AI Service 声明式接口,只有一行方法签名:

public interface Assistant { TokenStream chat(String message); }

返回类型TokenStream正是流式能力的入口——在 AnswerService.java 的ask方法中,对它链式注册回调后调用.start()才真正发起请求:

assistant.chat(action.getQuestion()) .onPartialResponse(responseHandler::onNext) .onCompleteResponse(responseHandler::onComplete) .onError(responseHandler::onError) .start();

这种「接口声明 +AiServices动态代理」的编程模型,把记忆、模型、流式处理全部收敛到声明层,业务代码只关心回调即可。

4.5 数据模型:SearchAction 与 JavaFX 属性绑定

SearchAction.java 是整个「绑定驱动流式渲染」方案的基石。它不依赖javafx.beans之外的任何模型代码,用四个 JavaFX 属性承载一条完整会话记录:

  • StringProperty timestamp:LocalDateTime.now().toString()生成提问时间戳;
  • StringProperty question:用户问题;
  • StringProperty answer:答案,appendAnswer(String token)通过this.answer.set(this.answer.getValue() + token)实现 Token 追加;
  • BooleanProperty finished:是否已完成。

正因为answer是StringProperty,ChatApp里的lastAnswer.textProperty().bind(searchAction.getAnswerProperty())才能做到「模型线程每追加一个 Token,右侧文本框和表格单元格就同步刷新一次」——这正是 README 强调的「用 JavaFX bindings 处理答案」的底层机制。

五、实战要点总结与扩展建议

5.1 值得直接复用的四个模式

  1. 后台线程 +Platform.runLater:凡是模型/IO 操作一律放非 UI 线程,凡是要更新界面一律经Platform.runLater切回 JavaFX 主线程;
  2. 属性绑定替代手动刷新:把流式答案写入StringProperty,让TextArea和TableView通过绑定自动感知变化,无需手动重绘;
  3. AiServices+TokenStream声明式流式编程:接口返回TokenStream,链式注册onPartialResponse/onCompleteResponse/onError后.start();
  4. 窗口记忆保持多轮语境:MessageWindowChatMemory.withMaxMessages(10)是控制上下文长度最简单直接的方式。

5.2 改造为自有应用的切入点

README 明确指出该项目「可以作为构建自己 JavaFX 版 LangChain4J 实现的起点」。从当前源码出发,可以清晰看到哪些地方适合替换扩展:

  • 更换模型:AnswerService.initChat中把OpenAiStreamingChatModel换成其他StreamingChatModel实现(如 Azure OpenAI、本地 Ollama 等,可参考仓库中 ollama-examples/ 等其他模块)即可,Assistant接口与 UI 层无需改动;
  • 扩展服务能力:Assistant接口可以增加带@Tool的方法或引入工具类,把桌面应用升级为可调用外部工具的智能助手;
  • 调整记忆策略:将MessageWindowChatMemory换成持久化记忆,可参考仓库 other-examples/ 中的ServiceWithPersistentMemoryExamples相关实现;
  • 美化界面:ChatApp中所有样式都是内联 CSS(如-fx-padding: 15px、-fx-font-size: 25px),可直接升级为 JavaFX CSS 样式表,或引入 FXML 做更复杂的布局。

5.3 运行验证路径

在javafx-example目录下执行mvn javafx:run后,可以对照 README 与截图验证三条行为:输入问题并回车或点击 Search 后,右侧文本框会逐字出现答案;左侧表格会新增一行记录,Finished列在流式结束后变为true;连续提问时,模型能结合之前的历史消息作答(由 10 条消息窗口记忆保证)。日志中可观察到"Complete response: ..."与"Answer is complete for '...', size: N"的输出,用于确认流式生命周期完整走完。

相关文件索引

  • javafx-example/README.md — 本文依据的官方说明文档
  • javafx-example/pom.xml — 依赖与启动插件配置
  • javafx-example/src/main/java/ChatApp.java — JavaFX 界面装配与事件处理
  • javafx-example/src/main/java/AnswerService.java — 流式模型与记忆装配
  • javafx-example/src/main/java/Assistant.java — AI Service 声明接口
  • javafx-example/src/main/java/CustomStreamingResponseHandler.java — 流式回调处理
  • javafx-example/src/main/java/SearchAction.java — JavaFX 属性数据模型
  • javafx-example/screenshot.png — 应用运行界面截图
  • 示例工程

【免费下载链接】langchain4j-examples

项目地址:https://gitcode.com/GitHub_Trending/la/langchain4j-examples
点击查看免费下载

相关推荐

上一篇:scrcpy 安卓投屏教程:3 步把手机变成电脑上的窗口
下一篇:Umi-OCR 完整指南:免费离线 OCR,快速把扫描 PDF 变成可搜索文档

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询