Chat同步调用与模板方法
上一篇讲了 executeWithFallback 如何遍历候选列表、逐个尝试、失败后切换到下一个候选。每个候选的实际调用就是一句 client.chat(request, target)。
那这一行代码背后到底发生了什么?一个 ChatRequest 对象是怎么变成一个发往百炼的 HTTP POST 请求的?百炼返回的 JSON 响应又是怎么变成一个 String 返回给业务层的?HTTP 500 错误是怎么触发故障转移的?
这一篇打开 Chat 子系统的黑盒,看看从业务层调用到供应商 HTTP 请求之间的完整链路。
接口分层:LLMService 与 ChatClient
1. 两层接口的设计
项目里和 Chat 相关的接口有两层。第一层是业务层使用的 LLMService:
public interface LLMService {
default String chat(String prompt) {
ChatRequest req = ChatRequest.builder()
.messages(List.of(ChatMessage.user(prompt)))
.build();
return chat(req);
}
String chat(ChatRequest request);
default String chat(ChatRequest request, String modelId) {
return chat(request);
}
default StreamCancellationHandle streamChat(String prompt, StreamCallback callback) {
ChatRequest req = ChatRequest.builder()
.messages(List.of(ChatMessage.user(prompt)))
.build();
return streamChat(req, callback);
}
StreamCancellationHandle streamChat(ChatRequest request, StreamCallback callback);
}
LLMService 面向业务层,接口里看不到任何 infra 概念——没有 ModelTarget,没有 ProviderConfig,没有供应商名称。业务代码只需要传一个 ChatRequest,就能拿到模型的回答。至于背后调的是百炼还是硅基流动,哪个模型先试哪个后试,业务层不需要关心。
第二层是供应商级别的 ChatClient:
public interface ChatClient {
String provider();
String chat(ChatRequest request, ModelTarget target);
StreamCancellationHandle streamChat(ChatRequest request, StreamCallback callback, ModelTarget target);
}
ChatClient 面向供应商层,多了两个东西:provider() 返回供应商标识(比如 "bailian"、"siliconflow"、"ollama"),chat 方法多了一个 ModelTarget 参数——它包含了这次调用需要的所有运行时信息:模型名称、供应商 URL、API Key。
两层之间的桥梁是 RoutingLLMService。它实现了 LLMService 接口,内部使用 ModelSelector + ModelRoutingExecutor + ChatClient 完成路由和调用:
@Override
public String chat(ChatRequest request) {
return executor.executeWithFallback(
ModelCapability.CHAT,
selector.selectChatCandidates(Boolean.TRUE.equals(request.getThinking())),
target -> clientsByProvider.get(target.candidate().getProvider()),
(client, target) -> client.chat(request, target)
);
}
业务层调 LLMService.chat(request),RoutingLLMService 通过 executeWithFallback 遍历候选列表,对每个候选查找对应的 ChatClient 实例,然后调 client.chat(request, target)。业务层完全不知道 ChatClient 的存在。