🎯 流式改造优化阶段

🎯 流式改造优化阶段:

流式改造的需求分析:

为了提升用户体验,让用户能实时看到AI生成代码的过程,而不是等待最终结果,需要将原来的同步调用改造为流式输出。

1. 配置流式模型

  • 在yaml配置文件中新增streaming-chat-model配置,设置最大token防止截断

  • 保持与原有chat-model相同的参数,但使用不同的Bean

  • 支持流式响应的模型配置

      # 流式响应不支持结构化输出,复制的时候需要注释掉
      # strict-json-schema: true
      # response-format: json_object

2. 服务工厂改造

  • 在AiCodeGeneratorServiceFactory中注入StreamingChatModel

  • 修改AI服务创建方式,从简单的create()改为builder()模式

  • 同时支持同步和流式两种模型,保持向后兼容

3. AI服务接口扩展

  • 在原有接口基础上新增流式方法generateHtmlCodeStream()和generateMultiFileCodeStream()

  • 关键差异:返回值从结构化对象(HtmlCodeResult)改为字符串流(Flux)

  • 使用相同的@SystemMessage注解,保持提示词一致性

4. 解析器登场

❓ 问题:为什么流式输出需要解析器,而结构化输出不需要?

✅ 答案:

  • 结构化输出:AI直接返回JSON格式,LangChain4j自动转换为对象

  • 流式输出:AI返回的是字符串片段流,包含html、css等标记,需要正则表达式提取纯代码

解析器工作原理:

  1. 定义三种正则模式:HTML_CODE_PATTERN、CSS_CODE_PATTERN、JS_CODE_PATTERN

    2.两个解析方法:parseHtmlCode()处理单文件,parseMultiFileCode()处理多文件

    3.提取逻辑:用正则匹配```标记包围的代码块,提取纯净代码内容

    4.容错处理:如果没找到代码块,将整个内容作为代码处理

5. 门面类流式方法改造

核心逻辑变化:

原来:AI服务 → 结构化对象 → 文件保存

现在:AI服务 → 字符串流 → 拼接完整字符串 → 解析器处理 → 结构化对象 → 文件保存

实现细节:

  1. 使用StringBuilder在doOnNext()中实时收集字符串片段

  2. 在doOnComplete()中触发解析和保存逻辑

  3. 调用原始的CodeParser静态方法解析完整字符串

 return result
            .doOnNext(chunk -> {
                // 实时收集代码片段
                codeBuilder.append(chunk);
            })
            .doOnComplete(() -> {
                // 流式返回完成后保存代码
                try {
                    String completeHtmlCode = codeBuilder.toString();
                    HtmlCodeResult htmlCodeResult = CodeParser.parseHtmlCode(completeHtmlCode);
                    // 保存代码到文件
                    File savedDir = CodeFileSaver.saveHtmlCodeResult(htmlCodeResult);
                    log.info("保存成功,路径为:" + savedDir.getAbsolutePath());
                } catch (Exception e) {
                    log.error("保存失败: {}", e.getMessage());
                }
            });

两段相似代码(下一节优化)

  1. 复用原有的CodeFileSaver保存文件

  2. 错误处理和日志记录

6. 统一入口扩展

  • 新增generateAndSaveCodeStream()方法,与原有的generateAndSaveCode()并行存在

  • 保持相同的参数和分发逻辑,但调用流式处理方法

  • 用户可以根据需要选择同步或流式调用

/**
 * 统一入口:根据类型生成并保存代码(流式)
 *
 * @param userMessage     用户提示词
 * @param codeGenTypeEnum 生成类型
 */
public Flux<String> generateAndSaveCodeStream(String userMessage, CodeGenTypeEnum codeGenTypeEnum) {
    if (codeGenTypeEnum == null) {
        throw new BusinessException(ErrorCode.SYSTEM_ERROR, "生成类型为空");
    }
    return switch (codeGenTypeEnum) {
        case HTML -> generateAndSaveHtmlCodeStream(userMessage);
        case MULTI_FILE -> generateAndSaveMultiFileCodeStream(userMessage);
        default -> {
            String errorMessage = "不支持的生成类型:" + codeGenTypeEnum.getValue();
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, errorMessage);
        }
    };
}

7. 测试验证

  • 编写单元测试验证解析器功能正确性

  • 测试流式调用的完整流程

  • 使用collectList().block()阻塞等待流式完成,验证最终结果

🔑 关键认知总结:

  1. 解析器的回归:流式输出重新需要解析器,因为AI返回的是原始文本流

    2.代码复用:巧妙复用了原有的文件保存器和结构化对象

    3.用户体验提升:用户可以实时看到生成过程,而不是等待最终结果

    4.架构兼容性:同时支持同步和流式两种调用方式

流式改造的本质:将"等待完整结果"变为"实时收集片段+最终解析"的模式!

posted @ 2025-08-13 10:47  你小志蒸不戳  阅读(12)  评论(0)    收藏  举报