一、基本信息

组号:69

组员:李重昊

负责模块:AI 工作流 —— 图片收集节点

二、任务概述

在 LangGraph4j 工作流中完成图片收集节点开发,根据用户自然语言需求自动规划并收集网站所需图片,为后续提示词增强与代码生成提供素材。具体包括:

  1. 封装 Pexels 内容图片搜索工具
  2. 封装 UnDraw 插画图片搜索工具
  3. 实现 Mermaid CLI + 腾讯云 COS 架构图绘制工具
  4. 接入 阿里云 DashScope Logo 文生图工具
  5. 实现 AI 图片收集规划服务,输出结构化 ImageCollectionPlan
  6. 在 ImageCollectorNode 中通过 CompletableFuture 并发调用四类工具并汇总结果

三、需求分析与方案设计

1.1 核心功能拆解

功能说明
智能规划AI 分析用户提示词,确定需要的图片类型及参数
内容图片Pexels 关键词搜索实景/产品类配图
插画图片UnDraw 关键词搜索装饰性矢量插画
架构图片

Mermaid 代码转 SVG,上传 COS 获取 URL

logo

DashScope 文生图按描述生成品牌标识

结果汇总写入 WorkflowContext.imageList 供下游使用

1.2 业务处理流程

用户输入 originalPrompt
    → ImageCollectionPlanService(AI 规划)
    → ImageCollectionPlan(四类任务列表)
    → 并发调用四类 Tool
    → 汇总 List<ImageResource>
    → 写入 WorkflowContext.imageList
    → 流转至「提示词增强」节点

工作流中图片收集节点作为入口,与后续节点串联如下:

                .addNode("image_collector", ImageCollectorNode.create())
                .addNode("prompt_enhancer", PromptEnhancerNode.create())
                .addNode("router", RouterNode.create())
                .addNode("code_generator", CodeGeneratorNode.create())
                .addNode("project_builder", ProjectBuilderNode.create())
                // 添加边
                .addEdge(START, "image_collector")
                .addEdge("image_collector", "prompt_enhancer")
                .addEdge("prompt_enhancer", "router")
                .addEdge("router", "code_generator")
                .addEdge("code_generator", "project_builder")
                .addEdge("project_builder", END)

START → image_collector → prompt_enhancer → ... 表明图片收集是整个 Agent 工作流的第一步;收集到的图片 URL 将在后续节点拼入增强提示词,供代码生成使用。

1.3 技术方案选型

方案优点缺点造型
AI多轮工具调用实现简单耗时长、Token 多、易误输出代码不采用
规划+节点内并发性能好、改动小、逻辑集中需手动管理线程池主选
LangGraph4j 并行分支可视化清晰需额外多个节点扩展

三、数据模型设计

3.1 图片资源对象 ImageResource

所有工具统一返回该结构,便于在工作流中传递

public class ImageResource implements Serializable {
    /**
     * 图片类别
     */
    private ImageCategoryEnum category;
    /**
     * 图片描述
     */
    private String description;
    /**
     * 图片地址
     */
    private String url;

category 标识用途,description 供 AI 理解图片语义,url 供前端或生成代码直接引用。

3.2 图片类型枚举

public enum ImageCategoryEnum {
    CONTENT("内容图片", "CONTENT"),
    LOGO("LOGO图片", "LOGO"),
    ILLUSTRATION("插画图片", "ILLUSTRATION"),
    ARCHITECTURE("架构图片", "ARCHITECTURE");

与四种工具一一对应,代码生成阶段可按类别决定图片在页面中的位置。

3.3 图片收集计划 ImageCollectionPlan

由 AI 结构化输出,使用 record 简化任务定义:

    private List<ImageSearchTask> contentImageTasks;
    private List<IllustrationTask> illustrationTasks;
    private List<DiagramTask> diagramTasks;
    private List<LogoTask> logoTasks;

    public record ImageSearchTask(String query) implements Serializable {}
    public record IllustrationTask(String query) implements Serializable {}
    public record DiagramTask(String mermaidCode, String description) implements Serializable {}
    public record LogoTask(String description) implements Serializable {}

各 record 字段与对应工具入参严格映射,节点调用时无需额外转换

四、四类图片收集工具实现

4.1 内容图片收集工具 ImageSearchTool

    @Tool("搜索内容相关的图片,用于网站内容展示")
    public List<ImageResource> searchContentImages(@P("搜索关键词") String query) {
        List<ImageResource> imageList = new ArrayList<>();
        try (HttpResponse response = HttpRequest.get(PEXELS_API_URL)
                .header("Authorization", pexelsApiKey)
                .form("query", query)
                .form("per_page", SEARCH_COUNT)
                .form("page", 1)
                .execute()) {
            // ...
                imageList.add(ImageResource.builder()
                        .category(ImageCategoryEnum.CONTENT)
                        .description(photo.getStr("alt", query))
                        .url(src.getStr("medium"))
                        .build());

调用 Pexels /v1/searchAuthorization 头传入 API Key;每次最多 6 张,取 medium 尺寸;失败返回空列表,不阻断整体流程。

4.2 插画图片收集工具UndrawIllustrationTool

    @Tool("搜索插画图片,用于网站美化和装饰")
    public List<ImageResource> searchIllustrations(@P("搜索关键词") String query) {
        String apiUrl = String.format(unDrawApiUrl, query, query);
        try (HttpResponse response = HttpRequest.get(apiUrl).timeout(10000).execute()) {
            // ... 解析 pageProps.initialResults ...
                    imageList.add(ImageResource.builder()
                            .category(ImageCategoryEnum.ILLUSTRATION)
                            .description(title)
                            .url(media)
                            .build());

API 地址从配置读取,支持更换 UnDraw 的 Next.js buildId;解析 initialResults 中的 title 与 media;10 秒超时防止阻塞工作流。

4.3 架构图绘制工具 MermaidDiagramTool

    @Tool("将 Mermaid 代码转换为架构图图片,用于展示系统结构和技术关系")
    public List<ImageResource> generateMermaidDiagram(@P("Mermaid 图表代码") String mermaidCode,
                                                      @P("架构图描述") String description) {
            File diagramFile = convertMermaidToSvg(mermaidCode);
            String cosUrl = cosManager.uploadFile(keyName, diagramFile);
                return Collections.singletonList(ImageResource.builder()
                        .category(ImageCategoryEnum.ARCHITECTURE)
                        .description(description)
                        .url(cosUrl)
                        .build());

Mermaid 转 SVG 核心逻辑:

        String command = SystemUtil.getOsInfo().isWindows() ? "mmdc.cmd" : "mmdc";
        String cmdLine = String.format("%s -i %s -o %s -b transparent",
                command,
                tempInputFile.getAbsolutePath(),
                tempOutputFile.getAbsolutePath());
        RuntimeUtil.execForStr(cmdLine);

先将 Mermaid 写入临时 .mmd,调用 mmdc 生成透明背景 SVG,再通过 CosManager 上传 COS 获得长期可访问 URL;自动区分 Windows/Linux 命令。

4.4 Logo 图片生成工具 LogoGeneratorTool

    @Tool("根据描述生成 Logo 设计图片,用于网站品牌标识")
    public List<ImageResource> generateLogos(@P("Logo 设计描述,如名称、行业、风格等,尽量详细") String description) {
            String logoPrompt = String.format("生成 Logo,Logo 中禁止包含任何文字!Logo 介绍:%s", description);
            ImageSynthesisParam param = ImageSynthesisParam.builder()
                    .apiKey(dashScopeApiKey)
                    .model(imageModel)
                    .prompt(logoPrompt)
                    .size("512*512")
                    .n(1)
                    .build();
            // ... 解析返回 url ...
                        logoList.add(ImageResource.builder()
                                .category(ImageCategoryEnum.LOGO)
                                .description(description)
                                .url(imageUrl)
                                .build());

五、AI图片收集规划服务

5.1 系统提示词

## 输出要求

请严格按照 JSON 格式返回图片收集计划,包含以下字段:
- contentImageTasks: 数组,元素含 query 字段
- illustrationTasks: 数组,元素含 query 字段
- diagramTasks: 数组,元素含 mermaidCode 和 description 字段
- logoTasks: 数组,元素含 description 字段

注意:
- 如果某种类型的图片不需要,对应数组可以为空
- mermaidCode 要是有效的 Mermaid 语法代码
- 只规划图片收集任务,不要输出任何网站代码

5.2 AI服务接口与工厂

public interface ImageCollectionPlanService {

    @SystemMessage(fromResource = "prompt/image-collection-plan-system-prompt.txt")
    ImageCollectionPlan planImageCollection(@UserMessage String userPrompt);
}

 @Bean
 public ImageCollectionPlanService imageCollectionPlanService() {
     return AiServices.builder(ImageCollectionPlanService.class)
                .chatModel(chatModel)
                .build();
}

复用 DeepSeek ChatModel,利用 LangChain4j 结构化输出,将 AI 回复直接映射为 ImageCollectionPlan,无需手写 JSON 解析。

六、图片收集工作节点实现

6.1 SpringContextUtil

节点通过静态 create() 创建,无法直接 @Resource 注入,故封装上下文工具:

@Component
public class SpringContextUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtil.applicationContext = applicationContext;
    }

    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }

6.2 ImageCollectorNode 核心逻辑

阶段一:AI 规划

                ImageCollectionPlanService planService = SpringContextUtil.getBean(ImageCollectionPlanService.class);
                ImageCollectionPlan plan = planService.planImageCollection(originalPrompt);
                log.info("获取到图片收集计划,开始并发执行");

阶段二:四类工具并发执行

                if (plan.getContentImageTasks() != null) {
                    ImageSearchTool imageSearchTool = SpringContextUtil.getBean(ImageSearchTool.class);
                    for (ImageCollectionPlan.ImageSearchTask task : plan.getContentImageTasks()) {
                        futures.add(CompletableFuture.supplyAsync(() ->
                                imageSearchTool.searchContentImages(task.query())));
                    }
                }
                // 插画、架构图、Logo 同理 ...
                CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

阶段三:汇总写入上下文

            context.setCurrentStep("图片收集");
            context.setImageList(collectedImages);
            return WorkflowContext.saveContext(context);

七、总结

本次修改主要完善了AI生成网页的图片的一些显示功能,之前生成的图片都是随机的占位符。本次改动的工作量较大,我这次完成的是其中的部分配置。

然后这次成果的测试中我们发现编辑模式选择编辑块有一点问题,不能完全确定形状,而且会重新生成一个新版本覆盖之前的版本,这样可能比较消耗token,也是后续改善的方向。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐