欢迎光临
我们一直在努力

【LangChain4j】5-AIServices实现原理

文章目录

      • 前言
      • 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);
    }

    解析策略:

  • 首先尝试匹配枚举名称(如MAMMAL)
  • 然后尝试匹配@Description注解的描述
  • 这就是为什么@Description注解很重要
  • 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的内部实现机制:

  • 动态代理:通过Java动态代理实现接口,无需编写实现类
  • 消息处理:完整的9步处理流程,从注解解析到结果返回
  • 结构化输出:ServiceOutputParser负责类型转换
  • 工具集成:ToolService处理工具调用循环
  • 多模态处理:Content类型体系封装非文本内容
  • 记忆管理:ChatMemoryService实现多用户隔离
  • 理解这些原理,将帮助你更好地使用AiServices,以及在遇到问题时能够快速定位和解决。


    相关博客:

    • AiServices初次体验
    • AiServices高级功能实战

    源码地址:https://github.com/langchain4j/langchain4j

    赞(0)
    未经允许不得转载:171主机测评 » 【LangChain4j】5-AIServices实现原理
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址