StackTraces
LangChain4j从零跑通

从零跑通 LangChain4j + Ollama 本地对话:5 步,外加 4 个容易卡住的地方

@debug_master2026-09-30

跟着这篇走完,你会在 Java 里把本地 Ollama 跑通:建一个 Maven 工程,用 LangChain4j 的 ChatModel 跟 qwen3:8b 对话,再换成流式输出逐字拿回答。整个过程 5 步,每步都有一条命令能验证自己做对了没有。文中 localhost:11434 是 Ollama 的默认监听地址,模型固定用 qwen3:8b;如果你换了端口或模型 tag,把示例值替换掉即可。

先说一个容易踩的版本坑:LangChain4j 1.x 把旧的 ChatLanguageModel 接口删了,换成 ChatModel,调用方法也从 generate() 改成了 chat()。网上很多教程还停在旧接口,照抄会编译失败。下面全部按 1.x 新接口写。

LangChain4j:从零跑通本地 Ollama 对话

前置条件

项 要求 说明
JDK 17 或更高 LangChain4j 1.x 从 Java 17 起支持
Maven 3.8 或更高 本文用 Maven 建工程、拉依赖
Ollama 0.34.4+,已 pull qwen3:8b 没装过的话先跑《从零跑通 Ollama + Qwen3 本地对话》,把服务拉起来再回来
内存 / 磁盘 8 GB 内存、6 GB 空闲磁盘 qwen3:8b 权重 5.2 GB,跑 8B 模型内存要兜得住

先跑这三条确认环境。

java -version
mvn -v
ollama --version

java -version 输出 17 或更高、mvn -v 能打出版本号、ollama --version 显示 0.34.4 以上,就算满足。ollama: command not found 说明 Ollama 还没装,先去 从零跑通 Ollama + Qwen3 本地对话 走完前两步再回来。模型没拉的话补一条 ollama pull qwen3:8b,用 ollama list 确认列表里有它。

第一步:建 Maven 工程并引入依赖

新建目录和 pom.xml,把 LangChain4j 的 BOM 和两个模块引进来。BOM 的作用是把 langchain4j 全家桶的版本统一锁住,省得核心包和 Ollama 模块版本参差,那种情况启动时会报 NoClassDefFoundError(后面常见问题有专门一条)。

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>langchain4j-ollama-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <langchain4j.version>1.20.2</langchain4j.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>dev.langchain4j</groupId>
                <artifactId>langchain4j-bom</artifactId>
                <version>${langchain4j.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-ollama</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.17</version>
        </dependency>
    </dependencies>
</project>

langchain4j-ollama 是对 Ollama 的实现模块,langchain4j 是核心 API。BOM 用了 1.20.2,这是截至本文的稳定版;换新版本只改 langchain4j.version 一处。

验证依赖能不能拉下来。

mvn compile

第一次会下载一堆 jar,最后出现 BUILD SUCCESS 就说明依赖装齐了。如果报 Could not find artifact 或 NoClassDefFoundError,多半是版本号写错或 BOM 没配好,检查 langchain4j.version 和 dependencyManagement 是否一致。

第二步:用 ChatModel 完成第一次对话

在 src/main/java/com/example/Main.java 写入口,用 OllamaChatModel 构建一个模型实例,直接 chat() 拿回答。

package com.example;

import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.ollama.OllamaChatModel;

import java.time.Duration;

public class Main {
    public static void main(String[] args) {
        ChatModel model = OllamaChatModel.builder()
                .baseUrl("http://localhost:11434")
                .modelName("qwen3:8b")
                .temperature(0.8)
                .timeout(Duration.ofSeconds(60))
                .build();

        String answer = model.chat("用一句话介绍你自己,再告诉我你是谁开发的。");
        System.out.println(answer);
    }
}

注意几个点。baseUrl 的默认值其实就是 http://localhost:11434,但建议显式写出来,排查时少一层猜测。modelName 必须和 ollama list 里的逐字一致,多一个 : 少一个 tag 都不行。chat(String) 是便捷方法,输入一句话返回一句话;想要更多控制(多轮、工具、JSON 输出)要用下面几版的 chat()。

跑起来。

mvn compile exec:java -Dexec.mainClass=com.example.Main

控制台打出一段像样的中文回答,就算通了。如果抛 NullPointerException 且栈里带 retrofit/OkHttp,别急着查代码——多半是模型名写错了,Ollama 对不存在的模型返回 404 空响应体,客户端解析时炸成了 NPE,完整根因看这篇 case。

第三步:带上上下文聊多轮

ChatModel 是无状态的,它不记得你上一轮说了什么。多轮会话要自己把历史消息拼起来再发。LangChain4j 提供了 UserMessage、AiMessage 这些消息类型,chat(List<ChatMessage>) 接受一组消息。

package com.example;

import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.ollama.OllamaChatModel;

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        ChatModel model = OllamaChatModel.builder()
                .baseUrl("http://localhost:11434")
                .modelName("qwen3:8b")
                .build();

        List<ChatMessage> history = new ArrayList<>();
        history.add(UserMessage.from("我叫阿禾,在做支付网关的限流。"));

        AiMessage firstReply = model.chat(history).aiMessage();
        System.out.println("AI: " + firstReply.text());
        history.add(firstReply);

        history.add(UserMessage.from("我刚才说我在做什么?"));
        AiMessage secondReply = model.chat(history).aiMessage();
        System.out.println("AI: " + secondReply.text());
    }
}

关键在 history 这个列表:每一轮都把用户消息和 AI 回答按顺序塞回去,第二轮再整个发出去。只塞 user 消息、漏掉 assistant 那一条,等于把上一轮的答案抹掉,模型自然接不上话。

验证。

mvn compile exec:java -Dexec.mainClass=com.example.Main

第二轮回答里出现「支付网关」或「限流」,说明上下文真的带进去了。答非所问就检查 history 有没有把 firstReply 加回去。

第四步:改成流式输出

model.chat() 会等模型把整段话生成完才返回,回答长的时候体验很干。换成 OllamaStreamingChatModel,回答会一段一段推给回调。

package com.example;

import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
import dev.langchain4j.model.ollama.OllamaStreamingChatModel;

public class Main {
    public static void main(String[] args) {
        StreamingChatModel model = OllamaStreamingChatModel.builder()
                .baseUrl("http://localhost:11434")
                .modelName("qwen3:8b")
                .build();

        model.chat("用 200 字给我讲一个程序员加班的故事。", new StreamingChatResponseHandler() {
            @Override
            public void onPartialResponse(String partialResponse) {
                System.out.print(partialResponse);
            }

            @Override
            public void onCompleteResponse(ChatResponse completeResponse) {
                System.out.println("\n[完成]");
            }

            @Override
            public void onError(Throwable error) {
                error.printStackTrace();
            }
        });
    }
}

三个回调各管一件事:onPartialResponse 每推来一段就打印一段,onCompleteResponse 整段结束收尾,onError 接住异常。注意这里返回类型是 StreamingChatModel,接口和同步的 ChatModel 不是一个,别引错 import。

验证。

mvn compile exec:java -Dexec.mainClass=com.example.Main

回答逐字蹦出来、最后打印 [完成],流式就通了。如果什么也没输出就结束,看 onError 有没有被触发,多半是模型名或 baseUrl 的问题,跟第二步同一个查法。

第五步(进阶):用 Spring Boot starter 装配

不想手写 builder 的话,LangChain4j 提供 Spring Boot starter,配置写在 application.yml 里,ChatModel 由容器自动装配。

<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-ollama-spring-boot-starter</artifactId>
</dependency>
langchain4j:
  ollama:
    chat-model:
      base-url: http://localhost:11434
      model-name: qwen3:8b
      temperature: 0.8
      timeout: PT60S
@RestController
public class ChatController {
    @Autowired
    private ChatModel chatModel;

    @GetMapping("/chat")
    public String chat(@RequestParam String q) {
        return chatModel.chat(q);
    }
}

验证。

mvn spring-boot:run
curl "http://localhost:8080/chat?q=你好"

返回中文回答就装配成功。LangChain4j 的 Spring Boot starter 要求 Java 17 + Spring Boot 3.2,如果你用了 Spring Boot 4,会直接不兼容,看这篇 case。

核心概念

下面这张图是整条调用链路的走向:Java 应用里的 ChatModel 通过 HTTP 访问 Ollama 服务,再由 Ollama 调度本地模型生成回答,响应原路返回。

LangChain4j 调用本地 Ollama 的完整架构图

概念 是什么 类比
ChatModel LangChain4j 1.x 与 LLM 对话的低层接口,chat(String) 是便捷方法 像 JDBC 里的 Connection,一个抽象入口
OllamaChatModel LangChain4j 对 Ollama 的具体实现,用 builder 构建 像数据库驱动,各家方言都在这层
base-url Ollama 服务的 HTTP 地址,默认 localhost:11434 像数据库连接串,写错就连不上
模型 tag 仓库名:规格 的模型标识,如 qwen3:8b 像镜像 tag,同一个仓库下不同规格
BOM 统一锁定 LangChain4j 全家桶版本 像 parent pom,一处改版本全部生效
StreamingChatModel 流式接口,回答通过回调增量推送 像逐行读大文件,不用等全部读完

常见问题

NullPointerException,栈里带 retrofit / OkHttp

现象:模型名写错了,客户端抛的却是 NPE,报错信息里连模型名都不提。

原因:Ollama 对不存在的模型返回 404、响应体为空,客户端解析空响应体时炸在 HTTP 层,NPE 只是二次伤害。

相关文章:LangChain4j Ollama 模型名写错抛 NullPointerException

编译报 ChatLanguageModel 找不到

现象:照旧教程写 ChatLanguageModel,IDE 或编译直接说这个接口不存在。

原因:LangChain4j 1.x 把旧接口删了,改名 ChatModel,方法也从 generate() 变成 chat();网上旧教程没跟上。

相关文章:LangChain4j 升级 1.x 报 ChatLanguageModel 找不到

启动报 NoClassDefFoundError

现象:依赖都能拉到,一运行就缺类,报 dev/langchain4j/xxx 找不到。

原因:核心包和 langchain4j-ollama 版本不一致,两个模块的类对不上;多半是没走 BOM 各自写了版本。

相关文章:LangChain4j 依赖版本参差报 NoClassDefFoundError

curl: (7) Failed to connect to localhost port 11434

现象:Java 程序一调就报 Connection refused,curl 也连不上 11434,不是超时。

原因:Ollama 服务没起来,或只绑在别的地址上(容器里最常见,容器内的 localhost 不是宿主机)。

相关文章:Spring AI 接 Ollama 报 Connection refused

下一步

接进 Spring Boot 做完整应用

starter 装配只是第一步,跑通之后要处理配置、日志、错误兜底,Boot 4 还直接不兼容。

LangChain4j Spring Boot 4 自动配置不兼容

用 AiServices 写结构化调用

ChatModel 是低层 API,想要工具调用、结构化输出,LangChain4j 的 AiServices 是更顺手的入口。

LangChain4j @Tool 参数名丢失报 arg0

管理长会话的上下文

多轮消息越堆越长,要主动截断或摘要,别等撞上上下文上限才回头加。

LangChain4j Ollama 模型名写错抛 NullPointerException

卡在哪一步了?

从零跑通的过程中撞到报错?把命令和输出记下来,帮助下一个刚起步的人。

贡献一条解法