适合人群:希望统一多厂商(OpenAI、阿里、科大讯飞等)调用体验的后端开发者或技术爱好者。
建议先看:实践三(RESTful API 设计原则)、实践六(阿里大模型对接)。
你将收获:一套可扩展的适配层模型与返回体规范,最小变更对接新厂商。
设计目标与边界
- 统一接口:隐藏厂商差异,暴露稳定域模型(模型、聊天、流式聊天、图片)。
- 易于扩展:新增厂商仅需实现适配器,不影响上层调用与返回结构。
- 一致的错误语义:统一 success/code/message,流式以 [process]/[done] 信号闭环。
- 安全与合规:不在代码中硬编码密钥,敏感信息通过环境变量或配置中心注入。
对输入参数进行统一
几乎不改
返回值统一
模型
@Data
public class BigModel {
private String id;
public BigModel(String id) {
this.id = id;
}
public BigModel() {}
}
文字聊天
@Data
public class ChatMessage {
private String id;
private String content;
private String status; // success fail
private String error;
}
文字聊天流
@Data
public class ChatMessageStream {
private String id;
private String content;
private String status; // done process
}
图片
@Data
public class ChatImage {
private String id;
private String imageUrl;
private String status; // success fail
private String error;
}
流程合并
使用统一接口
OpenAI
@Service
public class OpenAiAdopter {
@Autowired
private OpenAiService openAiService;
public List<BigModel> getModels() {
Map model = openAiService.getModel();
List<Map> models = (List<Map>) model.get("data");
return models.stream().map(m -> new BigModel(m.get("id").toString())).collect(Collectors.toList());
}
public ChatMessage chat(String model, String message) {
Map chat = openAiService.chat(model, message);
String id = chat.get("id").toString();
List<Map> output = (List<Map>) chat.get("output");
ChatMessage chatMessage = new ChatMessage();
chatMessage.setId(id);
String contentStr = "";
chatMessage.setStatus("completed");
for (Map map : output) {
if (!map.get("type").toString().equals("message")) {
continue;
}
if (!map.get("status").toString().equals("completed")) {
continue;
}
List<Map> contents = (List<Map>) map.get("content");
for (Map content : contents) {
contentStr += content.get("text").toString();
}
}
chatMessage.setContent(contentStr);
return chatMessage;
}
public void chatStream(String model, String message, Consumer<ChatMessageStream> onDelta){
openAiService.chatStream(model, message, (delta)->{
ChatMessageStream chatMessageStream = new ChatMessageStream();
chatMessageStream.setId(UUID.randomUUID().toString());
chatMessageStream.setStatus("process");
chatMessageStream.setContent(delta);
onDelta.accept(chatMessageStream);
});
}
public ChatImage generateImage(String model, String prompt, String size) {
try {
Map image = openAiService.generateImageUrl(model, prompt, size);
ChatImage chatImage = new ChatImage();
chatImage.setId(UUID.randomUUID().toString());
List<Map> images = (List<Map>)image.get("data");
chatImage.setImageUrl(images.get(0).get("url").toString());
chatImage.setStatus("success");
return chatImage;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
阿里
@RestController
@RequestMapping("/ai")
public class AiController {
@Autowired
private AliAiAdopter aliAiAdopter;
@Autowired
private OpenAiAdopter openAiAdopter;
@GetMapping("/get/models")
public List<BigModel> getModels(@RequestParam("platform") String platform) {
if ("ali".equals(platform)) {
return aliAiAdopter.getModels();
}
return openAiAdopter.getModels();
}
@PostMapping("/chat")
public ChatMessage chat(@RequestBody Map<String, String> param) throws JsonProcessingException {
String platform = "";
if (param.get("platform") != null) {
platform = param.get("platform");
}
String model = "";
if (param.get("model") != null) {
model = param.get("model");
}
String message = "";
if (param.get("message") != null) {
message = param.get("message");
}
if ("ali".equals(platform)) {
return aliAiAdopter.chat(model, message);
}
return openAiAdopter.chat(model, message);
}
@PostMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter chatStream(@RequestBody Map<String, String> param, HttpServletResponse response) {
String platform = "";
if (param.get("platform") != null) {
platform = param.get("platform");
}
String model;
if (param.get("model") != null) {
model = param.get("model");
} else {
model = "";
}
String message;
if (param.get("message") != null) {
message = param.get("message");
} else {
message = "";
}
SseEmitter emitter = new SseEmitter(0L); // 0 表示不超时;可按需设置毫秒
if ("ali".equals(platform)) {
CompletableFuture.runAsync(() -> {
try {
aliAiAdopter.chatStream(model, message, delta -> {
try {
emitter.send(SseEmitter.event().name("delta").data(delta));
} catch (IOException e) {
emitter.completeWithError(e);
}
});
// 上游完成
emitter.send(SseEmitter.event().name("done").data("[DONE]"));
emitter.complete();
} catch (Exception ex) {
emitter.completeWithError(ex);
}
});
} else {
CompletableFuture.runAsync(() -> {
try {
openAiAdopter.chatStream(model, message, delta -> {
try {
emitter.send(SseEmitter.event().name("delta").data(delta));
} catch (IOException e) {
emitter.completeWithError(e);
}
});
// 上游完成
ChatMessageStream chatMessageStream = new ChatMessageStream();
chatMessageStream.setId(UUID.randomUUID().toString());
chatMessageStream.setStatus("[done]");
chatMessageStream.setContent("");
emitter.send(SseEmitter.event().name("done").data(chatMessageStream));
emitter.complete();
} catch (Exception ex) {
emitter.completeWithError(ex);
}
});
}
return emitter;
}
@PostMapping("/generate/image")
public ChatImage generateImage(@RequestBody Map<String, String> param) {
String model = param.getOrDefault("model", "dall-e-3");
String platform = param.getOrDefault("platform", "ali");
String prompt = param.getOrDefault("prompt", "");
String size = param.getOrDefault("size", "1024x1024");
if ("ali".equals(platform)) {
return aliAiAdopter.generateImage(model, prompt, size);
}
return openAiAdopter.generateImage(model, prompt, size);
}
}
接口统一
@RestController
@RequestMapping("/ai")
public class AiController {
@Autowired
private AliAiAdopter aliAiAdopter;
@Autowired
private OpenAiAdopter openAiAdopter;
@GetMapping("/get/models")
public List<BigModel> getModels(@RequestParam("platform") String platform) {
if ("ali".equals(platform)) {
return aliAiAdopter.getModels();
}
return openAiAdopter.getModels();
}
@PostMapping("/chat")
public ChatMessage chat(@RequestBody Map<String, String> param) throws JsonProcessingException {
String platform = "";
if (param.get("platform") != null) {
platform = param.get("platform");
}
String model = "";
if (param.get("model") != null) {
model = param.get("model");
}
String message = "";
if (param.get("message") != null) {
message = param.get("message");
}
if ("ali".equals(platform)) {
return aliAiAdopter.chat(model, message);
}
return openAiAdopter.chat(model, message);
}
@PostMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter chatStream(@RequestBody Map<String, String> param, HttpServletResponse response) {
String platform = "";
if (param.get("platform") != null) {
platform = param.get("platform");
}
String model;
if (param.get("model") != null) {
model = param.get("model");
} else {
model = "";
}
String message;
if (param.get("message") != null) {
message = param.get("message");
} else {
message = "";
}
SseEmitter emitter = new SseEmitter(0L); // 0 表示不超时;可按需设置毫秒
if ("ali".equals(platform)) {
CompletableFuture.runAsync(() -> {
try {
aliAiAdopter.chatStream(model, message, delta -> {
try {
emitter.send(SseEmitter.event().name("delta").data(delta));
} catch (IOException e) {
emitter.completeWithError(e);
}
});
// 上游完成
emitter.send(SseEmitter.event().name("done").data("[DONE]"));
emitter.complete();
} catch (Exception ex) {
emitter.completeWithError(ex);
}
});
} else {
CompletableFuture.runAsync(() -> {
try {
openAiAdopter.chatStream(model, message, delta -> {
try {
emitter.send(SseEmitter.event().name("delta").data(delta));
} catch (IOException e) {
emitter.completeWithError(e);
}
});
// 上游完成
ChatMessageStream chatMessageStream = new ChatMessageStream();
chatMessageStream.setId(UUID.randomUUID().toString());
chatMessageStream.setStatus("[done]");
chatMessageStream.setContent("");
emitter.send(SseEmitter.event().name("done").data(chatMessageStream));
emitter.complete();
} catch (Exception ex) {
emitter.completeWithError(ex);
}
});
}
return emitter;
}
@PostMapping("/generate/image")
public ChatImage generateImage(@RequestBody Map<String, String> param) {
String model = param.getOrDefault("model", "dall-e-3");
String platform = param.getOrDefault("platform", "ali");
String prompt = param.getOrDefault("prompt", "");
String size = param.getOrDefault("size", "1024x1024");
if ("ali".equals(platform)) {
return aliAiAdopter.generateImage(model, prompt, size);
}
return openAiAdopter.generateImage(model, prompt, size);
}
}