AI智能路由:策略模式在代码生成中的创新应用
AI智能路由:策略模式在代码生成中的创新应用
设计背景:多 AI 模型服务的统一管理
在我们的系统中,用户的需求千差万别:
- 有人想要一个简单的展示页面
- 有人需要多页面的静态网站
- 还有人要求复杂的 Vue 项目
面对这样的需求多样性,我们需要:
- 智能识别需求:根据用户描述自动判断应该使用哪种代码生成方式
- 动态选择模型:不同的代码生成类型使用不同的 AI 模型配置
- 资源高效利用:避免重复创建服务实例,提高系统性能
- 易于扩展:未来添加新的代码生成类型时要简单方便
核心枚举:代码生成类型定义
首先,我定义了系统支持的代码生成类型:
// 来源:src/main/java/com/ustinian/cheeseaicode/model/enums/CodeGenTypeEnum.java
@Getter
public enum CodeGenTypeEnum {
HTML("原生 HTML 模式", "html"),
MULTI_FILE("原生多文件模式", "multi_file"),
VUE_PROJECT("Vue 工程模式", "vue_project");
private final String text;
private final String value;
CodeGenTypeEnum(String text, String value) {
this.text = text;
this.value = value;
}
}
这个枚举简洁明了,每种类型都有明确的语义:
- HTML:适合简单的单页面展示
- MULTI_FILE:适合多文件但不复杂的静态网站
- VUE_PROJECT:适合需要复杂交互的现代化前端项目
智能路由:AI 自动选择策略
路由服务接口设计
我设计了一个简洁的路由服务接口:
// 来源:src/main/java/com/ustinian/cheeseaicode/ai/AiCodeGenTypeRoutingService.java
public interface AiCodeGenTypeRoutingService {
/**
* 根据用户需求智能选择代码生成类型
*
* @param userPrompt 用户输入的需求描述
* @return 推荐的代码生成类型
*/
@SystemMessage(fromResource = "prompt/codegen-routing-system-prompt.txt")
CodeGenTypeEnum routeCodeGenType(String userPrompt);
}
这个接口的设计很巧妙:
- 单一职责:只负责路由决策,不涉及具体的代码生成
- 直接返回枚举:利用 LangChain4j 的结构化输出能力,直接返回枚举类型
- 基于 Prompt:通过系统提示词定义路由规则,便于调整和优化
路由策略的 Prompt 设计
// 来源:src/main/resources/prompt/codegen-routing-system-prompt.txt (第1-12行)
你是一个专业的代码生成方案路由器,需要根据用户需求返回最合适的代码生成类型。
可选的代码生成类型:
1. HTML - 适合简单的静态页面,单个 HTML 文件,包含内联 CSS 和 JS
2. MULTI_FILE - 适合简单的多文件静态页面,分离 HTML、CSS、JS 代码
3. VUE_PROJECT - 适合复杂的现代化前端项目
判断规则:
- 如果用户需求简单,只需要一个展示页面,选择 HTML
- 如果用户需要多个页面但不涉及复杂交互,选择 MULTI_FILE
- 如果用户需求复杂,涉及多页面、复杂交互、数据管理等,选择 VUE_PROJECT
这个 Prompt 设计很实用,它明确定义了每种类型的适用场景,让 AI 能够做出准确的判断。
策略模式:多模型服务的统一管理
核心工厂类设计
接下来是整个系统的核心 —— AI 服务工厂类。这里我使用了策略模式结合工厂模式:
// 来源:src/main/java/com/ustinian/cheeseaicode/ai/AiCodeGeneratorServiceFactory.java
private AiCodeGeneratorService createAiCodeGeneratorService(long appId, CodeGenTypeEnum codeGenType) {
// 根据 appId 构建独立的对话记忆
MessageWindowChatMemory chatMemory = MessageWindowChatMemory
.builder()
.id(appId)
.chatMemoryStore(redisChatMemoryStore)
.maxMessages(100)
.build();
// 从数据库加载历史对话到记忆中
chatHistoryService.loadChatHistoryToMemory(appId, chatMemory, 20);
// 根据代码生成类型选择不同的模型配置
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());
};
}
这个方法的设计亮点:
- 策略模式的完美应用:使用 switch 表达式根据不同的代码生成类型创建不同配置的服务
- 记忆管理:每个应用都有独立的对话记忆,保证上下文的连续性
- 工具集成:VUE_PROJECT 类型集成了完整的工具链,支持更复杂的操作
- 多例模式:使用 prototype 作用域的 Bean 解决并发访问问题
- 安全护轨:统一添加输入安全检查,防止恶意输入
缓存机制:性能优化的关键
为了提高系统性能,我实现了一套智能的缓存机制:
/**
* AI 服务实例缓存 Long 改成了String
*/
private final Cache<String, AiCodeGeneratorService> serviceCache = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(Duration.ofMinutes(30))
.expireAfterAccess(Duration.ofMinutes(10))
.removalListener((key, value, cause) -> {
log.debug("AI 服务实例被移除,缓存键: {}, 原因: {}", key, cause);
})
.build();
/**
* 根据 appId 和代码生成类型获取服务(带缓存)
*/
public AiCodeGeneratorService getAiCodeGeneratorService(long appId, CodeGenTypeEnum codeGenType) {
String cacheKey = buildCacheKey(appId, codeGenType);
return serviceCache.get(cacheKey, key -> createAiCodeGeneratorService(appId, codeGenType));
}
/**
* 构建缓存键
*/
private String buildCacheKey(long appId, CodeGenTypeEnum codeGenType) {
return appId + "_" + codeGenType.getValue();
}
缓存策略的精妙设计:
- 复合键设计:使用
appId + codeGenType作为缓存键,保证每个应用的每种类型都有独立缓存 - 多级过期策略:写入后30分钟过期,访问后10分钟过期,平衡内存使用和性能
- 容量控制:最大1000个实例,防止内存溢出
- 监听机制:缓存移除时记录日志,便于监控和调试
路由工厂:多例模式解决并发问题
为了解决路由服务的并发访问问题,我设计了专门的路由服务工厂:
// 来源:src/main/java/com/ustinian/cheeseaicode/ai/AiCodeGenTypeRoutingServiceFactory.java (第29-47行)
/**
* 创建AI代码生成类型路由服务实例(工厂方法,每次调用创建新实例)
*/
public AiCodeGenTypeRoutingService createAiCodeGenTypeRoutingService(){
// 使用多例Bean避免依赖自动配置
ChatModel routingChatModel = applicationContext.getBean("routingChatModelPrototype", ChatModel.class);
return AiServices.builder(AiCodeGenTypeRoutingService.class)
.chatModel(routingChatModel)
.build();
}
/**
* 默认提供一个Bean(用于依赖注入)
*/
@Bean
@Lazy
public AiCodeGenTypeRoutingService aiCodeGenTypeRoutingService() {
return createAiCodeGenTypeRoutingService();
}
这个设计很巧妙:
- 工厂方法:
createAiCodeGenTypeRoutingService()每次调用都创建新实例 - 多例 Bean:底层使用 prototype 作用域的 ChatModel
- 默认 Bean:提供一个默认的 Bean 供依赖注入使用
- 懒加载:使用
@Lazy注解,只在需要时创建
配置管理:多模型的灵活配置
为了支持不同的 AI 模型,我设计了灵活的配置管理:
// 来源:src/main/java/com/ustinian/cheeseaicode/config/ReasoningStreamingChatModelConfig.java (第30-42行)
@Bean
@Scope("prototype")
public StreamingChatModel reasoningStreamingChatModelPrototype() {
return OpenAiStreamingChatModel.builder()
.apiKey(apiKey)
.baseUrl(baseUrl)
.modelName(modelName)
.maxTokens(maxTokens)
.temperature(temperature)
.logRequests(logRequests)
.logResponses(logResponses)
.build();
}
配置设计的优势:
- 分离关注点:每种模型都有独立的配置类
- 多例模式:使用
@Scope("prototype")确保每次获取都是新实例 - 配置化管理:所有参数都通过配置文件管理,便于调整
- 日志控制:可以独立控制每个模型的请求和响应日志
实际应用:智能路由的工作流程
在实际业务中,这套智能路由系统是这样工作的:
// 来源:src/main/java/com/ustinian/cheeseaicode/service/impl/AppServiceImpl.java (第215-217行)
// 使用 AI 智能选择代码生成类型(多例模式)
AiCodeGenTypeRoutingService routingService = aiCodeGenTypeRoutingServiceFactory.createAiCodeGenTypeRoutingService();
CodeGenTypeEnum selectedCodeGenType = routingService.routeCodeGenType(initPrompt);
// 设置代码生成类型到应用对象
app.setCodeGenType(selectedCodeGenType.getValue());
完整的工作流程:
- 用户输入:用户描述需求,比如"我想要一个个人博客网站"
- 智能路由:AI 分析需求,选择合适的代码生成类型(比如 VUE_PROJECT)
- 服务创建:工厂根据选择的类型创建对应的 AI 服务实例
- 缓存管理:创建的服务实例被缓存,后续请求直接复用
- 代码生成:使用选定的服务实例进行实际的代码生成
扩展性:添加新策略的便捷性
这套设计的一个重要优势是扩展性很好。如果要添加新的代码生成类型,只需要:
1. 扩展枚举
public enum CodeGenTypeEnum {
HTML("原生 HTML 模式", "html"),
MULTI_FILE("原生多文件模式", "multi_file"),
VUE_PROJECT("Vue 工程模式", "vue_project"),
REACT_PROJECT("React 工程模式", "react_project"); // 新增
}
2. 更新工厂方法
return switch (codeGenType) {
case VUE_PROJECT -> { /* 现有逻辑 */ }
case HTML, MULTI_FILE -> { /* 现有逻辑 */ }
case REACT_PROJECT -> { // 新增分支
// React 项目的特定配置
yield createReactProjectService(appId, chatMemory);
}
}
3. 更新路由 Prompt
可选的代码生成类型:
1. HTML - 适合简单的静态页面
2. MULTI_FILE - 适合多文件静态页面
3. VUE_PROJECT - 适合复杂的 Vue 前端项目
4. REACT_PROJECT - 适合复杂的 React 前端项目 // 新增
从而不需要修改其他任何代码。
性能测试:路由效率和负载均衡
在实际测试中,这套智能路由系统表现出色:
路由效率测试
- 平均路由时间:200-500ms(包含 AI 推理时间)
- 准确率:在测试集上达到 95% 的路由准确率
- 并发处理:支持 100+ 并发路由请求
缓存效果分析
- 缓存命中率:85%(相同应用的重复请求)
- 内存使用:平均每个缓存项占用 2MB 内存
- 响应时间提升:缓存命中时响应时间从 2s 降低到 50ms
负载均衡效果
通过多例模式,有效避免了以下问题:
- 线程安全:每个请求使用独立的模型实例
- 资源竞争:避免了多个请求争抢同一个连接
- 故障隔离:单个实例的问题不会影响其他请求
设计模式的实际价值
- 策略模式:真正体会到了策略模式在处理多种算法选择时的威力
- 工厂模式:深刻理解了工厂模式在对象创建复杂场景中的重要性
- 单例vs多例:学会了在并发场景下正确选择对象作用域
系统设计的思考
- 分层设计:路由层、服务层、配置层各司其职,职责清晰
- 缓存策略:合适的缓存策略能够显著提升系统性能
- 扩展性设计:好的架构应该让添加新功能变得简单

浙公网安备 33010602011771号