跳到主要内容

ChatClient 对话客户端

概述

ChatClient 是模型协议的传输与响应处理层。BaseChatModel 先完成 Prompt、Options、拦截器和请求 Body 构建,再把最终 Body 交给 Client:

text
ChatModel -> ChatInterceptor -> RequestSpecBuilder
          -> ChatClient -> HTTP/SSE -> AiMessageParser

普通业务只调用 ChatModel,不需要直接创建 Client。只有接入新协议、替换网络实现或编写模型适配器时才使用本篇 API。

适用场景

  • 服务端不是 OpenAI 兼容协议,需要自定义传输与错误解析。
  • 需要使用公司统一的 HTTP Client、代理、证书和网络观测。
  • 流式协议是 NDJSON、WebSocket,而不是 SSE。
  • 响应 JSON 结构不同,需要注入自定义 AiMessageParser

只需修改 URL、Header 或 Body 时,优先扩展 ChatRequestSpecBuilder 或使用 ChatInterceptor

快速开始

OpenAI 兼容模型已经由 OpenAIChatClient 处理,通常只需通过 ChatModel 调用:

java
OpenAIChatModel model = new OpenAIChatModel(config);
AiMessageResponse response = model.chat(new SimplePrompt("你好"));

要替换同步网络实现,可在模型创建后注入:

java
OpenAIChatClient client = new OpenAIChatClient(model);
client.setHttpClient(new AgentsFlexHttpClient(customOkHttpClient));
model.setChatClient(client);

核心接口

java
public abstract class ChatClient {
    public abstract AiMessageResponse chat(String body);
    public abstract void chatStream(
        String body,
        StreamResponseListener listener
    );
}

Client 不持有请求级 ChatContext。实现类在调用期间通过 ChatContextHolder.currentContext() 读取 URL、Header、重试参数和 Prompt;因此方法必须由 BaseChatModel 的上下文范围内调用。

OpenAIChatClient

同步路径使用 AgentsFlexHttpClient.post(...),解析 JSON 错误或成功响应;流式路径每次调用新建 SseClient,并由 BaseStreamClientListener 聚合增量消息。

  • 空同步响应返回 error AiMessageResponse
  • 非法 JSON 返回 invalid json response 错误响应。
  • 响应含 error 对象时保留 message、type 和 code。
  • 成功时使用 AiMessageParser,并补充本地 Token 估算。

可以注入解析器:

java
OpenAIChatClient client = new OpenAIChatClient(model);
client.setAiMessageParser(customParser);
model.setChatClient(client);

getStreamClient() 当前每次返回新的 SseClient。若要支持其他流协议,应继承 Client 并覆盖该方法或实现完整的 chatStream(...)

自定义 Client

java
public class MyChatClient extends ChatClient {
    public MyChatClient(BaseChatModel<?> model) {
        super(model);
    }

    @Override
    public AiMessageResponse chat(String body) {
        ChatContext context = ChatContextHolder.currentContext();
        ChatRequestSpec spec = context.getRequestSpec();
        String raw = myTransport.post(spec.getUrl(), spec.getHeaders(), body);
        return parse(raw, context);
    }

    @Override
    public void chatStream(String body, StreamResponseListener listener) {
        // 连接流、解析分片,并完整转发 onMessage/onStop/onFailure。
    }
}

自定义实现必须把协议错误转换为一致的错误响应或 Listener 失败事件,并确保网络资源在正常结束、异常和主动停止时都能关闭。

生产建议

  1. Client 实例会被模型复用,字段必须线程安全;请求状态放在 ChatContext 或局部变量中。
  2. 不要在 Client 中再次序列化 Prompt,Body 已由责任链末端构建。
  3. 对同步与流式路径分别测试 2xx、4xx、5xx、空响应、非法 JSON 和中途断流。
  4. 日志中不要输出 Authorization 和未经脱敏的完整 Body。

常见问题

为什么直接调用 client.chat(body) 报上下文错误?

Client 依赖 ChatContextHolder,应由 ChatModel 调用。如果你需要独立 HTTP Client,请直接使用传输层 API。

可以在一个 Client 中保存当前 Context 吗?

不应这样做。模型可能被并发调用,请求级 Context 不能保存在共享字段。

下一步