我给 AI 接了一双"手"——记 Agent 图片生成能力的从零搭建
背景
我有一个 OpenClaw 龙虾叫 cc,跑在一台 Azure 的虚拟机上。它是基于 OpenClaw 搭的,平时帮我管管服务器、写写代码、整理文档,干活挺利索的。
但有个问题——不会画画。
作为一个 Agent,光会处理文字总觉得缺了点什么。如果它能根据自己的理解直接生成图片,能做的事情就多了很多。所以我决定给 cc 接一个图片生成的能力。
先放一张全景图,看看这一天都经历了啥:

技术选型
图片生成模型选的是 FLUX.2-pro,我的 Azure 订阅里有好几个模型可用(FLUX、Stable Diffusion、gpt-image 等),FLUX.2-pro 画质和灵活性都不错,支持自定义尺寸,按量计费也划算。
实现方式上,一开始走的是 MCP(Model Context Protocol)路线——写了一个 MCP Server 作为中间层,Agent 通过 JSON-RPC 调用它来生图。MCP 是 Anthropic 推的开放协议,好处是标准化、解耦,Agent 不用关心底层是哪个模型。
但后来发现,对于图片生成这个场景,MCP 有点重了。Agent 需要的只是”给一个 prompt,拿到一张图”,没必要跑一个常驻的 stdio 进程。所以最终我们把它重构成了一个 Skill。
什么是 Skill
在 OpenClaw 里,Skill 是一种轻量级的能力封装。简单说就是一个目录,里面有一个 SKILL.md 描述文件和对应的脚本。Agent 在收到相关请求时,读一下 SKILL.md 就知道该怎么调用。
图片生成的 Skill 结构长这样:
skills/azure-image-gen/
├── SKILL.md # 使用说明
├── .env # API 凭证
└── scripts/
└── generate.py # 生成脚本
比 MCP Server 简单多了——没有 JSON-RPC、没有 stdio 管道、没有常驻进程。就是一个 Python 脚本,命令行调用,标准输出返回文件名。
整个调用链路长这样:

踩坑
虽然最终方案很简洁,但搭建的过程一点都不简洁。说实话,远比我想的曲折。
第一个坑:URL 拼接多了一个斜杠。 FLUX 在 Azure 上不走标准的 OpenAI 接口,用的是 BFL(Black Forest Labs)的专属路径。而且模型名和路径之间有一层大小写映射,FLUX.2-pro 对应的路径是 flux-2-pro。
第一次调就 404 了。排查了好一阵,发现是 Python 的 urljoin 给我多拼了一个 /,路径变成了 //providers/...,Azure 直接不认。最后放弃 urljoin,老老实实手动拼字符串。
第二个坑更离谱:参数名拼错了一个字母。 我让 cc 画个太阳,结果出来一张天文望远镜。关键是 API 不报错!它就静默忽略了那个拼错的参数,用默认值给你出图。你还以为调用成功了,直到看到图才发现不对劲。
第三个坑:想优雅反而绕了远路。 我想用 OpenAI 的 Python SDK 来调,代码更简洁嘛:
client = OpenAI(base_url=AZURE_ENDPOINT, api_key=AZURE_API_KEY)
resp = client.images.generate(model="FLUX.2-pro", prompt=prompt)
结果 SDK 自动拼了 /images/generations 这个路径,FLUX 在 Azure 上根本不支持。又 404。滚回原生接口了。有时候”能用”比”好看”重要。
第四个坑算是意外收获。 我让 GitHub Copilot(跑的 Claude Opus)来 review cc 写的代码,被批了一顿:没异常处理、没重试、没参数校验、debug 流程太低效。虽然当时觉得有点扎心,但改完之后确实稳多了——加了重试、加了校验、加了结构化日志,单个请求挂了也不会整个崩掉。
最终效果
折腾了一整天,cc 终于能画画了。现在一行命令就能出图:
python3 skills/azure-image-gen/scripts/generate.py \
--prompt "a cute robot sitting on a server rack" \
--width 768 --height 1024
Agent 不需要记这个命令——读一遍 SKILL.md 就知道怎么用了。凭证存在 Skill 目录的 .env 里,自动加载,不用手动配环境变量。
下面这张就是 cc 自己画的,虽然画风有点 3D 渲染味,但它确实是 cc 的”处女作”之一:

现在 cc 能根据上下文自主生成插画了,从纯文字工具变成了图文创作者。
从 MCP 到 Skill 的取舍
说一下为什么最终没留 MCP 的方案。MCP 的设计初衷是让 Agent 用标准化的方式调用外部服务,这没问题。但对图片生成来说:
- Agent 不需要和生图服务保持长连接,一次性调用就够了
- Skill 形式更轻,一个 Python 脚本加一个说明文件,部署和维护都简单
- 凭证管理更直观——
.env文件放在 Skill 目录里,不用去配 MCP 注册表 - 以后换模型或者换 provider,改脚本就行,Agent 侧的 SKILL.md 接口不用变
本质上这是一个”够用就好”的选择。MCP 在需要复杂交互、流式通信、多工具编排的场景下很有价值,但对”给个 prompt 出张图”这种简单需求,Skill 就够了。
小结
这件事技术上不难,但坑比预期多。URL 的大小写、参数名的拼写、SDK 的隐式行为……每一个都能卡你半天。不过结果是值得的:cc 从”能写”到”能画”,确实不一样了。
不过故事还没完——cc 虽然能生成图片了,但它只能把图片存在服务器本地。我在 Web UI 上跟它聊天的时候,它根本没法把图片发给我看。 你让它”给我看看刚画的图”,它只能回你一个文件路径。就好比你朋友说”我画了一幅画”,然后给你发了个门牌号让你自己去取。
怎么解决这个问题?踩了更多的坑。下篇再聊。
moeyui
不是很懂你们程序员