大模型应用的复杂度越高,就越需要拆解为细小模块,这与传统软件开发的设计思想是一致的。
例如,若试图在系统提示词中塞入海量指令来覆盖所有场景,不仅容易出错,效率也极低。指令过多时,大语言模型(LLMs)反而可能忽略部分关键内容;再加上指令的先后顺序同样会影响效果,这会让整体开发难度显著上升。
这一拆分原则同样适用于工具调用、检索增强生成(RAG)以及模型参数(如 temperature、maxTokens 等)的设计。
你的聊天机器人并不需要时刻调用全部可用工具。比如,当用户仅进行问候或告别时,让大语言模型访问数十甚至上百个工具,不仅成本高昂(每次工具调用都会消耗大量 Token),还可能带来风险 —— 模型可能出现幻觉,或被诱导以非预期的方式调用工具,产生不可控的结果。
对于检索增强生成(RAG)同理:在必要时为模型补充上下文是合理的,但不应无差别使用。因为上下文越长,Token 消耗越多,响应延迟也会越高,带来额外成本与性能损耗。
对于模型参数:当你需要输出稳定、可控时,会使用较低的 temperature;在需要创意、多样性时,则会调高 temperature。不同场景应使用不同配置。
核心结论是:更小、更专一的组件,更容易开发、测试、维护与理解,整体成本也更低。
在设计时,还需要思考两个极端方向:
你希望应用高度确定,由程序控制整体流程,大语言模型仅作为其中一个组件?
还是希望大语言模型拥有完全自主权,主导整个应用的逻辑?
或是根据场景,采用两者混合的模式?
当你把应用拆分成更小、更易管理的模块时,以上所有架构模式都能轻松实现。
AI 服务可以像传统确定性组件一样使用,并与常规代码无缝结合:
可以依次调用多个 AI 服务(即链式调用)。
可以使用由 LLM 输出结果驱动的 if/else 逻辑(AI 可返回布尔值)。
可以使用由 LLM 驱动的 switch 分支(AI 可返回枚举值)。
可以使用由 LLM 驱动的 for/while 循环(AI 可返回整数等数值类型)。
可以在单元测试中对 AI 服务进行 Mock 模拟(因为它是标准接口)。
可以对每个 AI 服务单独做集成测试。
可以分别评估每个 AI 服务,独立调优最优参数。
等等。
举一个简单的例子:我想为公司搭建一个聊天机器人。
如果用户只是打招呼,希望用预设话术回复,不依赖 LLM 生成。
如果用户提出问题,再让 LLM 结合企业内部知识库(即 RAG)进行回答。
我们可以把这个需求拆分成两个独立的 AI 服务,具体实现思路如下:
// 定义业务接口
interface GreetingExpert {
@UserMessage("以下文本是问候语吗?文本:{{it}}")
boolean isGreeting(String text);
}
interface ChatBot {
@SystemMessage("你是公司的礼貌聊天机器人。")
String reply(String userMessage);
}
// 创建 AI 服务
private final GreetingExpert greetingExpert;
private final ChatBot chatBot;
public MultipleAiServicesDemo() {
// 判断是否为问候语的模型
ChatModel greetingExpertModel = OpenAiChatModel.builder()
.baseUrl("https://api.xty.app/v1")
.apiKey(API_KEY)
.modelName("gpt-3.5-turbo")
.logRequests(true)
.logResponses(true)
.build();
greetingExpert = AiServices.builder(GreetingExpert.class)
.chatModel(greetingExpertModel)
.build();
// 创建聊天模型
ChatModel chatModel = OpenAiChatModel.builder()
.baseUrl("https://api.xty.app/v1")
.apiKey(API_KEY)
.modelName("gpt-4")
.logRequests(true)
.logResponses(true)
.build();
chatBot = AiServices.builder(ChatBot.class)
.chatModel(chatModel)
.contentRetriever(createContentRetriever())
.build();
}
// 核心处理逻辑
public String handle(String userMessage) {
// 判断是否为问候语
if (greetingExpert.isGreeting(userMessage)) {
return "来公司的的问候!我能怎样让你的一天更美好呢?";
} else {
// 不是问候语,采用大模型进行回复
return chatBot.reply(userMessage);
}
}注意,我们如何将更便宜的 gpt-3.5-turbo 用于识别文本是否是问候语的简单任务,而将更昂贵的带有内容检索器(RAG)的 GPT-4 用于更复杂的任务。
现在,我可以模拟 GreetingExpert 和 ChatBot,并独立测试 MultipleAiServicesDemo。此外,我还可以分别对 GreetingExpert 和 ChatBot 进行集成测试。我可以分别评估它们两者,为每个子任务找到最优参数,或者从长远来看,甚至可以为每个特定子任务微调一个小型的专用模型。
下面是上面实现的完整示例:
package com.hxstrive.langchain4j.aiServices;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiEmbeddingModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import java.util.List;
import static dev.langchain4j.data.document.loader.FileSystemDocumentLoader.loadDocuments;
public class MultipleAiServicesDemo {
// 推荐:将OPEN_API_KEY设置成环境变量, 避免硬编码或随着代码泄露
// 注意,设置完环境变量记得重启IDEA,不然可能环境变量不会生效
private static final String API_KEY = System.getenv("OPEN_API_KEY");
// 定义业务接口
interface GreetingExpert {
@UserMessage("以下文本是问候语吗?文本:{{it}}")
boolean isGreeting(String text);
}
interface ChatBot {
@SystemMessage("你是公司的礼貌聊天机器人。")
String reply(String userMessage);
}
// 创建服务实例
private final GreetingExpert greetingExpert;
private final ChatBot chatBot;
public MultipleAiServicesDemo() {
// 判断是否为问候语的模型
ChatModel greetingExpertModel = OpenAiChatModel.builder()
.baseUrl("https://api.xty.app/v1")
.apiKey(API_KEY)
.modelName("gpt-3.5-turbo")
.logRequests(true)
.logResponses(true)
.build();
greetingExpert = AiServices.builder(GreetingExpert.class)
.chatModel(greetingExpertModel)
.build();
// 创建聊天模型
ChatModel chatModel = OpenAiChatModel.builder()
.baseUrl("https://api.xty.app/v1")
.apiKey(API_KEY)
.modelName("gpt-4")
.logRequests(true)
.logResponses(true)
.build();
chatBot = AiServices.builder(ChatBot.class)
.chatModel(chatModel)
.contentRetriever(createContentRetriever())
.build();
}
/**
* 创建文档内容检索器(RAG的核心依赖)
* @return 可用于检索的ContentRetriever实例
*/
private ContentRetriever createContentRetriever() {
// 第一步:加载用于RAG的本地文档(支持多个txt文件)
List<Document> documents = loadDocuments("E:\\~work_demo\\langchain4j\\document");
// 创建空的内存向量库(存储文档文本片段及其向量表示,程序退出后数据丢失)
InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
// 创建嵌入模型
OpenAiEmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder()
.baseUrl("https://api.xty.app/v1")
.apiKey(API_KEY)
.modelName("text-embedding-3-small")
.build();
// 配置文档拆分器
// 按固定长度拆分:每个片段200个字符,重叠20个字符(保证上下文连贯)
DocumentSplitter documentSplitter = DocumentSplitters.recursive(200, 20);
// 将文档导入向量库(底层自动完成:文档拆分 → 文本向量化 → 存入向量库)
// 注:langchain4j 封装了所有细节,无需手动处理拆分/向量化
EmbeddingStoreIngestor.builder()
.embeddingModel(embeddingModel) // 设置嵌入模型
.embeddingStore(embeddingStore) // 设置向量库,这里使用内存向量库
.documentSplitter(documentSplitter) // 配置文档拆分器
.build().ingest(documents);
// 基于向量库创建检索器(用于后续提问时匹配相关文档片段)
return EmbeddingStoreContentRetriever.builder()
.embeddingModel(embeddingModel) // 嵌入模型,用于匹配文档片段,将用户问题向量化
.embeddingStore(embeddingStore) // 设置向量库,上面我们已经处理了的文档
.maxResults(3) // 只召回最相关的3个段落(可根据需求调整)
.minScore(0.7) // 相似度阈值 ≥ 0.7 才召回(0-1之间,越高越精准)
.build();
}
public String handle(String userMessage) {
if (greetingExpert.isGreeting(userMessage)) {
return "来公司的的问候!我能怎样让你的一天更美好呢?";
} else {
return chatBot.reply(userMessage);
}
}
public static void main(String[] args) {
MultipleAiServicesDemo milesOfSmiles = new MultipleAiServicesDemo();
// 发起问候
String greeting = milesOfSmiles.handle("Hello");
System.out.println(greeting);
// 聊天
String answer = milesOfSmiles.handle("能简单说说随心变色手机壳产品吗?");
System.out.println(answer);
}
}运行示例,输出日志如下:
# 对文档进行嵌入
16:59:20.138 [main] DEBUG dev.langchain4j.store.embedding.EmbeddingStoreIngestor -- Starting to ingest 2 documents
16:59:20.180 [main] DEBUG dev.langchain4j.store.embedding.EmbeddingStoreIngestor -- Documents were split into 40 text segments
16:59:20.180 [main] DEBUG dev.langchain4j.store.embedding.EmbeddingStoreIngestor -- Starting to embed 40 text segments
16:59:35.241 [main] DEBUG dev.langchain4j.store.embedding.EmbeddingStoreIngestor -- Finished embedding 40 text segments
16:59:35.241 [main] DEBUG dev.langchain4j.store.embedding.EmbeddingStoreIngestor -- Starting to store 40 text segments into the embedding store
16:59:35.243 [main] DEBUG dev.langchain4j.store.embedding.EmbeddingStoreIngestor -- Finished storing 40 text segments into the embedding store
# 判断是否为问候语
16:59:35.337 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP request:
- method: POST
- url: https://api.xty.app/v1/chat/completions
- headers: [Authorization: Beare...00], ...
- body: {
"model" : "gpt-3.5-turbo",
"messages" : [ {
"role" : "user",
"content" : "以下文本是问候语吗?文本:Hello\nYou must answer strictly in the following format: one of [true, false]"
} ],
"stream" : false
}
# 响应,返回true,是问候语
16:59:39.176 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP response:
- status code: 200
- headers: [:status: 200], [access-control-allow-headers: *], ...
- body: {
"id": "chatcmpl-HqwfH4IJkP4Sb9oaQzC0439maEtpI",
"object": "chat.completion",
"created": 1770800378,
"model": "gpt-3.5-turbo-0613",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "true" // 返回 true
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 39,
"completion_tokens": 1,
"total_tokens": 40
},
"system_fingerprint": "fp_b28b39ffa8"
}
# 直接返回问候语
来公司的的问候!我能怎样让你的一天更美好呢?
# 第二次提问
16:59:39.202 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP request:
- method: POST
- url: https://api.xty.app/v1/chat/completions
- headers: [Authorization: Beare...00], ...
- body: {
"model" : "gpt-3.5-turbo",
"messages" : [ {
"role" : "user",
"content" : "以下文本是问候语吗?文本:能简单说说随心变色手机壳产品吗?\nYou must answer strictly in the following format: one of [true, false]"
} ],
"stream" : false
}
# 第二次响应
16:59:42.250 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP response:
- status code: 200
- headers: [:status: 200], ...
- body: {
"id": "chatcmpl-JZsw2XSmkDuKyGlvOIRUh9KMAWJi7",
"object": "chat.completion",
"created": 1770800381,
"model": "gpt-3.5-turbo-0613",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "false" // 不是问候语
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 54,
"completion_tokens": 1,
"total_tokens": 55
},
"system_fingerprint": "fp_b28b39ffa8"
}
# 不是问候语,发起大模型问答,同时有部分 RAG 信息召回
16:59:43.764 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP request:
- method: POST
- url: https://api.xty.app/v1/chat/completions
- headers: [Authorization: Beare...00], [User-Agent: langchain4j-openai], [Content-Type: application/json]
- body: {
"model" : "gpt-4",
"messages" : [ {
"role" : "system",
"content" : "你是公司的礼貌聊天机器人。"
}, {
"role" : "user",
"content" : "能简单说说随心变色手机壳产品吗?\n\nAnswer using the following information:\n随心变色手机壳产品说明书\n一、产品简介\n\n随心变色手机壳产品说明书\n\n随心变色手机壳产品说明书 一、产品简介 本产品为随心变色手机壳,核心功能为根据用户心情自动切换颜色,无需手动操作,使用便捷。外壳采用高分子耐磨材料与食品级记忆橡胶精心打磨制成,质感细腻亲肤,防刮耐摔且韧性出众,无需充电即可实现智能变色功能,广泛适配苹果(iPhone 12-15系列)、小米(小米14系列、Redmi K70系列)、华为(Mate 60系列、Pura"
} ],
"stream" : false
}
# 回复
16:59:51.441 [main] INFO dev.langchain4j.http.client.log.LoggingHttpClient -- HTTP response:
- status code: 200
- headers: [:status: 200], ...
- body: {
"id": "chatcmpl-D80MSHq1A16DQx1opIFHsPAzAgBYF",
"object": "chat.completion",
"created": 1770800384,
"model": "gpt-4",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "随心变色手机壳是一款智能科技与高品质材质相结合的创新产品,其核心功能是能够根据用户心情自动切换颜色,无需手动操作,使用非常便捷。手机壳采用高分子耐磨材料和食品级记忆橡胶制成,触感细腻亲肤,同时具备防刮、耐摔和优异的韧性特点。另外,这款手机壳无需充电也能实现智能变色功能,兼顾便利性与环保性能。目前支持多种热门手机型号,包括苹果(iPhone 12-15系列)、小米(小米14系列、Redmi K70系列)和华为(Mate 60系列)等机型,非常适配广泛用户需求。",
"refusal": null,
"annotations": []
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 206,
"completion_tokens": 168,
"total_tokens": 374,
"prompt_tokens_details": {
"cached_tokens": 0,
"audio_tokens": 0
},
"completion_tokens_details": {
"reasoning_tokens": 0,
"audio_tokens": 0,
"accepted_prediction_tokens": 0,
"rejected_prediction_tokens": 0
}
},
"system_fingerprint": "fp_b54fe76834"
}
随心变色手机壳是一款智能科技与高品质材质相结合的创新产品,其核心功能是能够根据用户心情自动切换颜色,无需手动操作,使用非常便捷。手机壳采用高分子耐磨材料和食品级记忆橡胶制成,触感细腻亲肤,同时具备防刮、耐摔和优异的韧性特点。另外,这款手机壳无需充电也能实现智能变色功能,兼顾便利性与环保性能。目前支持多种热门手机型号,包括苹果(iPhone 12-15系列)、小米(小米14系列、Redmi K70系列)和华为(Mate 60系列)等机型,非常适配广泛用户需求。更多 LangChain4j 知识请阅读后续教程……