Skip to main content

概述

AiServiceBuilder负责构建完整的AI Service,组装ChatMemory、SystemMessage、Tools、RAG等组件,最终返回一个LangChain4j的Agent实例用于执行AI推理。

设计模式

建造者模式(Builder Pattern)

AiServiceBuilder使用建造者模式来:
  1. 分步骤组装复杂的AI Service
  2. 灵活配置各个组件
  3. 统一构建接口
  4. 支持动态组件注入

核心组件

AiServiceBuilder

路径: langchat-core/src/main/java/cn/langchat/core/builder/AiServiceBuilder.java 职责: 构建完整的AI Service
@Slf4j
@Component
public class AiServiceBuilder {

    @Resource
    private LangChatProps langChatProps;

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Resource
    private List<DynamicToolFactory> toolFactoryList;

    @Resource
    private PromptProcessor promptProcessor;

    @Resource
    private RagRetrieverBuilder ragRetrieverBuilder;
}

核心方法

build()

/**
 * 构建AI Service
 *
 * @param model          流式聊天模型
 * @param req            聊天请求对象
 * @param conversationId 会话ID
 * @return LangChatAgent实例
 */
public LangChatAgent build(
    StreamingChatModel model, 
    LcChatReq req, 
    String conversationId
) {
    log.info("开始构建AiService: conversationId={}, modelId={}, userId={}",
            conversationId, req.getModelId(), req.getUserId());

    var builder = AiServices.builder(LangChatAgent.class)
        .streamingChatModel(model)
        .chatMemoryProvider(memoryId -> buildChatMemory(conversationId, req));

    // 1. 配置SystemMessage
    var prompt = promptProcessor.process(req);
    if (StrUtil.isNotBlank(prompt)) {
        builder.systemMessageProvider(memoryId -> prompt);
        log.debug("系统提示词已配置: conversationId={}, promptLength={}",
                conversationId, prompt.length());
    } else {
        log.debug("未配置系统提示词: conversationId={}", conversationId);
    }

    // 2. 动态构建Tools
    if (CollUtil.isNotEmpty(toolFactoryList)) {
        toolFactoryList.forEach(factory -> {
            try {
                factory.dynamicTools(req, builder);
            } catch (Exception e) {
                log.error("动态工具构建失败: conversationId={}, factoryClass={}",
                        conversationId, factory.getClass().getSimpleName(), e);
            }
        });
        log.debug("工具工厂配置完成: conversationId={}, factoryCount={}",
                conversationId, toolFactoryList.size());
    }

    // 3. 构建RAG
    var ragComponents = ragRetrieverBuilder.build(req);
    if (CollUtil.isNotEmpty(ragComponents.retrievers())) {
        var augmentor = DefaultRetrievalAugmentor.builder()
            .contentAggregator(ragComponents.aggregator())
            .queryRouter(new DefaultQueryRouter(ragComponents.retrievers()))
            .build();
        builder.retrievalAugmentor(augmentor);

        log.info("RAG配置完成: conversationId={}, retrieverCount={}, hasRerank={}",
                conversationId,
                ragComponents.retrievers().size(),
                ragComponents.aggregator() != null);
    } else {
        log.debug("未配置RAG: conversationId={}", conversationId);
    }

    // 4. 构建Agent
    var agent = builder.build();

    log.info("AiService构建完成: conversationId={}, modelId={}, userId={}, hasPrompt={}, toolFactoryCount={}, retrieverCount={}",
            conversationId,
            req.getModelId(),
            req.getUserId(),
            StrUtil.isNotBlank(prompt),
            toolFactoryList != null ? toolFactoryList.size() : 0,
            ragComponents.retrievers().size());

    return agent;
}

构建组件详解

1. ChatMemory(聊天记忆)

buildChatMemory()

/**
 * 构建ChatMemory
 */
private ChatMemory buildChatMemory(String conversationId, LcChatReq req) {
    var maxMessages = ObjectUtil.defaultIfNull(
            req.getHistoryCount(),
            langChatProps.getMemoryMaxMessage()
    );

    log.debug("构建ChatMemory: conversationId={}, maxMessages={}", 
            conversationId, maxMessages);

    return MessageWindowChatMemory.builder()
        .id(conversationId)
        .chatMemoryStore(new LcChatMemory(redisTemplate))
        .maxMessages(maxMessages)
        .build();
}
功能:
  • 存储历史对话消息
  • 基于Redis实现分布式存储
  • 消息窗口限制(maxMessages)
  • 自动过期清理
LcChatMemory:
  • 实现ChatMemoryStore接口
  • 使用Redis存储
  • 支持分布式部署

2. SystemMessage(系统提示词)

PromptProcessor

职责: 处理和生成系统提示词 构建流程:
var prompt = promptProcessor.process(req);

if (StrUtil.isNotBlank(prompt)) {
    builder.systemMessageProvider(memoryId -> prompt);
}
提示词来源:
  1. 应用级系统提示词(AigcApp配置)
  2. 模型级系统提示词(AigcModel配置)
  3. 用户自定义提示词
提示词内容可能包含:
  • 角色定义
  • 行为规则
  • 输出格式要求
  • 特定领域知识

3. Tools(动态工具)

DynamicToolFactory

接口路径: langchat-common/langchat-common-ai/src/main/java/cn/langchat/common/ai/component/DynamicToolFactory.java 接口定义:
public interface DynamicToolFactory {
    
    /**
     * 动态构建工具并添加到AiServices Builder
     *
     * @param req         聊天请求
     * @param aiServices  AiServices Builder
     */
    void dynamicTools(LcChatReq req, AiServices<LangChatAgent> aiServices);
}

工具构建流程

if (CollUtil.isNotEmpty(toolFactoryList)) {
    toolFactoryList.forEach(factory -> {
        try {
            factory.dynamicTools(req, builder);
        } catch (Exception e) {
            log.error("动态工具构建失败: conversationId={}, factoryClass={}",
                    conversationId, factory.getClass().getSimpleName(), e);
        }
    });
}
工具类型:
  1. Plugin工具 - 时间工具、搜索工具等
  2. MCP工具 - MCP协议提供的工具
  3. 自定义工具 - 开发者自定义的工具

实现示例

PluginToolFactory:
@Component
public class PluginToolFactory implements DynamicToolFactory {
    
    @Resource
    private AigcPluginService pluginService;
    
    @Override
    public void dynamicTools(LcChatReq req, AiServices<LangChatAgent> aiServices) {
        // 获取启用的插件
        var plugins = pluginService.getEnabledPlugins(req.getUserId(), req.getAppId());
        
        if (CollUtil.isEmpty(plugins)) {
            return;
        }
        
        // 构建工具列表
        var tools = plugins.stream()
            .map(this::convertToTool)
            .collect(Collectors.toList());
        
        // 添加到AiServices
        aiServices.tools(tools);
    }
    
    private Tool convertToTool(AigcPlugin plugin) {
        // 将插件转换为LangChain4j Tool
        return Tool.builder()
            .name(plugin.getName())
            .description(plugin.getDescription())
            .addParameter(/* 参数定义 */)
            .execute((toolExecutionRequest) -> {
                // 执行插件逻辑
                return executePlugin(plugin, toolExecutionRequest);
            })
            .build();
    }
}
McpToolFactory:
@Component
public class McpToolFactory implements DynamicToolFactory {
    
    @Resource
    private McpClientManager mcpClientManager;
    
    @Override
    public void dynamicTools(LcChatReq req, AiServices<LangChatAgent> aiServices) {
        // 获取启用的MCP服务器
        var mcpServers = mcpClientManager.getEnabledServers(req.getUserId());
        
        if (CollUtil.isEmpty(mcpServers)) {
            return;
        }
        
        // 获取所有MCP工具
        var tools = new ArrayList<Tool>();
        for (var server : mcpServers) {
            var serverTools = mcpClientManager.getTools(server.getId());
            tools.addAll(serverTools);
        }
        
        // 添加到AiServices
        aiServices.tools(tools);
    }
}

4. RAG(检索增强生成)

RagRetrieverBuilder

路径: langchat-core/src/main/java/cn/langchat/core/builder/RagRetrieverBuilder.java 职责: 构建RAG检索组件 构建流程:
var ragComponents = ragRetrieverBuilder.build(req);

if (CollUtil.isNotEmpty(ragComponents.retrievers())) {
    var augmentor = DefaultRetrievalAugmentor.builder()
        .contentAggregator(ragComponents.aggregator())
        .queryRouter(new DefaultQueryRouter(ragComponents.retrievers()))
        .build();
    builder.retrievalAugmentor(augmentor);
}
RAG组件组成:
  1. ContentRetriever - 内容检索器列表
    • KnowledgeRetriever - 知识库检索
    • SqlRetriever - SQL检索
  2. ContentAggregator - 内容聚合器
    • ReRankingContentAggregator - 重排序聚合器
  3. QueryRouter - 查询路由器
    • DefaultQueryRouter - 默认路由器
详细设计: 参见 06-RAG架构.md

完整构建流程

AiServiceBuilder.build()

  ├─→ streamingChatModel(model)
  │      # 设置流式聊天模型

  ├─→ chatMemoryProvider()
  │      # 构建ChatMemory
  │      └─→ buildChatMemory(conversationId, req)
  │           ├─→ 获取maxMessages
  │           └─→ MessageWindowChatMemory.builder()
  │                ├─→ id(conversationId)
  │                ├─→ chatMemoryStore(LcChatMemory)
  │                └─→ maxMessages(maxMessages)

  ├─→ systemMessageProvider()
  │      # 配置系统提示词
  │      └─→ PromptProcessor.process(req)
  │           ├─→ 获取应用级提示词
  │           ├─→ 获取模型级提示词
  │           └─→ 合并生成最终提示词

  ├─→ tools()
  │      # 动态构建工具
  │      └─→ toolFactoryList.forEach()
  │           ├─→ PluginToolFactory
  │           │    └─→ 获取启用的插件
  │           │         └─→ 转换为Tool对象
  │           ├─→ McpToolFactory
  │           │    └─→ 获取启用的MCP服务器
  │           │         └─→ 获取所有工具
  │           └─→ CustomToolFactory (自定义)

  ├─→ retrievalAugmentor()
  │      # 构建RAG组件
  │      └─→ RagRetrieverBuilder.build(req)
  │           ├─→ KnowledgeRetriever
  │           │    └─→ 知识库检索
  │           ├─→ SqlRetriever
  │           │    └─→ SQL检索
  │           ├─→ ContentAggregator
  │           │    └─→ ReRankingContentAggregator
  │           └─→ QueryRouter
  │                └─→ DefaultQueryRouter

  └─→ build()
       # 构建Agent
       └─→ LangChatAgent实例

LangChatAgent

路径: langchat-common/langchat-common-ai/src/main/java/cn/langchat/common/ai/agent/LangChatAgent.java 职责: AI Agent接口,定义AI推理方法 核心方法:
public interface LangChatAgent {
    
    /**
     * 流式聊天
     */
    TokenStream stream(String memoryId, String userMessage);
    
    /**
     * 聊天(非流式)
     */
    String chat(String memoryId, String userMessage);
}

配置说明

LangChatProps

@Component
@ConfigurationProperties(prefix = "langchat")
public class LangChatProps {
    
    /**
     * ChatMemory最大消息数量
     */
    private Integer memoryMaxMessage = 20;
    
    /**
     * 其他配置...
     */
}

application.yml

langchat:
  memory:
    max-message: 20  # ChatMemory最大消息数量

扩展点

1. 添加新的工具类型

步骤1: 实现DynamicToolFactory接口
@Component
public class CustomToolFactory implements DynamicToolFactory {
    
    @Resource
    private SomeService someService;
    
    @Override
    public void dynamicTools(LcChatReq req, AiServices<LangChatAgent> aiServices) {
        // 根据请求判断是否需要添加工具
        if (shouldAddTools(req)) {
            var tools = buildTools(req);
            aiServices.tools(tools);
        }
    }
    
    private List<Tool> buildTools(LcChatReq req) {
        return List.of(
            Tool.builder()
                .name("customTool")
                .description("自定义工具描述")
                .addParameter(/* 参数 */)
                .execute(this::executeTool)
                .build()
        );
    }
    
    private String executeTool(ToolExecutionRequest request) {
        // 执行工具逻辑
        return someService.execute(request);
    }
}
步骤2: 使用@Service@Component注解 Spring会自动将所有DynamicToolFactory注入到toolFactoryList

2. 自定义PromptProcessor

@Component
public class CustomPromptProcessor implements PromptProcessor {
    
    @Override
    public String process(LcChatReq req) {
        // 自定义提示词处理逻辑
        var prompt = new StringBuilder();
        
        // 添加角色定义
        prompt.append("你是一个专业的AI助手。\n");
        
        // 添加特定领域知识
        prompt.append("你精通以下领域:\n");
        prompt.append("- 编程\n");
        prompt.append("- 数据分析\n");
        
        // 添加行为规则
        prompt.append("请用简洁、准确的语言回答问题。\n");
        
        return prompt.toString();
    }
}

3. 自定义ChatMemory

public class CustomChatMemoryStore implements ChatMemoryStore {
    
    private final SomeStorage storage;
    
    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        // 自定义获取消息逻辑
        return storage.getMessages(memoryId.toString());
    }
    
    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        // 自定义更新消息逻辑
        storage.saveMessages(memoryId.toString(), messages);
    }
    
    @Override
    public void deleteMessages(Object memoryId) {
        // 自定义删除消息逻辑
        storage.deleteMessages(memoryId.toString());
    }
}
在AiServiceBuilder中使用:
private ChatMemory buildChatMemory(String conversationId, LcChatReq req) {
    return MessageWindowChatMemory.builder()
        .id(conversationId)
        .chatMemoryStore(new CustomChatMemoryStore(customStorage))
        .maxMessages(maxMessages)
        .build();
}

最佳实践

1. 工具开发

  1. 清晰的描述: 工具的description要清晰明了
  2. 参数定义: 使用add明确定义输入参数
  3. 错误处理: 在execute方法中妥善处理异常
  4. 性能优化: 避免阻塞操作,考虑异步处理

2. 提示词设计

  1. 简洁明了: 避免过长的提示词
  2. 明确角色: 清晰定义AI的角色
  3. 行为规范: 明确AI的行为规则
  4. 格式要求: 指定输出格式(JSON、Markdown等)

3. RAG配置

  1. 合理的检索数量: 避免检索过多文档
  2. 重排序: 启用Rerank提高准确性
  3. 分块策略: 选择合适的文档分块大小

4. ChatMemory管理

  1. 合理的消息数量: 根据场景调整maxMessages
  2. 过期策略: 设置合理的TTL
  3. 性能考虑: 避免过长的对话历史

性能优化

1. 懒加载

@Lazy
@Resource
private List<DynamicToolFactory> toolFactoryList;

2. 并行构建

// 并行执行多个工具工厂
toolFactoryList.parallelStream().forEach(factory -> {
    try {
        factory.dynamicTools(req, builder);
    } catch (Exception e) {
        log.error("动态工具构建失败", e);
    }
});

3. 缓存

  • PromptProcessor缓存提示词
  • ToolFactory缓存工具定义
  • ChatMemory使用Redis缓存

参考文档