# 文生图与图生图工具设计 > 日期:2026-05-02 > 状态:待实施 > 范围:后端工具层、图片 Provider 层、前端工具渲染、Prompt Builder ## 1. 背景与目标 系统已在模型渠道中配置了火山引擎和 MiniMax 的图片生成模型。需要在 Agent 对话中提供文生图和图生图能力,主要面向电商场景(产品主图、详情图、文章配图),并支持配置专用电商套图 Agent 一次性生成成套图片。 ### 目标 - 新增 `text_to_image` 和 `image_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 统一接口 ```typescript // 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; } 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; imageToImage(params: ImageToImageParams, creds: ImageProviderCredentials): Promise; } 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" 的问题: ```typescript 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_ratio` 或 `width + height` 传递 - 图生图通过 `subject_reference` 字段传参考图 - 特有参数:`style`(画风)、`prompt_optimizer`(prompt 优化)通过 `extra` 透传 - 响应从 `data.image_urls[]` 归一化到统一结构 - Bearer token 认证 - 60 秒超时保护 ### 3.5 扩展新渠道 新增 Provider 只需: 1. 在 `image_providers/` 下新建文件,实现 `ImageGenerationProvider` 接口 2. 在工厂函数 `getImageProvider` 中注册路由规则 ## 4. 工具层 ### 4.1 text_to_image ```typescript // 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 ```typescript // 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 后端类型扩展 ```typescript // 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 扩展 ```typescript 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 下载并转存到本地存储: ```typescript export class ImageStorageService { async persist(tempUrl: string, metadata?: { toolName: string }): Promise { 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` 类型: ```typescript 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'` 的单图渲染(`` 标签)。需要: - 将单图渲染升级为 `el-image`,支持点击预览大图 - 新增 `rawResult.type === 'images'` 分支,CSS Grid 网格布局,`el-image` 预览列表联动 - 其他类型保持原有 `
` 文本渲染

多图网格样式:`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 可覆盖)**:`n`、`aspectRatio`、`width`、`height`、`watermark`、`seed`、`responseFormat`

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

**约束参数(Agent 不可突破)**:`maxN`、`maxWidth`、`maxHeight`

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

### 10.3 extra 字段结构

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

```typescript
// 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 内部合并逻辑

```typescript
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_image` 或 `image_to_image` 时,在模型配置区域下方新增"图片生成配置"区块:

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

## 11. 专用电商套图 Agent

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

- **系统 Prompt**:电商视觉设计领域知识(平台尺寸规范、构图原则、套图工作流)
- **工具集**:`text_to_image` + `image_to_image` + `image_recognize` + `todo`
- **maxToolRounds**:30(套图需要多轮工具调用)
- **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` | 修改(图片生成配置区块) |