教授Claude Code绘画:基于Gemini交互API与MCP构建的有状态图像编辑技能
TL;DR: nb2lite-skill-claude 将 Google 的 gemini-3.1-flash-lite-image 模型封装在一个轻量级 FastMCP 服务器中,并打包为 Claude Code 技能。你在 Claude Code 中输入"生成一张赛博朋克厨房的图片",它就直接...生成了。然后你说"加一个霓虹灯 RAMEN 招牌",它会在同一张图片上编辑,而无需重新描述整个场景。哦,还有这篇文章的封面图?就是由文章所描述的工具生成的——全程自产自销。文末会详细说明。
背景:为什么还需要另一个图像工具?
大多数图像生成工作流都是无状态的。你发送一个提示词,得到像素结果,然后模型立刻忘记一切。想要调整结果?你得重新描述整个场景,并祈祷角色、光照和构图能在往返过程中幸存下来。(旁白:它们做不到。)
Google 的 Nano Banana 2 Lite——gemini-3.1-flash-lite-image 的友好昵称——采用了不同的方法。它是一个高效图像模型,生成时间低于 2 秒,支持 25 种以上语言的可靠文本渲染,以及——核心功能——支持有状态的 Interactions API,让你可以在多轮对话中迭代图像,同时模型在服务端保留视觉上下文。
这个仓库将该能力集成到 Claude Code 中,让你的编码助手能够在会话中自然地生成和迭代优化图像。它以一个仓库提供两种形态:
- 一个 Model Context Protocol (MCP) 服务器(nb2lite-agent,server.py 中的单文件 FastMCP 应用),暴露恰好四个工具。
- 一个 Claude Code 技能(nb2lite-image),教会 Claude 何时以及如何有效使用这些工具。
Interactions API:有记忆的图像
Interactions API 是 Gemini 的有状态端点。核心循环如下:
- 你调用 client.interactions.create(...) 并传入提示词,设置 store=True。
- 响应中包含一个 interaction_id——该轮视觉上下文的句柄,持久保存在 Google 服务器上。
- 下一次调用时,你传入 previous_interaction_id,模型会在现有画布上进行编辑——保留角色、风格、光照和像素连续性。
所以,你不再需要这样写(无状态的痛苦):
"水彩风格的狐狸,在黎明时分的森林中,薄雾,柔光,戴着红围巾,左边三棵白桦树,现在还要拿一盏灯笼"
...而是这样写:
"在它的爪子里加一盏灯笼。"
就这样。存储的上下文会保留其余所有信息。
服务器为你处理的一些实际细节:
- 每一轮都会返回一个新的 interaction_id。始终链接最新的那个;使用过期的 ID 进行编辑会静默地从旧状态分叉你的会话(如果手动操作,这是一个微妙且非常烦人的 bug)。
- 宽高比在生成时选择(1:1、16:9、9:16、4:3、3:4),并在有状态编辑时继承——在会话中途更改会降低像素连续性,因此编辑工具有意不接受宽高比参数。
- 思考级别:低(默认,快速草稿)或高(复杂渲染、精确文本布局、角色构图)。通用 API 规范还列出了最低和中等,但实时 API 对此模型会返回 HTTP 400 拒绝——服务器让你免于通过试错发现这一点。
一分钟了解 MCP
Model Context Protocol 是一个开放标准,用于将 AI 助手连接到工具和数据。在此之前,让模型访问某个服务需要为每个助手编写定制集成——N 个助手 × M 个服务,每个人都在重复造同样的管道。MCP 将其简化:工具作者编写一个 MCP 服务器,暴露类型化的工具,任何支持 MCP 的客户端(Claude Code、Claude Desktop 以及越来越多的其他客户端)都可以发现并调用它们,无需逐客户端编写胶水代码。
MCP 服务器通常是一个通过 stdio 使用 JSON-RPC 通信的小型本地进程。客户端启动它,询问"你有什么工具?",然后模型就可以像调用函数一样调用它们。
nb2lite-agent 服务器暴露恰好四个工具:
| 工具 | 功能 |
|---|---|
| generate_image | 文本 → 1k 图像。保存到本地,返回路径 + interaction_id。 |
| edit_image | 有状态编辑:传入上一个 interaction_id + 仅描述更改的文本。 |
| edit_local_image | 内联上传任何本地图像文件(base64)并应用编辑——处理现有文件的入口。 |
| get_help | 报告实时配置:API 密钥状态、活动模型、输出目录、完整工具参考。 |
图像以 gen_<timestamp>_<uuid8>.jpg(或 edit_/edit_local_ 前缀)保存到磁盘——UUID 后缀可防止并发生成相互覆盖。错误以 🔴 ... 文本字符串形式返回,而非协议错误,以便代理能够读取并做出反应。
什么是 Claude Code 技能?
如果说 MCP 是手(Claude 可以实际调用的工具),那么技能就是肌肉记忆——一个 markdown 文件(SKILL.md)加上捆绑的资源,加载到 Claude 的上下文中,教会它工作流程:应该使用哪个工具、按什么顺序、以及有哪些约束。
对于 nb2lite-image,技能编码了以下内容:
- 诊断设置问题时首先调用 get_help——如果 API 密钥缺失,其他所有操作都无法工作。
- 保持编辑提示词增量式:描述更改,而非场景。
- 始终链接最新的 interaction_id。
- 生成是计费的——批量处理相关编辑,草稿优先使用 thinking_level: low。
该技能还捆绑了 MCP 服务器本身(mcp/server.py)、其依赖项、安装脚本以及 Interactions API 开发者指南的副本——因此它是自包含的:安装技能后,你就拥有了启动服务器所需的一切。
安装:开箱即用版
你需要三样东西:Python 3.10+、Claude Code 和 Gemini API 密钥(免费从 Google AI Studio 获取)。选择以下路径之一。
路径 A:插件市场(最少按键次数)
在 Claude Code 中,输入:
/plugin marketplace add xbill9/nb2lite-skill-claude
/plugin install nb2lite-image@nb2lite-skill-claude
这将安装技能并自动注册 MCP 服务器。插件清单不携带 API 密钥(理应如此!)——服务器从环境变量 GEMINI_API_KEY 读取,因此请确保在启动 Claude Code 之前已导出该变量。
路径 B:克隆并引导(本仓库)
# 1. 获取代码
git clone https://github.com/xbill9/nb2lite-skill-claude.git
cd nb2lite-skill-claude
# 2. 一键设置:安装依赖、在 .mcp.json 中注册 MCP 服务器
# 并提示输入 API 密钥(存储在 ~/gemini.key)
./init.sh
# 3. 在此目录中重启 Claude Code,并在提示时批准服务器
# 使用以下命令验证:
/mcp # 应列出 nb2lite-agent
真的就这么简单。如果任何地方看起来不对,可以安全地重新运行 init.sh。
路径 C:安装到你的项目中
从仓库的克隆目录:
make init TARGET=/path/to/your/project ARGS='--output-dir ./images'
这会将技能复制到 <project>/.claude/skills/nb2lite-image/,并将 nb2lite-agent 条目写入该项目的 .mcp.json。如果你之前设置过,它会复用 ~/gemini.key。在目标项目中重启 Claude Code,批准服务器,完成。
路径 D:Docker(宿主机上只需 Docker)
服务器已发布为 xbill9/nb2lite-agent:
claude mcp add nb2lite-agent --env GEMINI_API_KEY="$(cat ~/gemini.key)" -- \
docker run --rm -i -e GEMINI_API_KEY -v "$PWD:$PWD" -w "$PWD" xbill9/nb2lite-agent
-v "$PWD:$PWD" -w "$PWD" 挂载很重要:服务器将图像保存到磁盘,并读取本地文件用于 edit_local_image,因此容器必须能够以与宿主机相同的绝对路径看到你的项目。
故障排除完整指南
- /mcp 没有列出服务器 → 在项目目录中重启 Claude Code。
- 工具返回 🔴 GEMINI_API_KEY is not set → 运行 source set_env.sh(或导出密钥)并重启。
- 其他情况 → 让 Claude 调用 get_help;它会报告实时配置。
示例:实践中的会话
安装后,你可以用自然语言与它交流。一个真实的流程如下:
你:"生成一个黄昏时分雪林中的舒适小木屋,16:9。"
Claude 调用:
generate_image(
prompt="黄昏时分雪林中的舒适木屋,窗户透出温暖灯光",
aspect_ratio="16:9",
thinking_level="low",
)
# 🟢 已保存至:./gen_1784759001_a1b2c3d4.jpg
# 交互 ID:v1_ChdpRU5...
你:"不错。在烟囱上添加袅袅炊烟。"
edit_image(
previous_interaction_id="v1_ChdpRU5...",
edit_prompt="在烟囱上添加轻柔的袅袅炊烟",
)
# 🟢 已保存至:./edit_1784759050_e5f6a7b8.jpg
# 交互 ID:v1_Xk9mPq2... ← 新的 ID;下一次编辑将链接到此 ID
你:"现在改成夜晚,天空中出现极光。"
使用相同的工具和最新的 ID,木屋、树木和炊烟保持不变——只有天空发生变化。无需重新提示,没有连续性风险。
对于并非来自模型的图像:
你:"将 ./whiteboard-sketch.png 渲染成干净的 3D 产品模型。"
edit_local_image(
image_path="./whiteboard-sketch.png",
edit_prompt="将此手绘草图渲染为高保真 3D 产品模型",
aspect_ratio="4:3",
)
它也会返回一个交互 ID——因此后续优化可以切换到 edit_image 并从此进入有状态模式。
自用测试:关于封面图像 🐕🍖
如果你对这个术语不熟悉:"eating your own dog food"(自用测试)意味着在实际工作中使用自己的产品,而不仅仅是演示。这体现了"这应该能用"和"我每天用它交付产品"之间的区别。如果工具对用户足够好,那对你也应该足够好——如果不够好,你会首先感受到痛点并修复它。
本仓库在每一层都进行了自用测试:
- 该技能在其自身仓库内处于激活状态——在克隆仓库中打开 Claude Code,nb2lite-image 技能和 nb2lite-agent 服务器已经配置好,因此每次开发会话都同时作为集成测试。
- 集成测试(make test)驱动最终用户会使用的相同四个 MCP 工具,针对实时 API 运行。
- 现在,本文的封面图像正是由本文描述的技能,在本仓库的 Claude Code 会话中生成的。一次工具调用,首次尝试,无需润色:
generate_image(
prompt="一幅宽幅科技博客封面插图:一个友好的机器人艺术家在画架上绘制发光的星系,"
"身后一系列相连的画框展示同一幅图像逐步演变的过程"
"(白昼天空,然后日落,然后暴风雨闪电)。扁平矢量风格,"
"深靛蓝背景,霓虹青色和橙色点缀。标题文字 'NB2Lite + MCP',"
"副标题 'Stateful image editing as a Claude Code skill'。清晰准确的字体。",
aspect_ratio="16:9",
thinking_level="high",
)
# 🟢 图像成功保存!
# • 保存至:gen_1784759177_cbab8b65.jpg
# • 交互 ID:v1_ChdpRU5hb2o3SWMzV2pNY1AtUFgy...
(该输出已完整提交到仓库中,文件名为 devto-cover.jpg,包含所有记录。)
值得注意:
- 文字渲染正确。"NB2Lite + MCP"和完整副标题清晰无错——这就是 thinking_level: "high" 在文字密集型布局上的效果。
- 模型展示了自身的理念。相连的画框(白昼→日落→暴风雨→星系)就是有状态编辑循环——这幅图像比我自己手绘的图表更好地解释了 Interactions API。
- 如果我想改变强调色,我不会重新生成——而是使用该交互 ID 调用 edit_image 并说"将橙色点缀改为洋红色"。这就是全部要点。
自用测试是最廉价的信誉保证:没有精心挑选的图库,没有"结果可能不同"的免责声明——工具的真实输出就是你打开本文时首先看到的内容。如果技能在文字渲染或布局上出错,你现在就会看到证据。相反,本文的标题直接嵌入了自己的证明。
链接
- 仓库:github.com/xbill9/nb2lite-skill-claude(Apache-2.0 许可)
- Docker 镜像:hub.docker.com/r/xbill9/nb2lite-agent
- Interactions API 参考:ai.google.dev/api/interactions-api
- 模型上下文协议:modelcontextprotocol.io
这是一个第三方社区项目,与 Anthropic 或 Google 无关且未获其认可。请自带 Gemini API 密钥——并记住生成内容需要计费,因此草稿阶段使用低级别,最终成品时再使用高级别。