chat - 支持哪些模型?及方言定制
1、支持哪些聊天模型?
支持聊天模型,其实是支持“接口风格”。比如 DeepSeek 官网的就支持“三种”接口: openai、openai-response、anthropic;同样是 DeepSeek 在 ollama 平台是“另一种”接口风格;在阿里百炼则有“两种”接口风格,一种兼容 openai,另一种则是百炼专属风格;在模力方舟(ai.gitee)则是兼容 openai。
聊天模型的这种“接口风格”,称为聊天方言(简称,方言)。ChatConfig 通过 standard 或 apiUrl识别模型服务是用哪种“接口风格”的。并自动选择对应的聊天方言适配。
目前服务平台有常见的两种配置信息:
- 方言类型 + bsaeUrl
- 例:
base_url (OpenAI)=https://api.deepseek.com - 对应过来是:
standard=openai, apiUrl=https://api.deepseek.com
- 例:
- fullUrl(curl 完整地址)
- 例:
curl https://api.deepseek.com/v1/chat/completions - 对应过来是:
apiUrl=https://api.deepseek.com/v1/chat/completions(通过 endsWit 自动识别)
- 例:
框架内置的方言适配有:
| 言方 | 配置要求 | 描述 |
|---|---|---|
| openai | 默认 或者 apiUrl=.../chat/completions | 兼容 openai 的接口规范(默认) |
| openai-responses | standard=openai-responses 或者 apiUrl=.../v1/responses | 兼容 openai-responses 的接口规范 |
| ollama | standard=ollama | 兼容 ollama 的接口规范 |
| gemini | standard=gemini 或者 apiUrl=.../v1beta/models/* | 兼容 google gemini 的接口规范(v3.8.1 后可试用) |
| gemini | standard=gemini-interactions 或者 apiUrl=.../v1beta/interactions/* | 兼容 google gemini-interactions 的接口规范(v4.0.3 后可试用) |
| anthropic | standard=anthropic 或者 apiUrl=.../v1/messages | 兼容 anthropic claude 的接口规范(v3.9.1 后可试用)。 claude 还有个 openai 的兼容模式(使用 openai 方言) |
| dashscope | standard=dashscope 或者 apiUrl=.../v1/services/* | 兼容 dashscope (阿里云的平台百炼)的接口规范。 dashscope 还有个 openai 的兼容模式(使用 openai 方言) |
重要提醒:
- solon-ai 支持
apiUrl为完整地址(curl 地址)形式,自动通过尾段识别方言。 - solon-ai 支持
apiUrl为基地址(baseUrl)形式 +standard(声明接口规范),识别方言并“自动补全”地址。 - 部分内网部署的地址,可能不需要自动补全,请以 "
#"结尾(表示完整地址,不需要自动补全)。
那支持哪些聊天模型?
- 所有兼容 openai 的模型或平台服务(比如:"DeepSeek"、"QWen"、"GLM"、"Kimi"、"MiniMax"、“Claude(openai 兼容模式)”、"Gemini(openai 兼容模式)"、"DashScope(openai 兼容模式)"、"GPT"、“模力方舟”、“硅基流动”、“魔搭社区(魔力空间)”、“Xinference”、“火山引擎”、“智谱”、“讯飞星火”、“百度千帆”、“阿里百炼”、"MiniMax" 等),都兼容
- 所有兼容 anthropic 的模型或平台服务,都兼容
- 所有 ollama 平台上的模型,都兼容
- 所有 gemini 相关模型,都兼容
- 所有 阿里百炼 平台上的模型(同时提供有 “百炼” 和 “openai” 两套接口),都兼容
构建示例:
ChatModel chatModel = ChatModel.of("http://127.0.0.1:11434/api/chat") //使用完整地址(而不是 api_base)
.headerSet("x-demo", "demo1")
.standard("ollama")
.model("llama3.2")
.build();
2、自带的方言依赖包
| 方言依赖包 | 描述 |
|---|---|
| org.noear:solon-ai | 包含 solon-ai-core 和下面所有的方言包。一般引用这个 |
| org.noear:solon-ai-dialect-openai | 兼容 openai 的方言包 |
| org.noear:solon-ai-dialect-ollama | 兼容 ollama 的方言包 |
| org.noear:solon-ai-dialect-dashscope | 兼容 dashscope 的方言包 |
| org.noear:solon-ai-dialect-gemini | 兼容 gemini 的方言包 |
提醒:一般匹配不到方言时?要么是 standard 配置有问题,要么是 pom 缺少相关的依赖包。
3、聊天方言接口定义
当前版本方言解析的唯一入口是 parseResponseJson(ChatStreamContext, String);正文、思考和工具调用等内容主干应写入累积器,由核心统一生成事件。以下接口代码与当前 ChatDialect.java 一致:
package org.noear.solon.ai.chat.dialect;
import org.noear.snack4.ONode;
import org.noear.solon.ai.AiModelDialect;
import org.noear.solon.ai.chat.ChatConfig;
import org.noear.solon.ai.chat.ChatAccumulator;
import org.noear.solon.ai.chat.event.ChatStreamContext;
import org.noear.solon.ai.chat.message.ToolMessage;
import org.noear.solon.ai.chat.tool.ToolCallBuilder;
import org.noear.solon.ai.chat.message.AssistantMessage;
import org.noear.solon.ai.chat.message.ChatMessage;
import org.noear.solon.ai.chat.ChatOptions;
import org.noear.solon.lang.Preview;
import org.noear.solon.net.http.HttpUtils;
import java.util.List;
import java.util.Map;
@Preview("3.1")
public interface ChatDialect extends AiModelDialect {
/**
* 是否为默认
*/
default boolean isDefault() {
return false;
}
/**
* 匹配检测
*
* @param config 聊天配置
*/
boolean matched(ChatConfig config);
/**
* 创建 http 工具
*
* @param config 聊天配置
* @param isStream 是否流式获取
*/
HttpUtils createHttpUtils(ChatConfig config, boolean isStream);
/**
* 准备输出架构
*/
void prepareOutputSchemaInstruction(String outputSchema, StringBuilder instructionBuilder);
void prepareOutputFormatOptions(ChatOptions options);
/**
* 构建请求数据
*
* @param config 聊天配置
* @param options 聊天选项
* @param messages 消息
* @param isStream 是否流式获取
*/
ONode buildRequestJson(ChatConfig config, ChatOptions options, List<ChatMessage> messages, boolean isStream);
/**
* 构建助理消息节点
*
* @param toolCallBuilders 工具调用构建器集合
*/
ONode buildAssistantToolCallMessageNode(ChatAccumulator acc, Map<String, ToolCallBuilder> toolCallBuilders);
/**
* 构建助理消息根据直接返回的工具消息
*
* @param toolMessages 直接返回的工具消息
*/
AssistantMessage buildAssistantMessageByToolMessages(AssistantMessage toolCallMessage, List<ToolMessage> toolMessages);
/**
* 分析响应数据(事件形态)
*
* @param ctx 流上下文
* @param respJson 响应数据
*/
void parseResponseJson(ChatStreamContext ctx, String respJson);
/**
* 分析工具调用
*
* @param acc 响应累积器
* @param oMessage 消息节点
*/
List<AssistantMessage> parseAssistantMessage(ChatAccumulator acc, ONode oMessage);
}
4、OllamaChatDialect 定制参考
如果方言有组件注解,会自动注册。否则,需要手动注册:
ChatDialectManager.register(new OllamaChatDialect());
以下是迁移到当前 ChatStreamContext / ChatAccumulator API 的示意代码(不是完整的方言实现);不要将旧版的 boolean parseResponseJson(...)、ChatResponseDefault 或旧的 parseAssistantMessage(...) 签名混用:
package org.noear.solon.ai.llm.dialect.ollama;
import org.noear.snack4.ONode;
import org.noear.solon.ai.chat.ChatException;
import org.noear.solon.ai.chat.event.ChatStreamContext;
import org.noear.solon.ai.chat.message.AssistantMessage;
import org.noear.solon.ai.chat.dialect.AbstractChatDialect;
/**
* Ollama 聊天模型方言(示意)
*/
public class OllamaChatDialect extends AbstractChatDialect {
@Override
public void parseResponseJson(ChatStreamContext ctx, String json) {
ONode oResp = ONode.ofJson(json);
if (!oResp.isObject()) {
return;
}
ONode message = oResp.get("message");
if (message != null && message.isObject()) {
ONode contentNode = message.get("content");
String content = contentNode == null ? null : contentNode.getString();
if (content != null && !content.isEmpty()) {
ctx.getAccumulator().addContentItem(new AssistantMessage(content));
}
}
if (oResp.hasKey("error")) {
ctx.getAccumulator().setError(new ChatException(oResp.get("error").getString()));
}
}
}
方言不要同时把同一份正文、思考或工具调用既写入内容项又通过 ctx.emit(...) 发射。内容主干应选择内容项这一条路径,否则订阅方可能收到重复增量,终态聚合也会重复。核心会将内容项转换为 TEXT_DELTA、THINKING_DELTA 或工具调用事件,并统一补齐边界。