侧边栏壁纸
博主头像
牧云

怀璧慎显,博识谨言。

  • 累计撰写 146 篇文章
  • 累计创建 13 个标签
  • 累计收到 8 条评论

目 录CONTENT

文章目录

上下文工程(Context Engineering)

秋之牧云
2026-04-07 / 0 评论 / 0 点赞 / 0 阅读 / 0 字

一、定位与设计哲学:底层引擎 vs 业务驾驶舱

在 Spring AI 的架构中,与大语言模型(LLM)交互的 API 被清晰地划分为两层:

层级

接口

设计定位

类比

底层

ChatModel

提供与 AI 提供商(OpenAI、Anthropic、Ollama、阿里云等)通信的基础能力,处理 Prompt 封装、HTTP 调用、原始响应解析。

类似 JDBC 的 Connection / Spring RestClient 基础层

高层

ChatClient

基于 ChatModel 构建的 Fluent Builder API,面向“对话型应用”提供上下文管理、函数调用、流式处理、重试/降级、提示词模板等企业级特性。

类似 Spring WebClient + Resilience4j + 会话管理的组合封装

二者不是替代关系,而是分层协作关系ChatClient 内部持有 ChatModel 实例,通过 Advisor 机制横向增强能力。


二、ChatModel:直面大模型的“基础通道”

2.1 核心职责

  • 接收 Prompt(包含 UserMessageSystemMessage 等)

  • 调用底层 LLM API,返回 ChatResponseFlux<ChatResponse>

  • 处理 Provider 特有的参数映射(如 temperaturetop_pmodel 等)

2.2 典型用法

@Service
public class SimpleChatService {
    private final ChatModel chatModel; // 自动注入具体实现,如 OpenAiChatModel

    public SimpleChatService(ChatModel chatModel) {
        this.chatModel = chatModel;
    }

    public String ask(String question) {
        Prompt prompt = new Prompt(
            List.of(new SystemMessage("你是一个专业的Java助手"),
                    new UserMessage(question))
        );
        ChatResponse response = chatModel.call(prompt);
        return response.getResult().getOutput().getContent();
    }
}

2.3 适用场景与局限

适合

  • 简单单次问答、批量非对话式任务

  • 自研 AI 网关/代理层,需完全控制请求生命周期

  • 对内存/性能有极致要求,不想承担额外封装开销

局限

  • 无状态:每次调用独立,需手动拼接历史消息实现“多轮对话”

  • 函数调用(Tool Calling)需手动构造 ToolDefinition 和解析响应

  • 流式响应需手动处理 Flux<ChatResponse> 的 delta 拼接

  • 重试、降级、日志追踪需自行集成 Resilience4j/Micrometer


三、ChatClient:面向对话场景的“高级编排器”

3.1 核心特性

Spring AI 官方推荐的生产级入口,采用链式构建模式,内置现代 LLM 应用所需的核心能力:

能力

说明

🔗 Fluent API

.prompt().system(...).user(...).functions(...).advisors(...).call()/stream()

🧠 会话上下文

自动维护消息历史,支持 ChatMemory 集成

🛠 函数调用

声明式注册 @Tool 方法,自动处理参数提取与结果回填

🌊 流式处理

.stream().content() 直接返回 Flux<String>,无需手动解析 delta

🔄 增强器(Advisors)

内置重试、限流、日志、评估、提示词缓存等横向切面

📦 提示词模板

支持 PromptTemplate 与外部 YAML/JSON 配置

3.2 典型用法

@Service
public class AdvancedChatService {
    private final ChatClient chatClient;

    public AdvancedChatService(ChatClient.Builder builder) {
        this.chatClient = builder
            .defaultAdvisors(new RetryAdvisor(3, Duration.ofSeconds(1)))
            .build();
    }

    public String askWithTools(String question) {
        return chatClient.prompt()
            .system("你是一个具备联网搜索和代码执行能力的助手。")
            .user(question)
            .functions("searchWeb", "executeCode") // 注册 Tool 名称
            .advisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory())) // 自动记忆
            .call()
            .content();
    }
}

3.3 适用场景与优势

适合

  • 聊天机器人、智能客服、Copilot 类应用

  • 需要函数调用、多轮对话、流式输出的生产系统

  • 追求开发效率、代码可读性与可维护性的团队

⚠️ 注意

  • ChatClient.Builder 每次构建会创建新实例,但底层 ChatModel 与线程池是共享的

  • 高级特性(如 AdvisorChatMemory)需额外引入依赖或自行实现接口


四、核心对比:一张表看懂差异

维度

ChatModel

ChatClient

抽象层级

底层/协议层

高层/业务层

编程范式

命令式直接调用

Fluent Builder 链式调用

对话状态

无状态(每次独立)

内置 ChatMemory 支持上下文记忆

函数调用

手动构造 ToolDefinition + 解析

声明式 @Tool + 自动编排

流式响应

返回 Flux<ChatResponse> 需手动拼接

.stream().content() 返回 Flux<String>

重试/降级

需自行集成 Resilience4j

内置 RetryAdvisorCircuitBreakerAdvisor

可观测性

需手动接入 Micrometer

内置 Trace/Metrics 支持

推荐场景

网关、底层封装、极简任务

企业级对话应用、快速迭代项目


五、如何选型?给开发者的决策树

📌 官方立场:Spring AI 团队明确推荐 新应用优先使用 ChatClientChatModel 主要作为 SPI(服务提供者接口)供底层实现使用,或供需要完全控制请求生命周期的场景调用。


六、实战最佳实践

6.1 依赖注入规范

@Configuration
public class AiConfig {
    @Bean
    public ChatClient chatClient(ChatModel chatModel) {
        return ChatClient.builder(chatModel)
            .defaultSystem("你是一个专业、严谨的AI助手。")
            .defaultAdvisors(new RetryAdvisor(2, Duration.ofMillis(500)))
            .build();
    }
}

ChatModelChatClient 均为线程安全,可直接注入为单例。

6.2 流式输出 + SSE 场景

@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamChat(@RequestParam String q) {
    return chatClient.prompt().user(q).stream().content()
        .map(chunk -> ServerSentEvent.builder(chunk).build());
}

6.3 函数调用正确姿势

@Tool(description = "查询当前天气")
public String getWeather(@Param("city") String city) {
    return "晴天,25°C";
}

// 注册 Tool
ChatClient client = ChatClient.builder(chatModel)
    .defaultFunctions("getWeather") // 或传入 bean 名称
    .build();

6.4 性能与内存提醒

  • 避免在每次请求中 ChatClient.builder(...).build(),应在初始化时构建并复用

  • InMemoryChatMemory 仅适用于开发/测试,生产请替换为 Redis/DB 实现

  • 流式响应注意背压(Backpressure)控制,避免 OOM


七、结语

ChatModelChatClient 的关系,正如 发动机与整车控制系统:前者提供原始动力,后者负责换挡、导航、安全辅助与用户体验。在 Spring AI 的设计哲学中,“简单场景不复杂,复杂场景可扩展” 是核心原则。

  • 🚀 起步/业务开发:直接拥抱 ChatClient,享受声明式 API 与内置企业级能力

  • 🔧 底层定制/网关开发:下沉到 ChatModel,掌握原始控制力

  • 📦 架构演进:二者可随时切换,ChatClientAdvisor 机制让增强能力插件化

随着 Spring AI 1.0 进入 GA 阶段,ChatClient 的 API 已趋于稳定。建议在新项目中以它为默认入口,将 ChatModel 留给需要深度定制的边界场景。


📖 延伸阅读

💬 欢迎在评论区分享你的实际使用场景与踩坑经验,我们一起构建更健壮的 AI 应用架构。

0

评论区