02-HelloWorld深度讲解

5分钟运行官方HelloWorld示例,理解LangChain4J的核心API

时间:30分钟 | 难度:⭐⭐


官方Example信息

  • GitHub链接HelloWorldExample.java
  • 文件名:HelloWorldExample.java
  • 所在路径:src/main/java/dev/langchain4j/examples/
  • 代码行数:约20行
  • 难度:初级 ⭐

学习目标

  • 在本地成功运行HelloWorldExample ✅ 2026-03-07
  • 理解每一行代码的含义 ✅ 2026-03-07
  • 掌握Builder模式的用法 ✅ 2026-03-07
  • 理解chat()方法的工作原理 ✅ 2026-03-07
  • 能修改参数并预测结果 ✅ 2026-03-07

官方代码分析

完整源代码

java 复制代码
public class HelloWorldExample {
    public static void main(String[] args) {
        // 创建一个ChatModel实例
        ChatModel model = OpenAiChatModel.builder()
                .baseUrl("https://api.openai.com/v1/")  // 或你的代理/本地地址
                .apiKey("your-api-key-here")
                .modelName("gpt-4o-mini")
                .build();

        // 开始交互
        String answer = model.chat("Hello world");

        System.out.println(answer);  // Hello! How can I assist you today?
    }
}

逐行讲解(推荐方式)

第1-6行:创建模型对象

java 复制代码
ChatModel model = OpenAiChatModel.builder()
        .baseUrl("https://api.openai.com/v1/")  // API地址
        .apiKey("your-api-key-here")             // API密钥
        .modelName("gpt-4o-mini")                 // 模型选择
        .build();

✨ 关键点:

要素 说明
ChatModel 接口,代表"能对话的语言模型",LangChain4J的标准API
OpenAiChatModel.builder() 使用Builder模式创建对象(而不是new)
baseUrl() 指定API服务地址,支持官方OpenAI或自定义代理
apiKey() API密钥,直接指定或从环境变量读取
modelName() 选择LLM模型,这里用gpt-4o-mini(成本最优)
.build() 完成对象创建,返回可用的模型对象

为什么用Builder模式?

  • 参数多且可选(可以设置temperature、maxTokens等)
  • 代码可读性好(链式调用,一目了然)
  • 易于扩展(添加新参数不破坏现有代码)
  • 避免构造函数参数过多(constructor hell)

第7-9行:调用模型生成回答

java 复制代码
String answer = model.chat("Hello world");
System.out.println(answer);

✨ 关键点:

要素 说明
chat() 调用LLM,传入文本问题
返回值 String类型,LLM生成的答案
同步调用 阻塞直到LLM返回结果(5-30秒)
println() 打印输出到控制台

执行流程

arduino 复制代码
输入: "Hello world"
     ↓
LLM处理(5-30秒)
     ↓
输出: "Hello! How can I assist you today?"
     ↓
控制台打印结果

baseUrl的说明

  • 官方OpenAIhttps://api.openai.com/v1/
  • 中国区代理https://dify.codewave-test.163yun.com/v1/(示例)
  • 本地服务http://localhost:8000/v1/(示例)
  • 其他供应商:根据实际地址配置

核心概念讲解

ChatModel 是什么?

一个接口,代表"能对话的语言模型",是LangChain4J的标准API:

arduino 复制代码
输入 → [ChatModel] → 输出
"你好"   ↓        "你好!很高兴认识你"
       LLM引擎

主要特点

  • 输入:文本问题(String)
  • 输出:文本答案(String)
  • 支持多个实现:OpenAiChatModel、AnthropicChatModel等
  • 支持自定义baseUrl(可使用代理或本地服务)
  • 代码简洁,易于使用

为什么chat()方法会阻塞?

scss 复制代码
调用chat()
     ↓
[等待中...] ← 网络请求到LLM API
     ↓
[等待中...] ← LLM处理你的问题
     ↓
[等待中...] ← 网络传输结果回来(5-30秒)
     ↓
返回答案并继续执行

特点

  • 这是同步调用,会阻塞当前线程
  • LLM响应通常需要5-30秒
  • 阻塞期间程序无法做其他事

解决方案(后续学习):

  • 用CompletableFuture包装成异步
  • 用Spring的@Async异步注解
  • 用reactive编程框架(Project Reactor)

官方Example vs 我们的扩展

官方Example只是演示API调用,生产环境需要扩展:

差异对比

特性 官方Example 我们的扩展版本
代码位置 Main函数(难以复用) Service类(易复用)
配置方式 硬编码环境变量 application.yml配置文件
错误处理 无(出错会crash) try-catch + 自动重试
集成方式 独立运行 Spring Boot Bean注入
测试 难以测试 易单元测试
监控 记录Metrics(成本、延迟等)

我们的扩展实现

扩展1:Spring Boot Service封装

java 复制代码
@Service
public class ChatService {
    private final ChatModel model;

    public ChatService(@Value("${openai.base-url}") String baseUrl,
                      @Value("${openai.api-key}") String apiKey) {
        this.model = OpenAiChatModel.builder()
            .baseUrl(baseUrl)
            .apiKey(apiKey)
            .modelName("gpt-4o-mini")
            .build();
    }

    public String chat(String question) {
        return model.chat(question);
    }
}

配置文件 (application.yml):

yaml 复制代码
openai:
  base-url: https://api.openai.com/v1/  # 支持自定义地址
  api-key: ${OPENAI_API_KEY}  # 从环境变量读取

使用方式(在其他Service中):

java 复制代码
@RestController
public class ChatController {
    @Autowired
    private ChatService chatService;

    @GetMapping("/chat")
    public String chat(@RequestParam String question) {
        return chatService.chat(question);
    }
}

✨ 改进点:

  • ✅ 使用Spring Bean管理生命周期(自动创建和销毁)
  • ✅ 配置外部化(不用改代码,只需改配置文件)
  • ✅ 可注入到其他Service中(复用性好)
  • ✅ 便于单元测试(可以Mock)

扩展2:支持多个模型

java 复制代码
@Service
public class MultiLlmService {
    private final ChatModel openaiModel;
    private final ChatModel claudeModel;

    public MultiLlmService(
        @Value("${openai.base-url}") String openaiBaseUrl,
        @Value("${openai.api-key}") String openaiKey,
        @Value("${anthropic.api-key}") String claudeKey) {

        this.openaiModel = OpenAiChatModel.builder()
            .baseUrl(openaiBaseUrl)
            .apiKey(openaiKey)
            .modelName("gpt-4o-mini")
            .build();

        this.claudeModel = AnthropicChatModel.builder()
            .apiKey(claudeKey)
            .modelName("claude-3-5-sonnet-20241022")
            .build();
    }

    public String chatWithOpenai(String question) {
        return openaiModel.chat(question);
    }

    public String chatWithClaude(String question) {
        return claudeModel.chat(question);
    }

    // 动态选择最好的模型
    public String chatWithBest(String question) {
        try {
            // 先试OpenAI(更快)
            return chatWithOpenai(question);
        } catch (Exception e) {
            // 失败时用Claude(更稳定)
            return chatWithClaude(question);
        }
    }
}

✨ 改进点:

  • ✅ 支持多个LLM切换,一份代码搞定
  • ✅ 灵活选择最优模型(便宜用gpt-4o-mini,复杂用claude)
  • ✅ 方便对比测试(同一个问题,看不同LLM的答案)
  • ✅ 容错能力强(一个模型失败,自动用备用模型)

扩展3:错误处理和自动重试

java 复制代码
@Service
public class RobustChatService {
    private final ChatModel model;
    private static final Logger logger = LoggerFactory.getLogger(RobustChatService.class);

    public RobustChatService(@Value("${openai.base-url}") String baseUrl,
                            @Value("${openai.api-key}") String apiKey) {
        this.model = OpenAiChatModel.builder()
            .baseUrl(baseUrl)
            .apiKey(apiKey)
            .modelName("gpt-4o-mini")
            .build();
    }

    /**
     * 带重试机制的chat方法
     * 失败时自动重试,使用指数退避(1秒→2秒→4秒)
     */
    public String chatWithRetry(String question, int maxRetries) {
        for (int attempt = 0; attempt < maxRetries; attempt++) {
            try {
                logger.info("Attempt {} for question: {}", attempt + 1, question);
                return model.chat(question);
            } catch (ApiException e) {
                // API错误(401=密钥失效, 429=限流, 500=服务器错误)
                if (attempt == maxRetries - 1) {
                    logger.error("Failed after {} attempts", maxRetries, e);
                    throw e;  // 最后一次重试仍失败,抛出异常
                }

                // 指数退避:1秒, 2秒, 4秒, 8秒...
                long backoffMs = 1000 * (1 << attempt);
                logger.warn("Retry after {}ms due to: {}", backoffMs, e.getMessage());

                try {
                    Thread.sleep(backoffMs);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("Interrupted while retrying", ie);
                }
            }
        }
        return null;  // 理论上不会到达这里
    }
}

使用方式

java 复制代码
String answer = robustChatService.chatWithRetry("你好", 3);
// 如果失败,自动重试3次,每次间隔递增

✨ 改进点:

  • ✅ 处理网络问题(自动重试)
  • ✅ 处理API限流(指数退避避免继续激怒API)
  • ✅ 处理超时(加日志便于调试)
  • ✅ 生产级别的健壮性

最佳实践

✅ 做这些

实践 为什么
从环境变量读取API Key 安全性,不会误提交到Git
使用Builder模式 参数清晰,易扩展
选择合适的模型 gpt-4o-mini(便宜+够用)
添加错误处理 网络不稳定,需要重试
使用Spring Bean管理 生命周期清晰,便于测试
配置外部化 支持不同环境,灵活切换
监控成本和延迟 知道钱花在哪里

❌ 不要做这些

反面 为什么
把API Key硬编码 安全风险,会被黑客利用
忽视错误处理 网络问题是常态,会crash
用低效的串行调用 阻塞线程,用户体验差
没有成本控制 API费用会暴增
没有日志记录 出问题时无法调试
硬编码API地址 无法适配不同环境

常见问题解答

Q1:为什么选择gpt-4o-mini而不是gpt-4o?

A: 成本考虑

  • gpt-4o-mini:$0.15/1M input tokens
  • gpt-4o:$5.00/1M input tokens
  • 价格差33倍!

但是性能足够90%的日常使用。只有遇到复杂推理/创意任务时才升级到gpt-4o。

Q2:chat()方法为什么会阻塞?有什么办法?

A: 因为它是同步调用。它要等LLM返回结果才能继续。

解决办法:

  1. 如果只是演示:无所谓(等就等了)
  2. 如果是Web API:用CompletableFuture包装
  3. 如果是Spring项目:用@Async异步方法
  4. 如果是高并发:用WebClient + reactive编程

Q3:能改变返回格式吗?

A: chat()只返回String。如果需要结构化输出:

java 复制代码
// 方法1:让LLM返回JSON
String prompt = "用JSON格式回答: {\"name\": \"...\", \"age\": ...}";
String jsonResponse = model.chat(prompt);
// 自己解析JSON

// 方法2:使用高级API(后续学)
// LangChain4J支持直接返回Java对象

Q4:API Key在哪里申请?

A:

Q5:如何处理API Key失效?

A:

java 复制代码
try {
    String answer = model.chat(question);
} catch (ApiException e) {
    if (e.statusCode() == 401) {
        // API Key失效,需要更新
        logger.error("API Key is invalid or expired");
        // 告诉用户需要重新配置
    }
}

思考问题

  1. 为什么使用Builder模式而不是普通构造函数?

    • 思考Builder的优势
    • 想象如果有20个可选参数会怎样
  2. generate()方法为什么会阻塞?

    • 追踪整个调用链:Java → HTTP → 网络 → OpenAI GPU → 结果回传
    • 如果想异步怎么办?
  3. 在生产环境,如何安全地管理API Key?

    • 环境变量?
    • 密钥管理服务?
    • 加密存储?
  4. 如果想支持多个LLM(OpenAI + Claude),代码怎么设计?

    • 工厂模式?
    • 策略模式?
    • 配置文件?

实战练习

任务1:本地运行Example

bash 复制代码
# 1. Clone仓库
git clone https://github.com/langchain4j/langchain4j-examples.git
cd langchain4j-examples

# 2. 配置API Key
export OPENAI_API_KEY=sk-proj-your-key-here

# 3. 编译(确保Maven已安装)
mvn clean compile

# 4. 运行HelloWorldExample
mvn exec:java -Dexec.mainClass="dev.langchain4j.examples.HelloWorldExample"

# 5. 观察输出
# 应该能看到LLM的回答

验收标准

  • 程序正常运行(没有报错)
  • 输出了LLM的回答(而不是API Key错误)
  • 理解了整个执行流程

任务2:修改参数再运行

修改 HelloWorldExample.java:

java 复制代码
// 改1:问题改成中文
String answer = model.chat("你好,世界");

// 改2:模型改成gpt-4o(注意成本会增加)
.modelName("gpt-4o")

// 改3:观察返回速度和内容有什么区别
// gpt-4o 能力更强,但更贵且更慢

验收标准

  • 能理解参数对输出质量的影响
  • 能理解参数对成本的影响
  • 能理解参数对响应速度的影响

任务3:在Spring Boot项目中复现

在你的hello-world项目中创建ChatService:

java 复制代码
package com.example.langchain4j;

import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class ChatService {
    private final ChatModel model;

    public ChatService(@Value("${openai.api-key}") String apiKey) {
        this.model = OpenAiChatModel.builder()
            .apiKey(apiKey)
            .modelName("gpt-4o-mini")
            .build();
    }

    public String chat(String question) {
        return model.chat(question);
    }
}

配置文件 (application.yml):

yaml 复制代码
openai:
  api-key: ${OPENAI_API_KEY}

测试代码:

java 复制代码
@SpringBootTest
public class ChatServiceTest {
    @Autowired
    private ChatService chatService;

    @Test
    public void testChat() {
        String result = chatService.chat("Hello world");
        assertNotNull(result);
        assertTrue(result.length() > 0);
    }
}

验收标准

  • ChatService类能编译通过 ✅ 2026-03-07
  • 能注入到其他Bean中 ✅ 2026-03-07
  • 单元测试能通过 ✅ 2026-03-07
  • 理解了Spring集成的每一步 ✅ 2026-03-07

相关Examples推荐

下一步学习这些Examples来深化理解:

  • [[2004-Prompt工程最佳实践]] (PromptExample)

    • 学会写好Prompt,让LLM输出更好的答案
  • [[011-ChatModel完整指南]] (ChatModelExample)

    • 掌握ChatModel API的完整用法
  • [[010-OpenAI和Claude集成详解]] (OpenAiExample)

    • 学会多LLM支持

补充资源

官方文档

OpenAI资源

成本计算


总结

🎯 这一篇你学到了

技术知识

  • ChatModel的基本用法
  • Builder模式的优雅
  • chat()的同步阻塞特性

最佳实践

  • 如何安全地管理API Key
  • 如何扩展成Spring Service
  • 如何添加错误处理

下一步方向

  • Week 1继续学Prompt工程、Token计数等
  • Week 2学Chain和Memory(多步骤流程)
  • Week 3学RAG(知识库系统)

💡 关键认知

HelloWorld看似简单,但包含了LangChain4J的核心:

  1. 统一的API - 一套代码,支持多个LLM
  2. 易于扩展 - Builder模式,参数清晰
  3. Spring集成 - Bean管理,易测试
  4. 生产级质量 - 需要自己添加错误处理、重试等

字数统计 :约3500字 | 阅读时间:30分钟

下一步:→ [[2004-Prompt工程最佳实践|003-Prompt工程最佳实践]]

相关推荐
玹外之音2 小时前
Spring AI 11 种文档切割策略全解析
java·spring·ai编程
Lei活在当下12 小时前
10分钟搭建 Windows + WSL + Codex环境
chatgpt·openai·ai编程
XPoet14 小时前
AI 编程工程化:Rule——给你的 AI 员工立规矩
前端·后端·ai编程
浮华落定14 小时前
OpenClaw 搭建软件研发团队与工作流程 操作文档
ai编程
C蔡博士14 小时前
VS Code+Claude Code+Deepseek
ai编程·vs code·deepseek·claude code
CHQIUU15 小时前
OpenSpec 规范驱动开发:从零到上手的完整集成指南
ai编程
pczpcz815 小时前
一次多agent情况下openclaw不回消息问题的排查经过(使用飞书通信,持续更新中~~~)
ai编程
程序新视界15 小时前
专业人士是如何看待OpenClaw(龙虾)的?
ai编程
可观测性用观测云17 小时前
代码智能体如何重塑工程、产品与设计工作
ai编程