最近在搞漫据系统,需要接各种 AI 的 API。文生图要接 Midjourney,视频生成要接 Runway,还有 Sora、Pika 什么的。
本来以为就是调调接口的事,结果发现坑太多了。
每个渠道的 API 风格都不一样。有的直接返回结果,有的要你轮询,有的给你发回调。参数名也是乱七八糟,这个叫 width,那个叫 image_width,还有叫 w 的…
更烦的是一个渠道可能有好几个账号,要做轮换。账号挂了要自动切换。限流了要排队。
写着写着代码就成了 if else 地狱。每次加新渠道都要改业务代码,改完还得测一堆东西。
想了想,既然每个渠道都不一样,那就在中间加一层呗。对业务系统暴露统一的接口,底下我去适配各个渠道。
然后就有了这个项目:Prism(棱镜)。
为啥叫棱镜?就是那个能把光分解又合成的东西。我觉得挺形象的,把各种乱七八糟的 API 统一成一个标准接口。
先说几个核心概念,理解了这些基本就知道这东西怎么用了。
能力就是 AI 能干的事,比如:
text2img - 文生图
text2video - 文生视频
img2video - 图生视频
每个能力有一套标准参数定义。比如 text2img 的标准参数可能是:
{
"prompt": "提示词",
"width": 1024,
"height": 1024,
"negative_prompt": "负面提示词"
}
渠道就是具体的 AI 服务商,比如 Midjourney、Runway、Sora。
一个渠道可以有多个账号,系统会自动轮换使用。
这是把能力和渠道关联起来的配置。告诉系统:
这个渠道支持哪些能力
怎么调用(API 地址、请求方式)
参数怎么映射(标准参数转成渠道参数)
结果怎么解析(渠道返回转成标准格式)
用什么模式获取结果(同步/轮询/回调)
这块是整个系统最核心的部分,配置好了之后,加新渠道基本就是填表的事。
一个请求进来大概是这么跑的:
用户请求 → 参数校验 → 选择渠道账号 → 参数映射 → 提交到上游
↓
用户查询 ← 结果映射 ← 获取结果(同步/轮询/回调)
不同渠道返回结果的方式不一样,我做了三种模式:
1. 同步模式(sync)
调上游接口,等着返回结果,直接给用户。适合那种秒出结果的接口。
2. 轮询模式(poll)
上游返回一个任务 ID,然后定时去查状态。适合 Midjourney 这种,提交之后要等几十秒才出图。
轮询间隔、最大次数都可以配置:
poll_config:
path: "/v1/tasks/{task_id}"
method: "GET"
interval: 5 # 每 5 秒查一次
max_attempts: 60 # 最多查 60 次
3. 回调模式(callback)
上游处理完主动推消息过来。系统提供一个回调地址给上游,收到回调就更新任务状态。
这块花了不少心思。不同渠道的参数名、格式都不一样,总不能每次都写代码转换。
我做了一套基于 JSON Path 的映射规则。在后台配置一下,标准参数就能自动转成渠道格式。
比如标准参数是:
{
"prompt": "一只猫",
"width": 1024,
"height": 1024
}
某渠道要求的格式是:
{
"input": {
"text": "一只猫"
},
"config": {
"image_width": 1024,
"image_height": 1024
}
}
配置映射规则:
{
"input.text": "{{.prompt}}",
"config.image_width": "{{.width}}",
"config.image_height": "{{.height}}"
}
系统自动转换,不用写代码。
响应也是一样的道理,渠道返回的乱七八糟的字段,映射成统一的格式给用户。
AI 生成任务通常要等,不可能让用户一直挂着连接。
提交请求后返回一个任务号,用户拿这个号去查进度和结果:
# 提交任务
POST /v1/capabilities/text2img
→ {"task_no": "T20250131001", "status": "pending"}
# 查询状态
GET /v1/tasks/T20250131001
→ {"status": "processing", "progress": 50}
# 完成后
GET /v1/tasks/T20250131001
→ {"status": "success", "result": {"url": "https://..."}}
任务状态有这几种:
pending - 排队中
processing - 处理中
success - 成功
failed - 失败
cancelled - 已取消
后台还有个超时检查,任务卡太久自动标记失败,不会一直挂着。
顺手做了个计费功能。
每个渠道能力配置可以设置单价,任务完成后自动扣费。用户有余额,Token 也有余额,扣哪个可以配置。
还能看消费记录,统计成本。
调试的时候最烦的就是不知道请求到底发了什么、返回了什么。
我把每次和上游的交互都记下来了:
请求头、请求体
响应头、响应体
状态码、耗时
错误信息
在后台能直接看,排查问题方便很多。
用 React 写了个管理后台,主要功能:
仪表盘 - 今日请求数、成功率、消耗统计、趋势图
渠道管理 - 增删改查渠道和账号
能力管理 - 定义标准能力和参数
渠道能力配置 - 配置映射规则、获取模式等
用户管理 - 用户列表、角色、余额
Token 管理 - API Token 的增删和充值
任务日志 - 查看任务执行记录
请求日志 - 查看和上游的原始交互
前端直接打包进 Go 二进制里,部署不用单独搞前端服务。
Go - 主语言,并发性能好,适合做网关
Gin - Web 框架,轻量够用
GORM - ORM,操作数据库
Asynq - 异步任务队列,基于 Redis
MySQL - 主数据库
Redis - 缓存 + 任务队列
项目结构:
├── cmd/server/ # 启动入口
├── internal/
│ ├── api/ # 接口层
│ ├── model/ # 数据模型
│ ├── service/ # 业务逻辑
│ ├── provider/ # 渠道适配
│ └── worker/ # 异步任务处理
├── pkg/ # 工具包
└── configs/ # 配置文件
异步任务用了几个 Worker:
Submit Worker - 处理任务提交
Poll Worker - 轮询上游状态
Notify Worker - 处理完成通知
Callback Handler - 处理上游回调
Timeout Checker - 检查超时任务
React 19 - UI 框架
TypeScript - 类型安全
Tailwind CSS - 样式
Vite - 构建工具
Recharts - 图表
对外提供的接口:
# 调用能力
POST /v1/capabilities/:capability
# 任务管理
GET /v1/tasks/:task_no
POST /v1/tasks/:task_no/cancel
# 查询可用渠道和能力
GET /v1/channels
GET /v1/capabilities
认证用 Bearer Token,在后台生成。
编译:
build.bat
会生成 dist/ 目录,里面就一个二进制文件和配置模板。
部署:
# 改配置
cp configs/config.example.yaml configs/config.yaml
vim configs/config.yaml
# 跑起来
./prism
默认端口 23523,访问 http://localhost:23523 就能看到管理后台。
接完之后漫据那边的 AI 调用代码砍掉了一大半。加新渠道在后台配配就行,业务系统不用改。
主要是省心,不用再记那些乱七八糟的 API 细节了。
项目开源了,MIT 协议。
GitHub:https://github.com/EaseeSoft/Prism
有问题提 Issue,觉得有用给个 Star。