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
官方代码分析
完整源代码
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行:创建模型对象
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行:调用模型生成回答
String answer = model.chat("Hello world");
System.out.println(answer);
✨ 关键点:
| 要素 | 说明 |
|---|---|
| chat() | 调用LLM,传入文本问题 |
| 返回值 | String类型,LLM生成的答案 |
| 同步调用 | 会阻塞直到LLM返回结果(5-30秒) |
| println() | 打印输出到控制台 |
执行流程:
输入: "Hello world"
↓
LLM处理(5-30秒)
↓
输出: "Hello! How can I assist you today?"
↓
控制台打印结果
baseUrl的说明:
- 官方OpenAI:
https://api.openai.com/v1/ - 中国区代理:
https://dify.codewave-test.163yun.com/v1/(示例) - 本地服务:
http://localhost:8000/v1/(示例) - 其他供应商:根据实际地址配置
核心概念讲解
ChatModel 是什么?
一个接口,代表"能对话的语言模型",是LangChain4J的标准API:
输入 → [ChatModel] → 输出
"你好" ↓ "你好!很高兴认识你"
LLM引擎
主要特点:
- 输入:文本问题(String)
- 输出:文本答案(String)
- 支持多个实现:OpenAiChatModel、AnthropicChatModel等
- 支持自定义baseUrl(可使用代理或本地服务)
- 代码简洁,易于使用
为什么chat()方法会阻塞?
调用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封装
@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):
openai:
base-url: https://api.openai.com/v1/ # 支持自定义地址
api-key: ${OPENAI_API_KEY} # 从环境变量读取
使用方式(在其他Service中):
@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:支持多个模型
@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:错误处理和自动重试
@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; // 理论上不会到达这里
}
}
使用方式:
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返回结果才能继续。
解决办法:
- 如果只是演示:无所谓(等就等了)
- 如果是Web API:用CompletableFuture包装
- 如果是Spring项目:用@Async异步方法
- 如果是高并发:用WebClient + reactive编程
Q3:能改变返回格式吗?
A: chat()只返回String。如果需要结构化输出:
// 方法1:让LLM返回JSON
String prompt = "用JSON格式回答: {\"name\": \"...\", \"age\": ...}";
String jsonResponse = model.chat(prompt);
// 自己解析JSON
// 方法2:使用高级API(后续学)
// LangChain4J支持直接返回Java对象
Q4:API Key在哪里申请?
A:
- OpenAI:platform.openai.com/api-keys (需要绑定信用卡)
- Anthropic Claude:console.anthropic.com/keys (同样需要信用卡)
Q5:如何处理API Key失效?
A:
try {
String answer = model.chat(question);
} catch (ApiException e) {
if (e.statusCode() == 401) {
// API Key失效,需要更新
logger.error("API Key is invalid or expired");
// 告诉用户需要重新配置
}
}
思考问题
-
为什么使用Builder模式而不是普通构造函数?
- 思考Builder的优势
- 想象如果有20个可选参数会怎样
-
generate()方法为什么会阻塞?
- 追踪整个调用链:Java → HTTP → 网络 → OpenAI GPU → 结果回传
- 如果想异步怎么办?
-
在生产环境,如何安全地管理API Key?
- 环境变量?
- 密钥管理服务?
- 加密存储?
-
如果想支持多个LLM(OpenAI + Claude),代码怎么设计?
- 工厂模式?
- 策略模式?
- 配置文件?
实战练习
任务1:本地运行Example
# 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:
// 改1:问题改成中文
String answer = model.chat("你好,世界");
// 改2:模型改成gpt-4o(注意成本会增加)
.modelName("gpt-4o")
// 改3:观察返回速度和内容有什么区别
// gpt-4o 能力更强,但更贵且更慢
✅ 验收标准:
- 能理解参数对输出质量的影响
- 能理解参数对成本的影响
- 能理解参数对响应速度的影响
任务3:在Spring Boot项目中复现
在你的hello-world项目中创建ChatService:
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):
openai:
api-key: ${OPENAI_API_KEY}
测试代码:
@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的核心:
- 统一的API - 一套代码,支持多个LLM
- 易于扩展 - Builder模式,参数清晰
- Spring集成 - Bean管理,易测试
- 生产级质量 - 需要自己添加错误处理、重试等
字数统计:约3500字 | 阅读时间:30分钟
下一步:→ [[2004-Prompt工程最佳实践|003-Prompt工程最佳实践]]