AIProviderFactory 是 Rhythm 社区平台的 AI 调用工厂类,提供了简洁易用的 API 来调用 OpenAI 兼容的大语言模型服务。
import org.b3log.symphony.ai.AIProviderFactory;
// 检查 AI 是否可用
if (AIProviderFactory.isAvailable()) {
// 同步调用,获取完整响应
String response = AIProviderFactory.chatSync(
"你是一个友好的助手",
"你好,请介绍一下自己"
);
System.out.println(response);
}AIProviderFactory.chatStream(
"你是一个助手",
"写一首关于春天的诗",
token -> System.out.print(token) // 实时打印每个 token
);在 symphony.properties 中配置 AI 相关参数:
#### AI ####
# AI 功能开关
ai.enabled=false
# 默认使用的 Provider: qwen, openai
ai.provider=qwen
# OpenAI 兼容配置 (适用于 OpenAI、Azure OpenAI、以及其他兼容 API)
ai.openai.apiKey=sk-xxxx
ai.openai.baseUrl=https://api.openai.com/v1/chat/completions
ai.openai.model=gpt-4o
ai.openai.maxTokens=2048
ai.openai.stream=true
# 通义千问配置
ai.qwen.apiKey=sk-xxxx
ai.qwen.baseUrl=https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
ai.qwen.model=qwen-plus
ai.qwen.maxTokens=2048
ai.qwen.stream=true
ai.qwen.enableSearch=false| 配置项 | 说明 | 默认值 |
|---|---|---|
ai.enabled |
是否启用 AI 功能 | false |
ai.provider |
默认 Provider 类型 (qwen 或 openai) |
qwen |
ai.*.apiKey |
API 密钥 | - |
ai.*.baseUrl |
API 端点 URL | - |
ai.*.model |
模型名称 | - |
ai.*.maxTokens |
最大输出 token 数 | 2048 |
ai.*.stream |
是否启用流式输出 | true |
ai.qwen.enableSearch |
通义千问是否启用联网搜索 | false |
只需修改 baseUrl 即可接入其他 OpenAI 兼容服务:
# DeepSeek
ai.openai.baseUrl=https://api.deepseek.com/v1/chat/completions
ai.openai.model=deepseek-chat
# 本地 Ollama
ai.openai.baseUrl=http://localhost:11434/v1/chat/completions
ai.openai.model=llama2
# Azure OpenAI
ai.openai.baseUrl=https://your-resource.openai.azure.com/openai/deployments/your-deployment/chat/completions?api-version=2024-02-01// 检查 AI 功能是否可用(已启用且 API Key 已配置)
boolean available = AIProviderFactory.isAvailable();
// 获取默认 Provider 类型
ProviderType type = AIProviderFactory.getDefaultProviderType();
// 直接访问配置
boolean enabled = AIProviderFactory.AI_ENABLED;
String provider = AIProviderFactory.DEFAULT_PROVIDER;最简单的调用方式,阻塞等待完整响应。
// 带系统提示词
String response = AIProviderFactory.chatSync(
"你是一个专业的翻译,将用户输入翻译成英文",
"今天天气真好"
);
// 输出: "The weather is really nice today."实时获取模型输出,适合需要即时反馈的场景。
@FunctionalInterface
public interface StreamHandler {
/**
* @param token 当前收到的文本片段
* @param fullContent 目前累积的完整内容
*/
void onToken(String token, String fullContent);
}String result = AIProviderFactory.chatStream(
"你是助手",
"讲个笑话",
(token, fullContent) -> {
System.out.print(token); // 实时输出
updateUI(fullContent); // 更新 UI 显示完整内容
}
);
System.out.println("\n最终结果: " + result);String result = AIProviderFactory.chatStream(
"你是助手",
"你好",
token -> System.out.print(token)
);AIProviderFactory.chatStream(
"你是助手",
"介绍自己",
token -> sendToWebSocket(token), // 每个 token 发送到前端
fullResponse -> saveToDatabase(fullResponse) // 完成后保存到数据库
);import org.b3log.symphony.ai.AIProviderFactory.ProviderType;
String result = AIProviderFactory.chatStream(
ProviderType.OPENAI, // 或 ProviderType.QWEN
"你是助手",
"你好",
(token, full) -> System.out.print(token)
);import org.b3log.symphony.ai.OpenAIProvider.Message;
import org.b3log.symphony.ai.Provider.Content;
var messages = Message.of(
new Message.System("你是一个翻译助手"),
new Message.User(new Content.Text("Hello World")),
new Message.System("请翻译成中文") // 可以有多轮对话
);
String result = AIProviderFactory.chatStream(messages, (token, full) -> {
System.out.print(token);
});返回 Stream<JSONObject>,提供最大灵活性。
import org.json.JSONObject;
import java.util.stream.Stream;
Stream<JSONObject> stream = AIProviderFactory.chat("你是助手", "你好");
stream.forEach(json -> {
// 原始 JSON 响应
System.out.println(json.toString(2));
// 手动解析
var choices = json.optJSONArray("choices");
if (choices != null && choices.length() > 0) {
var delta = choices.getJSONObject(0).optJSONObject("delta");
if (delta != null) {
String content = delta.optString("content", "");
System.out.print(content);
}
}
});Stream<JSONObject> stream = AIProviderFactory.chat("你好,请问你是谁?");完全控制 Provider 创建过程。
import org.b3log.symphony.ai.*;
import org.b3log.symphony.ai.OpenAIProvider.*;
import org.b3log.symphony.ai.Provider.*;
// 构建消息
var messages = Message.of(
new Message.System("你是一个代码助手"),
new Message.User(new Content.Text("写一个快速排序"))
);
// 创建 Provider(使用默认配置)
OpenAIProvider provider = AIProviderFactory.createProvider(messages);
// 或创建指定类型的 Provider
OpenAIProvider qwenProvider = AIProviderFactory.createQwenProvider(messages);
OpenAIProvider openaiProvider = AIProviderFactory.createOpenAIProvider(messages);
// 发送请求
Stream<JSONObject> response = AIProviderFactory.send(provider);import org.b3log.symphony.ai.Provider.*;
// 构建包含图片的消息
var content = new Content.Array(List.of(
new ContentType.Text("这张图片是什么?"),
new ContentType.Image("data:image/png;base64,iVBORw0KGgo...", "image/png")
));
var messages = Message.of(
new Message.User(content)
);
var provider = AIProviderFactory.createProvider(messages);
AIProviderFactory.send(provider).forEach(json -> {
System.out.print(extractContent(json));
});@OnMessage
public void onMessage(Session session, String userMessage) {
Symphonys.EXECUTOR_SERVICE.submit(() -> {
try {
AIProviderFactory.chatStream(
"你是社区助手",
userMessage,
token -> {
try {
session.getBasicRemote().sendText(token);
} catch (IOException e) {
e.printStackTrace();
}
},
fullResponse -> {
try {
session.getBasicRemote().sendText("[DONE]");
} catch (IOException e) {
e.printStackTrace();
}
}
);
} catch (Exception e) {
e.printStackTrace();
}
});
}@GetMapping(value = "/ai/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chat(@RequestParam String message) {
return Flux.create(sink -> {
try {
AIProviderFactory.chatStream(
"你是助手",
message,
token -> sink.next("data: " + token + "\n\n"),
full -> sink.complete()
);
} catch (Exception e) {
sink.error(e);
}
});
}AIProviderFactory (工厂类,读取配置,提供快捷方法)
│
├── AIClient (HTTP 客户端,处理请求/响应)
│ │
│ ├── SSE (Server-Sent Events 流式响应处理)
│ └── Text (普通文本响应处理)
│
├── OpenAIProvider (OpenAI 兼容格式的 Provider 实现)
│ │
│ ├── Message (消息类型: System, User, Developer)
│ └── Options (配置选项: Stream, MaxTokens, EnableSearch...)
│
├── Provider (Provider 接口)
│ │
│ ├── Content (内容类型: Text, Array)
│ ├── ContentType (具体内容: Text, Image)
│ └── Authorize (认证方式: Token)
│
└── Model (模型接口)
│
├── Model.Supported.Text (支持文本)
└── Model.Supported.Image (支持图像)
| 异常 | 说明 |
|---|---|
ModelNotSupportException |
模型不支持某种内容类型(如发送图片给纯文本模型) |
IOException |
网络请求失败 |
InterruptedException |
请求被中断 |
try {
String response = AIProviderFactory.chatSync("助手", "你好");
System.out.println(response);
} catch (ModelNotSupportException e) {
System.err.println("模型不支持: " + e.toString());
} catch (IOException e) {
System.err.println("网络错误: " + e.getMessage());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("请求被中断");
}if (!AIProviderFactory.isAvailable()) {
System.out.println("AI 功能未启用或未配置 API Key");
return;
}
// 安全调用
String response = AIProviderFactory.chatSync("助手", "你好");| 方法 | 返回值 | 说明 |
|---|---|---|
isAvailable() |
boolean |
检查 AI 是否可用 |
getDefaultProviderType() |
ProviderType |
获取默认 Provider 类型 |
chatSync(sys, user) |
String |
同步对话,返回完整响应 |
chat(sys, user) |
Stream<JSONObject> |
原始流对话 |
chat(user) |
Stream<JSONObject> |
无系统提示词的原始流 |
chat(type, sys, user) |
Stream<JSONObject> |
指定 Provider 的原始流 |
chatStream(sys, user, StreamHandler) |
String |
流式对话,带完整回调 |
chatStream(sys, user, Consumer) |
String |
流式对话,简化回调 |
chatStream(sys, user, onToken, onComplete) |
void |
流式对话,带完成回调 |
chatStream(type, sys, user, StreamHandler) |
String |
指定 Provider 的流式对话 |
chatStream(messages, StreamHandler) |
String |
自定义消息的流式对话 |
createProvider(messages) |
OpenAIProvider |
创建默认 Provider |
createProvider(type, messages) |
OpenAIProvider |
创建指定类型 Provider |
createOpenAIProvider(messages) |
OpenAIProvider |
创建 OpenAI Provider |
createQwenProvider(messages) |
OpenAIProvider |
创建通义千问 Provider |
send(provider) |
Stream<JSONObject> |
发送请求 |
getClient() |
AIClient |
获取 AIClient 实例 |
| 常量 | 类型 | 说明 |
|---|---|---|
AI_ENABLED |
boolean |
AI 是否启用 |
DEFAULT_PROVIDER |
String |
默认 Provider 名称 |
OPENAI_API_KEY |
String |
OpenAI API Key |
OPENAI_BASE_URL |
String |
OpenAI API URL |
OPENAI_MODEL |
String |
OpenAI 模型名 |
OPENAI_MAX_TOKENS |
int |
OpenAI 最大 tokens |
OPENAI_STREAM |
boolean |
OpenAI 是否流式 |
QWEN_API_KEY |
String |
通义千问 API Key |
QWEN_BASE_URL |
String |
通义千问 API URL |
QWEN_MODEL |
String |
通义千问模型名 |
QWEN_MAX_TOKENS |
int |
通义千问最大 tokens |
QWEN_STREAM |
boolean |
通义千问是否流式 |
QWEN_ENABLE_SEARCH |
boolean |
通义千问是否启用搜索 |