483 lines
18 KiB
Markdown
483 lines
18 KiB
Markdown
|
|
# 文生图与图生图工具设计
|
|||
|
|
|
|||
|
|
> 日期: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<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" 的问题:
|
|||
|
|
|
|||
|
|
```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<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` 类型:
|
|||
|
|
|
|||
|
|
```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'` 的单图渲染(`<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 可覆盖)**:`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` | 修改(图片生成配置区块) |
|