11 KiB
ai-image
验收记录与当前限制见 docs/acceptance.md。
AI 绘图聚合服务。对外提供统一的多供应商生图能力(文生图 / 图生图),内置 Web 控制台,Agent 可通过 REST API 或 MCP 调用。
一、快速开始
pip install -r requirements.txt
# 启动(Web + REST + MCP 同端口)
python main.py
# 仅 MCP stdio(给 MCP 客户端当子进程用)
python main.py --stdio
打开 http://127.0.0.1:8765/ 即可进入控制台。
首次使用:进入「供应商」页填写上游的 Base URL 与 API Key → 「拉取模型」→ 勾选含 image 的模型保存 → 到「出图」页输入提示词出图。
二、当前状态(v2 · 已落地)
技术栈
| 层 | 选型 |
|---|---|
| Web 框架 | FastAPI |
| 前端 | 原生 HTML/CSS/JS 静态页(web/),与后端同端口同源 |
| 持久化 | SQLite(标准库 sqlite3) |
| HTTP 客户端 | httpx |
| 密钥加密 | cryptography / Fernet |
| MCP | fastmcp(stdio + streamable HTTP) |
| 部署 | Docker / docker-compose |
目录结构
main.py 入口:REST + Web + MCP
app/
config.py 路径与运行配置
db.py SQLite 连接与建表
security.py 密钥加解密与掩码
schemas.py Pydantic 请求模型
providers/
base.py 适配器基类与能力声明
openai_compat.py OpenAI 兼容网关
agnes.py Agnes 原生协议
__init__.py 适配器注册表
services/
providers.py 供应商 / 模型 CRUD 与模型发现
generation.py 出图、落盘、记录
gallery.py output/ 扫描
skill_doc.py SKILL.md 读取
routers/
api_providers.py 供应商与模型 API
api_models.py
api_generate.py
api_gallery.py
api_skills.py
mcp_server.py MCP 工具
web/ 前端静态页
data/ SQLite + 密钥文件(不入库)
output/ 图片输出(画廊数据源)
SKILL.md Agent 使用说明
Web 控制台
| 页面 | 文件 | 功能 |
|---|---|---|
| 出图 | web/create.html |
输入提示词、选供应商与模型、选尺寸与数量,直接出图 |
| 画廊 | web/index.html |
按画廊(profile)浏览、搜索、进入查看、删除 |
| 生成记录 | web/records.html |
查看历史生成任务与失败原因 |
| 供应商 | web/providers.html |
增删改供应商、拉取上游模型、勾选启用 |
| 模型 | web/models.html |
跨供应商查看模型、启用 / 停用 |
| SKILL.md | web/skills.html |
在线查看 Agent 使用说明 |
前端只有一层数据出入口 web/api.js,全部走真实 REST;无 mock 数据。
三、核心机制
统一参数体系
所有供应商收敛到同一套入参,内部再翻译成各上游格式:
| 入参 | 类型 | 说明 |
|---|---|---|
provider_id |
str | 供应商 ID,界面创建时自定义 |
model |
str | 模型 ID,来自上游模型列表 |
prompt |
str | 提示词 |
size |
str | 统一 WxH 像素,如 1024x1024;也接受 1K/2K/3K/4K |
ratio |
str | 仅 Agnes 支持,如 16:9;省略时从 size 推导 |
n |
int | 生成数量(1~4) |
images |
list[str] | 图生图参考图(URL 或 Data URI),仅部分供应商支持 |
profile_name |
str | 调用方标识,用于输出目录隔离 |
适配器声明自身能力(supports_image_input、size_hint、kind),服务端据此校验,避免把不支持的参数透传给上游。
数据模型(SQLite)
providers
| 字段 | 类型 | 说明 |
|---|---|---|
id |
TEXT PK | 用户自定义唯一标识 |
name |
TEXT | 显示名 |
adapter |
TEXT | openai_compat / agnes |
base_url |
TEXT | 上游地址 |
api_key |
TEXT | 上游密钥(Fernet 加密存储,接口返回掩码) |
default_model |
TEXT | 默认模型 |
enabled |
INTEGER | 是否启用 |
note |
TEXT | 备注 |
created_at / updated_at |
TEXT | 时间戳 |
models
| 字段 | 类型 | 说明 |
|---|---|---|
id |
TEXT PK | provider_id:model_id |
provider_id |
TEXT | 外键(ON DELETE CASCADE) |
model_id |
TEXT | 上游模型标识 |
display_name |
TEXT | 显示名 |
enabled |
INTEGER | 是否对外暴露 |
kind |
TEXT | 类型(image) |
size_hint |
TEXT | 尺寸提示 |
supports_image_input |
INTEGER | 是否支持参考图 |
note |
TEXT | 备注 |
discovered_at |
TEXT | 最近一次发现时间 |
generations
| 字段 | 类型 | 说明 |
|---|---|---|
id |
TEXT PK | 任务 ID |
provider_id / provider_name |
TEXT | 实际使用的供应商 |
model |
TEXT | 模型 |
profile_name |
TEXT | 调用方 |
prompt |
TEXT | 提示词 |
size / ratio / n |
— | 入参快照 |
status |
TEXT | success / failed |
error |
TEXT | 失败原因 |
files |
TEXT(JSON) | 落盘文件名列表 |
duration_ms |
INTEGER | 耗时 |
created_at |
TEXT | 时间戳 |
模型发现
用户在界面填完 API Key 与 Base URL 后点击「拉取模型」,服务端请求上游 GET {base_url}/models,筛选规则:模型 ID 转小写后包含 image。命中的模型写入 models 表并标记 discovered_at,用户可勾选哪些对外启用(未勾选的自动停用)。拉取失败不阻塞保存,仅提示错误。
两种适配器的发现与出图路径:
| 适配器 | 模型发现 | 出图 |
|---|---|---|
openai_compat |
GET /models |
POST /images/generations(data[0].b64_json 或 data[0].url) |
agnes |
GET /models |
POST /images/generations(size 档位 + ratio,response_format 放在 extra_body 内) |
画廊
output/ 目录即画廊数据源:
output/
├── slime-fellowship/ ← 一个画廊(profile_name)
│ ├── gpt-image-2-1K-0014ef2b.png
│ └── ...
├── smoke-gpt-1/
└── user/ ← 网页端手动出图(固定 profile_name = user)
目录名语义 = 谁生成的图片:Agent 调用时由调用方指定 profile_name(默认 agent),网页端手动出图固定 user;目录不存在时后端自动创建。
| 接口 | 说明 |
|---|---|
GET /api/galleries |
画廊列表:名称、图片数、占用空间、更新时间、封面、涉及的模型 |
GET /api/galleries/{profile} |
某个画廊内的图片(含模型、供应商、时间) |
DELETE /api/galleries/{profile}/{filename} |
删除单张(物理删除 + 同步 generations 记录) |
MCP 对接
Agent 侧可用工具:
| 工具 | 说明 | 顺序 |
|---|---|---|
list_providers |
列出可用供应商及其启用模型数 | 1 |
discover_models |
拉取指定供应商的上游模型,返回 { provider_id, total, matched } |
2(必做) |
generate_image |
出图(文生图 / 图生图) | 3 |
list_generations |
查询生成记录 | 可选 |
get_skill_doc |
获取最新版 SKILL.md 内容 | 任意时刻 |
MCP 只暴露用户在界面中启用的供应商,不泄露未启用配置与密钥。
Agent 强制流程:list_providers() → discover_models(provider_id) → 用返回的 matched 出图。
matched 为空时,Agent 必须直接结束并回复「没找到有效的模型」,不得猜测模型名、不得调用出图接口。
SKILL.md
GET /api/skills 返回 SKILL.md 内容(JSON)或原文(/api/skills/raw,text/markdown)。页面提供在线查看,Agent 通过 MCP 工具 get_skill_doc 拉取。
SKILL.md 中不写任何固定模型名称:只描述「先拉模型、再出图」的流程与参数说明,模型清单一律来自 discover_models 的实时返回。
四、接口清单
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /health |
健康检查 |
| GET | /api/providers |
供应商列表(密钥掩码) |
| POST | /api/providers |
新增 / 更新供应商(api_key 留空表示不修改) |
| DELETE | /api/providers/{id} |
删除供应商及其模型 |
| POST | /api/providers/discover |
手填 Base URL + Key 临时拉模型 |
| POST | /api/providers/{id}/discover |
按已保存供应商拉模型并写库 |
| POST | /api/providers/{id}/models |
保存勾选的模型,未勾选的自动停用 |
| GET | /api/models |
模型列表(可 ?provider_id=) |
| PATCH | /api/models/{id} |
启用 / 停用单个模型 |
| POST | /api/models/bulk |
批量启用 / 停用 |
| POST | /api/generate |
出图 |
| GET | /api/generations |
生成记录 |
| GET | /api/galleries |
画廊列表 |
| GET | /api/galleries/{profile} |
画廊内图片 |
| DELETE | /api/galleries/{profile}/{filename} |
删除图片 |
| GET | /api/skills |
SKILL.md(JSON) |
| GET | /api/skills/raw |
SKILL.md 原文(markdown) |
| GET | /output/{profile}/{filename} |
图片访问 |
| POST | /mcp |
MCP streamable HTTP |
五、配置
.env(参考 .env.example):
| 变量 | 默认值 | 说明 |
|---|---|---|
AI_IMAGE_HOST |
127.0.0.1 |
监听地址 |
AI_IMAGE_PORT |
8765 |
监听端口 |
AI_IMAGE_DATA_DIR |
data |
SQLite 与密钥文件目录 |
AI_IMAGE_OUTPUT_DIR |
output |
图片输出根目录 |
AI_IMAGE_SECRET_KEY |
自动生成 | 密钥加密主密钥,不填则落 data/secret.key |
供应商的 API Key 与 Base URL 不再走环境变量,在控制台填写,服务端加密后存 SQLite。
六、部署
docker compose up -d
宿主机 output/ 与 data/ 挂载进容器,端口映射 8765:8000。
七、MCP 客户端接入
config.json 示例:
{
"mcpServers": {
"ai-image": {
"command": "python",
"args": ["d:\\source\\MyProject\\ai-image\\main.py", "--stdio"],
"cwd": "d:\\source\\MyProject\\ai-image",
"env": {}
}
}
}
也可用 streamable HTTP 直连:http://127.0.0.1:8765/mcp。
八、测试
# 后端回归:标准库 unittest,无需额外测试框架
python -m unittest discover -s tests -p test_app.py -v
# 完整验收:包含 Chromium 浏览器交互、桌面/移动布局、MCP HTTP/stdio
pip install -r requirements-dev.txt
python -m playwright install chromium
python -m unittest discover -s tests -v
测试使用临时目录、独立 SQLite、虚拟密钥和本地模拟上游,不读取现有数据库或调用付费生图服务。 浏览器测试会启动临时本地服务,结束后关闭服务并清除临时数据。