2026山东大学软件学院项目实训(七)
·
汇报人:者亚杰
日期:2026-05-23
本周重点
一、跳过构建-条件边
对于 HTML 和 MULTI_FILE 网站生成类型,网站生成工作节点中已经会自动保存网站文件,不需要进入项目构建节点。
除了在项目构建工作节点中写 if-else 外,还可以使用 LangGraph4j 的条件边特性:
1.工作流新增路由函数和条件边配置
.addEdge("router", "code_generator")
// 使用条件边:根据代码生成类型决定是否需要构建
.addConditionalEdges("code_generator",
edge_async(this::routeBuildOrSkip),
Map.of(
"build", "project_builder", // 需要构建的情况
"skip_build", END // 跳过构建直接结束
))
.addEdge("project_builder", END)
2.路由函数决定代码生成后是否需要项目构建
private String routeBuildOrSkip(MessagesState<String> state) {
WorkflowContext context = WorkflowContext.getContext(state);
CodeGenTypeEnum generationType = context.getGenerationType();
// HTML 和 MULTI_FILE 类型不需要构建,直接结束
if (generationType == CodeGenTypeEnum.HTML || generationType == CodeGenTypeEnum.MULTI_FILE) {
return "skip_build";
}
// VUE_PROJECT 需要构建
return "build";
}
3.项目构建工作节点中移除 if-else 逻辑
String buildResultDir;
// 一定是 Vue 项目类型:使用 VueProjectBuilder 进行构建
try {
VueProjectBuilder vueBuilder = SpringContextUtil.getBean(VueProjectBuilder.class);
// 执行 Vue 项目构建(npm install + npm run build)
boolean buildSuccess = vueBuilder.buildProject(generatedCodeDir);
if (buildSuccess) {
// 构建成功,返回 dist 目录路径
buildResultDir = generatedCodeDir + File.separator + "dist";
log.info("Vue 项目构建成功,dist 目录: {}", buildResultDir);
} else {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "Vue 项目构建失败");
}
} catch (Exception e) {
log.error("Vue 项目构建异常: {}", e.getMessage(), e);
buildResultDir = generatedCodeDir; // 异常时返回原路径
}
用条件边的优势是:
- 可视化更清晰:工作流图能直观显示不同路径
- 性能更好:直接跳过不需要的节点,避免无用的 Bean 加载
- 关注点分离:节点专注业务逻辑,边专注流程控制
二、质量检查 - 循环边
新增代码质检 Agent 工作节点,在生成代码后,检验代码是否有异常、能否正常打包,如果有异常则返回到网站生成 Agent 重新生成。
该节点所需的状态:generatedCodeDir+generationType
该节点返回的状态:qualityResult质检结果
isValid是否通过errors错误列表suggestions改进建议
1.方案选择
选择使用:工作节点 + 条件边的组合
原因如下:
1)代码质检本身是一个复杂的工作任务:
- 需要读取并拼接代码文件
- 调用 AI 进行代码分析
- 生成质检结果(错误、建议等)
- 这是实际的业务逻辑,应该用工作节点
2)根据质检结果的路由决策:
- 质检通过 → 继续到项目构建
- 质检失败 → 回到代码生成节点
- 这是流程控制逻辑,应该用条件边
2.定义数据模型
langgraph4j.model下增加QualityResult类
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class QualityResult implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 是否通过质检
*/
private Boolean isValid;
/**
* 错误列表
*/
private List<String> errors;
/**
* 改进建议
*/
private List<String> suggestions;
}
WorkflowContext状态补充字段
/**
* 质量检查结果
*/
private QualityResult qualityResult;
3.质量检查 AI 服务
1)编写质量检查 AI 提示词 code-quality-check-system-prompt.txt,提示词重点突出检查语法错误,并且强调结构化输出的 JSON 格式。
你是一个专业的代码质量检查专家。你的任务是分析用户提供的网站代码,检查语法错误等方面的问题,确保项目可以正常运行和打包。
## 检查重点
### 1. 语法和结构错误
- HTML 标签是否正确闭合
- CSS 语法是否正确
- JavaScript 语法错误
- 文件引用路径是否正确
- 缺失的依赖或资源
### 2. 代码质量
- 代码结构是否合理
- 命名规范是否一致
- 代码重复性检查
### 3. 功能完整性
- 页面功能是否完整
- 交互逻辑是否正确
- 响应式设计检查
## 输出格式
请严格按照以下 JSON 格式返回检查结果:
json
{
"isValid": true/false,
"errors": [
"具体的错误描述1",
"具体的错误描述2"
],
"suggestions": [
"改进建议1",
"改进建议2"
]
}
## 判断标准
- isValid = true: 代码无严重语法错误,能够正常运行和打包
- isValid = false: 存在语法错误、结构问题或其他会导致无法正常运行的问题
- errors: 必须修复的问题,如语法错误、缺失文件等
- suggestions: 关于如何修复错误和改进代码的建议
请仔细分析代码,提供专业的质量检查结果。
2)开发 AI 服务,利用结构化输出
public interface CodeQualityCheckService {
/**
* 检查代码质量
* AI 会分析代码并返回质量检查结果
*/
@SystemMessage(fromResource = "prompt/code-quality-check-system-prompt.txt")
QualityResult checkCodeQuality(@UserMessage String codeContent);
}
3)开发创建 AI 服务的工厂
@Slf4j
@Configuration
public class CodeQualityCheckServiceFactory {
@Resource
private ChatModel chatModel;
/**
* 创建代码质量检查 AI 服务
*/
@Bean
public CodeQualityCheckService createCodeQualityCheckService() {
return AiServices.builder(CodeQualityCheckService.class)
.chatModel(chatModel)
.build();
}
}
4.开发质量检查工作节点
1)开发一个方法来读取生成代码的文件结构和具体内容,拼接好后交给 AI 来检查,可以利用 Hutool 工具类完成
/**
* 需要检查的文件扩展名
*/
private static final List<String> CODE_EXTENSIONS = Arrays.asList(
".html", ".htm", ".css", ".js", ".json", ".vue", ".ts", ".jsx", ".tsx"
);
/**
* 读取并拼接代码目录下的所有代码文件
*/
private static String readAndConcatenateCodeFiles(String codeDir) {
if (StrUtil.isBlank(codeDir)) {
return "";
}
File directory = new File(codeDir);
if (!directory.exists() || !directory.isDirectory()) {
log.error("代码目录不存在或不是目录: {}", codeDir);
return "";
}
StringBuilder codeContent = new StringBuilder();
codeContent.append("# 项目文件结构和代码内容\n\n");
// 使用 Hutool 的 walkFiles 方法遍历所有文件
FileUtil.walkFiles(directory, file -> {
// 过滤条件:跳过隐藏文件、特定目录下的文件、非代码文件
if (shouldSkipFile(file, directory)) {
return;
}
if (isCodeFile(file)) {
String relativePath = FileUtil.subPath(directory.getAbsolutePath(),
file.getAbsolutePath());
codeContent.append("## 文件: ").append(relativePath).append("\n\n");
String fileContent = FileUtil.readUtf8String(file);
codeContent.append(fileContent).append("\n\n");
}
});
return codeContent.toString();
}
/**
* 判断是否应该跳过此文件
*/
private static boolean shouldSkipFile(File file, File rootDir) {
String relativePath = FileUtil.subPath(rootDir.getAbsolutePath(), file.getAbsolutePath());
// 跳过隐藏文件
if (file.getName().startsWith(".")) {
return true;
}
// 跳过特定目录下的文件
return relativePath.contains("node_modules" + File.separator) ||
relativePath.contains("dist" + File.separator) ||
relativePath.contains("target" + File.separator) ||
relativePath.contains(".git" + File.separator);
}
/**
* 判断是否是需要检查的代码文件
*/
private static boolean isCodeFile(File file) {
String fileName = file.getName().toLowerCase();
return CODE_EXTENSIONS.stream().anyMatch(fileName::endsWith);
}
2)开发具体的代码质量检查逻辑,调用 AI 完成检查,并更新质量检查结果状态
/**
* 代码质量检查节点
*/
@Slf4j
public class CodeQualityCheckNode {
public static AsyncNodeAction<MessagesState<String>> create() {
return node_async(state -> {
WorkflowContext context = WorkflowContext.getContext(state);
log.info("执行节点: 代码质量检查");
String generatedCodeDir = context.getGeneratedCodeDir();
QualityResult qualityResult;
try {
// 1. 读取并拼接代码文件内容
String codeContent = readAndConcatenateCodeFiles(generatedCodeDir);
if (StrUtil.isBlank(codeContent)) {
log.warn("未找到可检查的代码文件");
qualityResult = QualityResult.builder()
.isValid(false)
.errors(List.of("未找到可检查的代码文件"))
.suggestions(List.of("请确保代码生成成功"))
.build();
} else {
// 2. 调用 AI 进行代码质量检查
CodeQualityCheckService qualityCheckService =
SpringContextUtil.getBean(CodeQualityCheckService.class);
qualityResult = qualityCheckService.checkCodeQuality(codeContent);
log.info("代码质量检查完成 - 是否通过: {}", qualityResult.getIsValid());
}
} catch (Exception e) {
log.error("代码质量检查异常: {}", e.getMessage(), e);
qualityResult = QualityResult.builder()
.isValid(true) // 异常直接跳到下一个步骤
.build();
}
// 3. 更新状态
context.setCurrentStep("代码质量检查");
context.setQualityResult(qualityResult);
return WorkflowContext.saveContext(context);
});
}
}
5.修改代码生成节点
代码生成节点要判断状态中有没有错误信息,如果有的话,需要根据错误信息构造提示词,引导 AI 修复错误
1)编写构造用户消息的方法,如果存在质检失败结果则添加错误修复信息
/**
* 构造用户消息,如果存在质检失败结果则添加错误修复信息
*/
private static String buildUserMessage(WorkflowContext context) {
String userMessage = context.getEnhancedPrompt();
// 检查是否存在质检失败结果
QualityResult qualityResult = context.getQualityResult();
if (isQualityCheckFailed(qualityResult)) {
// 直接将错误修复信息作为新的提示词(起到了修改的作用)
userMessage = buildErrorFixPrompt(qualityResult);
}
return userMessage;
}
/**
* 判断质检是否失败
*/
private static boolean isQualityCheckFailed(QualityResult qualityResult) {
return qualityResult != null &&
!qualityResult.getIsValid() &&
qualityResult.getErrors() != null &&
!qualityResult.getErrors().isEmpty();
}
/**
* 构造错误修复提示词
*/
private static String buildErrorFixPrompt(QualityResult qualityResult) {
StringBuilder errorInfo = new StringBuilder();
errorInfo.append("\n\n## 上次生成的代码存在以下问题,请修复:\n");
// 添加错误列表
qualityResult.getErrors().forEach(error ->
errorInfo.append("- ").append(error).append("\n"));
// 添加修复建议(如果有)
if (qualityResult.getSuggestions() != null && !qualityResult.getSuggestions().isEmpty()) {
errorInfo.append("\n## 修复建议:\n");
qualityResult.getSuggestions().forEach(suggestion ->
errorInfo.append("- ").append(suggestion).append("\n"));
}
errorInfo.append("\n请根据上述问题和建议重新生成代码,确保修复所有提到的问题。");
return errorInfo.toString();
}
2)节点方法中调用构造用户消息
// 构造用户消息(包含原始提示词和可能的错误修复信息)
String userMessage = buildUserMessage(context);
6.修改工作流
1)新增节点和边,质检条件边可以和之前的构建条件边合并
.addNode("code_quality_check", CodeQualityCheckNode.create())
// ...
.addEdge("code_generator", "code_quality_check")
// 新增质检条件边:根据质检结果决定下一步
.addConditionalEdges("code_quality_check",
edge_async(this::routeAfterQualityCheck),
Map.of(
"build", "project_builder", // 质检通过且需要构建
"skip_build", END, // 质检通过但跳过构建
"fail", "code_generator" // 质检失败,重新生成
))
2)新写一个路由函数,根据质检结果决定下一步,直接复用之前的routeBuildOrSkip方法
private String routeAfterQualityCheck(MessagesState<String> state) {
WorkflowContext context = WorkflowContext.getContext(state);
QualityResult qualityResult = context.getQualityResult();
// 如果质检失败,重新生成代码
if (qualityResult == null || !qualityResult.getIsValid()) {
log.error("代码质检失败,需要重新生成代码");
return "fail";
}
// 质检通过,使用原有的构建路由逻辑
log.info("代码质检通过,继续后续流程");
return routeBuildOrSkip(state);
}
思考
- 通过将原有的代码逻辑判断替换为LangGraph4j条件边配置,实现了业务逻辑与流程控制的解耦。不仅简化了节点冗余的if-else判断,让工作流链路可视化、逻辑更清晰,同时规避了无效Bean加载,优化了整体执行性能,也让代码结构更规范、可维护性更强。
- 完成AI代码质检节点与循环路由能力的落地,搭建了完整的代码生成-质检-修复闭环流程。通过自定义质检数据模型、精细化AI检测提示词、文件批量解析工具方法,实现了代码语法、结构、规范性的自动化校验,同时利用条件边实现质检失败自动重试生成,大幅提升了代码生成的准确率与可用性。
- 本次开发让我深刻意识到,工作流开发中应遵循单一职责原则,节点聚焦核心业务,条件边专注流程路由。同时,自动化质检+迭代修复的模式,有效解决了代码生成出错、打包失败的问题,为后续项目稳定运行提供了保障。后续将持续优化AI质检精准度,精简代码逻辑,进一步提升工作流的稳定性和运行效率。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)