GPU_GUARD_MONOREPO/docs/superpowers/specs/2026-05-02-image-generation-tools-design.md
2026-05-20 21:39:12 +08:00

18 KiB
Raw Blame History

文生图与图生图工具设计

日期2026-05-02 状态:待实施 范围:后端工具层、图片 Provider 层、前端工具渲染、Prompt Builder

1. 背景与目标

系统已在模型渠道中配置了火山引擎和 MiniMax 的图片生成模型。需要在 Agent 对话中提供文生图和图生图能力,主要面向电商场景(产品主图、详情图、文章配图),并支持配置专用电商套图 Agent 一次性生成成套图片。

目标

  • 新增 text_to_imageimage_to_image 两个独立工具
  • 建立可扩展的图片生成 Provider 层,当前支持火山引擎和 MiniMax未来可扩展其他渠道
  • 扩展 ToolResultContent 支持多图返回
  • 前端 tool-card 支持单图/多图渲染
  • 生成图片转存本地,解决 Provider 临时 URL 过期问题
  • Prompt Builder 根据可用工具动态生成附件提示语

不做

  • 不改造 LLM Provider 层(图片生成走独立工具,不走 chat completions
  • 不新增附件上传能力(现有机制已满足图生图的参考图传递)
  • 不做图片编辑器 UI生成结果在 tool-card 内渲染和预览)

2. 架构总览

packages/backend/src/modules/netaclaw/
├── image_providers/                    # 新增:图片生成 Provider 层
│   ├── types.ts                        # 统一接口、凭证、错误类型、工厂函数
│   ├── ark.ts                          # 火山引擎OpenAI SDK images.generate
│   └── minimax.ts                      # MiniMax自有 REST API
├── tools/
│   ├── common.ts                       # 修改:新增 images 类型
│   ├── catalog.ts                      # 修改:注册两个新工具
│   └── builtin/
│       ├── text_to_image.ts            # 新增:文生图工具
│       └── image_to_image.ts           # 新增:图生图工具
├── service/
│   ├── tool_resolver.ts                # 修改:注入凭证和 provider
│   └── image_storage.ts               # 新增:图片转存本地服务
└── runtime/
    └── prompt_builder.ts               # 修改:附件提示语扩展

packages/frontend/src/modules/agent/
├── tools/renderer-registry.ts          # 修改rawResult 新增 images 类型
└── components/tool-card.vue            # 修改:单图/多图渲染

3. Provider 层

3.1 统一接口

// image_providers/types.ts

export interface ImageProviderCredentials {
  baseUrl: string;
  apiKey: string;
  supplier: string;
  modelId: string;
  promptHint: string | null;
  extra?: ToolGovernanceExtra | null;
}

export interface TextToImageParams {
  prompt: string;
  width?: number;
  height?: number;
  aspectRatio?: string;
  n?: number;
  responseFormat?: 'url' | 'base64';
  watermark?: boolean;
  seed?: number;
  extra?: Record<string, unknown>;
}

export interface ImageToImageParams extends TextToImageParams {
  referenceImage: string;
  strength?: number;
}

export interface ImageGenerationResult {
  images: { url?: string; base64?: string; width?: number; height?: number }[];
  model: string;
  provider: string;
}

export interface ImageGenerationProvider {
  readonly id: string;
  textToImage(params: TextToImageParams, creds: ImageProviderCredentials): Promise<ImageGenerationResult>;
  imageToImage(params: ImageToImageParams, creds: ImageProviderCredentials): Promise<ImageGenerationResult>;
}

export class ImageGenerationError extends Error {
  constructor(
    message: string,
    public readonly code: 'content_safety' | 'rate_limit' | 'insufficient_balance' |
                          'invalid_params' | 'timeout' | 'network' | 'unknown',
    public readonly retryable: boolean,
  ) {
    super(message);
  }
}

3.2 Provider 路由

supplier + baseUrl 联合路由,解决火山引擎 supplier 可能配为 "OpenAI" 的问题:

export function getImageProvider(supplier: string, baseUrl: string): ImageGenerationProvider | null {
  const s = supplier.toLowerCase();
  if (s === 'minimax') return providers.get('minimax')!;
  if (s === 'ark' || s === 'volcengine') return providers.get('ark')!;
  if (s === 'openai') {
    if (baseUrl.includes('volces.com') || baseUrl.includes('volcengine')) return providers.get('ark')!;
    if (baseUrl.includes('minimax')) return providers.get('minimax')!;
  }
  return null;
}

3.3 火山引擎实现 (ark.ts)

  • 使用 OpenAI SDK client.images.generate()
  • 尺寸通过 size 字段传递(如 "1024x1024""2K"
  • 图生图通过 extra_body.image 传参考图
  • 特有参数watermark 等)通过 extra_body 透传
  • 60 秒超时保护

3.4 MiniMax 实现 (minimax.ts)

  • 使用自有 REST API POST {baseUrl}/v1/image_generation
  • 尺寸通过 aspect_ratiowidth + height 传递
  • 图生图通过 subject_reference 字段传参考图
  • 特有参数:style(画风)、prompt_optimizerprompt 优化)通过 extra 透传
  • 响应从 data.image_urls[] 归一化到统一结构
  • Bearer token 认证
  • 60 秒超时保护

3.5 扩展新渠道

新增 Provider 只需:

  1. image_providers/ 下新建文件,实现 ImageGenerationProvider 接口
  2. 在工厂函数 getImageProvider 中注册路由规则

4. 工具层

4.1 text_to_image

// tools/builtin/text_to_image.ts

const Params = Type.Object({
  prompt: Type.String({ description: '图片描述,尽量详细具体' }),
  aspectRatio: Type.Optional(Type.String({
    description: '宽高比。可选: 1:1, 16:9, 4:3, 3:2, 2:3, 3:4, 9:16',
  })),
  width: Type.Optional(Type.Integer({ description: '精确宽度(像素),优先级低于 aspectRatio' })),
  height: Type.Optional(Type.Integer({ description: '精确高度(像素)' })),
  n: Type.Optional(Type.Integer({ description: '生成数量,默认 1最大 9', minimum: 1, maximum: 9 })),
  watermark: Type.Optional(Type.Boolean({ description: '是否添加水印' })),
  seed: Type.Optional(Type.Integer({ description: '随机种子,相同 seed 可复现相近结果' })),
  extra: Type.Optional(Type.Record(Type.String(), Type.Unknown(), {
    description: 'Provider 特有参数,如 MiniMax 的 style、prompt_optimizer',
  })),
});

registerSchema({
  name: 'text_to_image',
  toolset: 'vision',
  description: '根据文字描述生成图片,支持指定尺寸、数量、风格等参数。',
  capability: 'multimodal',
  visibility: 'tool',
  isCore: false,
  canDisable: true,
  supportsPromptHint: true,
  requiresModel: true,
});

工具描述会拼接 promptHint,管理员可在后台配置行业特定的默认提示词。

4.2 image_to_image

// tools/builtin/image_to_image.ts

const Params = Type.Object({
  prompt: Type.String({ description: '对参考图的修改描述' }),
  referenceImage: Type.String({ description: '参考图片 URL从用户上传附件获取' }),
  strength: Type.Optional(Type.Number({
    description: '参考图影响强度 0-1越大越接近原图', minimum: 0, maximum: 1,
  })),
  aspectRatio: Type.Optional(Type.String({ description: '宽高比' })),
  width: Type.Optional(Type.Integer({ description: '精确宽度(像素)' })),
  height: Type.Optional(Type.Integer({ description: '精确高度(像素)' })),
  n: Type.Optional(Type.Integer({ description: '生成数量,默认 1最大 9', minimum: 1, maximum: 9 })),
  watermark: Type.Optional(Type.Boolean({ description: '是否添加水印' })),
  seed: Type.Optional(Type.Integer({ description: '随机种子' })),
  extra: Type.Optional(Type.Record(Type.String(), Type.Unknown(), {
    description: 'Provider 特有参数',
  })),
});

registerSchema({
  name: 'image_to_image',
  toolset: 'vision',
  description: '基于参考图片生成新图片支持风格迁移、内容编辑等。传入参考图URL和修改描述。',
  capability: 'multimodal',
  visibility: 'tool',
  isCore: false,
  canDisable: true,
  supportsPromptHint: true,
  requiresModel: true,
});

4.3 tool_resolver 集成

两个工具在 tool_resolver 中的注入逻辑与 image_recognize 对称:

  1. 检查 filteredNames 是否包含工具名
  2. toolRegistry.getToolModelConfig() 获取模型渠道配置
  3. modelChannelService.resolveForAgent() 解析凭证
  4. 通过 getImageProvider(supplier, baseUrl) 获取 provider 实例
  5. 创建工具实例,注入 creds 和 provider
  6. 未配置时写入 disabledReasons

两个工具可以绑定不同的模型渠道(如文生图用火山引擎,图生图用 MiniMax

5. 多图返回

5.1 后端类型扩展

// tools/common.ts

export interface ImageItem {
  url: string;
  mimeType?: string;
  width?: number;
  height?: number;
  seed?: number;
}

export type ToolResultContent =
  | { type: 'text'; text: string }
  | { type: 'json'; data: unknown }
  | { type: 'image'; url: string; mimeType?: string; text?: string;
      width?: number; height?: number; bytes?: number;
      originalWidth?: number; originalHeight?: number;
      originalBytes?: number; resized?: boolean; }
  | { type: 'images'; images: ImageItem[]; text?: string };

export function imagesResult(images: ImageItem[], text?: string): ToolResultContent {
  return { type: 'images', images, text };
}

5.2 toolResultToText 扩展

if (value.type === 'images') {
  const lines = value.images.map((img, i) =>
    `[图${i + 1}] ${img.url}${img.width && img.height ? ` (${img.width}x${img.height})` : ''}`
  );
  const header = value.text || `已生成 ${value.images.length} 张图片`;
  return `${header}\n${lines.join('\n')}`;
}

5.3 返回策略

  • 单图n=1返回 type: 'image',向后兼容
  • 多图n>1返回 type: 'images',前端网格渲染

6. 图片转存

6.1 问题

MiniMax URL 有效期 24 小时,火山引擎类似。历史会话中图片会失效。

6.2 方案

新增 service/image_storage.ts,工具 execute 完成后立即将临时 URL 下载并转存到本地存储:

export class ImageStorageService {
  async persist(tempUrl: string, metadata?: { toolName: string }): Promise<string> {
    const response = await fetch(tempUrl);
    const buffer = Buffer.from(await response.arrayBuffer());
    const ext = this.detectExtension(response.headers.get('content-type'));
    const filename = `${Date.now()}-${randomUUID().slice(0, 8)}${ext}`;
    // 复用现有文件存储基础设施写入本地
    return await this.fileService.saveBuffer(buffer, filename);
  }
}

工具内部在 provider 返回后、构造 ToolResultContent 前执行转存session tree 中存储的是永久本地 URL。

7. 前端渲染

7.1 renderer-registry 扩展

ToolRenderSource.rawResult 新增 images 类型:

rawResult?: {
  type: 'text' | 'json' | 'image' | 'images';
  // 现有字段...
  images?: { url: string; mimeType?: string; width?: number; height?: number; seed?: number }[];
};

7.2 message-item.vue 渲染

当前 message-item.vue 已有 rawResult.type === 'image' 的单图渲染(<img> 标签)。需要:

  • 将单图渲染升级为 el-image,支持点击预览大图
  • 新增 rawResult.type === 'images' 分支CSS Grid 网格布局,el-image 预览列表联动
  • 其他类型保持原有 <pre> 文本渲染

多图网格样式:grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)),自适应列数。

8. Prompt Builder 扩展

8.1 改动

buildLLMMessages 新增 toolNames 参数,附件提示语根据 Agent 实际可用工具动态生成:

  • image_recognize → 提示"如需分析图片内容,请使用 image_recognize 工具"
  • image_to_image → 提示"如需基于图片生成新图片,请使用 image_to_image 工具,将图片 URL 作为 referenceImage 参数"
  • 两者都有 → 两条提示都输出

避免提到 Agent 没有的工具。

9. 错误处理

Provider 层统一抛出 ImageGenerationError,包含错误码和是否可重试标记:

错误码 含义 可重试 Agent 可见信息
content_safety 内容安全拦截 "提示词触发内容安全策略,请调整描述"
rate_limit 限流 "当前请求过多,请稍后重试"
insufficient_balance 余额不足 "模型渠道余额不足"
invalid_params 参数错误 具体参数错误信息
timeout 超时60s "生成超时,可尝试降低图片尺寸"
network 网络错误 "网络连接失败"

工具层捕获后返回结构化错误文本Agent 可据此决策(换 prompt 重试或告知用户)。

10. 工具参数控制权

10.1 问题

图片生成工具的参数n、aspectRatio、watermark、width/height 等)存在三个来源:

来源 设置者 例子
工具管理页 extra 管理员 默认 n=1、watermark=true
Agent 系统 prompt 管理员 "电商主图用 1:1"
Agent 运行时 tool_use Agent响应用户 { n: 4, aspectRatio: "16:9" }

如果管理员在工具管理页设置了 n=1但用户说"生成 4 张"Agent 传了 n=4应该听谁的

10.2 设计原则Agent 决策优先,管理员设默认值

参数分为两类:

默认值参数Agent 可覆盖)naspectRatiowidthheightwatermarkseedresponseFormat

管理员在工具管理页的 extra 字段中配置默认值。Agent 不传时用默认值Agent 传了则以 Agent 为准。这些参数在工具编辑页可查看可编辑,标注"默认值Agent 可覆盖"。

约束参数Agent 不可突破)maxNmaxWidthmaxHeight

管理员设置硬上限,工具 execute 内部做 clamp。防止 Agent 被用户诱导生成过多或过大图片导致成本失控。这些参数在工具编辑页可查看可编辑,标注"硬上限"。

10.3 extra 字段结构

扩展 netaclaw_tool.extra,图片工具新增:

// ToolGovernanceExtra 扩展
export type ToolGovernanceExtra = {
  allowInSubagent?: boolean;
  workerRoutingStrategy?: ToolWorkerRoutingStrategy;
  // 图片工具新增
  imageDefaults?: {
    n?: number;
    aspectRatio?: string;
    width?: number;
    height?: number;
    watermark?: boolean;
    responseFormat?: 'url' | 'base64';
  };
  imageConstraints?: {
    maxN?: number;          // 默认 9
    maxWidth?: number;      // 默认 2048
    maxHeight?: number;     // 默认 2048
  };
};

10.4 工具 execute 内部合并逻辑

async execute(_id, params) {
  const defaults = creds.extra?.imageDefaults ?? {};
  const constraints = creds.extra?.imageConstraints ?? {};

  const merged = {
    prompt: params.prompt,
    n: Math.min(params.n ?? defaults.n ?? 1, constraints.maxN ?? 9),
    aspectRatio: params.aspectRatio ?? defaults.aspectRatio,
    width: clamp(params.width ?? defaults.width, constraints.maxWidth ?? 2048),
    height: clamp(params.height ?? defaults.height, constraints.maxHeight ?? 2048),
    watermark: params.watermark ?? defaults.watermark ?? false,
    // ...
  };

  const result = await provider.textToImage(merged, creds);
  // ...
}

10.5 前端工具编辑页

工具编辑抽屉中,当工具名为 text_to_imageimage_to_image 时,在模型配置区域下方新增"图片生成配置"区块:

┌─ 图片生成配置 ──────────────────────────────┐
│                                              │
│  默认值Agent 可覆盖)                        │
│  ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│  │ 数量: 1  │ │ 比例: 1:1│ │ 水印: ☑     │ │
│  └──────────┘ └──────────┘ └──────────────┘ │
│                                              │
│  硬上限                                       │
│  ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│  │最大数量:9│ │最大宽:2048│ │最大高:2048   │ │
│  └──────────┘ └──────────┘ └──────────────┘ │
│                                              │
│  ⓘ 默认值在 Agent 未指定时生效Agent 可根据   │
│    用户指令覆盖。硬上限不可突破。               │
└──────────────────────────────────────────────┘

11. 专用电商套图 Agent

通过管理后台 Agent 编辑页配置:

  • 系统 Prompt:电商视觉设计领域知识(平台尺寸规范、构图原则、套图工作流)
  • 工具集text_to_image + image_to_image + image_recognize + todo
  • maxToolRounds30套图需要多轮工具调用
  • promptHint:各工具在后台配置行业特定的默认提示词
  • imageDefaults:工具管理页配置电商常用默认值(如 aspectRatio: "1:1"、watermark: false
  • imageConstraints:设置 maxN 防止成本失控

典型流程:用户描述产品 → Agent 规划套图方案 → 逐张生成 → 如有参考图则用图生图 → 汇总输出。

12. 完整改动清单

文件 改动类型
Provider image_providers/types.ts 新增
Provider image_providers/ark.ts 新增
Provider image_providers/minimax.ts 新增
工具 tools/common.ts 修改
工具 tools/builtin/text_to_image.ts 新增
工具 tools/builtin/image_to_image.ts 新增
注册 tools/catalog.ts 修改
治理 tools/manifest.ts 修改ToolGovernanceExtra 扩展)
解析 service/tool_resolver.ts 修改
存储 service/image_storage.ts 新增
Prompt runtime/prompt_builder.ts 修改
前端 tools/renderer-registry.ts 修改
前端 components/message-item.vue 修改(图片渲染升级 + 多图支持)
前端 views/tools.vue 修改(图片生成配置区块)