建造者模式进阶:复杂AI服务的优雅构建

建造者模式进阶:复杂 AI 服务的优雅构建

AI 服务配置的多样性

在我们的系统中,一个完整的 AI 服务需要配置众多组件:

基础模型配置:

  • 聊天模型(ChatModel)
  • 流式聊天模型(StreamingChatModel)
  • 模型参数(API Key、Base URL、温度等)

功能组件配置:

  • 对话记忆(ChatMemory)
  • 工具集合(Tools)
  • 安全护轨(Guardrails)
  • 异常处理策略(Error Handling)

业务逻辑配置:

  • 记忆提供者(Memory Provider)
  • 工具执行策略(Tool Execution Strategy)
  • 输入输出处理器(Processors)

如果用传统方式构建这样复杂的对象,代码会变成这样:

// 传统方式 - 混乱且难以维护
public AiCodeGeneratorService createService(long appId, CodeGenTypeEnum type) {
    AiCodeGeneratorService service = new AiCodeGeneratorService();
    service.setChatModel(getChatModel(type));
    service.setStreamingChatModel(getStreamingChatModel(type));
    service.setChatMemory(buildChatMemory(appId));
    service.setTools(getTools(type));
    service.setInputGuardrails(getInputGuardrails());
    service.setOutputGuardrails(getOutputGuardrails(type));
    service.setErrorHandlingStrategy(getErrorStrategy());
    // ... 还有更多配置
    return service;
}

这种方式的问题显而易见:

  1. 配置分散:配置逻辑散落在各个方法中
  2. 顺序依赖:某些配置可能依赖其他配置的结果
  3. 可读性差:无法直观看出对象的完整配置
  4. 扩展困难:新增配置项需要修改多处代码

建造者模式的优雅解决方案

LangChain4j 的 AiServices Builder

LangChain4j 框架提供了一个优雅的建造者模式实现:

// 来源:src/main/java/com/ustinian/cheeseaicode/ai/AiCodeGeneratorServiceFactory.java (第125-134行)
yield AiServices.builder(AiCodeGeneratorService.class)
        .streamingChatModel(reasoningStreamingChatModel)
        .chatMemoryProvider(memoryId -> chatMemory)
        .tools(toolManager.getAllTools())
        .hallucinatedToolNameStrategy(toolExecutionRequest -> ToolExecutionResultMessage.from(
                toolExecutionRequest, "Error: there is no tool called " + toolExecutionRequest.name()
        ))
        .inputGuardrails(List.of(new PromptSafetyInputGuardrail()))
        .build();

建造者模式的优势一目了然:

  1. 链式调用:方法链式调用,代码流畅易读
  2. 配置集中:所有配置在一个地方完成
  3. 类型安全:编译时就能发现配置错误
  4. 可选配置:支持可选参数,有合理的默认值
  5. 不可变对象:构建完成后对象不可修改,保证线程安全

不同业务场景的差异化构建

在我们的项目中,不同的代码生成类型需要不同的 AI 服务配置:

// 来源:src/main/java/com/ustinian/cheeseaicode/ai/AiCodeGeneratorServiceFactory.java (第121-148行)
return switch (codeGenType) {
    case VUE_PROJECT -> {
        // 使用多例模式的 StreamingChatModel 解决并发问题
        StreamingChatModel reasoningStreamingChatModel = applicationContext.getBean("reasoningStreamingChatModelPrototype", StreamingChatModel.class);
        yield AiServices.builder(AiCodeGeneratorService.class)
                .streamingChatModel(reasoningStreamingChatModel)
                .chatMemoryProvider(memoryId -> chatMemory)
                .tools(toolManager.getAllTools())
                .hallucinatedToolNameStrategy(toolExecutionRequest -> ToolExecutionResultMessage.from(
                        toolExecutionRequest, "Error: there is no tool called " + toolExecutionRequest.name()
                ))
                .inputGuardrails(List.of(new PromptSafetyInputGuardrail()))
                .build();
    }
    case HTML, MULTI_FILE -> {
        // 使用多例模式的 ChatModel 和 StreamingChatModel 解决并发问题
        ChatModel chatModel = applicationContext.getBean("routingChatModelPrototype", ChatModel.class);
        StreamingChatModel openAiStreamingChatModel = applicationContext.getBean("streamingChatModelPrototype", StreamingChatModel.class);
        yield AiServices.builder(AiCodeGeneratorService.class)
                .chatModel(chatModel)
                .streamingChatModel(openAiStreamingChatModel)
                .chatMemory(chatMemory)
                .inputGuardrails(new PromptSafetyInputGuardrail())
                .build();
    }
    default -> throw new BusinessException(ErrorCode.SYSTEM_ERROR,
            "不支持的代码生成类型: " + codeGenType.getValue());
};

差异化配置的精妙设计:

  1. VUE_PROJECT 配置

    • 使用推理模型(reasoningStreamingChatModel)
    • 集成完整工具链(getAllTools)
    • 配置工具幻觉处理策略
    • 添加输入安全护轨
  2. HTML/MULTI_FILE 配置

    • 同时配置聊天模型和流式模型
    • 使用简化的记忆管理
    • 只添加基础的输入护轨

这种差异化配置体现了建造者模式的灵活性,同一个构建器可以根据不同参数构建出功能各异的对象。

记忆管理的建造者实现

MessageWindowChatMemory 的构建

AI 服务的记忆管理也是一个复杂的配置过程:

// 来源:src/main/java/com/ustinian/cheeseaicode/ai/AiCodeGeneratorServiceFactory.java (第112-117行)
MessageWindowChatMemory chatMemory = MessageWindowChatMemory
        .builder()
        .id(appId)
        .chatMemoryStore(redisChatMemoryStore)
        .maxMessages(100)
        .build();

记忆构建的配置要点:

  1. 唯一标识:每个应用有独立的记忆 ID
  2. 持久化存储:使用 Redis 作为记忆存储后端
  3. 容量限制:最多保留 100 条历史消息
  4. 滑动窗口:自动清理超出限制的旧消息

RedisChatMemoryStore 的建造者配置

连 Redis 存储的配置也使用了建造者模式:

// 来源:src/main/java/com/ustinian/cheeseaicode/config/RedisChatMemoryStoreConfig.java (第28-36行)
@Bean
public RedisChatMemoryStore redisChatMemoryStore() {
    RedisChatMemoryStore.Builder builder = RedisChatMemoryStore.builder()
            .host(host)
            .port(port)
            .password(password)
            .ttl(ttl);
    if (StrUtil.isNotBlank(password)) {
        builder.user("default");
    }
    return builder.build();
}

Redis 存储配置的细节处理:

  1. 条件配置:只有在密码非空时才设置用户名
  2. TTL 管理:设置记忆数据的过期时间
  3. 连接参数:从配置文件读取连接信息
  4. 容错处理:支持无密码的 Redis 实例

路由服务的简化构建

对于相对简单的路由服务,建造者模式同样展现了其简洁性:

// 来源:src/main/java/com/ustinian/cheeseaicode/ai/AiCodeGenTypeRoutingServiceFactory.java (第32-37行)
public AiCodeGenTypeRoutingService createAiCodeGenTypeRoutingService(){
    // 使用多例Bean避免依赖自动配置
    ChatModel routingChatModel = applicationContext.getBean("routingChatModelPrototype", ChatModel.class);
    return AiServices.builder(AiCodeGenTypeRoutingService.class)
            .chatModel(routingChatModel)
            .build();
}

简化构建的设计思路:

  1. 最小配置:只配置必需的聊天模型
  2. 默认行为:其他配置使用框架默认值
  3. 专用目标:专门用于代码生成类型路由判断
  4. 轻量实现:避免不必要的功能组件

模型配置的建造者应用

OpenAI 模型的统一构建

在模型配置层面,建造者模式也发挥了重要作用:

// 来源:src/main/java/com/ustinian/cheeseaicode/config/RoutingAiModelConfig.java (第39-47行)
@Bean
@Scope("prototype")
public ChatModel routingChatModelPrototype() {
    return OpenAiChatModel.builder()
            .apiKey(apiKey)
            .modelName(modelName)
            .baseUrl(baseUrl)
            .maxTokens(maxTokens)
            .temperature(temperature)
            .logRequests(logRequests)
            .logResponses(logResponses)
            .build();
}
// 来源:src/main/java/com/ustinian/cheeseaicode/config/StreamingChatModelConfig.java (第32-41行)
@Bean
@Scope("prototype")
public StreamingChatModel streamingChatModelPrototype() {
    return OpenAiStreamingChatModel.builder()
            .apiKey(apiKey)
            .baseUrl(baseUrl)
            .modelName(modelName)
            .maxTokens(maxTokens)
            .temperature(temperature)
            .logRequests(logRequests)
            .logResponses(logResponses)
            .build();
}

模型构建的统一性:

  1. 配置一致性:所有模型使用相同的配置参数结构
  2. 类型区分:ChatModel 和 StreamingChatModel 的差异化构建
  3. 参数化配置:通过 Spring 配置文件管理所有参数
  4. 调试支持:可配置的请求和响应日志记录

建造者模式的技术优势

可读性与可维护性

对比传统构造方法:

// 传统方式 - 参数过多且顺序固定
AiService service = new AiService(
    chatModel, 
    streamingModel, 
    memory, 
    tools, 
    guardrails, 
    strategy, 
    true, 
    false, 
    100
); // 这些布尔值和数字代表什么?

// 建造者方式 - 自文档化且灵活
AiService service = AiServices.builder(AiService.class)
    .chatModel(chatModel)
    .streamingChatModel(streamingModel)
    .chatMemory(memory)
    .tools(tools)
    .inputGuardrails(guardrails)
    .errorHandlingStrategy(strategy)
    .logRequests(true)
    .logResponses(false)
    .maxRetries(100)
    .build();

可读性提升显著:

  1. 参数语义化:每个配置都有明确的方法名
  2. 可选参数:不需要的配置可以省略
  3. 顺序无关:配置顺序可以任意调整
  4. IDE 友好:代码提示和自动补全支持更好

扩展性与灵活性

新增配置项的便利性:

// 只需要在 Builder 中添加新方法,不影响现有代码
AiServices.builder(AiCodeGeneratorService.class)
    .streamingChatModel(model)
    .chatMemory(memory)
    .tools(tools)
    .inputGuardrails(guardrails)
    // 新增配置项
    .customProcessor(processor)
    .retryPolicy(policy)
    .timeoutSettings(timeout)
    .build();

类型安全与编译检查

建造者模式提供了强类型检查:

// 编译时就能发现类型错误
AiServices.builder(AiCodeGeneratorService.class)
    .streamingChatModel(chatModel)  // 如果类型不匹配,编译失败
    .maxTokens("invalid")           // 如果类型不匹配,编译失败
    .build();

类型安全的优势:

  1. 编译时检查:类型错误在编译阶段就被发现
  2. 重构友好:IDE 可以安全地进行重构操作
  3. 文档价值:类型本身就是最好的文档

关键技术收获

  1. 复杂度管理:学会了如何优雅地处理复杂对象的构建
  2. API 设计:理解了好的 API 应该是自文档化和类型安全的
  3. 可扩展性思维:考虑了如何设计易于扩展的架构
  4. 用户体验:从开发者体验的角度思考 API 设计

设计模式的实际价值

  1. 不是为了模式而模式:建造者模式解决了实际的复杂性问题
  2. 框架设计的智慧:LangChain4j 的 API 设计值得学习和借鉴
  3. 渐进式应用:可以从简单场景开始,逐步应用到复杂场景
  4. 团队效率提升:好的设计模式能显著提升团队协作效率
posted @ 2025-09-09 16:55  你小志蒸不戳  阅读(14)  评论(0)    收藏  举报