AI
under-utils-ai
OpenAI-compatible AI 大模型基础调用封装,覆盖同步、流式和命名客户端。
依赖
<dependency>
<groupId>io.github.yexianglun-d</groupId>
<artifactId>under-utils-ai</artifactId>
<version>1.0.2</version>
</dependency> 适用场景
需要统一 AI 调用入口和 provider 扩展的服务。
能力边界
| 文档来源 | under-utils-ai/README.md |
|---|---|
| API 入口 | 查看 AI 相关 API |
| 设计约束 | 保留失败语义、配置 key 和 public API 兼容性说明,不以营销描述替代真实边界。 |
README 同步内容
来源:under-utils-ai/README.md,构建时自动读取并渲染。
AI 大模型基础调用封装模块,提供 OpenAI-compatible 文本对话客户端。
本模块目标是让业务项目只配置模型服务的基础参数,就能完成最常见的同步文本对话和 SSE 流式对话调用;它不是 Agent 框架,也不覆盖工具调用、RAG、向量数据库或厂商私有全部参数。
依赖
<dependency>
<groupId>io.github.yexianglun-d</groupId>
<artifactId>under-utils-ai</artifactId>
<version>1.0.2</version>
</dependency>
快速使用
AiClient aiClient = AiClient.builder()
.baseUrl("https://api.example.com/v1")
.apiKey(apiKey)
.model("your-model-name")
.timeout(Duration.ofSeconds(30))
.build();
ChatResponse response = aiClient.chat(ChatRequest.user("请总结这段文本"));
String text = response.text();
构建器会调用 {baseUrl}/chat/completions,请求体使用 OpenAI-compatible Chat Completions 结构:
{
"model": "your-model-name",
"messages": [
{
"role": "user",
"content": "请总结这段文本"
}
]
}
流式响应
OpenAiCompatibleAiClient 同时实现 StreamingAiClient。流式响应基于 OpenAI-compatible SSE,调用方必须关闭 ChatStream:
StreamingAiClient streamingClient = (StreamingAiClient) aiClient;
try (ChatStream stream = streamingClient.streamChat(ChatRequest.user("请逐步输出一句话"))) {
for (ChatStreamEvent event : stream) {
if (event.hasText()) {
System.out.print(event.text());
}
if (event.isDone()) {
break;
}
}
}
流式请求会在请求体中写入 "stream": true,并读取 data: SSE 分片。ChatStreamEvent 暴露增量文本、角色、结束原因、token 用量和元数据;连接中断、超时、HTTP 错误和分片解析失败都会映射为 AiException。
多轮消息
ChatResponse response = aiClient.chat(ChatRequest.builder()
.system("你是一个简洁的助手")
.user("请把下面内容压缩成三句话")
.assistant("请提供原文")
.user(content)
.temperature(0.2D)
.maxTokens(512)
.requestId(requestId)
.build());
ChatRequest 可以按请求覆盖默认模型,也可以通过 extraBody 透传少量兼容参数:
ChatRequest request = ChatRequest.builder()
.user("ping")
.model("another-model")
.extraBody("top_p", 0.9D)
.build();
多客户端注册表
该能力位于当前 1.0.3-SNAPSHOT 开发周期,正式发布后再使用对应稳定版本坐标。
同一个应用需要同时接入多个模型服务时,可以使用 AiClientRegistry 按名称路由:
Map<String, AiClient> clients = new LinkedHashMap<>();
clients.put("deepseek", AiClient.builder()
.baseUrl("https://api.deepseek.example/v1")
.apiKey(deepseekKey)
.model("deepseek-chat")
.build());
clients.put("qwen", AiClient.builder()
.baseUrl("https://dashscope-compatible.example/v1")
.apiKey(qwenKey)
.model("qwen-plus")
.build());
AiClientRegistry registry = new DefaultAiClientRegistry(clients, "deepseek");
String text = registry.get("qwen")
.chat(ChatRequest.user("请总结这段文本"))
.text();
AiClientRegistry#getDefaultClient() 返回默认客户端,getStreaming(name) 会在目标客户端不支持流式响应时抛出明确异常。
响应元数据
ChatResponse 提供文本、模型名、结束原因和 token 用量,同时通过 AiResponseMetadata 暴露 provider、调用方 request id、模型服务 response id、模型指纹和请求耗时:
ChatResponse response = aiClient.chat(ChatRequest.builder()
.user("ping")
.requestId("req-001")
.build());
String responseId = response.getResponseId();
String modelFingerprint = response.getModelFingerprint();
Duration duration = response.getDuration();
getRequestId() 保留为兼容字段,优先返回模型服务响应 ID;如果需要区分调用方 request id 与模型服务 response id,请使用 response.getMetadata()。
Provider 扩展
默认 provider 为 openai-compatible。如果业务项目需要接入非兼容协议,可以实现 AiClientProvider,不需要把具体厂商 SDK 放进 Under-Utils 默认依赖面:
AiClientProvider provider = new AiClientProvider() {
@Override
public String provider() {
return "native-vendor";
}
@Override
public AiClient create(AiClientOptions options) {
return new NativeVendorAiClient(options);
}
};
AiClient client = AiClient.builder()
.baseUrl("https://api.example.com")
.model("vendor-model")
.provider(provider)
.build();
错误语义
模型调用失败统一抛出 AiException:
| 分类 | 触发场景 |
|---|---|
AUTHENTICATION |
HTTP 401/403。 |
RATE_LIMIT |
HTTP 429。 |
SERVER_ERROR |
HTTP 5xx。 |
CLIENT_ERROR |
其他 HTTP 4xx。 |
TIMEOUT |
连接、读取或写入超时。 |
NETWORK |
网络连接失败。 |
RESPONSE_PARSE |
成功响应无法解析,或缺少 assistant 文本。 |
AiException 暴露 statusCode、errorCode 和 retryable,调用方可以据此决定重试或降级。异常信息不会包含 API key、Authorization header 或完整请求体。
安全边界
AiClientOptions.toString()会隐藏 API key,只显示是否已配置。ChatRequest.toString()不输出完整 prompt,只输出消息数量和参数摘要。ChatResponse.toString()不输出完整模型回复,只输出文本长度和元数据。- 默认测试使用
MockWebServer,不会访问外网。
当前限制
- 流式响应只覆盖 OpenAI-compatible SSE,不封装 WebSocket 或厂商私有流式协议。
- 不封装工具调用、函数调用、Agent 工作流和多步骤推理。
- 默认不引入具体厂商 SDK;厂商原生协议应通过业务侧
AiClientProvider扩展。 - Spring Boot 自动装配已放入独立
under-utils-ai-starter,不会被under-utils-starter聚合引入。