文章目录
-
-
- 前言
- 1. 整体架构概览
- 2. AiServices类结构分析
- 3. DefaultAiServices.build()方法详解
- 4. invoke方法:核心处理流程
- 5. 结构化输出实现原理
-
- 5.1 类型检测与解析流程
- 5.2 布尔值解析
- 5.3 枚举解析
- 5.4 POJO解析
- 5.5 JSON Schema支持(高级特性)
- 6. 工具集成实现原理
-
- 6.1 工具规范生成
- 6.2 工具调用循环
- 6.3 工具执行过程
- 7. 多模态处理实现原理
-
- 7.1 Content类型体系
- 7.2 消息构建过程
- 7.3 模型适配层
- 8. ChatMemoryService实现原理
-
- 8.1 核心实现
- 8.2 多用户隔离原理
- 9. AiServiceContext:配置中心
- 10. 设计模式总结
-
- 10.1 代理模式
- 10.2 Builder模式
- 10.3 策略模式
- 10.4 工厂模式
- 11. 常见问题排查
-
- 11.1 结构化输出解析失败
- 11.2 工具调用不生效
- 11.3 多模态图片识别失败
- 总结
-
前言
在上一篇博客中,我们介绍了AiServices的三大高级功能:结构化输出、工具集成和多模态处理。本篇将深入源码层面,剖析AiServices的内部实现机制。
适合人群:想要深入理解Langchain4j框架原理的开发者,或者在使用中遇到问题需要定位的开发者。
源码地址:https://github.com/langchain4j/langchain4j
1. 整体架构概览
AiServices的架构可以分为以下几个核心组件:
┌─────────────────────────────────────────────────────────────┐
│ AiServices<T> │
│ (抽象类,定义Builder模式和配置方法) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ DefaultAiServices<T> │
│ (默认实现类,核心逻辑所在) │
│ – build(): 创建动态代理 │
│ – InvocationHandler.invoke(): 处理所有方法调用 │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│AiServiceContext│ │ChatMemoryService│ │ ToolService │
│ (配置上下文) │ │ (记忆管理) │ │ (工具执行) │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ ChatModel │ │ ChatMemory │ │ToolSpecification│
│ (大模型) │ │ (聊天记忆) │ │ (工具规范) │
└──────────────┘ └──────────────┘ └──────────────┘
核心组件说明:
| AiServices<T> | 抽象类,定义Builder模式和配置方法 |
| DefaultAiServices<T> | 默认实现类,包含核心处理逻辑 |
| AiServiceContext | 配置中心,存储所有运行时配置 |
| ChatMemoryService | 聊天记忆管理,支持多用户隔离 |
| ToolService | 工具执行服务,处理工具调用循环 |
| ChatModel | 大模型抽象,负责与LLM通信 |
2. AiServices类结构分析
AiServices<T>是一个泛型抽象类,采用Builder模式进行配置:
public abstract class AiServices<T> {
protected final AiServiceContext context;
// 静态工厂方法 – 简化创建
public static <T> T create(Class<T> aiService, ChatModel chatModel) {
return builder(aiService).chatModel(chatModel).build();
}
// Builder入口
public static <T> AiServices<T> builder(Class<T> aiService) {
AiServiceContext context = new AiServiceContext(aiService);
return builder(context);
}
// 配置ChatMemory(单实例模式)
public AiServices<T> chatMemory(ChatMemory chatMemory) {
if (chatMemory != null) {
this.context.initChatMemories(chatMemory);
}
return this;
}
// 配置ChatMemoryProvider(多实例模式,支持多用户)
public AiServices<T> chatMemoryProvider(ChatMemoryProvider chatMemoryProvider) {
if (chatMemoryProvider != null) {
this.context.initChatMemories(chatMemoryProvider);
}
return this;
}
// 配置系统消息提供者
public AiServices<T> systemMessageProvider(Function<Object, String> provider) {
this.context.systemMessageProvider = provider;
return this;
}
// 注册工具
public AiServices<T> tools(Object... objects) {
// 将工具对象存储到context中
// 后续会通过ToolSpecifications生成工具规范
this.context.toolObjects.addAll(Arrays.asList(objects));
return this;
}
// 抽象方法,由DefaultAiServices实现
public abstract T build();
}
关键设计点:
Builder模式:允许链式调用,配置灵活
AiServices.builder(Assistant.class)
.chatModel(chatModel)
.chatMemory(memory)
.tools(new MechanismScore())
.build();
AiServiceContext:集中存储所有配置,便于在构建过程中传递
泛型设计:支持任意接口类型作为AI服务契约
3. DefaultAiServices.build()方法详解
build()方法是AiServices的核心,它通过Java动态代理创建接口的实现:
class DefaultAiServices<T> extends AiServices<T> {
public T build() {
// 1. 验证配置
this.validate();
// 2. 初始化工具服务
ToolService toolService = new ToolService(context.toolObjects);
context.toolService = toolService;
// 3. 生成工具规范
List<ToolSpecification> toolSpecifications =
toolService.toolSpecifications();
context.toolSpecifications = toolSpecifications;
// 4. 创建动态代理
Object proxyInstance = Proxy.newProxyInstance(
// 类加载器
context.aiServiceClass.getClassLoader(),
// 代理的接口
new Class[]{context.aiServiceClass},
// 调用处理器
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 核心处理逻辑
return handleMethodInvocation(method, args);
}
}
);
return (T) proxyInstance;
}
}
动态代理原理:
用户代码 框架内部
│ │
▼ │
assistant.chat("你好") │
│ │
▼ │
代理对象.chat("你好") │
│ │
▼ │
InvocationHandler.invoke() ◄─────────┘
│
▼
handleMethodInvocation()
│
▼
调用ChatModel,返回结果
- Proxy.newProxyInstance()创建一个实现了指定接口的代理对象
- 当调用代理对象的任何方法时,都会路由到InvocationHandler.invoke()
- 这就是为什么我们只需要定义接口,而不需要编写实现类
4. invoke方法:核心处理流程
invoke()方法是整个AiServices的心脏,处理所有的方法调用:
private Object handleMethodInvocation(Method method, Object[] args) {
// ========== 第一步:方法路由 ==========
// 处理Object类的方法(toString, hashCode, equals)
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// 处理ChatMemoryAccess接口方法
if (method.getDeclaringClass() == ChatMemoryAccess.class) {
return handleChatMemoryAccess(method, args);
}
// ========== 第二步:准备消息 ==========
// 1. 提取并处理注解参数
List<ChatMessage> messages = new ArrayList<>();
// 处理@SystemMessage注解
String systemMessage = extractSystemMessage(method, args);
if (systemMessage != null) {
messages.add(SystemMessage.from(systemMessage));
}
// 处理@MemoryId注解
Object memoryId = extractMemoryId(method, args);
// 处理@UserMessage注解(支持多模态内容)
List<Content> userContents = extractUserMessage(method, args);
// ========== 第三步:加载聊天记忆 ==========
// 从ChatMemoryService获取或创建ChatMemory
ChatMemory chatMemory = context.chatMemoryService
.getOrCreateChatMemory(memoryId);
// 加载历史消息
List<ChatMessage> memoryMessages = chatMemory.messages();
messages.addAll(memoryMessages);
// ========== 第四步:构建用户消息 ==========
// 将用户输入转换为UserMessage
UserMessage userMessage = UserMessage.from(userContents);
messages.add(userMessage);
// ========== 第五步:RAG增强(如果配置了) ==========
if (context.retrievalAugmentor != null) {
// 对用户查询进行增强
userMessage = context.retrievalAugmentor.augment(
userMessage, metadata
);
// 替换消息列表中的用户消息
messages.set(messages.size() – 1, userMessage);
}
// ========== 第六步:调用ChatModel ==========
ChatRequest request = ChatRequest.builder()
.messages(messages)
.toolSpecifications(context.toolSpecifications)
.build();
ChatResponse response = context.chatModel.chat(request);
AiMessage aiMessage = response.aiMessage();
// ========== 第七步:处理工具调用循环 ==========
if (context.toolService != null && aiMessage.hasToolExecutions()) {
// 进入工具调用循环
response = executeInferenceAndToolsLoop(
messages, aiMessage, chatMemory
);
aiMessage = response.aiMessage();
}
// ========== 第八步:保存消息到记忆 ==========
// 保存用户消息
chatMemory.add(userMessage);
// 保存AI响应
chatMemory.add(aiMessage);
// ========== 第九步:解析并返回结果 ==========
return parseResponse(method, aiMessage);
}
完整流程图:
用户调用 assistant.chat("你好")
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 1. 方法路由 │
│ 检查是否是Object方法或ChatMemoryAccess方法 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. 解析注解 │
│ @SystemMessage → SystemMessage │
│ @MemoryId → memoryId │
│ @UserMessage → UserMessage │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. 加载聊天记忆 │
│ ChatMemoryService.getOrCreateChatMemory(memoryId) │
│ 获取历史消息列表 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. 构建消息列表 │
│ [SystemMessage, 历史消息1, 历史消息2, …, UserMessage] │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 5. 调用ChatModel │
│ ChatModel.chat(ChatRequest) │
│ 返回 AiMessage │
└─────────────────────────────────────────────────────────────┘
│
▼
┌───────────────┐
│ 有工具调用? │
└───────────────┘
│ │
Yes No
│ │
▼ │
┌──────────────────┐ │
│ 6. 工具调用循环 │ │
│ 执行工具 → 再次 │ │
│ 调用ChatModel │ │
└──────────────────┘ │
│ │
└────┬────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 7. 保存消息到记忆 │
│ chatMemory.add(UserMessage) │
│ chatMemory.add(AiMessage) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 8. 解析并返回结果 │
│ ServiceOutputParser.parse(text, returnType) │
└─────────────────────────────────────────────────────────────┘
│
▼
返回结果给用户
5. 结构化输出实现原理
结构化输出的核心在于将AI的文本响应转换为Java类型。这个过程由ServiceOutputParser负责。
5.1 类型检测与解析流程
public class ServiceOutputParser {
public static <T> T parse(String text, Class<T> returnType) {
// 1. 基本类型处理
if (returnType == boolean.class || returnType == Boolean.class) {
return (T) parseBoolean(text);
}
if (returnType == int.class || returnType == Integer.class) {
return (T) parseInteger(text);
}
// … 其他基本类型
// 2. 枚举类型处理
if (returnType.isEnum()) {
return (T) parseEnum(text, returnType);
}
// 3. String类型
if (returnType == String.class) {
return (T) text;
}
// 4. POJO类型 – 使用JSON解析
return parsePojo(text, returnType);
}
}
5.2 布尔值解析
private static Boolean parseBoolean(String text) {
String normalized = text.toLowerCase().trim();
// 支持多种表达方式
if (normalized.contains("true") ||
normalized.contains("是") ||
normalized.contains("yes")) {
return true;
}
if (normalized.contains("false") ||
normalized.contains("否") ||
normalized.contains("no")) {
return false;
}
throw new OutputParsingException("Cannot parse boolean: " + text);
}
解析策略:
- 将文本转为小写并去除空格
- 检查是否包含关键词:true、是、yes
- 检查是否包含关键词:false、否、no
- 如果都无法匹配,抛出异常
5.3 枚举解析
private static <T extends Enum<T>> T parseEnum(String text, Class<T> enumClass) {
// 1. 尝试直接匹配枚举名称
for (T enumConstant : enumClass.getEnumConstants()) {
if (text.contains(enumConstant.name())) {
return enumConstant;
}
}
// 2. 尝试匹配@Description注解的描述
for (T enumConstant : enumClass.getEnumConstants()) {
Description description = enumConstant.getClass()
.getField(enumConstant.name())
.getAnnotation(Description.class);
if (description != null && text.contains(description.value())) {
return enumConstant;
}
}
throw new OutputParsingException("Cannot parse enum: " + text);
}
解析策略:
5.4 POJO解析
private static <T> T parsePojo(String text, Class<T> pojoClass) {
// 1. 从文本中提取JSON
String json = extractJson(text);
// 2. 使用Jackson反序列化
return objectMapper.readValue(json, pojoClass);
}
POJO解析流程:
AI响应: "根据您的需求,火车票信息如下:{\\"startAddress\\":\\"上海\\",\\"endAddress\\":\\"南京\\"}"
│
▼
┌───────────────────────┐
│ extractJson(text) │
│ 提取JSON部分 │
└───────────────────────┘
│
▼
"{\\"startAddress\\":\\"上海\\",\\"endAddress\\":\\"南京\\"}"
│
▼
┌───────────────────────┐
│ objectMapper.readValue │
│ Jackson反序列化 │
└───────────────────────┘
│
▼
TrainTicket(startAddress=上海, endAddress=南京)
5.5 JSON Schema支持(高级特性)
对于支持JSON Schema的模型(如OpenAI),AiServices可以自动生成Schema,让模型直接返回符合格式的JSON:
public class JsonSchemaGenerator {
public static String generateSchema(Class<?> clazz) {
// 分析POJO类的字段
// 生成JSON Schema定义
// {
// "type": "object",
// "properties": {
// "startAddress": {"type": "string"},
// "endAddress": {"type": "string"},
// "startTime": {"type": "string"}
// }
// }
}
}
JSON Schema的优势:
- 模型返回的JSON格式更规范
- 减少解析失败的概率
- 支持嵌套对象和数组
6. 工具集成实现原理
工具集成是AiServices最强大的功能之一,它让AI具备了执行实际操作的能力。
6.1 工具规范生成
public class ToolSpecifications {
public static List<ToolSpecification> toolSpecificationsFrom(Class<?>... classes) {
List<ToolSpecification> specs = new ArrayList<>();
for (Class<?> clazz : classes) {
// 扫描类中所有带@Tool注解的方法
for (Method method : clazz.getDeclaredMethods()) {
Tool toolAnnotation = method.getAnnotation(Tool.class);
if (toolAnnotation != null) {
// 创建工具规范
ToolSpecification spec = ToolSpecification.builder()
.name(method.getName())
.description(toolAnnotation.value())
.parameters(extractParameters(method))
.build();
specs.add(spec);
}
}
}
return specs;
}
// 提取参数规范
private static List<ToolParameterSpecification> extractParameters(Method method) {
List<ToolParameterSpecification> params = new ArrayList<>();
for (Parameter parameter : method.getParameters()) {
P pAnnotation = parameter.getAnnotation(P.class);
if (pAnnotation != null) {
params.add(ToolParameterSpecification.builder()
.name(parameter.getName())
.description(pAnnotation.value())
.type(parameter.getType())
.required(true)
.build());
}
}
return params;
}
}
工具规范结构:
{
"name": "compute",
"description": "计算机制运行评分",
"parameters": {
"type": "object",
"properties": {
"alpha": {
"type": "number",
"description": "这是alpha"
},
"a": {
"type": "integer",
"description": "这是A或者是a"
},
"b": {
"type": "integer",
"description": "这是B或者是b"
}
},
"required": ["alpha", "a", "b"]
}
}
6.2 工具调用循环
public ChatResponse executeInferenceAndToolsLoop(
List<ChatMessage> messages,
AiMessage aiMessage,
ChatMemory chatMemory) {
int maxIterations = 10; // 防止无限循环
for (int i = 0; i < maxIterations; i++) {
// 1. 检查是否有工具调用请求
if (!aiMessage.hasToolExecutions()) {
break;
}
// 2. 将AI消息(包含工具调用请求)添加到消息列表
messages.add(aiMessage);
chatMemory.add(aiMessage);
// 3. 执行所有工具调用
for (ToolExecution toolExecution : aiMessage.toolExecutions()) {
// 查找对应的工具对象
Object toolObject = findToolObject(toolExecution.name());
// 调用工具方法
Object result = executeTool(toolObject, toolExecution);
// 创建工具结果消息
ToolExecutionResultMessage resultMessage =
ToolExecutionResultMessage.from(toolExecution, result.toString());
messages.add(resultMessage);
chatMemory.add(resultMessage);
}
// 4. 再次调用ChatModel,让AI整合工具结果
ChatRequest request = ChatRequest.builder()
.messages(messages)
.toolSpecifications(toolSpecifications)
.build();
ChatResponse response = chatModel.chat(request);
aiMessage = response.aiMessage();
}
return response;
}
完整工具调用流程图:
用户输入: "alpha为0.3,A为30,B为20,机制评分为多少?"
│
▼
┌─────────────────────────────────────────────────────────────┐
│ AiServices.invoke() │
│ 1. 准备消息 │
│ 2. 调用ChatModel │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ChatModel.chat() │
│ AI分析用户输入,判断需要调用工具 │
│ 返回: AiMessage(toolExecutions=[ │
│ ToolExecution(name="compute", args={alpha=0.3, a=30, b=20})│
│ ]) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ToolService.executeInferenceAndToolsLoop() │
│ 1. 检测到aiMessage.hasToolExecutions() == true │
│ 2. 执行工具: MechanismScore.compute(0.3, 30, 20) │
│ 3. 获取结果: 15.0 │
│ 4. 创建ToolExecutionResultMessage │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ChatModel.chat() │
│ AI整合工具结果,生成最终回答 │
│ 返回: AiMessage(text="机制评分为15.0") │
└─────────────────────────────────────────────────────────────┘
│
▼
返回结果: "机制评分为15.0"
6.3 工具执行过程
private Object executeTool(Object toolObject, ToolExecution toolExecution) {
try {
// 1. 查找方法
Method method = toolObject.getClass()
.getDeclaredMethod(toolExecution.name(),
extractParameterTypes(toolExecution));
// 2. 解析参数
Object[] args = parseArguments(toolExecution.arguments());
// 3. 调用方法
return method.invoke(toolObject, args);
} catch (Exception e) {
throw new ToolExecutionException("Tool execution failed", e);
}
}
参数解析:
// AI返回的参数
// {"alpha": 0.3, "a": 30, "b": 20}
// 解析过程
Object[] args = parseArguments(toolExecution.arguments());
// args = [0.3, 30, 20]
// 调用方法
method.invoke(toolObject, args);
// 等同于: mechanismScore.compute(0.3, 30, 20)
7. 多模态处理实现原理
多模态处理的核心是将非文本内容(图片、音频等)转换为LLM可理解的格式。
7.1 Content类型体系
// 内容类型层次结构
public abstract class Content {
// 基类
}
public class TextContent extends Content {
private final String text;
public static TextContent from(String text) {
return new TextContent(text);
}
}
public class ImageContent extends Content {
private final Image image;
// 从URL创建
public static ImageContent from(String url) {
return new ImageContent(Image.builder().url(url).build());
}
// 从Base64创建
public static ImageContent from(String base64, String mimeType) {
return new ImageContent(
Image.builder().base64Data(base64).mimeType(mimeType).build()
);
}
// 从Image对象创建
public static ImageContent from(Image image) {
return new ImageContent(image);
}
}
Content类型层次:
Content (抽象基类)
│
├── TextContent (文本内容)
│
├── ImageContent (图片内容)
│
├── AudioContent (音频内容,如果模型支持)
│
└── VideoContent (视频内容,如果模型支持)
7.2 消息构建过程
// 当调用 assistant.chatMultiModality("描述图片", imageContent) 时
// 1. 提取@UserMessage注解的参数
List<Content> contents = new ArrayList<>();
contents.add(TextContent.from("描述图片"));
contents.add(imageContent);
// 2. 创建UserMessage
UserMessage userMessage = UserMessage.from(contents);
// 3. 转换为模型特定格式
消息格式转换:
OpenAI API格式:
{
"role": "user",
"content": [
{"type": "text", "text": "描述图片"},
{"type": "image_url", "image_url": {"url": "data:image/png;base64,…"}}
]
}
Ollama API格式:
{
"role": "user",
"content": "描述图片",
"images": ["base64数据"]
}
7.3 模型适配层
public class OllamaChatModel implements ChatModel {
@Override
public ChatResponse chat(ChatRequest request) {
// 转换消息格式
List<Map<String, Object>> messages = new ArrayList<>();
for (ChatMessage message : request.messages()) {
if (message instanceof UserMessage) {
UserMessage userMessage = (UserMessage) message;
Map<String, Object> msg = new HashMap<>();
msg.put("role", "user");
// 检查是否包含图片
List<ImageContent> images = extractImages(userMessage);
if (!images.isEmpty()) {
// 提取图片Base64数据
List<String> imageBase64List = images.stream()
.map(img -> img.image().base64Data())
.collect(Collectors.toList());
msg.put("images", imageBase64List);
}
// 提取文本内容
String text = extractText(userMessage);
msg.put("content", text);
messages.add(msg);
}
}
// 调用Ollama API
// …
}
}
8. ChatMemoryService实现原理
ChatMemoryService负责管理多个用户的聊天记忆,实现用户隔离。
8.1 核心实现
public class ChatMemoryService {
public static final String DEFAULT = "default";
// 单实例模式的ChatMemory
private ChatMemory defaultChatMemory;
// 多实例模式的ChatMemory Map(key为memoryId)
private Map<Object, ChatMemory> chatMemories;
// ChatMemory工厂
private ChatMemoryProvider chatMemoryProvider;
// 单实例模式构造
public ChatMemoryService(ChatMemory chatMemory) {
this.defaultChatMemory = chatMemory;
}
// 多实例模式构造
public ChatMemoryService(ChatMemoryProvider chatMemoryProvider) {
this.chatMemories = new ConcurrentHashMap<>();
this.chatMemoryProvider = chatMemoryProvider;
}
// 获取或创建ChatMemory
public ChatMemory getOrCreateChatMemory(Object memoryId) {
// 如果是默认ID,返回单实例
if (memoryId == null || memoryId.equals(DEFAULT)) {
if (defaultChatMemory == null) {
defaultChatMemory = chatMemoryProvider.get(DEFAULT);
}
return defaultChatMemory;
}
// 否则,使用computeIfAbsent实现懒加载
return chatMemories.computeIfAbsent(memoryId,
id -> chatMemoryProvider.get(id));
}
}
8.2 多用户隔离原理
用户A (memoryId="userA") 用户B (memoryId="userB")
│ │
▼ ▼
┌───────────────────┐ ┌───────────────────┐
│ ChatMemory A │ │ ChatMemory B │
│ – 消息1 │ │ – 消息1 │
│ – 消息2 │ │ – 消息2 │
│ – … │ │ – … │
└───────────────────┘ └───────────────────┘
│ │
└─────────────┬───────────────────┘
▼
┌───────────────────┐
│ ChatMemoryService │
│ chatMemories = { │
│ "userA": A, │
│ "userB": B │
│ } │
└───────────────────┘
关键设计:
- 使用ConcurrentHashMap保证线程安全
- 使用computeIfAbsent实现懒加载,避免不必要的内存占用
- 每个用户的ChatMemory完全隔离,互不影响
9. AiServiceContext:配置中心
AiServiceContext是AiServices的配置中心,存储了所有运行时需要的信息:
public class AiServiceContext {
// AI服务接口类
public final Class<?> aiServiceClass;
// ChatModel(大模型)
public ChatModel chatModel;
// ChatMemoryService(记忆管理)
public ChatMemoryService chatMemoryService;
// 系统消息提供者
public Function<Object, String> systemMessageProvider;
// 工具对象列表
public List<Object> toolObjects = new ArrayList<>();
// 工具规范列表
public List<ToolSpecification> toolSpecifications;
// 工具服务
public ToolService toolService;
// RAG增强器
public RetrievalAugmentor retrievalAugmentor;
// 内容检索器
public ContentRetriever contentRetriever;
// 初始化ChatMemory(单实例模式)
public void initChatMemories(ChatMemory chatMemory) {
this.chatMemoryService = new ChatMemoryService(chatMemory);
}
// 初始化ChatMemory(多实例模式)
public void initChatMemories(ChatMemoryProvider provider) {
this.chatMemoryService = new ChatMemoryService(provider);
}
}
配置项说明:
| aiServiceClass | Class<?> | AI服务接口类 |
| chatModel | ChatModel | 大模型实例 |
| chatMemoryService | ChatMemoryService | 聊天记忆服务 |
| systemMessageProvider | Function | 系统消息提供者 |
| toolObjects | List | 工具对象列表 |
| toolSpecifications | List | 工具规范列表 |
| toolService | ToolService | 工具执行服务 |
| retrievalAugmentor | RetrievalAugmentor | RAG增强器 |
10. 设计模式总结
通过以上源码分析,我们可以理解AiServices的核心设计思想:
10.1 代理模式
// 我们定义接口
public interface Assistant {
String chat(String message);
}
// 框架通过动态代理生成实现
Assistant assistant = AiServices.create(Assistant.class, chatModel);
// 调用时,实际执行的是InvocationHandler.invoke()
assistant.chat("你好");
10.2 Builder模式
AiServices.builder(Assistant.class)
.chatModel(chatModel) // 配置大模型
.chatMemory(memory) // 配置聊天记忆
.systemMessageProvider(...) // 配置系统消息
.tools(new Tool()) // 注册工具
.build(); // 构建实例
10.3 策略模式
// 不同的输出类型使用不同的解析策略
if (returnType == boolean.class) {
return parseBoolean(text);
} else if (returnType.isEnum()) {
return parseEnum(text, returnType);
} else {
return parsePojo(text, returnType);
}
10.4 工厂模式
// ChatMemoryProvider作为工厂
ChatMemoryProvider provider = memoryId ->
MessageWindowChatMemory.builder()
.maxMessages(10)
.build();
// 根据memoryId创建不同的ChatMemory
ChatMemory memory = provider.get("userA");
11. 常见问题排查
11.1 结构化输出解析失败
问题:返回boolean时抛出OutputParsingException
原因:AI响应中包含了<think>标签(如DeepSeek模型)
解决方案:
- 使用Qwen等不包含<think>标签的模型
- 或者返回String类型,手动解析
11.2 工具调用不生效
问题:AI没有调用工具,而是直接回答
原因:
- @Tool或@P注解缺失
- 工具描述不够清晰
- 用户问题不够明确
解决方案:
- 检查注解是否正确
- 提供更清晰的工具描述
- 明确用户问题中需要调用工具的意图
11.3 多模态图片识别失败
问题:AI无法识别图片内容
原因:
- 使用了不支持多模态的模型
- 图片格式不支持
- Base64编码错误
解决方案:
- 使用Qwen-VL、LLaVA等支持多模态的模型
- 确保图片格式为PNG或JPEG
- 检查Base64编码是否正确
总结
通过本篇博客,我们深入分析了AiServices的内部实现机制:
理解这些原理,将帮助你更好地使用AiServices,以及在遇到问题时能够快速定位和解决。
相关博客:
- AiServices初次体验
- AiServices高级功能实战
源码地址:https://github.com/langchain4j/langchain4j



