1. 简介
上一章节我们讲了如何使用LangChain4J
的底层组件来进行AI的交互,如 ChatLanguageModel
、ChatMessage
、ChatMemory
等。 在这个层面上工作非常灵活/自由,但也迫使我们编写大量的样板代码。 由于 LLM 驱动的应用程序通常不仅需要单个组件,还需要多个组件协同工作 (例如,提示模板、聊天记忆、LLM、输出解析器、RAG 组件:嵌入模型和存储) 并且经常涉及多次交互,协调所有这些组件变得更加繁琐。
而我们使用框架的目的是希望专注于业务逻辑,而不是低级实现细节。 为此,LangChain4J
衍生出一个高级概念可以帮助更快的进行AI应用的开发:AI Services
。
2. 环境信息
本章的环境信息跟上一章完全一样,这里就不过多展示了,感兴趣的可以看上一章内容
3. 构建AI Service
3.1 申明AI Service 接口
package com.ldx.langchaintest.aisvc;
import com.ldx.langchaintest.model.PersonTest;
import dev.langchain4j.service.Result;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.TokenStream;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import reactor.core.publisher.Flux;
import java.util.List;
/**
* ai svc
*
* @author ludangxin
* @date 2025/6/5
*/
public
interface
AiAssistantServiceTest {
String
chat
(String message);
TokenStream
chatWithTokenStream
(String message);
Flux<String>
chatWithFlux
(String message);
@SystemMessage("""
👉 将文本改写成类似小红书的 Emoji 风格。
请使用 Emoji 风格编辑以下段落,该风格以引人入胜的标题、每个段落中包含表情符号和在末尾添加相关标签为特点。请确保保持原文的意思。
""")
String
chatWithSysPrompt
(String userMessage);
@UserMessage("请帮我判断一下这段表达式「{{expression}}」的返回值 如果成立则返回true 反之 false")
boolean
chatWithPrompt
(@V("expression") String expression);
@UserMessage("需要你帮我mock需要的人员信息")
PersonTest
chatWithPojo
();
@UserMessage("需要你帮我mock人员姓名, 帮我生成{{total}}个")
List<String>
chatWithList
(@V("total") Integer total);
Result<String>
chatWithMeta
(String question);
// can not support return list pojo
@UserMessage("需要你帮我mock需要的人员信息, 帮我生成{{total}}个")
List<PersonTest>
chatWithPojoList
(@V("total") Integer total);
}
关于聊天记忆的svc
3.2 创建AI Service 实例
使用AiServices对象创建上述我们自定义的svc,其底层是通过jdk的动态代理实现的,感兴趣的可以留言,后续可以加一章源码解析
private
static ChatModel chatModel;
private
static StreamingChatModel streamingChatModel;
private
static AiAssistantServiceTest assistant;
private
static AiAssistantServiceTest streamingAssistant;
@BeforeAll
public
static
void
init_chat_svc
() {
chatModel = OpenAiChatModel
.builder()
.apiKey(System.getenv(
"LLM_API_KEY"))
.modelName(
"qwen-plus")
.baseUrl(
"https://dashscope.aliyuncs.com/compatible-mode/v1")
.build();
streamingChatModel = OpenAiStreamingChatModel
.builder()
.apiKey(System.getenv(
"LLM_API_KEY"))
.modelName(
"qwen-plus")
.baseUrl(
"https://dashscope.aliyuncs.com/compatible-mode/v1")
.build();
// 创建普通对话的ai svc
assistant = AiServices.create(AiAssistantServiceTest.class, chatModel);
// 创建流式对话的ai svc
streamingAssistant = AiServices.create(AiAssistantServiceTest.class, streamingChatModel);
}
4. 返回字符串
测试结果如下
[main]
INFO
com
.ldx
.langchaintest
.aisvc
.AiChatWithSvcTest
--
call
ai
q:你是谁
a:我是通义千问,阿里巴巴集团旗下的通义实验室自主研发的超大规模语言模型。我能够回答问题、创作文字,比如写故事、写公文、写邮件、写剧本、逻辑推理、编程等等,还能表达观点,玩游戏等。如果你有任何问题或需要帮助,欢迎随时告诉我!
5. 返回流
5.1 TokenStream
测试结果如下:
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 我是
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 通
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 义
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 千问,阿里巴巴
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 集团旗下的通义
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 实验室自主研发的超
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 大规模语言模型。
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 我能够回答问题
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 、创作文字,
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 比如写故事、
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 写公文、
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 写邮件、写
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 剧本、逻辑推理
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 、编程等等,
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 还能表达观点,
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 玩游戏等。如果你
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 有任何问题或需要
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 帮助,欢迎随时
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response partial data: 告诉我!
[ForkJoinPool.commonPool-worker
-1] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- ai response complete.
5.2 Flux
测试结果如下:
6. 提示词/模板
测试结果如下:
7. 结构化输出
7.1 申明pojo
7.2 实现
暂不支持返回 List<自定义对象>,执行会报错
@Test
public
void
should_return_custom_obj_when_use_normal_model
() {
final
PersonTest
personTest
= assistant.chatWithPojo();
final List<String> names = assistant.chatWithList(
3);
final Result<String> resultMeta = assistant.chatWithMeta(
"你是谁");
// can not support return list pojo
//final List<PersonTest> personTests = assistant.chatWithPojoList(3);
log.info(
"call ai personTest:{}", personTest);
log.info(
"call ai mock names:{}", names);
String
content
= resultMeta.content();
// AI 服务调用期间使用的令牌总数
TokenUsage
tokenUsage
= resultMeta.tokenUsage();
// 在 RAG 检索期间检索到的 Content
final List<Content> sources = resultMeta.sources();
// 已执行的工具
List<ToolExecution> toolExecutions = resultMeta.toolExecutions();
// 完成标识
FinishReason
finishReason
= resultMeta.finishReason();
log.info(
"call ai returnContent:{}, useToken:{}, sources:{}, toolExecutions:{}, finishReason:{}", content, tokenUsage, sources, toolExecutions, finishReason);
}
测试结果如下:
8. 聊天记忆
测试结果如下:
9. 内容持久化&function call
测试结果如下:
[main] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- session
id
: zhangtieniu
-01, message
type
: USER, message
: UserMessage { name
=
null contents
= [TextContent { text
=
"张铁牛是一个高富帅,你是张铁牛的助手" }] }
[main] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- session
id
: zhangtieniu
-01, message
type
: AI, message
: AiMessage { text
=
"好的,我是张铁牛的助手。请问有什么我可以帮您的吗?如果您需要任何信息或者帮助处理某些事情,请告诉我具体的要求。比如,如果需要获取张铁牛相关的代码信息,我们可以利用已有的功能来查询。请明确您的需求!" toolExecutionRequests
= [] }
[main] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- session
id
: zhangtieniu
-01, message
type
: USER, message
: UserMessage { name
=
null contents
= [TextContent { text
=
"张铁牛是谁" }] }
[main] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- session
id
: zhangtieniu
-01, message
type
: AI, message
: AiMessage { text
=
"根据当前上下文,"张铁牛
"是一个我们假设的人物名字,他是您的代表人物。在这个场景中,我作为张铁牛的助手被设定来帮助您完成各种任务或者提供所需信息。如果您需要具体关于“张铁牛”的更多信息或者是与之相关的操作(例如获取其用户代码等),请告诉我更详细的信息或提出具体请求。
如果我们要基于实际应用情境进行互动,比如查询某个用户的特定代码,您可以要求我执行像通过用户名获取用户代码这样的功能。 若要这样做,请提供具体的用户名,我将为您查找对应的代码。
如果有其他任何问题或需求,也欢迎随时告知!" toolExecutionRequests
= [] }
[main] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
--
==================分割线
==================
[main] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- session
id
: zhangtieniu
-01, message
type
: USER, message
: UserMessage { name
=
null contents
= [TextContent { text
=
"张铁牛是谁" }] }
[main] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- session
id
: zhangtieniu
-01, message
type
: AI, message
: AiMessage { text
=
null toolExecutionRequests
= [ToolExecutionRequest {
id
=
"call_f936aff30d024ae1ae6793", name
=
"getUserCodeByUsername", arguments
=
"{"username
": "张铁牛
"}" }] }
[main] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- session
id
: zhangtieniu
-01, message
type
: TOOL_EXECUTION_RESULT, message
: ToolExecutionResultMessage {
id
=
"call_f936aff30d024ae1ae6793" toolName
=
"getUserCodeByUsername" text
=
"003" }
[main] INFO com.ldx.langchaintest.aisvc.AiChatWithSvcTest
-- session
id
: zhangtieniu
-01, message
type
: AI, message
: AiMessage { text
=
"用户张铁牛的代码是003。这是系统中唯一标识该用户的代码。如果您需要更多关于张铁牛的信息,您可以提供更具体的问题或者查询请求。" toolExecutionRequests
= [] }
10. 小结
本章使用了LangChain4J
高阶Api来请求大模型,展示了AI服务中常见的使用方法,下一章我们将使用LangChain4J
的RAG功能。
__EOF__

本文作者:
张铁牛
本文链接: https://www.cnblogs.com/ludangxin/p/18913362.html
关于博主:评论和私信会在第一时间回复。或者 直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角 【推荐】一下。您的鼓励是博主的最大动力!
本文链接: https://www.cnblogs.com/ludangxin/p/18913362.html
关于博主:评论和私信会在第一时间回复。或者 直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角 【推荐】一下。您的鼓励是博主的最大动力!