项目支持多种AI代码生成模式,从架构层面是如何设计和隔离不同模式的生成逻辑的?
*** AI代码生成服务接口(不同模式的策略接口)*//*** 生成HTML代码流(HTML模式)*//*** 生成多文件代码流(多文件模式)*//*** 生成Vue工程代码流(Vue工程模式)*//*** AI代码生成服务实现类(核心策略实现)*/@Service@Autowired@Autowired@Autowired/*** 构建基础AI Service(HTML/多文件模式)*/// 加
标题 项目支持多种AI代码生成模式,从架构层面是如何设计和隔离不同模式的生成逻辑的?
推荐答案内容
为了支持原生HTML、多文件和Vue工程三种不同的代码生成模式,在架构设计上采用了门面模式+策略模式+工厂模式相结合的方式,实现逻辑的清晰隔离和灵活扩展。
核心设计如下:
- 创建
AiCodeGeneratorFacade门面类,对外提供统一的generateAndSaveCodeStream方法。该方法接收用户消息和代码生成类型作为参数,屏蔽了内部不同生成模式的实现细节。 - 在
AiCodeGeneratorService接口中,为每种生成模式定义了不同的方法(如generateHtmlCodeStream和generateVueProjectCodeStream)。每种方法通过@SystemMessage注解关联一个独立的系统提示词文件,确保AI执行任务时遵循该模式特定的规则和约束。 - 在
AiCodeGeneratorServiceFactory中,实现了工厂方法getAiCodeGeneratorService,它会根据传入的CodeGenTypeEnum动态构建和配置AiCodeGeneratorService实例:- 对于Vue工程模式,会配置使用能力更强的推理模型,并注入文件写入等工具(该模式依赖工具调用生成项目文件结构);
- 对于原生HTML和多文件模式,会配置使用成本更低的对话模型,且不注入任何工具(这两种模式直接解析AI输出的Markdown代码块保存文件)。
- 由于不同模式下AI的流式输出格式不同,设计了
StreamHandlerExecutor执行器,它会根据生成类型选择对应的流处理器来解析和处理AI的实时响应,实现了处理逻辑的隔离。
通过这种方式,每种生成模式的核心逻辑都被封装在各自的提示词、AI Service配置和流处理器中。当未来需要支持新的生成模式时,只需新增相应的枚举、提示词文件,并在工厂类和执行器中增加一个分支即可,无需改动现有逻辑。
流程架构图说明
用户发送消息 → 调用AppService.chatToGenCode → 判断生成类型:
- 若为HTML/MULTI_FILE模式:调用普通流式模型(返回
Flux<String>); - 若为VUE_PROJECT模式:调用推理流式模型(返回处理后的
Flux<String>);
→ 传递至StreamHandlerExecutor流处理执行器 → 选择流处理器: - 对应HTML/MULTI_FILE:使用
SimpleTextStreamHandler处理原生文本流; - 对应VUE_PROJECT:使用
JsonMessageStreamHandler处理JSON消息流。
AI代码生成多模式隔离架构完整代码实现
一、核心设计架构
基于「门面模式+策略模式+工厂模式」实现多生成模式(HTML/多文件/Vue工程)的逻辑隔离,整体架构如下:
二、完整代码实现
1. 基础枚举与配置
/**
* 代码生成类型枚举
*/
public enum CodeGenTypeEnum {
// 原生HTML模式
HTML("html", "原生HTML生成", "system_prompt/html_system_prompt.txt"),
// 多文件模式
MULTI_FILE("multi_file", "多文件生成", "system_prompt/multi_file_system_prompt.txt"),
// Vue工程模式
VUE_PROJECT("vue_project", "Vue工程生成", "system_prompt/vue_project_system_prompt.txt");
private final String code;
private final String name;
private final String systemPromptPath;
// 构造器、getter省略
}
/**
* AI模型配置类
*/
@Data
@ConfigurationProperties(prefix = "ai.model")
@Component
public class AiModelConfig {
// 基础对话模型(HTML/多文件模式)
private String chatModel;
// 推理模型(Vue工程模式)
private String reasoningModel;
// 模型超时时间
private Integer timeout = 30;
}
2. 核心AI服务接口(策略定义)
/**
* AI代码生成服务接口(不同模式的策略接口)
*/
public interface AiCodeGeneratorService {
/**
* 生成HTML代码流(HTML模式)
*/
Flux<String> generateHtmlCodeStream(String userMessage);
/**
* 生成多文件代码流(多文件模式)
*/
Flux<String> generateMultiFileCodeStream(String userMessage);
/**
* 生成Vue工程代码流(Vue工程模式)
*/
Flux<String> generateVueProjectCodeStream(String userMessage);
}
/**
* AI代码生成服务实现类(核心策略实现)
*/
@Service
public class AiCodeGeneratorServiceImpl implements AiCodeGeneratorService {
@Autowired
private AiModelConfig aiModelConfig;
@Autowired
private ToolManager toolManager;
@Autowired
private PromptTemplateLoader promptLoader;
/**
* 构建基础AI Service(HTML/多文件模式)
*/
private AiServices<AiCodeAssistant> buildBasicAiService(CodeGenTypeEnum genType) {
// 加载对应模式的系统提示词
String systemPrompt = promptLoader.loadSystemPrompt(genType.getSystemPromptPath());
return AiServices.builder()
.chatLanguageModel(OpenAiChatModel.builder()
.apiKey(aiModelConfig.getApiKey())
.modelName(aiModelConfig.getChatModel())
.timeout(Duration.ofSeconds(aiModelConfig.getTimeout()))
.build())
.systemMessage(systemPrompt)
.build(AiCodeAssistant.class);
}
/**
* 构建Vue工程AI Service(带工具注入)
*/
private AiServices<AiCodeAssistant> buildVueProjectAiService() {
String systemPrompt = promptLoader.loadSystemPrompt(CodeGenTypeEnum.VUE_PROJECT.getSystemPromptPath());
return AiServices.builder()
.chatLanguageModel(OpenAiChatModel.builder()
.apiKey(aiModelConfig.getApiKey())
.modelName(aiModelConfig.getReasoningModel()) // 推理模型
.timeout(Duration.ofSeconds(aiModelConfig.getTimeout()))
.build())
.systemMessage(systemPrompt)
.tools(toolManager.getAllTools()) // 注入文件操作工具
.build(AiCodeAssistant.class);
}
// HTML模式实现
@Override
public Flux<String> generateHtmlCodeStream(String userMessage) {
AiServices<AiCodeAssistant> aiService = buildBasicAiService(CodeGenTypeEnum.HTML);
return aiService.streaming()
.chat(UserMessage.of(userMessage))
.map(aiMessage -> aiMessage.text());
}
// 多文件模式实现
@Override
public Flux<String> generateMultiFileCodeStream(String userMessage) {
AiServices<AiCodeAssistant> aiService = buildBasicAiService(CodeGenTypeEnum.MULTI_FILE);
return aiService.streaming()
.chat(UserMessage.of(userMessage))
.map(aiMessage -> aiMessage.text());
}
// Vue工程模式实现
@Override
public Flux<String> generateVueProjectCodeStream(String userMessage) {
AiServices<AiCodeAssistant> aiService = buildVueProjectAiService();
return aiService.streaming()
.chat(UserMessage.of(userMessage))
.map(aiMessage -> aiMessage.text());
}
/**
* AI助手接口(定义对话能力)
*/
public interface AiCodeAssistant {
@UserMessage("{userMessage}")
Flux<AiMessage> chat(String userMessage);
}
}
3. 工厂类(模式实例化控制)
/**
* AI代码生成服务工厂(核心:模式隔离的核心控制)
*/
@Service
public class AiCodeGeneratorServiceFactory {
@Autowired
private AiCodeGeneratorServiceImpl aiCodeGeneratorService;
@Autowired
private AiModelConfig aiModelConfig;
/**
* 根据生成类型获取配置好的AI服务
*/
public AiCodeGeneratorService getAiCodeGeneratorService(CodeGenTypeEnum genType) {
// 可扩展:为不同模式创建独立的服务实例,此处简化为返回统一实例(核心差异在方法调用)
switch (genType) {
case HTML:
case MULTI_FILE:
// HTML/多文件模式:使用基础模型,无工具注入(已在ServiceImpl中配置)
return aiCodeGeneratorService;
case VUE_PROJECT:
// Vue工程模式:使用推理模型,注入工具(已在ServiceImpl中配置)
return aiCodeGeneratorService;
default:
throw new IllegalArgumentException("不支持的生成类型:" + genType);
}
}
/**
* 获取对应模式的流式生成方法
*/
public Function<String, Flux<String>> getGenerateFunction(CodeGenTypeEnum genType) {
AiCodeGeneratorService service = getAiCodeGeneratorService(genType);
return switch (genType) {
case HTML -> service::generateHtmlCodeStream;
case MULTI_FILE -> service::generateMultiFileCodeStream;
case VUE_PROJECT -> service::generateVueProjectCodeStream;
};
}
}
4. 流处理器(输出格式隔离)
/**
* 流处理器接口(策略模式)
*/
public interface StreamHandler {
/**
* 处理AI流式输出
*/
Flux<String> handle(Flux<String> rawStream);
}
/**
* 简单文本流处理器(HTML/多文件模式)
*/
@Component
public class SimpleTextStreamHandler implements StreamHandler {
@Override
public Flux<String> handle(Flux<String> rawStream) {
// 处理逻辑:直接返回文本流,仅过滤空内容
return rawStream
.filter(StringUtils::isNotBlank)
.map(text -> text.replace("```html", "").replace("```", ""));
}
}
/**
* JSON消息流处理器(Vue工程模式)
*/
@Component
public class JsonMessageStreamHandler implements StreamHandler {
@Override
public Flux<String> handle(Flux<String> rawStream) {
// 处理逻辑:解析JSON格式的工具调用消息
return rawStream
.filter(StringUtils::isNotBlank)
.map(this::parseJsonMessage)
.filter(Objects::nonNull);
}
private String parseJsonMessage(String jsonStr) {
try {
// 解析AI返回的JSON工具调用消息
JSONObject json = JSON.parseObject(jsonStr);
String toolName = json.getString("toolName");
String toolResult = json.getString("toolResult");
return String.format("[%s工具执行结果]:%s", toolName, toolResult);
} catch (Exception e) {
log.error("JSON流解析失败", e);
return null;
}
}
}
/**
* 流处理器执行器(工厂模式)
*/
@Component
public class StreamHandlerExecutor {
@Autowired
private SimpleTextStreamHandler simpleTextStreamHandler;
@Autowired
private JsonMessageStreamHandler jsonMessageStreamHandler;
/**
* 根据生成类型获取对应处理器
*/
public StreamHandler getStreamHandler(CodeGenTypeEnum genType) {
return switch (genType) {
case HTML, MULTI_FILE -> simpleTextStreamHandler;
case VUE_PROJECT -> jsonMessageStreamHandler;
default -> throw new IllegalArgumentException("不支持的生成类型:" + genType);
};
}
/**
* 统一处理流
*/
public Flux<String> executeHandle(CodeGenTypeEnum genType, Flux<String> rawStream) {
StreamHandler handler = getStreamHandler(genType);
return handler.handle(rawStream);
}
}
5. 门面类(对外统一入口)
/**
* AI代码生成门面类(对外统一接口,屏蔽内部实现)
*/
@Service
public class AiCodeGeneratorFacade {
@Autowired
private AiCodeGeneratorServiceFactory serviceFactory;
@Autowired
private StreamHandlerExecutor streamHandlerExecutor;
@Autowired
private CodeFileSaverExecutor fileSaverExecutor;
/**
* 统一生成并保存代码流(对外核心方法)
* @param userMessage 用户生成指令
* @param genType 生成类型
* @param appId 应用ID
* @return 处理后的流式输出
*/
public Flux<String> generateAndSaveCodeStream(String userMessage, CodeGenTypeEnum genType, Long appId) {
// 1. 获取对应模式的生成方法
Function<String, Flux<String>> generateFunction = serviceFactory.getGenerateFunction(genType);
// 2. 调用AI生成原始流
Flux<String> rawStream = generateFunction.apply(userMessage);
// 3. 处理流(不同模式不同解析逻辑)
Flux<String> handledStream = streamHandlerExecutor.executeHandle(genType, rawStream);
// 4. 流式保存代码(模板方法模式,此处简化为收集完流后保存)
return handledStream.doOnComplete(() -> {
// 实际项目中可通过缓存收集流内容,完成后调用保存逻辑
fileSaverExecutor.save(genType, appId, "收集的代码内容");
});
}
}
6. 业务层调用(最终使用)
/**
* 应用服务(用户请求入口)
*/
@Service
public class AppService {
@Autowired
private AiCodeGeneratorFacade aiCodeGeneratorFacade;
/**
* 对话生成代码(用户最终调用的方法)
*/
public Flux<String> chatToGenCode(String userMessage, String genTypeCode, Long appId) {
// 1. 解析生成类型
CodeGenTypeEnum genType = CodeGenTypeEnum.valueOf(genTypeCode.toUpperCase());
// 2. 调用门面类,屏蔽内部所有复杂逻辑
return aiCodeGeneratorFacade.generateAndSaveCodeStream(userMessage, genType, appId);
}
}
/**
* 控制器(API接口)
*/
@RestController
@RequestMapping("/app")
public class AppController {
@Autowired
private AppService appService;
/**
* 流式生成代码接口
*/
@PostMapping(value = "/chat/gen-code", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chatToGenCode(
@RequestParam String userMessage,
@RequestParam String genType,
@RequestHeader Long appId) {
return appService.chatToGenCode(userMessage, genType, appId);
}
}
7. 提示词加载工具(辅助类)
/**
* 系统提示词加载工具
*/
@Component
public class PromptTemplateLoader {
/**
* 加载对应模式的系统提示词
*/
public String loadSystemPrompt(String promptPath) {
try (InputStream is = getClass().getClassLoader().getResourceAsStream(promptPath)) {
if (is == null) {
throw new IllegalArgumentException("提示词文件不存在:" + promptPath);
}
return new String(is.readAllBytes(), StandardCharsets.UTF_8);
} catch (Exception e) {
log.error("加载提示词失败", e);
return "默认提示词:生成符合要求的代码";
}
}
}
三、核心设计亮点
1. 模式隔离核心
- 配置隔离:不同模式绑定不同的系统提示词、AI模型、工具集;
- 逻辑隔离:每种模式的生成方法独立实现,流处理逻辑通过策略模式隔离;
- 入口统一:门面类对外提供唯一入口,上层无需关注内部差异。
2. 扩展性设计
新增生成模式(如React工程)仅需:
- 在
CodeGenTypeEnum中新增枚举值,指定提示词路径; - 在
AiCodeGeneratorService中新增generateReactProjectCodeStream方法; - 在工厂类
AiCodeGeneratorServiceFactory中新增模式分支; - (可选)新增对应的流处理器
ReactStreamHandler,并在StreamHandlerExecutor中注册。
无需修改现有模式的任何代码,完全遵循“开闭原则”。
四、关键点回顾
- 门面模式:
AiCodeGeneratorFacade屏蔽内部所有复杂逻辑,对外提供统一调用入口; - 工厂模式:
AiCodeGeneratorServiceFactory控制不同模式的AI服务实例化,StreamHandlerExecutor控制流处理器选择; - 策略模式:
AiCodeGeneratorService定义不同生成策略,StreamHandler定义不同流处理策略; - 隔离核心:通过枚举绑定提示词/模型/处理器,每种模式的核心逻辑封装在独立组件中,互不干扰。
这套架构既保证了多模式逻辑的清晰隔离,又具备极强的扩展性,新增模式仅需新增组件,无需改动现有代码。
package com.yupi.yuaicodemother.core;
import cn.hutool.json.JSONUtil;
import com.yupi.yuaicodemother.ai.AiCodeGeneratorService;
import com.yupi.yuaicodemother.ai.AiCodeGeneratorServiceFactory;
import com.yupi.yuaicodemother.ai.model.HtmlCodeResult;
import com.yupi.yuaicodemother.ai.model.MultiFileCodeResult;
import com.yupi.yuaicodemother.ai.model.message.AiResponseMessage;
import com.yupi.yuaicodemother.ai.model.message.ToolExecutedMessage;
import com.yupi.yuaicodemother.ai.model.message.ToolRequestMessage;
import com.yupi.yuaicodemother.constant.AppConstant;
import com.yupi.yuaicodemother.core.builder.VueProjectBuilder;
import com.yupi.yuaicodemother.core.parser.CodeParserExecutor;
import com.yupi.yuaicodemother.core.saver.CodeFileSaverExecutor;
import com.yupi.yuaicodemother.exception.BusinessException;
import com.yupi.yuaicodemother.exception.ErrorCode;
import com.yupi.yuaicodemother.model.enums.CodeGenTypeEnum;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.service.TokenStream;
import dev.langchain4j.service.tool.ToolExecution;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import java.io.File;
/**
* AI 代码生成门面类,组合代码生成和保存功能
*/
@Service
@Slf4j
public class AiCodeGeneratorFacade {
@Resource
private AiCodeGeneratorServiceFactory aiCodeGeneratorServiceFactory;
@Resource
private VueProjectBuilder vueProjectBuilder;
/**
* 统一入口:根据类型生成并保存代码
*
* @param userMessage 用户提示词
* @param codeGenTypeEnum 生成类型
* @param appId 应用 ID
* @return 保存的目录
*/
public File generateAndSaveCode(String userMessage, CodeGenTypeEnum codeGenTypeEnum, Long appId) {
if (codeGenTypeEnum == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "生成类型不能为空");
}
// 根据 appId 获取相应的 AI 服务实例
AiCodeGeneratorService aiCodeGeneratorService = aiCodeGeneratorServiceFactory.getAiCodeGeneratorService(appId, codeGenTypeEnum);
return switch (codeGenTypeEnum) {
case HTML -> {
HtmlCodeResult result = aiCodeGeneratorService.generateHtmlCode(userMessage);
yield CodeFileSaverExecutor.executeSaver(result, CodeGenTypeEnum.HTML, appId);
}
case MULTI_FILE -> {
MultiFileCodeResult result = aiCodeGeneratorService.generateMultiFileCode(userMessage);
yield CodeFileSaverExecutor.executeSaver(result, CodeGenTypeEnum.MULTI_FILE, appId);
}
default -> {
String errorMessage = "不支持的生成类型:" + codeGenTypeEnum.getValue();
throw new BusinessException(ErrorCode.SYSTEM_ERROR, errorMessage);
}
};
}
/**
* 统一入口:根据类型生成并保存代码(流式)
*
* @param userMessage 用户提示词
* @param codeGenTypeEnum 生成类型
* @param appId 应用 ID
* @return 保存的目录
*/
public Flux<String> generateAndSaveCodeStream(String userMessage, CodeGenTypeEnum codeGenTypeEnum, Long appId) {
if (codeGenTypeEnum == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "生成类型不能为空");
}
// 根据 appId 获取相应的 AI 服务实例
AiCodeGeneratorService aiCodeGeneratorService = aiCodeGeneratorServiceFactory.getAiCodeGeneratorService(appId, codeGenTypeEnum);
return switch (codeGenTypeEnum) {
case HTML -> {
Flux<String> codeStream = aiCodeGeneratorService.generateHtmlCodeStream(userMessage);
yield processCodeStream(codeStream, CodeGenTypeEnum.HTML, appId);
}
case MULTI_FILE -> {
Flux<String> codeStream = aiCodeGeneratorService.generateMultiFileCodeStream(userMessage);
yield processCodeStream(codeStream, CodeGenTypeEnum.MULTI_FILE, appId);
}
case VUE_PROJECT -> {
TokenStream tokenStream = aiCodeGeneratorService.generateVueProjectCodeStream(appId, userMessage);
yield processTokenStream(tokenStream, appId);
}
default -> {
String errorMessage = "不支持的生成类型:" + codeGenTypeEnum.getValue();
throw new BusinessException(ErrorCode.SYSTEM_ERROR, errorMessage);
}
};
}
/**
* 将 TokenStream 转换为 Flux<String>,并传递工具调用信息
*
* @param tokenStream TokenStream 对象
* @param appId 应用 ID
* @return Flux<String> 流式响应
*/
private Flux<String> processTokenStream(TokenStream tokenStream, Long appId) {
return Flux.create(sink -> {
tokenStream.onPartialResponse((String partialResponse) -> {
AiResponseMessage aiResponseMessage = new AiResponseMessage(partialResponse);
sink.next(JSONUtil.toJsonStr(aiResponseMessage));
})
.onPartialToolExecutionRequest((index, toolExecutionRequest) -> {
ToolRequestMessage toolRequestMessage = new ToolRequestMessage(toolExecutionRequest);
sink.next(JSONUtil.toJsonStr(toolRequestMessage));
})
.onToolExecuted((ToolExecution toolExecution) -> {
ToolExecutedMessage toolExecutedMessage = new ToolExecutedMessage(toolExecution);
sink.next(JSONUtil.toJsonStr(toolExecutedMessage));
})
.onCompleteResponse((ChatResponse response) -> {
// 执行 Vue 项目构建(同步执行,确保预览时项目已就绪)
String projectPath = AppConstant.CODE_OUTPUT_ROOT_DIR + "/vue_project_" + appId;
vueProjectBuilder.buildProject(projectPath);
sink.complete();
})
.onError((Throwable error) -> {
error.printStackTrace();
sink.error(error);
})
.start();
});
}
/**
* 通用流式代码处理方法
*
* @param codeStream 代码流
* @param codeGenType 代码生成类型
* @param appId 应用 ID
* @return 流式响应
*/
private Flux<String> processCodeStream(Flux<String> codeStream, CodeGenTypeEnum codeGenType, Long appId) {
// 字符串拼接器,用于当流式返回所有的代码之后,再保存代码
StringBuilder codeBuilder = new StringBuilder();
return codeStream.doOnNext(chunk -> {
// 实时收集代码片段
codeBuilder.append(chunk);
}).doOnComplete(() -> {
// 流式返回完成后,保存代码
try {
String completeCode = codeBuilder.toString();
// 使用执行器解析代码
Object parsedResult = CodeParserExecutor.executeParser(completeCode, codeGenType);
// 使用执行器保存代码
File saveDir = CodeFileSaverExecutor.executeSaver(parsedResult, codeGenType, appId);
log.info("保存成功,目录为:{}", saveDir.getAbsolutePath());
} catch (Exception e) {
log.error("保存失败: {}", e.getMessage());
}
});
}
}
AiCodeGeneratorFacade 类完整解析
一、类核心定位
这是AI零代码平台的门面类(Facade Pattern),核心职责是对外提供统一的代码生成+保存入口,屏蔽内部不同生成模式(HTML/多文件/Vue工程)的实现细节,同时整合代码生成、解析、保存、流式处理等核心能力。
二、核心依赖与前置说明
1. 关键依赖组件
| 组件 | 作用 |
|---|---|
AiCodeGeneratorServiceFactory |
工厂类:根据应用ID和生成类型,创建对应配置的AI生成服务实例 |
CodeParserExecutor |
策略模式执行器:解析不同模式的AI生成代码(如HTML/多文件) |
CodeFileSaverExecutor |
模板方法模式执行器:保存解析后的代码文件 |
VueProjectBuilder |
Vue工程构建器:Vue模式下生成完代码后,构建可预览的项目 |
TokenStream |
LangChain4j的流式响应对象:处理Vue工程模式的AI实时输出(含工具调用) |
Flux |
Reactor响应式编程组件:实现非阻塞的流式返回 |
2. 核心枚举/模型
| 类型 | 说明 |
|---|---|
CodeGenTypeEnum |
生成类型枚举:HTML/MULTI_FILE/VUE_PROJECT |
AiResponseMessage/ToolRequestMessage/ToolExecutedMessage |
消息封装模型:标准化Vue模式下AI的流式输出(文本/工具调用请求/工具执行结果) |
HtmlCodeResult/MultiFileCodeResult |
解析结果模型:存储HTML/多文件模式的解析后代码 |
三、核心方法逐行解析
1. 同步生成方法:generateAndSaveCode
public File generateAndSaveCode(String userMessage, CodeGenTypeEnum codeGenTypeEnum, Long appId) {
if (codeGenTypeEnum == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "生成类型不能为空");
}
// 1. 工厂模式:获取对应模式的AI生成服务实例(按appId+生成类型定制)
AiCodeGeneratorService aiCodeGeneratorService = aiCodeGeneratorServiceFactory.getAiCodeGeneratorService(appId, codeGenTypeEnum);
return switch (codeGenTypeEnum) {
case HTML -> {
// 2. 调用AI服务生成HTML代码(同步)
HtmlCodeResult result = aiCodeGeneratorService.generateHtmlCode(userMessage);
// 3. 模板方法模式:执行保存,返回保存目录
yield CodeFileSaverExecutor.executeSaver(result, CodeGenTypeEnum.HTML, appId);
}
case MULTI_FILE -> {
MultiFileCodeResult result = aiCodeGeneratorService.generateMultiFileCode(userMessage);
yield CodeFileSaverExecutor.executeSaver(result, CodeGenTypeEnum.MULTI_FILE, appId);
}
default -> {
String errorMessage = "不支持的生成类型:" + codeGenTypeEnum.getValue();
throw new BusinessException(ErrorCode.SYSTEM_ERROR, errorMessage);
}
};
}
核心逻辑:
- 入参校验:先校验生成类型非空,抛出标准化业务异常;
- 工厂获取服务:通过工厂类拿到适配当前appId和生成类型的AI服务实例(隔离不同模式的AI配置);
- 分支执行:根据生成类型调用对应AI方法,生成代码后通过保存执行器落地文件,返回保存目录;
- 异常兜底:不支持的类型抛出系统异常,保证错误可识别。
2. 流式生成方法:generateAndSaveCodeStream
public Flux<String> generateAndSaveCodeStream(String userMessage, CodeGenTypeEnum codeGenTypeEnum, Long appId) {
if (codeGenTypeEnum == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "生成类型不能为空");
}
AiCodeGeneratorService aiCodeGeneratorService = aiCodeGeneratorServiceFactory.getAiCodeGeneratorService(appId, codeGenTypeEnum);
return switch (codeGenTypeEnum) {
case HTML -> {
Flux<String> codeStream = aiCodeGeneratorService.generateHtmlCodeStream(userMessage);
yield processCodeStream(codeStream, CodeGenTypeEnum.HTML, appId);
}
case MULTI_FILE -> {
Flux<String> codeStream = aiCodeGeneratorService.generateMultiFileCodeStream(userMessage);
yield processCodeStream(codeStream, CodeGenTypeEnum.MULTI_FILE, appId);
}
case VUE_PROJECT -> {
TokenStream tokenStream = aiCodeGeneratorService.generateVueProjectCodeStream(appId, userMessage);
yield processTokenStream(tokenStream, appId);
}
default -> {
String errorMessage = "不支持的生成类型:" + codeGenTypeEnum.getValue();
throw new BusinessException(ErrorCode.SYSTEM_ERROR, errorMessage);
}
};
}
核心逻辑:
- 是同步方法的“流式版本”,返回
Flux<String>实现前端实时接收代码片段; - 模式隔离:
- HTML/多文件模式:调用通用的
processCodeStream处理文本流; - Vue工程模式:调用专属的
processTokenStream处理LangChain4j的TokenStream(含工具调用);
- HTML/多文件模式:调用通用的
- 核心设计:不同模式的流式处理逻辑完全隔离,通过分支语句分发,符合“开闭原则”。
3. Vue模式专属:processTokenStream
private Flux<String> processTokenStream(TokenStream tokenStream, Long appId) {
return Flux.create(sink -> {
tokenStream.onPartialResponse((String partialResponse) -> {
// 1. 处理AI实时文本输出:封装为AiResponseMessage并返回
AiResponseMessage aiResponseMessage = new AiResponseMessage(partialResponse);
sink.next(JSONUtil.toJsonStr(aiResponseMessage));
})
.onPartialToolExecutionRequest((index, toolExecutionRequest) -> {
// 2. 处理工具调用请求:封装为ToolRequestMessage(如文件写入请求)
ToolRequestMessage toolRequestMessage = new ToolRequestMessage(toolExecutionRequest);
sink.next(JSONUtil.toJsonStr(toolRequestMessage));
})
.onToolExecuted((ToolExecution toolExecution) -> {
// 3. 处理工具执行结果:封装为ToolExecutedMessage并返回
ToolExecutedMessage toolExecutedMessage = new ToolExecutedMessage(toolExecution);
sink.next(JSONUtil.toJsonStr(toolExecutedMessage));
})
.onCompleteResponse((ChatResponse response) -> {
// 4. 流式完成后:构建Vue工程(同步执行,确保预览可用)
String projectPath = AppConstant.CODE_OUTPUT_ROOT_DIR + "/vue_project_" + appId;
vueProjectBuilder.buildProject(projectPath);
sink.complete();
})
.onError((Throwable error) -> {
// 5. 异常处理:传递错误给Flux
error.printStackTrace();
sink.error(error);
})
.start(); // 6. 启动TokenStream监听
});
}
核心逻辑:
- 适配Vue工程模式:Vue模式依赖AI工具调用(如创建文件、写入代码),需处理
TokenStream的多类回调; - 流式事件映射:
onPartialResponse:AI实时返回的文本内容(如代码片段);onPartialToolExecutionRequest:AI发起的工具调用请求(如“调用FileWriteTool写入App.vue”);onToolExecuted:工具执行完成的结果(如“文件写入成功”);
- 工程构建:流式完成后同步构建Vue项目,保证前端预览时项目已就绪;
- 响应式封装:通过
Flux.create将LangChain4j的TokenStream转换为Spring Reactor的Flux,适配WebFlux流式返回。
4. HTML/多文件通用:processCodeStream
private Flux<String> processCodeStream(Flux<String> codeStream, CodeGenTypeEnum codeGenType, Long appId) {
// 1. 代码拼接器:收集所有流式片段,完成后统一保存
StringBuilder codeBuilder = new StringBuilder();
return codeStream.doOnNext(chunk -> {
// 2. 实时收集:每收到一个代码片段,追加到builder
codeBuilder.append(chunk);
}).doOnComplete(() -> {
// 3. 流式完成后:解析+保存代码
try {
String completeCode = codeBuilder.toString();
// 策略模式:解析代码(HTML/多文件不同解析逻辑)
Object parsedResult = CodeParserExecutor.executeParser(completeCode, codeGenType);
// 模板方法模式:保存代码
File saveDir = CodeFileSaverExecutor.executeSaver(parsedResult, codeGenType, appId);
log.info("保存成功,目录为:{}", saveDir.getAbsolutePath());
} catch (Exception e) {
log.error("保存失败: {}", e.getMessage());
}
});
}
核心逻辑:
- 适配简单文本流:HTML/多文件模式仅返回纯文本代码,无需工具调用,逻辑更简单;
- 延迟保存设计:
doOnNext:实时收集代码片段,但不立即保存(避免多次IO);doOnComplete:所有片段收集完成后,统一解析+保存,减少文件操作次数;
- 策略+模板方法:调用
CodeParserExecutor解析代码(策略模式),CodeFileSaverExecutor保存代码(模板方法模式),复用底层逻辑。
四、核心设计亮点
1. 模式隔离
- 通过
switch分支将HTML/多文件/Vue三种模式的处理逻辑完全隔离,新增模式仅需加分支; - Vue模式适配复杂的
TokenStream(工具调用),HTML/多文件适配简单的Flux<String>,各司其职。
2. 流式处理规范
- 实时返回:前端可实时接收代码片段/工具调用信息,提升交互体验;
- 延迟保存:避免流式传输中频繁写文件,提升性能;
- 异常透传:流式异常通过
sink.error传递,保证前端能捕获错误。
3. 设计模式落地
| 模式 | 应用位置 | 作用 |
|---|---|---|
| 门面模式 | 类本身 | 对外统一入口,屏蔽内部解析、保存、流式处理细节 |
| 工厂模式 | AiCodeGeneratorServiceFactory |
按需创建不同配置的AI服务实例 |
| 策略模式 | CodeParserExecutor |
不同生成类型用不同解析策略 |
| 模板方法模式 | CodeFileSaverExecutor |
统一保存流程,不同类型定制保存步骤 |
4. 异常处理
- 入参校验抛出标准化
BusinessException(含错误码); - 保存环节的异常仅打日志,不中断流程(避免流式响应失败);
- 流式处理的异常通过
sink.error传递,保证响应式规范。
五、使用场景与调用流程
典型调用流程(Vue工程模式)
前端请求 → AppController → AiCodeGeneratorFacade.generateAndSaveCodeStream →
AiCodeGeneratorServiceFactory.getAiCodeGeneratorService →
aiCodeGeneratorService.generateVueProjectCodeStream →
processTokenStream(处理工具调用+流式返回)→
vueProjectBuilder.buildProject(工程构建)→ 前端接收流式JSON
典型调用流程(HTML模式)
前端请求 → AppController → AiCodeGeneratorFacade.generateAndSaveCodeStream →
AiCodeGeneratorServiceFactory.getAiCodeGeneratorService →
aiCodeGeneratorService.generateHtmlCodeStream →
processCodeStream(收集代码+解析+保存)→ 前端接收代码片段
六、关键点回顾
- 该类是AI代码生成的“统一入口”,通过门面模式屏蔽内部复杂度;
- 核心区分“同步生成”和“流式生成”两种场景,适配不同前端交互需求;
- Vue模式需处理工具调用的多类事件,HTML/多文件模式仅处理纯文本流,逻辑隔离清晰;
- 流式处理采用“实时返回+延迟保存”策略,兼顾体验与性能;
- 底层复用策略模式(解析)、模板方法模式(保存)、工厂模式(服务创建),符合设计原则。
标题 LangChain4j 在项目中有什么作用?你是如何将 AI 大模型能力集成到业务代码中的?
推荐答案内容
LangChain4j 在项目中是核心的 AI 开发框架,它的主要作用是简化和标准化与大模型交互的过程。它通过一套声明式的 API,让开发可以不用关心底层 HTTP 请求和复杂的 JSON 处理,更专注于业务逻辑的实现。
我主要是通过 AI Service 这种模式将大模型能力集成到业务代码中的,这个过程可以分为几个步骤:
-
定义服务接口:创建了一个
AiCodeGeneratorService接口,并在其中定义了与业务相关的方法,比如generateHtmlCodeStream和generateVueProjectCodeStream。 -
使用注解驱动:在接口方法上,使用了 LangChain4j 提供的注解来配置 AI 的行为:
@SystemMessage:用于关联一个系统提示词文件,设定 AI 的角色和任务约束。@UserMessage:用于标记哪个方法参数是用户的输入。@MemoryId:用于为不同的对话分配独立的记忆空间,这在实现按应用隔离对话记忆时非常关键。
-
创建服务实例:编写了一个
AiCodeGeneratorServiceFactory工厂类,使用AiServices.builder()来构建AiCodeGeneratorService接口的动态代理实现。在这个构建过程中,可以注入配置好的ChatModel或StreamingChatModel,还可以注册工具、配置对话记忆和防护等。 -
业务层调用:最后,在业务逻辑中,可以直接注入并调用
AiCodeGeneratorService接口的方法,就像调用一个普通的 Java 方法一样,LangChain4j 框架会自动处理与大模型的所有通信细节。
除了基本的对话功能,还利用了 LangChain4j 的其他核心特性,比如通过结构化输出将 AI 的 JSON 响应自动映射为 Java 对象,通过工具调用让 AI 能够使用我提供的 FileWriteTool 等工具来生成复杂的 Vue 工程项目。
AiCodeGeneratorService 接口完整解析
一、接口核心定位
这是基于 LangChain4j 框架 定义的 AI 代码生成服务接口,是项目中“大模型能力与业务逻辑解耦”的核心层。它通过 LangChain4j 提供的注解驱动方式,将大模型的对话、流式输出、工具调用、记忆管理等能力封装为标准化的 Java 接口方法,让业务层可以像调用普通方法一样使用 AI 能力。
二、核心注解与返回值说明
1. LangChain4j 核心注解
| 注解 | 作用 | 应用场景 |
|---|---|---|
@SystemMessage |
配置 AI 的系统提示词(角色/约束/任务),fromResource 指定从资源文件加载提示词 |
为不同生成模式(HTML/多文件/Vue)绑定专属的系统提示词,保证 AI 生成逻辑符合业务规则 |
@UserMessage |
标记方法参数为“用户输入”,LangChain4j 会自动将该参数作为用户消息发送给大模型 | Vue 模式中明确指定 userMessage 为用户输入,适配多参数场景的消息组装 |
@MemoryId |
为对话分配唯一的记忆 ID,实现“按应用隔离对话记忆” | Vue 模式中用 appId 作为记忆 ID,保证不同应用的对话上下文互不干扰 |
2. 关键返回值类型
| 类型 | 说明 | 适用场景 |
|---|---|---|
HtmlCodeResult/MultiFileCodeResult |
自定义业务模型 | 同步生成场景:LangChain4j 会自动将 AI 返回的 JSON 结构化输出映射为该对象 |
Flux<String> |
Spring Reactor 响应式类型 | HTML/多文件流式生成:返回实时的代码片段流,适配 WebFlux 前端流式接收 |
TokenStream |
LangChain4j 原生流式类型 | Vue 项目流式生成:支持工具调用、部分响应、异常回调等复杂流式场景 |
三、接口方法逐行解析
1. 同步生成 HTML 代码:generateHtmlCode
@SystemMessage(fromResource = "prompt/codegen-html-system-prompt.txt")
HtmlCodeResult generateHtmlCode(String userMessage);
核心逻辑:
- 注解配置:
@SystemMessage从resources/prompt/目录加载codegen-html-system-prompt.txt文件作为系统提示词,例如提示 AI“仅生成纯 HTML 代码,不含多余解释,符合 W3C 规范”; - 同步调用:方法直接返回
HtmlCodeResult(自定义模型),LangChain4j 会自动完成:- 将
userMessage作为用户消息发送给大模型; - 接收 AI 的同步响应;
- 将 AI 返回的 JSON 字符串自动映射为
HtmlCodeResult对象(无需手动解析 JSON);
- 将
- 适用场景:简单的 HTML 代码生成,无需实时展示生成过程,追求结果的完整性。
2. 同步生成多文件代码:generateMultiFileCode
@SystemMessage(fromResource = "prompt/codegen-multi-file-system-prompt.txt")
MultiFileCodeResult generateMultiFileCode(String userMessage);
核心逻辑:
- 与 HTML 同步生成逻辑一致,核心差异是:
- 绑定的系统提示词不同(要求 AI 生成 HTML/CSS/JS 分离的多文件代码);
- 返回值为
MultiFileCodeResult(包含 htmlContent/cssContent/jsContent 三个字段);
- LangChain4j 会根据
MultiFileCodeResult的字段结构,自动要求 AI 返回结构化 JSON,并完成映射。
3. 流式生成 HTML 代码:generateHtmlCodeStream
@SystemMessage(fromResource = "prompt/codegen-html-system-prompt.txt")
Flux<String> generateHtmlCodeStream(String userMessage);
核心逻辑:
- 流式输出:返回
Flux<String>,LangChain4j 会将 AI 的实时输出拆分为多个字符串片段,通过Flux逐段返回; - 前端适配:前端可通过 SSE(Server-Sent Events)实时接收代码片段,实现“边生成边展示”的交互效果;
- 提示词复用:与同步方法复用同一个系统提示词,保证生成规则一致;
- 无记忆隔离:未使用
@MemoryId,因为 HTML 生成是单次请求,无需对话上下文。
4. 流式生成多文件代码:generateMultiFileCodeStream
@SystemMessage(fromResource = "prompt/codegen-multi-file-system-prompt.txt")
Flux<String> generateMultiFileCodeStream(String userMessage);
核心逻辑:
- 与 HTML 流式生成逻辑一致,差异仅在于系统提示词和业务语义(生成多文件代码片段);
Flux<String>中的每个片段是多文件代码的一部分(例如先返回 HTML 片段,再返回 CSS 片段)。
5. 流式生成 Vue 项目代码:generateVueProjectCodeStream
@SystemMessage(fromResource = "prompt/codegen-vue-project-system-prompt.txt")
TokenStream generateVueProjectCodeStream(@MemoryId long appId, @UserMessage String userMessage);
核心逻辑(最复杂,也是 Vue 模式的核心):
- 专属提示词:绑定 Vue 项目生成的系统提示词,要求 AI 生成完整的 Vue 工程结构(含 package.json、App.vue、main.js 等),并支持工具调用;
- 记忆隔离:
@MemoryId long appId为每个应用分配独立的对话记忆,例如 AI 生成 Vue 工程时,后续的“修改某个组件”请求能关联到之前的上下文; - 用户消息标记:
@UserMessage String userMessage明确指定该参数为用户输入(多参数场景下必须标记,否则 LangChain4j 无法识别); - TokenStream 类型:区别于
Flux<String>,TokenStream是 LangChain4j 原生流式类型,支持:- 监听 AI 的部分响应(
onPartialResponse); - 监听工具调用请求(
onPartialToolExecutionRequest); - 监听工具执行结果(
onToolExecuted); - 异常回调(
onError);
- 监听 AI 的部分响应(
- 适用场景:Vue 工程生成需要 AI 调用文件写入/修改工具,
TokenStream能完整处理工具调用的全生命周期。
四、核心设计亮点
1. 注解驱动,无侵入式集成
- 无需编写任何 HTTP 请求、JSON 解析、流式处理的底层代码,仅通过注解配置即可实现大模型能力集成;
- 业务层调用该接口时,完全感知不到大模型的存在,如同调用普通 Java 服务。
2. 模式隔离,规则统一
- 不同生成模式(HTML/多文件/Vue)通过绑定不同的系统提示词文件实现规则隔离;
- 同步/流式方法分离,适配不同的前端交互需求(一次性返回 vs 实时展示)。
3. 结构化输出,简化解析
- 同步方法直接返回自定义业务模型(
HtmlCodeResult/MultiFileCodeResult),LangChain4j 自动完成 JSON 到 Java 对象的映射,避免手动解析出错。
4. 记忆管理,上下文隔离
- Vue 模式通过
@MemoryId绑定appId,实现“按应用隔离对话记忆”,解决多应用并发时的上下文混乱问题。
五、接口使用流程(与工厂类配合)
六、关键点回顾
- 该接口是 LangChain4j “AI Service 模式”的典型应用,通过注解封装大模型能力,实现业务与 AI 解耦;
- 核心区分“同步/流式”“普通文本/工具调用”两类场景,分别用
HtmlCodeResult/Flux<String>/TokenStream适配; @SystemMessage实现不同生成模式的规则隔离,@MemoryId实现对话记忆的应用级隔离;- Vue 模式使用
TokenStream是因为需要处理工具调用的复杂流式事件,而 HTML/多文件模式用Flux<String>即可满足需求。
HtmlCodeResult 类完整解析
一、类核心定位
这是 LangChain4j 框架下的结构化输出模型,用于定义 AI 生成 HTML 代码后返回结果的格式规范。LangChain4j 会根据该类的字段和注解,自动要求大模型返回符合结构的 JSON 数据,并将其映射为该 Java 对象,无需手动解析 JSON。
二、核心注解与作用
1. 关键注解说明
| 注解 | 所属包 | 核心作用 |
|---|---|---|
@Description |
dev.langchain4j.model.output.structured |
给模型/字段添加描述,LangChain4j 会将这些描述传递给大模型,指导 AI 生成符合要求的结构化输出 |
@Data |
lombok |
自动生成 getter/setter/toString/equals/hashCode 等方法,简化代码 |
2. 注解的核心价值
- 约束 AI 输出格式:
@Description中的文字会被 LangChain4j 拼接进提示词,告诉 AI“你需要返回一个包含htmlCode(HTML代码)和description(生成代码的描述)的 JSON 对象”; - 自动映射:AI 返回 JSON 后,LangChain4j 会根据字段名自动将 JSON 字段映射到该类的属性,无需手动调用
JSONUtil.parseObject等工具; - 可读性提升:注解描述让代码语义更清晰,同时也让 AI 理解每个字段的含义,减少生成格式错误。
三、类结构与字段解析
@Description("生成 HTML 代码文件的结果") // 给整个模型加描述,指导AI理解返回对象的用途
@Data // Lombok注解,自动生成基础方法
public class HtmlCodeResult {
/**
* HTML 代码
*/
@Description("HTML代码") // 字段描述:告诉AI该字段需要填充纯HTML代码,无多余解释
private String htmlCode;
/**
* 描述
*/
@Description("生成代码的描述") // 字段描述:告诉AI该字段需要填充代码的功能说明
private String description;
}
字段详解
| 字段名 | 类型 | 作用 | AI 输出示例 |
|---|---|---|---|
htmlCode |
String | 存储 AI 生成的纯 HTML 代码(核心字段) | <html><body><h1>Hello World</h1></body></html> |
description |
String | 存储 AI 对生成代码的功能描述(辅助字段) | “一个简单的Hello World页面,包含基础HTML结构” |
四、使用场景与工作流程
1. 关联的 AI 服务接口方法
在 AiCodeGeneratorService 中,该类作为同步方法的返回值:
@SystemMessage(fromResource = "prompt/codegen-html-system-prompt.txt")
HtmlCodeResult generateHtmlCode(String userMessage);
2. 完整工作流程
3. AI 实际收到的提示词(简化版)
你需要生成符合要求的HTML代码,并返回以下格式的JSON:
{
"htmlCode": "HTML代码内容",
"description": "生成代码的描述"
}
(注:LangChain4j 会自动将 @Description 注解的内容转化为这类约束性提示词)
五、扩展与最佳实践
1. 字段校验(可选)
如果需要对 AI 生成的内容做校验,可添加 JSR 380 注解(如 @NotBlank):
import jakarta.validation.constraints.NotBlank;
@Data
@Description("生成 HTML 代码文件的结果")
public class HtmlCodeResult {
@NotBlank(message = "HTML代码不能为空")
@Description("HTML代码")
private String htmlCode;
@Description("生成代码的描述")
private String description;
}
调用时通过 Validator 校验:
HtmlCodeResult result = aiCodeGeneratorService.generateHtmlCode(userMessage);
Set<ConstraintViolation<HtmlCodeResult>> violations = validator.validate(result);
if (!violations.isEmpty()) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "AI生成的HTML代码为空");
}
2. 扩展字段(如添加错误信息)
如果需要 AI 返回错误信息,可新增字段:
@Description("生成 HTML 代码文件的结果")
@Data
public class HtmlCodeResult {
@Description("HTML代码")
private String htmlCode;
@Description("生成代码的描述")
private String description;
@Description("错误信息(无错误则为空)")
private String errorMsg;
}
六、关键点回顾
- 该类是 LangChain4j 结构化输出 的核心载体,通过
@Description注解约束 AI 输出格式; @Data注解简化了 POJO 类的基础方法编写,符合项目开发规范;- 无需手动解析 JSON,LangChain4j 自动完成“AI JSON 输出 → Java 对象”的映射;
- 注解描述不仅提升代码可读性,更是指导 AI 生成正确格式的关键。
这种方式相比手动处理 JSON 解析,大幅降低了代码复杂度,也减少了 AI 输出格式不统一导致的解析异常。
loadChatHistoryToMemory 方法完整解析
一、方法核心定位
这是项目中对话记忆加载的核心方法,作用是从数据库中读取指定应用(appId)的历史对话记录,加载到 LangChain4j 的 MessageWindowChatMemory(消息窗口对话记忆)中,实现大模型对话的上下文关联能力。方法最终返回成功加载的历史消息条数,加载失败时返回 0 且不影响主流程。
二、核心依赖与前置说明
1. 关键组件
| 组件 | 作用 |
|---|---|
ChatHistory |
对话历史实体类:存储 appId、消息内容、消息类型(用户/AI)、创建时间等 |
MessageWindowChatMemory |
LangChain4j 提供的对话记忆组件:仅保留最近 N 条消息,防止上下文过长 |
QueryWrapper |
MyBatis-Plus 查询构造器:构建数据库查询条件 |
CollUtil |
Hutool 集合工具类:判空操作 |
ChatHistoryMessageTypeEnum |
消息类型枚举:区分用户消息(USER)和 AI 消息(AI) |
2. 核心参数
| 参数 | 说明 |
|---|---|
appId |
应用 ID:隔离不同应用的对话记忆,保证数据不串流 |
chatMemory |
LangChain4j 对话记忆对象:加载的历史消息最终存入此处 |
maxCount |
最大加载条数:控制记忆窗口大小,避免上下文过长导致模型推理效率下降 |
三、方法逻辑逐行解析
@Override
public int loadChatHistoryToMemory(Long appId, MessageWindowChatMemory chatMemory, int maxCount) {
try {
// 1. 构建查询条件:按appId过滤 + 按创建时间倒序 + 限制最多maxCount条
QueryWrapper queryWrapper = QueryWrapper.create()
.eq(ChatHistory::getAppId, appId) // 只查当前应用的对话
.orderBy(ChatHistory::getCreateTime, false) // 倒序(最新的在前)
.limit(1, maxCount); // 分页:第1页,最多maxCount条(MyBatis-Plus的limit是pageNum, pageSize)
// 2. 执行查询,获取历史对话列表
List<ChatHistory> historyList = this.list(queryWrapper);
// 3. 判空:无历史消息则返回0
if (CollUtil.isEmpty(historyList)) {
return 0;
}
// 4. 反转列表:数据库查出来是倒序(新→老),反转后变为正序(老→新),符合对话记忆的时间顺序
historyList = historyList.reversed();
// 5. 初始化计数器,记录成功加载的条数
int loadedCount = 0;
// 6. 清理记忆缓存:防止重复加载导致上下文重复
chatMemory.clear();
// 7. 遍历历史消息,按类型添加到对话记忆中
for (ChatHistory history : historyList) {
if (ChatHistoryMessageTypeEnum.USER.getValue().equals(history.getMessageType())) {
// 用户消息:封装为LangChain4j的UserMessage并添加到记忆
chatMemory.add(UserMessage.from(history.getMessage()));
} else if (ChatHistoryMessageTypeEnum.AI.getValue().equals(history.getMessageType())) {
// AI消息:封装为LangChain4j的AiMessage并添加到记忆
chatMemory.add(AiMessage.from(history.getMessage()));
}
loadedCount++; // 计数+1
}
// 8. 日志记录:打印加载结果,便于排查问题
log.info("成功为 appId: {} 加载 {} 条历史消息", appId, loadedCount);
// 9. 返回成功加载的条数
return loadedCount;
} catch (Exception e) {
// 10. 异常处理:打印错误日志,返回0(加载失败不影响主流程)
log.error("加载历史对话失败,appId: {}, error: {}", appId, e.getMessage(), e);
// 加载失败不影响系统运行,只是没有历史上下文
return 0;
}
}
关键步骤拆解
步骤 1:查询条件构建
eq(ChatHistory::getAppId, appId):核心隔离逻辑,只加载当前应用的对话,避免不同应用的上下文串用;orderBy(ChatHistory::getCreateTime, false):倒序查询,优先获取最新的对话(符合“消息窗口”只保留最近消息的设计);limit(1, maxCount):分页查询,只取前 maxCount 条(MyBatis-Plus 的 limit 第一个参数是页码,第二个是条数)。
步骤 4:列表反转
- 数据库查询结果是“最新的在前,最老的在后”,而对话记忆需要“最老的在前,最新的在后”(符合人类对话的时间顺序),因此通过
reversed()反转列表。
步骤 6:清理记忆缓存
- 调用
chatMemory.clear()防止重复加载:如果不清理,多次调用该方法会导致同一条消息被重复添加到记忆中,造成上下文冗余。
步骤 7:消息类型映射
- 将数据库中的
ChatHistory转换为 LangChain4j 能识别的UserMessage/AiMessage:这是“业务数据”到“LangChain4j 记忆数据”的核心映射,保证大模型能正确识别对话角色。
异常处理设计
- 捕获所有异常并返回 0,且注释明确“加载失败不影响系统运行”:这是容错设计,即使历史对话加载失败,大模型仍可基于当前请求生成响应(只是无上下文),保证核心功能可用。
四、核心设计亮点
1. 隔离性
通过 appId 过滤查询,保证不同应用的对话记忆完全隔离,避免数据串流。
2. 性能优化
- 限制最大加载条数(
maxCount):防止加载过多历史消息导致大模型上下文过长,影响推理效率; - 倒序查询 + 分页:只查需要的最新消息,减少数据库查询和内存占用。
3. 容错性
- 异常捕获后返回 0,不抛出异常:核心业务(代码生成)不受历史记忆加载失败的影响;
- 空列表直接返回 0:无历史消息时快速结束流程,避免无效遍历。
4. 兼容性
严格遵循 LangChain4j 的对话记忆规范:将业务消息类型映射为框架的 UserMessage/AiMessage,保证记忆能被大模型正确识别。
五、潜在优化点(可选)
1. 分页参数校验
maxCount 可能传入负数/0,可添加校验:
if (maxCount <= 0) {
log.warn("maxCount 非法:{},默认设置为10", maxCount);
maxCount = 10;
}
2. 消息内容脱敏
如果对话内容包含敏感信息,可在添加到记忆前脱敏:
String desensitizedMsg = DesensitizationUtil.desensitize(history.getMessage());
chatMemory.add(UserMessage.from(desensitizedMsg));
3. 批量添加消息
LangChain4j 的 chatMemory 支持批量添加,可优化遍历效率:
List<Message> messageList = new ArrayList<>();
for (ChatHistory history : historyList) {
if (USER_TYPE.equals(history.getMessageType())) {
messageList.add(UserMessage.from(history.getMessage()));
} else if (AI_TYPE.equals(history.getMessageType())) {
messageList.add(AiMessage.from(history.getMessage()));
}
}
chatMemory.addAll(messageList);
loadedCount = messageList.size();
六、关键点回顾
- 方法核心是将数据库对话历史加载到 LangChain4j 对话记忆,实现上下文关联;
- 核心逻辑:查询 → 反转顺序 → 清理缓存 → 类型映射 → 计数返回;
- 设计亮点:应用隔离、性能控制(maxCount)、容错处理(异常返回0);
- 关键细节:列表反转保证时间正序,clear() 防止重复加载。
该方法是 LangChain4j 对话记忆能力在业务中落地的核心,既保证了上下文的连续性,又通过参数控制和容错设计保证了系统稳定性。
StreamingChatModel 接口完整解析
一、接口核心定位
这是 LangChain4j 框架中流式对话模型的核心接口,定义了大模型流式交互的标准化 API,是所有支持“逐 Token 实时返回响应”的大模型(如 OpenAI GPT、文心一言等)的统一抽象。它继承了 ChatModel 的核心能力,重点扩展了流式响应处理和工具调用流式回调能力,是 Vue 工程模式中 TokenStream 实现的底层基础。
二、核心设计理念
- 标准化:统一不同大模型厂商的流式交互 API(如 OpenAI 的 SSE、Anthropic 的流式响应);
- 可扩展:通过默认方法提供基础实现,子类只需实现核心的
doChat方法; - 可监听:内置
ChatModelListener机制,支持请求/响应/异常的全生命周期监听; - 工具调用支持:原生支持工具调用的流式回调(部分请求/完整请求)。
三、核心结构与方法解析
1. 接口核心方法总览
| 方法 | 类型 | 核心作用 |
|---|---|---|
chat(ChatRequest, StreamingChatResponseHandler) |
默认方法 | 主入口:封装请求、绑定监听器、调用实际处理逻辑 |
doChat(ChatRequest, StreamingChatResponseHandler) |
默认方法 | 核心实现入口:子类需重写,实现具体厂商的流式调用 |
chat(String, StreamingChatResponseHandler) |
默认方法 | 简化版:直接传入用户消息字符串,自动封装为 ChatRequest |
chat(List<ChatMessage>, StreamingChatResponseHandler) |
默认方法 | 简化版:传入消息列表,自动封装为 ChatRequest |
defaultRequestParameters() |
默认方法 | 返回默认请求参数(如温度、最大 Token 数) |
listeners() |
默认方法 | 返回监听器列表,支持请求/响应监听 |
provider() |
默认方法 | 返回模型厂商(如 OPENAI/OTHER) |
supportedCapabilities() |
默认方法 | 返回模型支持的能力(如工具调用、结构化输出) |
2. 核心方法:chat(ChatRequest, StreamingChatResponseHandler)
这是接口的核心默认方法,封装了流式对话的全生命周期逻辑,拆解如下:
步骤 1:请求参数处理
ChatRequest finalChatRequest = ChatRequest.builder()
.messages(chatRequest.messages())
.parameters(defaultRequestParameters().overrideWith(chatRequest.parameters()))
.build();
- 合并默认请求参数(
defaultRequestParameters())和传入的请求参数,保证参数优先级(传入参数 > 默认参数); - 构建最终的
ChatRequest,包含消息列表和合并后的参数。
步骤 2:监听器与上下文初始化
List<ChatModelListener> listeners = listeners(); // 获取监听器列表
Map<Object, Object> attributes = new ConcurrentHashMap<>(); // 上下文属性(传递监听器数据)
ChatModelListener:用于监听请求发送、响应接收、异常发生等事件;attributes:线程安全的 Map,用于在监听器之间传递上下文数据。
步骤 3:包装响应处理器(核心)
StreamingChatResponseHandler observingHandler = new StreamingChatResponseHandler() {
// 1. 处理实时 Token 片段(如代码片段)
@Override
public void onPartialResponse(String partialResponse) {
handler.onPartialResponse(partialResponse); // 透传到底层处理器
}
// 2. 处理工具调用的部分请求(流式返回工具调用参数)
@Override
public void onPartialToolExecutionRequest(int index, ToolExecutionRequest partialToolExecutionRequest) {
handler.onPartialToolExecutionRequest(index, partialToolExecutionRequest);
}
// 3. 处理工具调用的完整请求(工具调用参数拼接完成)
@Override
public void onCompleteToolExecutionRequest(int index, ToolExecutionRequest completeToolExecutionRequest) {
handler.onCompleteToolExecutionRequest(index, completeToolExecutionRequest);
}
// 4. 处理完整响应(流式结束)
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
onResponse(completeResponse, finalChatRequest, provider(), attributes, listeners); // 触发监听器的响应回调
handler.onCompleteResponse(completeResponse); // 透传
}
// 5. 处理异常(核心容错)
@Override
public void onError(Throwable error) {
ChatModelListenerUtils.onError(error, finalChatRequest, provider(), attributes, listeners); // 触发监听器的异常回调
handler.onError(error); // 透传
}
};
核心作用:
- 包装用户传入的
handler,在透传回调的同时,触发监听器的生命周期事件; - 区分了“部分响应”和“完整响应”,适配工具调用的流式特性(工具调用参数可能分片段返回);
- 异常统一处理,保证监听器能捕获到所有错误。
步骤 4:触发监听器与执行实际调用
onRequest(finalChatRequest, provider(), attributes, listeners); // 触发请求发送前的监听器
doChat(finalChatRequest, observingHandler); // 调用实际的流式对话逻辑(子类实现)
3. 核心抽象方法:doChat
default void doChat(ChatRequest chatRequest, StreamingChatResponseHandler handler) {
throw new RuntimeException("Not implemented");
}
- 接口的核心扩展点,子类(如
OpenAiStreamingChatModel)必须重写; - 实现具体厂商的流式 API 调用(如 OpenAI 的 SSE 连接、字节流解析);
- 所有流式回调(
onPartialResponse/onError等)最终由该方法触发。
4. 简化版 chat 方法
default void chat(String userMessage, StreamingChatResponseHandler handler) {
ChatRequest chatRequest = ChatRequest.builder()
.messages(UserMessage.from(userMessage))
.build();
chat(chatRequest, handler);
}
default void chat(List<ChatMessage> messages, StreamingChatResponseHandler handler) {
ChatRequest chatRequest = ChatRequest.builder()
.messages(messages)
.build();
chat(chatRequest, handler);
}
- 提供简化的调用方式,无需手动构建
ChatRequest; - 适配简单场景(如仅传入用户消息字符串、固定消息列表);
- 底层仍调用核心的
chat(ChatRequest, StreamingChatResponseHandler)方法。
四、核心回调接口:StreamingChatResponseHandler
该接口是流式响应处理的核心,接口方法对应不同的流式事件:
| 方法 | 触发时机 | 业务场景 |
|---|---|---|
onPartialResponse |
模型返回单个 Token/片段 | HTML 代码片段实时返回、Vue 代码逐行生成 |
onPartialToolExecutionRequest |
工具调用参数分片段返回 | Vue 工程模式中,AI 逐步生成文件写入工具的参数 |
onCompleteToolExecutionRequest |
工具调用参数拼接完成 | 工具调用参数完整,可执行文件写入/修改操作 |
onCompleteResponse |
流式响应全部完成 | 代码生成完成,触发 Vue 工程构建 |
onError |
任何环节发生异常 | 网络错误、模型返回格式错误、工具调用失败 |
五、实际应用场景(结合项目代码)
在项目的 AiCodeGeneratorService 中,generateVueProjectCodeStream 方法返回的 TokenStream,底层就是基于 StreamingChatModel 实现的:
六、关键设计亮点
1. 开闭原则
- 默认方法提供基础实现,子类只需重写
doChat实现厂商特有逻辑; - 新增模型厂商(如百度文心一言)时,只需实现
StreamingChatModel接口,无需修改现有代码。
2. 监听器机制
- 通过
ChatModelListener实现请求/响应/异常的无侵入式监听; - 可扩展监控、日志、埋点等功能,无需修改核心调用逻辑。
3. 工具调用原生支持
- 区分“部分工具调用请求”和“完整工具调用请求”,适配复杂的工具调用流式场景;
- 是 Vue 工程模式中 AI 调用文件操作工具的底层支撑。
4. 异常容错
- 统一的
onError回调,保证所有异常能被捕获和处理; - 监听器的
onError回调,支持异常的统一监控和告警。
七、关键点回顾
StreamingChatModel是 LangChain4j 流式对话的核心抽象,统一了不同大模型的流式 API;- 核心逻辑封装在默认方法
chat中,子类只需实现doChat完成厂商特有逻辑; StreamingChatResponseHandler覆盖了流式响应的全生命周期(片段/工具调用/完成/异常);- 项目中 Vue 工程模式的
TokenStream底层依赖该接口实现工具调用的流式处理; - 监听器机制实现了无侵入式的请求/响应监控,便于扩展日志、埋点等功能。
该接口是 LangChain4j 框架“抽象统一、实现差异化”设计思想的典型体现,既降低了多模型集成的复杂度,又保证了业务层调用的简洁性。
AuthInterceptor 切面类完整解析
一、类核心定位
这是基于 Spring AOP 实现的权限校验切面,核心作用是通过 @AuthCheck 注解实现方法级别的权限控制:在目标方法执行前,自动校验当前登录用户的角色是否符合注解中指定的权限要求,不符合则抛出权限异常,符合则放行执行目标方法。
二、核心依赖与前置说明
1. 关键组件
| 组件 | 作用 |
|---|---|
@Aspect |
Spring AOP 核心注解,标记该类为切面类 |
@Around |
环绕通知注解,可在目标方法执行前后插入逻辑(此处用于执行前权限校验) |
@AuthCheck |
自定义权限校验注解,通过 mustRole 属性指定所需角色(如 ADMIN) |
UserService.getLoginUser |
从 HttpServletRequest 中获取当前登录用户(底层通常基于 Token 解析) |
UserRoleEnum |
用户角色枚举(如 ADMIN/USER),统一角色值的管理 |
BusinessException |
自定义业务异常,权限校验失败时抛出,携带错误码(NO_AUTH_ERROR) |
2. 核心注解(@AuthCheck 逻辑补充)
该切面依赖的 @AuthCheck 注解通常定义如下(补充便于理解):
@Target(ElementType.METHOD) // 仅作用于方法
@Retention(RetentionPolicy.RUNTIME) // 运行时生效,供AOP解析
public @interface AuthCheck {
/**
* 必须拥有的角色(如 "ADMIN")
*/
String mustRole() default "";
}
三、方法逻辑逐行解析
@Around("@annotation(authCheck)") // 切入点:所有标注了@AuthCheck的方法
public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {
// 1. 获取注解中指定的需要校验的角色(如 "ADMIN")
String mustRole = authCheck.mustRole();
// 2. 获取当前请求的HttpServletRequest对象(从RequestContextHolder中获取)
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
// 3. 从请求中解析当前登录用户(底层:解析Token→查询用户信息→返回User对象)
User loginUser = userService.getLoginUser(request);
// 4. 将注解中的角色字符串转换为枚举(如 "ADMIN" → UserRoleEnum.ADMIN)
UserRoleEnum mustRoleEnum = UserRoleEnum.getEnumByValue(mustRole);
// 5. 无需权限校验的场景:注解中mustRole为空 → 直接放行
if (mustRoleEnum == null) {
return joinPoint.proceed();
}
// 6. 校验当前用户的角色:将用户的角色值转换为枚举
UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValue(loginUser.getUserRole());
// 7. 用户角色无效(如数据库中角色值错误)→ 抛出无权限异常
if (userRoleEnum == null) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
// 8. 核心权限校验:要求管理员角色,但当前用户不是 → 抛出无权限异常
if (UserRoleEnum.ADMIN.equals(mustRoleEnum) && !UserRoleEnum.ADMIN.equals(userRoleEnum)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
// 9. 权限校验通过 → 执行目标方法(如接口逻辑、服务方法)
return joinPoint.proceed();
}
关键步骤拆解
步骤 1-2:获取注解参数与请求对象
@Around("@annotation(authCheck)"):切入点表达式表示“拦截所有标注了@AuthCheck的方法”,并将注解实例注入到方法参数authCheck中;RequestContextHolder:Spring 提供的请求上下文持有器,用于在非 Controller 层(如切面、服务层)获取 HttpServletRequest。
步骤 3:获取登录用户
userService.getLoginUser(request)是核心封装方法,底层逻辑通常是:- 从 request 的 Header 中获取 Token;
- 解析 Token 得到用户 ID;
- 从数据库/缓存中查询用户信息并返回;
- 若 Token 无效/过期/用户不存在,会直接抛出未登录异常。
步骤 4-5:无权限要求的放行
- 若
@AuthCheck(mustRole = "")(默认值),则mustRoleEnum为 null,直接放行; - 该设计让注解支持“可选权限校验”,无需校验时只需不指定
mustRole。
步骤 6-8:核心权限校验
- 角色枚举转换:通过
UserRoleEnum.getEnumByValue避免硬编码,保证角色值的统一性; - 异常场景:
- 用户角色值无效(如数据库中存了 “TEST”,但枚举中无该值)→ 无权限;
- 要求管理员角色,但用户是普通用户 → 无权限;
- 抛出的
BusinessException(ErrorCode.NO_AUTH_ERROR)会被全局异常处理器捕获,返回统一的权限错误响应(如{"code":403,"msg":"无权限"})。
步骤 9:放行执行目标方法
joinPoint.proceed():执行被拦截的目标方法(如 Controller 的接口方法、Service 的业务方法);- 方法返回值会作为切面的返回值,透传给上层调用者。
四、实际使用场景
1. 管理员接口权限控制
@RestController
@RequestMapping("/admin")
public class AdminController {
// 标注@AuthCheck(mustRole = "ADMIN"),仅管理员可访问
@AuthCheck(mustRole = "ADMIN")
@PostMapping("/codegen/config")
public Result<?> updateCodeGenConfig(CodeGenConfigDTO config) {
// 管理员专属逻辑:修改代码生成配置
return Result.success();
}
}
- 普通用户调用该接口 → 切面拦截 → 抛出 NO_AUTH_ERROR → 全局异常处理器返回 403;
- 管理员调用 → 切面放行 → 执行接口逻辑。
2. 无需权限的接口
@RestController
@RequestMapping("/user")
public class UserController {
// 未指定mustRole,或指定为空 → 无需权限校验
@AuthCheck
@GetMapping("/info")
public Result<?> getUserInfo(HttpServletRequest request) {
// 所有登录用户均可访问
return Result.success(userService.getLoginUser(request));
}
}
五、核心设计亮点
1. 注解驱动,无侵入式
- 权限校验逻辑与业务逻辑完全分离,只需在方法上添加注解即可实现权限控制;
- 新增/修改权限规则时,只需调整注解参数或切面逻辑,无需改动业务代码。
2. 枚举化管理角色
- 使用
UserRoleEnum而非字符串硬编码,避免角色值写错(如 “admin” vs “ADMIN”); getEnumByValue方法提供了容错能力,角色值无效时返回 null,便于统一处理。
3. 轻量级校验逻辑
- 仅校验管理员角色(当前逻辑),满足基础权限需求;
- 若需扩展多角色(如 OPERATOR 运营角色),只需新增枚举值和校验分支:
// 扩展示例:支持运营角色 if (UserRoleEnum.OPERATOR.equals(mustRoleEnum) && !UserRoleEnum.OPERATOR.equals(userRoleEnum)) { throw new BusinessException(ErrorCode.NO_AUTH_ERROR); }
4. 兼容无权限场景
- 注解默认
mustRole = "",无需校验时直接放行,降低使用成本。
六、潜在优化点
1. 支持多角色校验
当前仅支持单角色校验,可扩展为支持多角色(如 mustRoles = {"ADMIN", "OPERATOR"}):
// 注解修改
public @interface AuthCheck {
String[] mustRoles() default {};
}
// 切面逻辑修改
String[] mustRoles = authCheck.mustRoles();
if (mustRoles.length > 0) {
Set<UserRoleEnum> mustRoleSet = Arrays.stream(mustRoles)
.map(UserRoleEnum::getEnumByValue)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (!mustRoleSet.contains(userRoleEnum)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
}
2. 登录态校验增强
当前依赖 userService.getLoginUser 抛出未登录异常,可在切面中显式校验:
if (loginUser == null) {
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
}
3. 日志记录
添加权限校验日志,便于排查权限问题:
log.info("权限校验:用户ID={},角色={},需要角色={}",
loginUser.getId(), loginUser.getUserRole(), mustRole);
七、关键点回顾
- 该类是 Spring AOP 环绕通知的典型应用,核心实现方法级别的注解式权限校验;
- 核心逻辑:获取注解角色 → 获取登录用户 → 角色匹配校验 → 放行/抛异常;
- 设计亮点:注解驱动(无侵入)、枚举化角色(防硬编码)、兼容无权限场景;
- 核心依赖:
RequestContextHolder获取请求对象、UserService.getLoginUser获取登录用户、自定义异常统一返回权限错误。
该切面是项目权限控制的核心组件,保证了敏感接口(如管理员配置、代码生成模板修改)只能被指定角色访问,同时不侵入业务逻辑,符合“单一职责”原则。
如何实现将AI大模型的输出稳定地转换为结构化对象
一、核心问题与背景
在大模型开发中,将AI的自由文本输出转换为结构化对象(如 HtmlCodeResult、MultiFileCodeResult)是实现业务逻辑的关键步骤,但大模型输出存在非JSON格式、结构错误、截断等问题,直接导致解析失败。
该方案基于LangChain4j框架,通过配置优化+注解驱动+提示词约束三重手段,显著提升结构化输出的稳定性。
二、基础实现:LangChain4j 原生结构化输出
LangChain4j 提供了零侵入式的结构化输出能力,仅需两步即可实现:
- 定义结构化模型:用
@Description注解为结果类和字段添加描述,例如:@Description("生成 HTML 代码文件的结果") @Data public class HtmlCodeResult { @Description("HTML代码") private String htmlCode; @Description("生成代码的描述") private String description; } - 定义AI Service接口:将接口方法的返回值类型从
String改为自定义模型类:@SystemMessage(fromResource = "prompt/codegen-html-system-prompt.txt") HtmlCodeResult generateHtmlCode(String userMessage);
此时LangChain4j会自动:
- 在提示词中添加约束,要求AI返回符合结构的JSON;
- 将AI返回的JSON自动映射为Java对象。
三、稳定性优化:解决AI输出不稳定问题
1. 增大 max_tokens 避免输出截断
- 问题:大模型的
max_tokens限制会导致生成的JSON被截断,出现不完整的JSON结构(如缺少闭合括号)。 - 优化:在模型配置中增大
max_tokens(如设置为8192),确保完整输出JSON内容。 - 配置示例(application.yml):
ai: model: chat-model: max-tokens: 8192
2. 开启模型原生JSON模式
- 问题:部分大模型(如DeepSeek)支持通过参数强制输出JSON格式,无需依赖提示词约束。
- 优化:在配置中添加
response-format: json_object,让模型直接输出JSON,避免自由文本格式错误。 - 配置示例:
ai: model: chat-model: response-format: json_object
3. 用 @Description 注解增强字段指引
- 问题:AI可能对字段含义理解模糊,导致输出JSON字段缺失或类型错误。
- 优化:为结果类和每个字段添加
@Description注解,清晰描述字段含义,LangChain4j会将这些描述添加到提示词中,给AI更明确的生成指引。 - 示例:
@Description("多文件代码生成结果,包含HTML/CSS/JS") @Data public class MultiFileCodeResult { @Description("HTML代码内容,完整可直接运行") private String htmlCode; @Description("CSS样式代码") private String cssCode; @Description("JavaScript交互代码") private String jsCode; }
4. 提示词中补充JSON格式约束
- 问题:仅依赖注解可能不够,需在系统提示词中明确要求JSON格式。
- 优化:在系统提示词文件(如
codegen-html-system-prompt.txt)中补充:请严格按照以下JSON格式返回结果,不要添加任何多余解释: { "htmlCode": "生成的HTML代码", "description": "代码功能描述" }
四、兜底机制:LangChain4j 重试与容错
- 内置重试:LangChain4j 对结构化输出失败的请求会自动重试(默认3次),解决偶发的格式错误;
- 异常捕获:在业务代码中捕获
JsonMappingException等解析异常,提供降级处理(如返回默认对象)。
五、效果与总结
通过上述优化,结构化输出成功率可从70%左右提升至95%以上,核心优化逻辑可总结为:
| 优化手段 | 作用 |
|---|---|
增大 max_tokens |
避免JSON截断,保证结构完整 |
| 开启模型JSON模式 | 强制输出JSON,从源头减少格式错误 |
@Description 注解 |
给AI更清晰的字段指引,减少结构错误 |
| 提示词格式约束 | 双重保证JSON输出的正确性 |
| 内置重试机制 | 处理偶发的输出异常 |
这套方案既利用了LangChain4j的原生能力,又结合了模型配置和提示词优化,是生产环境中实现稳定结构化输出的最佳实践。
需要我帮你整理一份LangChain4j 结构化输出稳定化配置清单吗?里面会包含模型配置、注解示例和提示词模板,你可以直接复制到项目中使用。
更多推荐



所有评论(0)