OpenClaw 的图片处理(resize、HEIC 转换、EXIF 旋转)支持两种后端:Sharp(C++ 原生模块)和 Sips(macOS 系统命令)。通过环境变量 OPENCLAW_IMAGE_BACKEND 控制选择。

切换方法

设置环境变量:

# 强制使用 sips(仅 macOS)
export OPENCLAW_IMAGE_BACKEND=sips
 
# 强制使用 sharp(所有平台)
export OPENCLAW_IMAGE_BACKEND=sharp
 
# 不设置:自动判断(macOS + Bun → sips,其他 → sharp)

prefersSips() 逻辑(src/media/image-ops.ts:50):

function prefersSips(): boolean {
  return (
    process.env.OPENCLAW_IMAGE_BACKEND === "sips" ||           // 显式设置 sips
    (process.env.OPENCLAW_IMAGE_BACKEND !== "sharp" && isBun() && process.platform === "darwin")  // macOS + Bun 自动判断
  );
}

优先级:显式环境变量 > 平台自动判断。

Sharp vs Sips 对比

维度SharpSips
实现C++ 原生模块(libvips)+ Node bindingsmacOS 系统命令(/usr/bin/sips)
平台跨平台(需安装对应平台原生二进制:@img/sharp-linux-arm64、@img/sharp-darwin-x64 等)仅 macOS
依赖npm 包(sharp + optional dependencies @img/sharp-*)系统自带,零依赖
性能高性能(C++ libvips),处理 4K 图片约 100-300ms中等(进程启动开销 + 处理),约 200-500ms
功能resize、rotate、HEIC→JPEG、PNG alpha channel、PNG compression 精细控制resize、rotate、基础格式转换,不支持 PNG with alpha 精细控制、不支持 HEIC 输入
适用场景生产环境、高性能处理、跨平台部署macOS 本地开发、快速验证、避免安装 sharp 依赖
失败处理原生二进制缺失时抛错 → 图片被丢弃(见 read-tool 的 sanitization 章节)macOS 上必定存在,不存在失败场景

功能边界差异

PNG with Alpha

resizeToPng(src/media/image-ops.ts:609)无 sips fallback:

export async function resizeToPng(params: { ... }): Promise<Buffer> {
  await assertImagePixelLimit(params.buffer);
  const ops = await loadMediaAttachmentImageOps();  // ← 必须走 sharp
  return await ops.resizeToPng(params);
}

原因:Sips 不支持 PNG alpha channel 的压缩级别精细控制。

HEIC 转换

convertHeicToJpeg(extensions/media-understanding-core/image-ops.ts)依赖 Sharp,Sips 不支持 HEIC 输入:

async convertHeicToJpeg(buffer: Buffer): Promise<Buffer> {
  const sharp = await loadSharp(maxInputPixels);
  return await sharp(buffer).jpeg({ quality: 90, mozjpeg: true }).toBuffer();
}

src/media/image-ops.ts:568 的 fallback 尝试 macOS sips,但仅限于 macOS 平台:

export async function convertHeicToJpeg(buffer: Buffer): Promise<Buffer> {
  await assertImagePixelLimit(buffer);
  if (prefersSips()) {
    return await sipsConvertToJpeg(buffer);  // ← macOS 上尝试 sips
  }
  const ops = await loadMediaAttachmentImageOps();
  try {
    return await ops.convertHeicToJpeg(buffer);
  } catch (error) {
    if (process.platform !== "darwin") {
      throw error;  // ← 非 macOS,无 sips fallback,直接抛错
    }
    return await sipsConvertToJpeg(buffer);  // ← macOS 上 fallback 到 sips
  }
}

JPEG Resize

resizeToJpeg(src/media/image-ops.ts:531)支持 sips fallback,但仅限 macOS:

export async function resizeToJpeg(params: { ... }): Promise<Buffer> {
  await assertImagePixelLimit(params.buffer);
  if (prefersSips()) {
    const normalized = await normalizeExifOrientationSips(params.buffer);
    // ... sips resize logic ...
    return await sipsResizeToJpeg({ buffer: normalized, ... });
  }
  const ops = await loadMediaAttachmentImageOps();
  return await ops.resizeToJpeg(params);
}

失败处理机制

Sharp 失败时(如原生二进制缺失),在 src/agents/tool-images.ts 的 sanitizeContentBlocksImages 中:

try {
  const resized = await resizeImageBase64IfNeeded({ ... });
  out.push({ ...block, data: resized.base64, mimeType: resized.mimeType });
} catch (err) {
  out.push({
    type: "text",
    text: `[${label}] omitted image payload: ${String(err)}`,
  });  // ← 图片被替换为错误文本
}

LLM 收到错误文本而非图片内容,无法进行视觉理解。

相关案例:notes/sharp-linux-arm64-missing — linux-arm64 上 sharp 原生二进制缺失导致图片被丢弃。

推荐策略

Linux 环境(包括 linux-arm64):

  • 必须用 Sharp(Sips 不存在)
  • 确保安装对应平台原生二进制(见 notes/sharp-linux-arm64-missing)

macOS 环境:

  • 本地开发可用 Sips(OPENCLAW_IMAGE_BACKEND=sips),避免安装 Sharp 依赖
  • 生产环境仍推荐 Sharp(性能更好、跨平台一致)
  • 需处理 PNG with alpha 或 HEIC 时,必须用 Sharp

通用建议:

  • 不要在代码中强制用 sips(prefersSips 已是懒加载 fallback)
  • 遇到 Sharp 安装问题时,优先解决 Sharp 安装而非降级到 sips(Linux 上不适用)
  • macOS 上如需快速验证可临时切 sips,但长期仍用 Sharp

相关

  • read-tool — Read 工具中图片 sanitization 调用链
  • src/media/image-ops — 图片处理核心逻辑(resizeToJpeg、getImageMetadata、HEIC 转换)
  • notes/sharp-linux-arm64-missing — Sharp 原生二进制缺失的实际案例
  • tool-image-generation — 图片生成工具(不涉及 resize)