从零写一个FireRed‑Image‑Edit v1.1插件:在OpenClaw里玩转AI修图与ID一致性

1 阅读10分钟

无意间发现了一个巨牛的人工智能教程,忍不住分享一下给大家。很通俗易懂,重点是还非常风趣幽默,像看小说一样。网址是captainbed.cn/jj。希望更多人能加入到我们AI领域。

引子:当你修了图却丢了脸

上周陪朋友拍证件照,摄影师一番操作猛如虎,磨皮美白瘦脸上齐活。结果拿到底片我懵了——这照片里的人确实是个帅哥,但跟我有半毛钱关系吗?我妈看了都得问一句"这谁啊"。这种"修图修到换头"的惨案,估计很多人都遇到过。

现在市面上那些修图软件,为了讨好眼球,往往把"美颜"和"变形"划等号。拍张自拍照,P完发出去,朋友见面还得问"你朋友圈那个是你?"。这种尴尬,直到我遇见 FireRed‑Image‑Edit v1.1 才算终结——这玩意儿是小红书在2025年底甩出来的开源大招,主打的就是让你变美,但不让你变样

更妙的是,这么强的修图能力,完全能塞进 OpenClaw 这个AI代理框架里。想象一下,你在微信里@你的AI助理:"把这张游客照里的路人P掉,背景换成马尔代夫,但脸必须是我",几秒钟后,一张足以乱真的度假大片就发回来了,而且你女朋友绝对认得出这是你。

今天咱们就聊聊怎么手搓一个插件,把这俩技术拧成一股绳。

先搞懂这俩"大拿"是干嘛的

FireRed v1.1:开源修图圈的"认脸狂魔"

FireRed‑Image‑Edit v1.1 这名字听着像宝可梦火红叶绿,实际是个30B参数的大模型。它最牛的地方在于ID一致性保持——你可以让它给你换背景、换衣服、换发型,甚至把你塞进赛博朋克2077的世界里,但你的五官轮廓、facial features(面部特征)基本纹丝不动。

它基于Flux架构,训练数据里塞了大量多元素编辑样本,支持同时处理人物、服装、背景、动作等十余种元素的自由组合。简单说,你丢给它几张参考图,它能把这些元素像拼乐高一样重组,还能自动裁剪对齐,不用你手动抠图。

硬件要求方面,本地跑需要30GB显存,推理大概4.5秒一张图。如果你跟我一样穷得叮当响,也可以薅fal.ai、WaveSpeedAI这些平台的羊毛,他们托管了现成的API,按量付费。

OpenClaw:你的AI"数字分身"

OpenClaw 前身叫Clawdbot,后来改名叫Moltbot,现在定名OpenClaw,是2026年最火的AI Agent框架之一。它跟ChatGPT那种"聊完就忘"的对话不同,OpenClaw住在你服务器上,有记忆、有状态,能调用工具,还能接入Telegram、Discord、飞书、企业微信等各种IM平台。

它的插件系统特别灵活,你可以用TypeScript写各种"技能"给它装上。比如今天我们要写的修图插件,本质上就是在教OpenClaw"如何当一个好摄影师"。

搭台子:准备工作

把OpenClaw跑起来

最简单的办法是找个腾讯云轻量服务器,选Ubuntu 22.04镜像,然后在应用市场里一键部署OpenClaw。部署好后,你应该能在终端里执行:

openclaw --version

看到版本号就稳了

然后初始化开发环境:

mkdir -p ~/.openclaw-dev/workspace/extensions/firered-editor
cd ~/.openclaw-dev/workspace/extensions/firered-editor
npm init -y
npm install typescript @types/node node-fetch --save-dev

准备FireRed的"画笔"

如果你打算本地部署FireRed v1.1,得确保有30GB显存。模型权重在HuggingFace和ModelScope都能下到,还支持GGUF轻量化格式。不过更实际的做法是用云端API,比如fal.ai的端点,这样不用买A100也能玩。

假设你已经有了API地址(无论是http://localhost:8080还是https://fal.ai/api/fal-ai/firered-image-edit-v1.1),记下来,待会儿写代码要用。

插件开发:给OpenClaw装上"修图手"

OpenClaw的插件遵循清单+逻辑的双文件结构。你需要写:

  1. openclaw.plugin.json —— 插件身份证,告诉系统你是谁、能干啥、要啥权限
  2. src/index.ts —— 业务逻辑,TypeScript实现
  3. tsconfig.json —— 编译配置

步骤1:写好插件清单

创建 openclaw.plugin.json

{
"id": "firered-image-edit-v1",
"name": "FireRed智能修图",
"version": "1.1.0",
"description": "集成FireRed-Image-Edit v1.1,实现高质量图像编辑与ID一致性保持",
"author": "YourName",
"license": "MIT",
"entry": "dist/index.js",
"permissions": [
"network:fetch",
"filesystem:read",
"filesystem:write"
],
"config": {
"schema": {
"type": "object",
"properties": {
"apiEndpoint": {
"type": "string",
"description": "FireRed API端点地址",
"default": "https://fal.ai/api/fal-ai/firered-image-edit-v1.1"
},
"apiKey": {
"type": "string",
"description": "API密钥(云端服务需要)"
},
"imageSize": {
"type": "string",
"enum": ["square_hd", "portrait_4_3", "landscape_16_9"],
"default": "square_hd"
},
"guidanceScale": {
"type": "number",
"default": 4.0,
"description": "提示词遵循度,范围1-10"
}
},
"required": ["apiEndpoint"]
}
}
}

注意permissions字段,OpenClaw对权限管控很严,你必须显式声明要联网(调API)、要读写文件(存图片)。这比你手机APP的权限管理还严格,防止插件瞎搞。

步骤2:核心逻辑实现

创建 src/index.ts

import { Plugin, Tool, ToolContext } from '@openclaw/sdk';
import * as fs from 'fs';
import * as path from 'path';
import fetch from 'node-fetch';

interface FireRedConfig {
apiEndpoint: string;
apiKey?: string;
imageSize: string;
guidanceScale: number;
}

export default class FireRedPlugin implements Plugin {
private config: FireRedConfig;
constructor(config: FireRedConfig) {
this.config = config;
}

async initialize(): Promise<void> {
console.log('🎨 FireRed Image Edit v1.1 插件初始化完成');
}

getTools(): Tool[] {
return [
{
name: 'edit_image',
description: '使用FireRed-Image-Edit v1.1编辑图像,保持人物ID一致性。支持多元素融合,如换背景、换衣服等',
parameters: {
type: 'object',
properties: {
sourceImage: { type: 'string', description: '原图路径或URL' },
prompt: { type: 'string', description: '编辑指令,例如:换成海边背景,保持人物姿势' },
referenceImages: { type: 'array', items: { type: 'string' }, description: '参考图片路径(可选,用于多元素融合)' },
outputPath: { type: 'string', description: '输出图片保存路径' },
steps: { type: 'integer', default: 30, description: '推理步数,30-50之间效果较好' }
},
required: ['sourceImage', 'prompt', 'outputPath']
},
handler: this.handleEdit.bind(this)
},
{
name: 'portrait_retouch',
description: '人像精修模式,自动美颜但保持面部特征',
parameters: {
type: 'object',
properties: {
imagePath: { type: 'string', description: '人像照片路径' },
style: { type: 'string', enum: ['natural', 'studio', 'film'], description: '修图风格' },
outputPath: { type: 'string' }
},
required: ['imagePath', 'style', 'outputPath']
},
handler: this.handleRetouch.bind(this)
}
];
}

private async handleEdit(ctx: ToolContext, params: any): Promise<any> {
const { sourceImage, prompt, referenceImages = [], outputPath, steps = 30 } = params;

// 构建FireRed v1.1 API请求体
const requestBody: any = {
  prompt,
  image_size: this.config.imageSize,
  num_inference_steps: steps,
  guidance_scale: this.config.guidanceScale,
  num_images: 1,
  output_format: 'png',
  sync_mode: false  // 异步模式,适合生产环境
};

// 处理图片输入:支持URL和本地文件
const imageUrls: string[] = [];

if (sourceImage.startsWith('http')) {
  imageUrls.push(sourceImage);
} else {
  const buffer = fs.readFileSync(sourceImage);
  const base64 = buffer.toString('base64');
  imageUrls.push(`data:image/png;base64,${base64}`);
}

// 处理参考图(多元素融合的关键)
for (const ref of referenceImages) {
  if (ref.startsWith('http')) {
    imageUrls.push(ref);
  } else {
    const buffer = fs.readFileSync(ref);
    imageUrls.push(`data:image/png;base64,${buffer.toString('base64')}`);
  }
}

requestBody.image_urls = imageUrls;

ctx.log(`🚀 正在调用FireRed v1.1...`);
ctx.log(`📝 提示词: ${prompt}`);
ctx.log(`🖼️ 共${imageUrls.length}张参考图`);

try {
  const response = await fetch(this.config.apiEndpoint, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Key ${this.config.apiKey}`,
      'Accept': 'application/json'
    },
    body: JSON.stringify(requestBody)
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`API返回错误: ${response.status} - ${errorText}`);
  }

  const result = await response.json() as any;
  
  // 提取生成图片的URL
  const imageUrl = result.images?.[0]?.url || result.data?.images?.[0]?.url;
  
  if (!imageUrl) {
    throw new Error('无法从响应中提取图片URL');
  }

  // 下载保存到本地
  const imgRes = await fetch(imageUrl);
  const buffer = await imgRes.buffer();
  
  const dir = path.dirname(outputPath);
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
  
  fs.writeFileSync(outputPath, buffer);
  
  return {
    success: true,
    outputPath,
    message: '图像编辑完成,已保持ID一致性',
    inferenceTime: result.timings?.inference || 'unknown',
    promptUsed: prompt
  };
  
} catch (error) {
  ctx.error(`编辑失败: ${error.message}`);
  throw error;
}
}

private async handleRetouch(ctx: ToolContext, params: any): Promise<any> {
const { imagePath, style, outputPath } = params;
// 定义不同风格的提示词
const stylePrompts: Record<string, string> = {
  natural: '自然美颜,轻微磨皮,去除瑕疵但保留皮肤纹理和毛孔细节,保持面部特征不变',
  studio: '专业影棚精修,柔和光影,精致妆容,杂志封面质感',
  film: '胶片电影风格,暖色调,轻微颗粒感,怀旧氛围'
};

return this.handleEdit(ctx, {
  sourceImage: imagePath,
  prompt: stylePrompts[style] || stylePrompts.natural,
  outputPath,
  steps: 35  // 人像用更高质量
});
}
}

这段代码的核心在于handleEdit方法。FireRed v1.1支持多元素融合,关键就是image_urls数组。你可以把原图、参考服装图、参考背景图一股脑塞进去,模型会自动理解"我要把这个人的脸,放到那件衣服和那个背景里",还能自动处理裁剪对齐。

步骤3:编译配置

创建 tsconfig.json

{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"moduleResolution": "node",
"resolveJsonModule": true
},
"include": ["src/**/*"]
}

编译:

npx tsc

安装到开发环境:

openclaw --dev plugins install .
openclaw --dev plugins enable firered-image-edit-v1
openclaw --dev gateway restart

实战:在微信里指挥AI修图

假设你已经把OpenClaw接入了微信机器人,现在可以直接在聊天窗口里测试:

场景1:单图修图

用户: 帮我把这张照片的背景换成图书馆,但要看起来自然 [附带图片]

OpenClaw会调用edit_image工具,FireRed v1.1会在4-5秒内生成新图。关键是,你的脸还是你的脸,不会变成"图书馆里某个帅哥"——这就是ID一致性的威力。

场景2:多元素融合

用户: 把图1的我,换成图2的那件西装,背景换成图3的办公室

这时候referenceImages参数就派上用场了。FireRed的Agent会自动处理这三张图的关系,把你的头像"缝"到西装上,再放到办公室里,还能保持光影一致。

场景3:批量证件照精修

用户: /portrait_retouch style:studio ./raw_photos/ ./retouched/

直接批量处理一个文件夹,所有照片统一修成"影棚风格",但每张脸都还是对应的那个人。这对于摄影工作室或HR部门来说,简直是生产力核弹。

进阶:让修图自动化

OpenClaw支持Hooks机制,可以在特定事件触发时自动执行修图。比如在~/.openclaw-dev/workspace/hooks/下创建:

export default {
onImageReceived: async (ctx) => {
// 如果图片文件名包含"待修"
if (ctx.filename.includes('待修')) {
const outputPath = ctx.filepath.replace('待修', '已修');
  await ctx.agent.tools.call('portrait_retouch', {
    imagePath: ctx.filepath,
    style: 'natural',
    outputPath
  });
  
  ctx.reply('✨ 自动美颜完成,已保持面部特征');
  ctx.sendFile(outputPath);
}
}
};

这样,只要你往指定文件夹丢一张带"待修"字样的照片,AI自动帮你修好发回来,连话都不用说。

性能优化:别让服务器"爆显存"

FireRed v1.1虽然强,但胃口也大。本地部署需要30GB显存,如果你跟我一样用不起A100,有几个优化方案:

  1. 云端API:fal.ai、WaveSpeedAI都托管了v1.1,按需付费,不用买卡。
  2. 队列控制:在插件里加并发限制,防止10个人同时发图把你的服务器干趴下。可以在handleEdit里用p-limit之类的库控制并发数。
  3. LoRA微调:FireRed v1.1的训练代码也开源了。如果你有特定风格需求(比如你们公司的品牌色调),可以训练一个小LoRA,然后在API请求里加上lora_path参数,实现"一键品牌风"。
  4. 图片预处理:FireRed对大图处理慢,建议在插件里先压缩图片到1024px左右,能大幅提升速度。

避坑指南

  • 坑1:提示词太长 FireRed v1.1虽然语义理解强,但提示词写太长它容易"走神"。建议把核心指令放前面,比如"换成海边背景,保持人物姿势",而不是"在保持人物原有姿态和表情不变,面部特征完全保留,服装细节清晰的前提下,将当前背景环境替换为阳光沙滩的场景"。

  • 坑2:参考图格式不一致 如果你传了3张参考图,最好保持同样的尺寸和方向(都是竖屏或都是横屏),不然模型对齐时可能会把脸拉变形。

  • 坑3:透明通道问题 PNG图片如果有透明背景,建议先转成白底再送进去,否则FireRed可能会把透明区域当成"需要填充的内容",结果在人物边缘生成奇怪的东西。

  • 坑4:同步模式超时 如果不用sync_mode: false,API可能会因为生成时间超过HTTP超时时间而断开。建议用异步模式,轮询结果。

结语:AI代理的"视觉觉醒"

写完这个插件,我最大的感触是:AI正在从"只会聊天"进化到"能动手干活"。OpenClaw给了AI一个"身体"(接入各种平台的能力),FireRed给了它"眼睛"(图像理解)和"手"(图像生成)。

以前我们要P张图,得打开Photoshop,学蒙版、学调色、学液化,折腾半小时。现在,给AI发一句话,4.5秒出图,而且不像那些美颜软件一样把你P成网红脸——它还是你,只是更好看的你。

更重要的是,这一切都在你自己的服务器上运行,数据不会乱飞。无论是处理私人照片,还是企业级的商品图批量生成,这套组合都够用。

现在就去试试吧,让你的OpenClaw学会"Photoshop技能",下次朋友让你帮忙P图时,你可以优雅地回一句:"放着,让我的AI来。"