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

卡在哪一步了?

從零跑通的過程中撞到報錯?把命令和輸出記下來,幫助下一個剛起步的人。

貢獻一條解法